From 456af04b07c739d3aacc8a50ed15a45aab2b0391 Mon Sep 17 00:00:00 2001 From: hyeonK <157864988+okmac03@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:47:14 +0900 Subject: [PATCH 01/83] =?UTF-8?q?feat:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=86=A0=ED=83=80=EC=9E=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/PrimerFlow-FE.iml | 4 +- app/globals.css | 18 +- app/layout.tsx | 21 +- app/page.tsx | 886 +++++++++++++++++++++++++++++++++------- package-lock.json | 24 +- package.json | 3 +- 6 files changed, 758 insertions(+), 198 deletions(-) diff --git a/.idea/PrimerFlow-FE.iml b/.idea/PrimerFlow-FE.iml index c956989..17fb735 100644 --- a/.idea/PrimerFlow-FE.iml +++ b/.idea/PrimerFlow-FE.iml @@ -1,8 +1,6 @@ - + - - \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index a2dc41e..85850bc 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,28 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --background: #060b16; + --foreground: #e2e8f0; + --muted: #94a3b8; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --font-sans: var(--font-body); + --font-mono: var(--font-mono); } -@media (prefers-color-scheme: dark) { +@media (prefers-color-scheme: light) { :root { - --background: #0a0a0a; - --foreground: #ededed; + --background: #f6f7fb; + --foreground: #0f172a; + --muted: #475569; } } body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-body), system-ui, -apple-system, sans-serif; } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..0ca5c62 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,25 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { JetBrains_Mono, Noto_Sans, Space_Grotesk } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +const display = Space_Grotesk({ + variable: "--font-display", subsets: ["latin"], }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const body = Noto_Sans({ + variable: "--font-body", + subsets: ["latin"], +}); + +const mono = JetBrains_Mono({ + variable: "--font-mono", subsets: ["latin"], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "PrimerFlow - Primer Design Workbench", + description: "Dark-mode playground for primer visualization and tuning.", }; export default function RootLayout({ @@ -25,7 +30,7 @@ export default function RootLayout({ return ( {children} diff --git a/app/page.tsx b/app/page.tsx index 8cf411f..562c3f9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,6 @@ "use client"; +import { useState } from "react"; import GenomeCanvas, { type GenomeData } from "@/components/canvas/GenomeCanvas"; import { createBpScale } from "@/lib/math/coords"; // 만약 이 파일이 없다면 아래 주석 참고 import { useViewStore } from "@/store/useViewStore"; @@ -65,177 +66,744 @@ export default function Home() { ], }; + const steps = [ + { id: 1, label: "Template & Essential" }, + { id: 2, label: "Primer Properties" }, + { id: 3, label: "Binding Location" }, + { id: 4, label: "Specificity & Preview" }, + ] as const; + + const [step, setStep] = useState<1 | 2 | 3 | 4>(1); + const totalSteps = steps.length; + const handleStepChange = (next: number) => { + const clamped = Math.min(Math.max(next, 1), totalSteps) as 1 | 2 | 3 | 4; + setStep(clamped); + }; + const handleNext = () => handleStepChange(step + 1); + const handleBack = () => handleStepChange(step - 1); + const isLastStep = step === totalSteps; + + const trackCount = genome.tracks.length; + const featureCount = genome.tracks.reduce( + (count, track) => count + track.features.length, + 0, + ); + return ( -
-
-
-

Genome Canvas Preview

-

- 더미 데이터를 이용해 GenomeCanvas 렌더링을 확인합니다. -

+
+
+
+
+
+
+ +
+
+
+
+
+ PF +
+
+ + Primerflow Lab + +

+ Primer Design Input +

+

+ demodesign 흐름을 따라 입력 -> 특성 -> 위치 -> 특이성/미리보기 순으로 진행합니다. +

+
+
+
+ + {genome.length.toLocaleString()} bp genome + + + {trackCount} tracks + + + {featureCount} features + +
+
+ +
+
+
+ {steps.map((item) => { + const status = + item.id === step ? "active" : item.id < step ? "done" : "upcoming"; + const isUnlocked = item.id <= step; + const circle = + status === "active" + ? "bg-blue-600 border-blue-400 text-white shadow-lg shadow-blue-900/40" + : status === "done" + ? "bg-blue-500 border-blue-500 text-white" + : "bg-slate-900 border-slate-800 text-slate-500"; + return ( +
+ + + {item.label} + +
+ ); + })} +
+
-
- {/* 컨트롤 버튼 영역 */} -
-
- Zoom: {viewState.scale.toFixed(2)}x + {step === 1 && ( +
+
+
+
+ * +

PCR Template Sequence

+
+ + FASTA / Raw + +
+
+