diff --git a/apps/desktop/components.json b/apps/desktop/components.json
new file mode 100644
index 0000000000..73dfc91aca
--- /dev/null
+++ b/apps/desktop/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "src/styles/global.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "src/components",
+ "utils": "src/lib/utils",
+ "ui": "src/components/ui",
+ "lib": "src/lib",
+ "hooks": "src/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 66543c8371..a0a61fc244 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -21,17 +21,20 @@
"@radix-ui/react-select": "^2.1.3",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2",
+ "@remixicon/react": "^4.5.0",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.0.3",
"@tauri-apps/plugin-log": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.0.0",
"@tiptap/extension-highlight": "^2.10.3",
+ "@tiptap/extension-placeholder": "^2.10.3",
"@tiptap/extension-typography": "^2.10.3",
"@tiptap/pm": "^2.10.3",
"@tiptap/react": "^2.10.3",
"@tiptap/starter-kit": "^2.10.3",
- "clsx": "^2.0.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"cmdk": "^1.0.4",
"date-fns": "^4.1.0",
"embla-carousel": "^8.5.1",
@@ -41,7 +44,8 @@
"react-dom": "^18.2.0",
"react-resizable-panels": "^2.1.7",
"react-router": "^7.0.2",
- "tailwind-merge": "^2.1.0",
+ "tailwind-merge": "^2.5.5",
+ "tailwindcss-animate": "^1.0.7",
"zustand": "^5.0.2"
},
"devDependencies": {
diff --git a/apps/desktop/public/bgm.mp3 b/apps/desktop/public/bgm.mp3
new file mode 100644
index 0000000000..dd84562515
Binary files /dev/null and b/apps/desktop/public/bgm.mp3 differ
diff --git a/apps/desktop/public/tauri.svg b/apps/desktop/public/tauri.svg
deleted file mode 100644
index 31b62c9280..0000000000
--- a/apps/desktop/public/tauri.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/apps/desktop/public/vite.svg b/apps/desktop/public/vite.svg
deleted file mode 100644
index e7b8dfb1b2..0000000000
--- a/apps/desktop/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/icons/128.png b/apps/desktop/src-tauri/icons/128.png
index 499e874cad..f91c085707 100644
Binary files a/apps/desktop/src-tauri/icons/128.png and b/apps/desktop/src-tauri/icons/128.png differ
diff --git a/apps/desktop/src-tauri/icons/256.png b/apps/desktop/src-tauri/icons/256.png
index 9b5c905881..37788cd039 100644
Binary files a/apps/desktop/src-tauri/icons/256.png and b/apps/desktop/src-tauri/icons/256.png differ
diff --git a/apps/desktop/src-tauri/icons/32.png b/apps/desktop/src-tauri/icons/32.png
index 3a03921746..daf6bc1566 100644
Binary files a/apps/desktop/src-tauri/icons/32.png and b/apps/desktop/src-tauri/icons/32.png differ
diff --git a/apps/desktop/src-tauri/icons/Square107x107Logo.png b/apps/desktop/src-tauri/icons/Square107x107Logo.png
index f91daf3328..c6fdabd883 100644
Binary files a/apps/desktop/src-tauri/icons/Square107x107Logo.png and b/apps/desktop/src-tauri/icons/Square107x107Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square142x142Logo.png b/apps/desktop/src-tauri/icons/Square142x142Logo.png
index 4d8cbd01a8..46a8ad8e29 100644
Binary files a/apps/desktop/src-tauri/icons/Square142x142Logo.png and b/apps/desktop/src-tauri/icons/Square142x142Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square150x150Logo.png b/apps/desktop/src-tauri/icons/Square150x150Logo.png
index 1803bc7aef..3662c9056c 100644
Binary files a/apps/desktop/src-tauri/icons/Square150x150Logo.png and b/apps/desktop/src-tauri/icons/Square150x150Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square284x284Logo.png b/apps/desktop/src-tauri/icons/Square284x284Logo.png
index 741029ec41..72ff79c7e4 100644
Binary files a/apps/desktop/src-tauri/icons/Square284x284Logo.png and b/apps/desktop/src-tauri/icons/Square284x284Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square30x30Logo.png b/apps/desktop/src-tauri/icons/Square30x30Logo.png
index 066de71dba..c92986bb3f 100644
Binary files a/apps/desktop/src-tauri/icons/Square30x30Logo.png and b/apps/desktop/src-tauri/icons/Square30x30Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square310x310Logo.png b/apps/desktop/src-tauri/icons/Square310x310Logo.png
index f58082cb3c..f5b5a4b62d 100644
Binary files a/apps/desktop/src-tauri/icons/Square310x310Logo.png and b/apps/desktop/src-tauri/icons/Square310x310Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square44x44Logo.png b/apps/desktop/src-tauri/icons/Square44x44Logo.png
index 52d6783139..7dfa589a21 100644
Binary files a/apps/desktop/src-tauri/icons/Square44x44Logo.png and b/apps/desktop/src-tauri/icons/Square44x44Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square71x71Logo.png b/apps/desktop/src-tauri/icons/Square71x71Logo.png
index 77853eb955..aedeeb0891 100644
Binary files a/apps/desktop/src-tauri/icons/Square71x71Logo.png and b/apps/desktop/src-tauri/icons/Square71x71Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square89x89Logo.png b/apps/desktop/src-tauri/icons/Square89x89Logo.png
index 5934049418..9ef1211d00 100644
Binary files a/apps/desktop/src-tauri/icons/Square89x89Logo.png and b/apps/desktop/src-tauri/icons/Square89x89Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/StoreLogo.png b/apps/desktop/src-tauri/icons/StoreLogo.png
index 69b14f718c..3e320c7481 100644
Binary files a/apps/desktop/src-tauri/icons/StoreLogo.png and b/apps/desktop/src-tauri/icons/StoreLogo.png differ
diff --git a/apps/desktop/src-tauri/icons/icon.icns b/apps/desktop/src-tauri/icons/icon.icns
index 1b047e8034..59ffeb81cc 100644
Binary files a/apps/desktop/src-tauri/icons/icon.icns and b/apps/desktop/src-tauri/icons/icon.icns differ
diff --git a/apps/desktop/src-tauri/icons/icon.ico b/apps/desktop/src-tauri/icons/icon.ico
index e02c4a3837..68197c7f3a 100644
Binary files a/apps/desktop/src-tauri/icons/icon.ico and b/apps/desktop/src-tauri/icons/icon.ico differ
diff --git a/apps/desktop/src-tauri/icons/icon.png b/apps/desktop/src-tauri/icons/icon.png
index e8a64945ad..68197c7f3a 100644
Binary files a/apps/desktop/src-tauri/icons/icon.png and b/apps/desktop/src-tauri/icons/icon.png differ
diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx
index 28d934c3f4..3788e620fe 100644
--- a/apps/desktop/src/App.tsx
+++ b/apps/desktop/src/App.tsx
@@ -1,11 +1,20 @@
import { useEffect } from "react";
-import { BrowserRouter, Routes, Route } from "react-router";
-
+import { BrowserRouter, Routes, Route, Navigate } from "react-router";
import { UIProvider } from "./contexts/UIContext";
-
import NavBar from "./components/layout/NavBar";
import Home from "./pages/Home";
import Note from "./pages/Note";
+import Login from "./pages/Login";
+
+interface ProtectedRouteProps {
+ children: React.ReactNode;
+}
+
+const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
+ // Replace this with your actual authentication check
+ const isAuthenticated = localStorage.getItem("isAuthenticated") === "true";
+ return isAuthenticated ? <>{children}> : ;
+};
function App() {
useEffect(() => {
@@ -28,13 +37,35 @@ function App() {
-
-
-
- } />
- } />
-
-
+
+ } />
+
+ <>
+
+
+
+
+ >
+
+ }
+ />
+
+ <>
+
+
+
+
+ >
+
+ }
+ />
+
diff --git a/apps/desktop/src/components/home/NewUserBanner.tsx b/apps/desktop/src/components/home/NewUserBanner.tsx
index e48b0a0e3c..fba1c67f59 100644
--- a/apps/desktop/src/components/home/NewUserBanner.tsx
+++ b/apps/desktop/src/components/home/NewUserBanner.tsx
@@ -1,4 +1,4 @@
-import { ArrowRight } from "lucide-react";
+import { RiArrowRightLine } from "@remixicon/react";
interface NewUserBannerProps {
onDemoClick: () => void;
@@ -14,7 +14,7 @@ export const NewUserBanner = ({ onDemoClick }: NewUserBannerProps) => {
className="flex items-center gap-2 rounded-full bg-white px-4 py-2 text-blue-600 transition-colors hover:bg-blue-50"
>
데모 체험
-
+
diff --git a/apps/desktop/src/components/home/UpcomingEvents.tsx b/apps/desktop/src/components/home/UpcomingEvents.tsx
index 7b62c7e962..e90a985829 100644
--- a/apps/desktop/src/components/home/UpcomingEvents.tsx
+++ b/apps/desktop/src/components/home/UpcomingEvents.tsx
@@ -1,7 +1,7 @@
import useEmblaCarousel from "embla-carousel-react";
import { Note } from "../../types";
import { EventCard } from "./EventCard";
-import { ChevronLeft, ChevronRight } from "lucide-react";
+import { RiArrowLeftSLine, RiArrowRightSLine } from "@remixicon/react";
import { useCallback } from "react";
interface UpcomingEventsProps {
@@ -50,13 +50,13 @@ export const UpcomingEvents = ({
onClick={scrollPrev}
className="absolute -left-4 top-1/2 -translate-y-1/2 rounded-full bg-white p-2 shadow-lg hover:bg-gray-50"
>
-
+
>
)}
diff --git a/apps/desktop/src/components/layout/ExportMenu.tsx b/apps/desktop/src/components/layout/ExportMenu.tsx
index 0db6ac5593..86467c818e 100644
--- a/apps/desktop/src/components/layout/ExportMenu.tsx
+++ b/apps/desktop/src/components/layout/ExportMenu.tsx
@@ -1,5 +1,10 @@
import { useRef, useState } from "react";
-import { Share, Copy, File, FileText } from "lucide-react";
+import {
+ RiShareLine,
+ RiFileCopyLine,
+ RiFileTextLine,
+ RiFileTextFill,
+} from "@remixicon/react";
import { useClickOutside } from "../../hooks/useClickOutside";
@@ -20,7 +25,7 @@ export default function ExportMenu() {
className="rounded-md p-2 text-gray-700 hover:bg-gray-100"
aria-label={isOpen ? "Close export menu" : "Open export menu"}
>
-
+
{isOpen && (
@@ -32,7 +37,7 @@ export default function ExportMenu() {
}}
className="flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100"
>
-
+
클립보드에 복사
diff --git a/apps/desktop/src/components/layout/NavBar.tsx b/apps/desktop/src/components/layout/NavBar.tsx
index e81c053491..a40b42efba 100644
--- a/apps/desktop/src/components/layout/NavBar.tsx
+++ b/apps/desktop/src/components/layout/NavBar.tsx
@@ -1,6 +1,10 @@
import { useCallback } from "react";
import { useNavigate, useLocation } from "react-router";
-import { PanelRightClose, PanelRightOpen, Menu } from "lucide-react";
+import {
+ RiMenuLine,
+ RiSidebarUnfoldLine,
+ RiSidebarFoldLine,
+} from "@remixicon/react";
import SearchModal from "../modals/search/SearchModal";
import SettingsModal from "../modals/settings/SettingsModal";
@@ -51,7 +55,7 @@ export default function NavBar() {
onClick={handleSettingsClick}
className="flex items-center rounded p-2 hover:bg-gray-100"
>
-
+
)}
@@ -70,9 +74,9 @@ export default function NavBar() {
aria-label={isPanelOpen ? "Close panel" : "Open panel"}
>
{isPanelOpen ? (
-
+
) : (
-
+
)}
) : (
diff --git a/apps/desktop/src/components/layout/NavigationButtons.tsx b/apps/desktop/src/components/layout/NavigationButtons.tsx
index 298655725c..e5c251f807 100644
--- a/apps/desktop/src/components/layout/NavigationButtons.tsx
+++ b/apps/desktop/src/components/layout/NavigationButtons.tsx
@@ -1,4 +1,4 @@
-import { Home, ChevronLeft } from "lucide-react";
+import { RiHome2Line, RiArrowLeftSLine } from "@remixicon/react";
interface NavigationButtonsProps {
onHomeClick: () => void;
@@ -12,10 +12,10 @@ export default function NavigationButtons({
return (
<>
>
);
diff --git a/apps/desktop/src/components/modals/settings/SettingsTabs.tsx b/apps/desktop/src/components/modals/settings/SettingsTabs.tsx
index 69995ee0c7..61a8cfbd81 100644
--- a/apps/desktop/src/components/modals/settings/SettingsTabs.tsx
+++ b/apps/desktop/src/components/modals/settings/SettingsTabs.tsx
@@ -1,13 +1,13 @@
import * as Tabs from "@radix-ui/react-tabs";
import {
- Settings,
- MessageSquare,
- CreditCard,
- Calendar,
- Bell,
- UserCircle,
- Cable,
-} from "lucide-react";
+ RiSettings4Line,
+ RiMessage2Line,
+ RiBankCardLine,
+ RiCalendarLine,
+ RiNotification3Line,
+ RiUser3Line,
+ RiPlugLine,
+} from "@remixicon/react";
interface TabItem {
value: string;
@@ -17,13 +17,13 @@ interface TabItem {
export function SettingsTabs() {
const mainTabs: TabItem[] = [
- { value: "profile", label: "프로필", icon: UserCircle },
- { value: "general", label: "일반", icon: Settings },
- { value: "feedback", label: "피드백", icon: MessageSquare },
- { value: "billing", label: "결제", icon: CreditCard },
- { value: "calendar", label: "캘린더", icon: Calendar },
- { value: "notification", label: "알림", icon: Bell },
- { value: "integrations", label: "연동", icon: Cable },
+ { value: "profile", label: "프로필", icon: RiUser3Line },
+ { value: "general", label: "일반", icon: RiSettings4Line },
+ { value: "feedback", label: "피드백", icon: RiMessage2Line },
+ { value: "billing", label: "결제", icon: RiBankCardLine },
+ { value: "calendar", label: "캘린더", icon: RiCalendarLine },
+ { value: "notification", label: "알림", icon: RiNotification3Line },
+ { value: "integrations", label: "연동", icon: RiPlugLine },
];
const TabButton = ({ tab }: { tab: TabItem }) => (
@@ -32,7 +32,7 @@ export function SettingsTabs() {
value={tab.value}
className="flex w-full items-center gap-2 rounded-md px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 focus:outline-none data-[state=active]:bg-white data-[state=active]:text-blue-600"
>
-
+
{tab.label}
);
diff --git a/apps/desktop/src/components/modals/settings/tabs/Integrations.tsx b/apps/desktop/src/components/modals/settings/tabs/Integrations.tsx
index 77acfcfb62..2c04ecc6ec 100644
--- a/apps/desktop/src/components/modals/settings/tabs/Integrations.tsx
+++ b/apps/desktop/src/components/modals/settings/tabs/Integrations.tsx
@@ -1,6 +1,8 @@
-import SlackIcon from "../../../../constants/icons/SlackIcon";
-import NotionIcon from "../../../../constants/icons/NotionIcon";
-import { ExternalLink } from "lucide-react";
+import {
+ RiSlackFill,
+ RiNotionFill,
+ RiExternalLinkLine,
+} from "@remixicon/react";
interface IntegrationCardProps {
title: string;
@@ -28,7 +30,7 @@ function IntegrationCard({
onClick={onClick}
className="inline-flex items-center gap-2 rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
-
+
연결하기
@@ -40,7 +42,7 @@ export function Integrations() {
{
title: "Slack 연동하기",
description: "Slack에서 미팅 알림을 받고 상태를 자동으로 업데이트하세요",
- icon: ,
+ icon: ,
onClick: () => {
/* TODO: Implement Slack connection */
},
@@ -48,8 +50,7 @@ export function Integrations() {
{
title: "Notion 연동하기",
description: "미팅 노트를 Notion에 자동으로 동기화하세요",
- icon: ,
-
+ icon: ,
onClick: () => {
/* TODO: Implement Notion connection */
},
diff --git a/apps/desktop/src/components/modals/settings/tabs/Profile.tsx b/apps/desktop/src/components/modals/settings/tabs/Profile.tsx
index b80d2de788..d223bcf76f 100644
--- a/apps/desktop/src/components/modals/settings/tabs/Profile.tsx
+++ b/apps/desktop/src/components/modals/settings/tabs/Profile.tsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { UserCircle } from "lucide-react";
+import { RiUser3Line } from "@remixicon/react";
export function Profile() {
const [fullName, setFullName] = useState("");
@@ -46,7 +46,7 @@ export function Profile() {
className="h-full w-full object-cover"
/>
) : (
-
+
)}
) : (
녹음 시작
diff --git a/apps/desktop/src/components/note/NoteEditor.tsx b/apps/desktop/src/components/note/NoteEditor.tsx
index 597630fc6d..37850e035c 100644
--- a/apps/desktop/src/components/note/NoteEditor.tsx
+++ b/apps/desktop/src/components/note/NoteEditor.tsx
@@ -1,11 +1,11 @@
import "../../styles/editor.css";
import { useEffect } from "react";
-
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Highlight from "@tiptap/extension-highlight";
import Typography from "@tiptap/extension-typography";
+import Placeholder from "@tiptap/extension-placeholder";
interface NoteEditorProps {
content: string;
@@ -14,7 +14,14 @@ interface NoteEditorProps {
export default function NoteEditor({ content, onChange }: NoteEditorProps) {
const editor = useEditor({
- extensions: [StarterKit, Highlight, Typography],
+ extensions: [
+ StarterKit,
+ Highlight,
+ Typography,
+ Placeholder.configure({
+ placeholder: "Write something...",
+ }),
+ ],
content,
onUpdate: ({ editor }) => {
onChange(editor.getHTML());
diff --git a/apps/desktop/src/components/note/NoteHeader.tsx b/apps/desktop/src/components/note/NoteHeader.tsx
index 34502251c9..e1cffb01fd 100644
--- a/apps/desktop/src/components/note/NoteHeader.tsx
+++ b/apps/desktop/src/components/note/NoteHeader.tsx
@@ -1,4 +1,4 @@
-import { Calendar } from "lucide-react";
+import { RiCalendarLine } from "@remixicon/react";
import type { Note } from "../../types";
import { formatMeetingTime } from "../../utils/time";
import NoteControl from "./NoteControl";
@@ -42,7 +42,7 @@ export default function NoteHeader({
{note?.calendarEvent && (
-
+
{note.calendarEvent.summary}{" "}
{formatMeetingTime(note.calendarEvent.start)} ~{" "}
diff --git a/apps/desktop/src/components/note/SidePanel.tsx b/apps/desktop/src/components/note/SidePanel.tsx
index e142ce781f..0169d908a9 100644
--- a/apps/desktop/src/components/note/SidePanel.tsx
+++ b/apps/desktop/src/components/note/SidePanel.tsx
@@ -1,51 +1,17 @@
-import { useState } from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
-import { sendChatMessage } from "../../api/noteApi";
+import type { TranscriptBlock } from "../../types";
+import TranscriptPanel from "./TranscriptPanel";
+import ChatPanel from "./ChatPanel";
interface SidePanelProps {
transcript: string;
+ timestamps?: TranscriptBlock[];
}
-export default function SidePanel({ transcript }: SidePanelProps) {
- const [message, setMessage] = useState("");
- const [messages, setMessages] = useState<
- Array<{ text: string; isUser: boolean }>
- >([]);
-
- const quickActions = [
- { label: "질의응답 사항 정리", action: "SUMMARIZE_QA" },
- { label: "액션 아이템 나열", action: "LIST_ACTIONS" },
- { label: "회의록 요약", action: "SUMMARIZE_MEETING" },
- { label: "팔로우업 이메일 작성", action: "WRITE_EMAIL" },
- { label: "다음 회의 안건 제안", action: "SUGGEST_AGENDA" },
- ];
-
- const handleSubmit = async () => {
- if (!message.trim()) return;
-
- // 사용자 메시지 추가
- setMessages((prev) => [...prev, { text: message, isUser: true }]);
- setMessage("");
-
- // AI 응답 처리
- try {
- const response = await sendChatMessage(message, transcript);
- setMessages((prev) => [...prev, { text: response.text, isUser: false }]);
- } catch (error) {
- console.error("AI 응답 처리 중 오류:", error);
- setMessages((prev) => [
- ...prev,
- { text: "죄송합니다. 오류가 발생했습니다.", isUser: false },
- ]);
- }
- };
-
- const adjustTextareaHeight = (e: React.ChangeEvent) => {
- const textarea = e.target;
- textarea.style.height = "auto";
- textarea.style.height = `${Math.min(textarea.scrollHeight, 72)}px`; // 최대 3줄 (24px * 3)
- };
-
+export default function SidePanel({
+ transcript,
+ timestamps = [],
+}: SidePanelProps) {
return (
@@ -55,119 +21,13 @@ export default function SidePanel({ transcript }: SidePanelProps) {
maxSize={50}
className="border-b border-gray-200"
>
-
-
실시간 트랜스크립트
-
{transcript}
-
+
-
-
AI 채팅
- {messages.length === 0 && (
-
-
-
AI 어시스턴트에게 물어보세요
-
- 회의 내용을 분석하고 정리하는데 도움을 드립니다
-
-
-
- {quickActions.map((action) => (
- {
- // 사용자 메시지 추가
- setMessages((prev) => [
- ...prev,
- { text: action.label, isUser: true },
- ]);
-
- // AI 응답 처리
- try {
- const response = await sendChatMessage(
- action.label,
- transcript,
- );
- setMessages((prev) => [
- ...prev,
- { text: response.text, isUser: false },
- ]);
- } catch (error) {
- console.error("AI 응답 처리 중 오류:", error);
- setMessages((prev) => [
- ...prev,
- {
- text: "죄송합니다. 오류가 발생했습니다.",
- isUser: false,
- },
- ]);
- }
- }}
- className="whitespace-nowrap rounded-lg border px-1.5 py-1 text-xs hover:bg-gray-50"
- >
- {action.label}
-
- ))}
-
-
- )}
-
- {messages.length > 0 && (
-
- {messages.map((msg, index) => (
-
- {msg.text}
-
- ))}
-
- )}
-
-
-
+
diff --git a/apps/desktop/src/components/note/TranscriptPanel.tsx b/apps/desktop/src/components/note/TranscriptPanel.tsx
new file mode 100644
index 0000000000..b96cbdbe6f
--- /dev/null
+++ b/apps/desktop/src/components/note/TranscriptPanel.tsx
@@ -0,0 +1,35 @@
+import type { TranscriptBlock } from "../../types";
+
+interface TranscriptPanelProps {
+ transcript: string;
+ timestamps?: TranscriptBlock[];
+}
+
+export default function TranscriptPanel({
+ transcript,
+ timestamps = [],
+}: TranscriptPanelProps) {
+ return (
+
+ {timestamps.length > 0 ? (
+
+ {timestamps.map((block, index) => (
+
+
+
+ {block.speaker}
+
+ {block.timestamp}
+
+
+ {block.text}
+
+
+ ))}
+
+ ) : (
+
{transcript}
+ )}
+
+ );
+}
diff --git a/apps/desktop/src/components/ui/retro-grid.tsx b/apps/desktop/src/components/ui/retro-grid.tsx
new file mode 100644
index 0000000000..b026f00008
--- /dev/null
+++ b/apps/desktop/src/components/ui/retro-grid.tsx
@@ -0,0 +1,39 @@
+import { cn } from "../../lib/utils";
+
+export function RetroGrid({
+ className,
+ angle = 65,
+}: {
+ className?: string;
+ angle?: number;
+}) {
+ return (
+
+ {/* Grid */}
+
+
+ {/* Background Gradient */}
+
+
+ );
+}
diff --git a/apps/desktop/src/lib/utils.ts b/apps/desktop/src/lib/utils.ts
new file mode 100644
index 0000000000..bd0c391ddd
--- /dev/null
+++ b/apps/desktop/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/apps/desktop/src/mocks/data.ts b/apps/desktop/src/mocks/data.ts
index ad510ff904..dc39dde1f4 100644
--- a/apps/desktop/src/mocks/data.ts
+++ b/apps/desktop/src/mocks/data.ts
@@ -1,4 +1,4 @@
-import type { Note, CalendarEvent } from "../types";
+import type { Note, CalendarEvent, TranscriptBlock } from "../types";
// Helper function to create dates relative to today
const today = new Date("2024-12-14T00:00:00Z");
@@ -169,6 +169,14 @@ export const mockNotes: Note[] = [
},
];
+export const mockTranscripts: TranscriptBlock[] = [
+ { timestamp: "09:30:00", text: "안녕하세요, 오늘 회의를 시작하겠습니다.", speaker: "김철수" },
+ { timestamp: "09:31:15", text: "지난 회의에서 논의된 사항들을 먼저 리뷰해보겠습니다.", speaker: "김철수" },
+ { timestamp: "09:33:20", text: "첫 번째 안건은 신규 프로젝트 일정 조정입니다.", speaker: "이영희" },
+ { timestamp: "09:35:45", text: "두 번째로 리소스 할당에 대해 이야기해보겠습니다.", speaker: "박지원" },
+ { timestamp: "09:38:10", text: "마지막으로 다음 주 마일스톤 설정에 대해 논의하겠습니다.", speaker: "김철수" }
+];
+
export const mockEvents: CalendarEvent[] = [
{
kind: "calendar#event",
diff --git a/apps/desktop/src/pages/Home.tsx b/apps/desktop/src/pages/Home.tsx
index 5a532ea8f5..0b2df71446 100644
--- a/apps/desktop/src/pages/Home.tsx
+++ b/apps/desktop/src/pages/Home.tsx
@@ -1,6 +1,5 @@
import { useState } from "react";
import { useNavigate } from "react-router";
-
import { mockNotes } from "../mocks/data";
import { UpcomingEvents } from "../components/home/UpcomingEvents";
import { PastNotes } from "../components/home/PastNotes";
@@ -51,17 +50,10 @@ export default function Home() {
};
return (
-
-
+
+ {isNewUser && }
+
+
);
}
diff --git a/apps/desktop/src/pages/Login.tsx b/apps/desktop/src/pages/Login.tsx
new file mode 100644
index 0000000000..1b4321b92d
--- /dev/null
+++ b/apps/desktop/src/pages/Login.tsx
@@ -0,0 +1,76 @@
+import { useState, useEffect } from "react";
+import {
+ RiGoogleFill,
+ RiAppleFill,
+ RiVolumeMuteFill,
+ RiVolumeUpFill,
+} from "@remixicon/react";
+import { RetroGrid } from "../components/ui/retro-grid.tsx";
+
+const Login = () => {
+ const [isMuted, setIsMuted] = useState(false);
+ const [audio] = useState(new Audio("/bgm.mp3"));
+
+ useEffect(() => {
+ audio.loop = true;
+ audio.play().catch((error) => console.log("Audio autoplay failed:", error));
+
+ return () => {
+ audio.pause();
+ audio.currentTime = 0;
+ };
+ }, [audio]);
+
+ useEffect(() => {
+ audio.muted = isMuted;
+ }, [isMuted, audio]);
+
+ const handleGoogleSignIn = () => {
+ // Implement Google Sign In
+ console.log("Google Sign In clicked");
+ };
+
+ const handleAppleSignIn = () => {
+ // Implement Apple Sign In
+ console.log("Apple Sign In clicked");
+ };
+
+ return (
+
+
+
+
setIsMuted(!isMuted)}
+ className="fixed right-5 top-5 z-10 rounded-full bg-black p-2 transition-colors duration-200 hover:bg-black/70"
+ >
+ {isMuted ? (
+
+ ) : (
+
+ )}
+
+
+
+
Welcome to Hypr
+
+
+
+ Continue with Google
+
+
+
+
+ Continue with Apple
+
+
+
+ );
+};
+
+export default Login;
diff --git a/apps/desktop/src/pages/Note.tsx b/apps/desktop/src/pages/Note.tsx
index c8227c261e..e12630a824 100644
--- a/apps/desktop/src/pages/Note.tsx
+++ b/apps/desktop/src/pages/Note.tsx
@@ -1,15 +1,13 @@
import { useEffect } from "react";
import { useParams } from "react-router";
-
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
-
import SidePanel from "../components/note/SidePanel";
import NoteHeader from "../components/note/NoteHeader";
import NoteEditor from "../components/note/NoteEditor";
-
import { useUI } from "../contexts/UIContext";
import { useNoteState } from "../hooks/useNoteState";
import { useSpeechRecognition } from "../hooks/useSpeechRecognition";
+import { mockTranscripts } from "../mocks/data";
export default function Note() {
const { id } = useParams();
@@ -95,7 +93,10 @@ export default function Note() {
<>
-
+
>
)}
diff --git a/apps/desktop/src/styles/editor.css b/apps/desktop/src/styles/editor.css
index e98a2825cd..4562233d9b 100644
--- a/apps/desktop/src/styles/editor.css
+++ b/apps/desktop/src/styles/editor.css
@@ -95,4 +95,12 @@
border-top: 1px solid var(--gray-2);
margin: 2rem 0;
}
+
+ p.is-editor-empty:first-child::before {
+ color: #adb5bd;
+ content: attr(data-placeholder);
+ float: left;
+ height: 0;
+ pointer-events: none;
+ }
}
diff --git a/apps/desktop/src/styles/global.css b/apps/desktop/src/styles/global.css
index 0af8f521e7..9b3e9b4497 100644
--- a/apps/desktop/src/styles/global.css
+++ b/apps/desktop/src/styles/global.css
@@ -70,3 +70,66 @@ h6 {
#__next {
isolation: isolate;
}
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
+ --primary: 0 0% 9%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 0 0% 96.1%;
+ --secondary-foreground: 0 0% 9%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 89.8%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ }
+ .dark {
+ --background: 0 0% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 0 0% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 0 0% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 0 0% 9%;
+ --secondary: 0 0% 14.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 0 0% 14.9%;
+ --muted-foreground: 0 0% 63.9%;
+ --accent: 0 0% 14.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 14.9%;
+ --input: 0 0% 14.9%;
+ --ring: 0 0% 83.1%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ }
+}
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/apps/desktop/src/types/db.ts b/apps/desktop/src/types/db.ts
index 4fd2ab41f8..82483f8fc7 100644
--- a/apps/desktop/src/types/db.ts
+++ b/apps/desktop/src/types/db.ts
@@ -2,20 +2,9 @@
// This file has been generated by Specta. DO NOT EDIT.
-export type Session = {
- id: string;
- start: string;
- end: string | null;
- tags: string[];
- raw_memo: string;
- processed_memo: string;
- raw_transcript: string;
-};
+export type Session = { id: string; start: string; end: string | null; tags: string[]; raw_memo: string; processed_memo: string; raw_transcript: string }
-export type Transcript = { speakers: string[]; blocks: TranscriptBlock[] };
+export type Transcript = { speakers: string[]; blocks: TranscriptBlock[] }
+
+export type TranscriptBlock = { timestamp: string; text: string; speaker: string }
-export type TranscriptBlock = {
- timestamp: string;
- text: string;
- speaker: string;
-};
diff --git a/apps/desktop/tailwind.config.js b/apps/desktop/tailwind.config.js
index 37e0ee1da9..c38aeb54c7 100644
--- a/apps/desktop/tailwind.config.js
+++ b/apps/desktop/tailwind.config.js
@@ -1,35 +1,92 @@
/** @type {import('tailwindcss').Config} */
export default {
- content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
+ darkMode: ["class"],
+ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
- extend: {
- colors: {
- background: "#ffffff",
- border: "rgba(0, 0, 0, 0.1)",
- },
- fontFamily: {
- sans: [
- "-apple-system",
- "BlinkMacSystemFont",
- "Apple SD Gothic Neo",
- "Pretendard",
- "system-ui",
- "sans-serif",
- ],
- },
- keyframes: {
- marquee: {
- "0%": { transform: "translateX(100%)" },
- "100%": { transform: "translateX(-100%)" },
- },
- },
- animation: {
- marquee: "marquee 10s linear infinite",
- },
- transitionTimingFunction: {
- "ease-in-out-expo": "cubic-bezier(0.87, 0, 0.13, 1)",
- },
- },
+ extend: {
+ colors: {
+ background: 'hsl(var(--background))',
+ border: 'hsl(var(--border))',
+ foreground: 'hsl(var(--foreground))',
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
+ },
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))'
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
+ },
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ chart: {
+ '1': 'hsl(var(--chart-1))',
+ '2': 'hsl(var(--chart-2))',
+ '3': 'hsl(var(--chart-3))',
+ '4': 'hsl(var(--chart-4))',
+ '5': 'hsl(var(--chart-5))'
+ }
+ },
+ fontFamily: {
+ sans: [
+ '-apple-system',
+ 'BlinkMacSystemFont',
+ 'Apple SD Gothic Neo',
+ 'Pretendard',
+ 'system-ui',
+ 'sans-serif'
+ ]
+ },
+ keyframes: {
+ marquee: {
+ '0%': {
+ transform: 'translateX(100%)'
+ },
+ '100%': {
+ transform: 'translateX(-100%)'
+ }
+ },
+ grid: {
+ '0%': {
+ transform: 'translateY(-50%)'
+ },
+ '100%': {
+ transform: 'translateY(0)'
+ }
+ }
+ },
+ animation: {
+ marquee: 'marquee 10s linear infinite',
+ grid: 'grid 15s linear infinite'
+ },
+ transitionTimingFunction: {
+ 'ease-in-out-expo': 'cubic-bezier(0.87, 0, 0.13, 1)'
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
+ }
+ }
},
- plugins: [require("@tailwindcss/typography")],
+ plugins: [require("@tailwindcss/typography"), require("tailwindcss-animate")],
};
diff --git a/apps/web/src/components/layout/header.tsx b/apps/web/src/components/layout/header.tsx
index 9bbb1556ba..d8ef7e5c3d 100644
--- a/apps/web/src/components/layout/header.tsx
+++ b/apps/web/src/components/layout/header.tsx
@@ -1,22 +1,7 @@
-"use client";
-
-import { useState, useEffect } from "react";
import Image from "next/image";
import { HeaderButton } from "@/components/ui/header-button";
-import { cn } from "@/lib/utils";
export default function Header() {
- const [isScrolled, setIsScrolled] = useState(false);
-
- useEffect(() => {
- const handleScroll = () => {
- setIsScrolled(window.scrollY > 0);
- };
-
- window.addEventListener("scroll", handleScroll);
- return () => window.removeEventListener("scroll", handleScroll);
- }, []);
-
return (
);
diff --git a/apps/web/src/components/ui/header-button.tsx b/apps/web/src/components/ui/header-button.tsx
index 0c386c420c..d213deb5cc 100644
--- a/apps/web/src/components/ui/header-button.tsx
+++ b/apps/web/src/components/ui/header-button.tsx
@@ -44,12 +44,12 @@ export function HeaderButton({ className }: HeaderButtonProps) {
variant="outline"
size="default"
className={cn(
- "inline-flex items-center justify-center gap-2 transition-all duration-200 rounded-xl",
+ "inline-flex items-center justify-center gap-2 transition-all duration-200 rounded-xl border border-gray-300 hover:border-gray-400",
{
- "bg-gradient-to-br from-[#f97316] to-[#6366f1] text-white border-transparent hover:from-[#f97316] hover:to-[#6366f1] hover:text-white":
+ "bg-gradient-to-br from-[#f97316] to-[#6366f1] text-white border-transparent hover:from-[#f97316] border border-gray-300 hover:border-gray-400 hover:to-[#6366f1] hover:text-white":
isScrolled,
},
- className,
+ className
)}
>
{os === "Windows"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 708384eb5c..134b1a473f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -38,6 +38,9 @@ importers:
'@radix-ui/react-tabs':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.16))(@types/react@18.3.16)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@remixicon/react':
+ specifier: ^4.5.0
+ version: 4.5.0(react@18.3.1)
'@tauri-apps/api':
specifier: ^2
version: 2.1.1
@@ -56,6 +59,9 @@ importers:
'@tiptap/extension-highlight':
specifier: ^2.10.3
version: 2.10.3(@tiptap/core@2.10.3(@tiptap/pm@2.10.3))
+ '@tiptap/extension-placeholder':
+ specifier: ^2.10.3
+ version: 2.10.3(@tiptap/core@2.10.3(@tiptap/pm@2.10.3))(@tiptap/pm@2.10.3)
'@tiptap/extension-typography':
specifier: ^2.10.3
version: 2.10.3(@tiptap/core@2.10.3(@tiptap/pm@2.10.3))
@@ -68,8 +74,11 @@ importers:
'@tiptap/starter-kit':
specifier: ^2.10.3
version: 2.10.3
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
clsx:
- specifier: ^2.0.0
+ specifier: ^2.1.1
version: 2.1.1
cmdk:
specifier: ^1.0.4
@@ -99,8 +108,11 @@ importers:
specifier: ^7.0.2
version: 7.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwind-merge:
- specifier: ^2.1.0
+ specifier: ^2.5.5
version: 2.5.5
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.16)
zustand:
specifier: ^5.0.2
version: 5.0.2(@types/react@18.3.16)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1))
@@ -1512,6 +1524,12 @@ packages:
peerDependencies:
'@tiptap/core': ^2.7.0
+ '@tiptap/extension-placeholder@2.10.3':
+ resolution: {integrity: sha512-0OkwnDLguZgoiJM85cfnOySuMmPUF7qqw7DHQ+c3zwTAYnvzpvqrvpupc+2Zi9GfC1sDgr+Ajrp8imBHa6PHfA==}
+ peerDependencies:
+ '@tiptap/core': ^2.7.0
+ '@tiptap/pm': ^2.7.0
+
'@tiptap/extension-strike@2.10.3':
resolution: {integrity: sha512-jYoPy6F6njYp3txF3u23bgdRy/S5ATcWDO9LPZLHSeikwQfJ47nqb+EUNo5M8jIOgFBTn4MEbhuZ6OGyhnxopA==}
peerDependencies:
@@ -4763,6 +4781,10 @@ snapshots:
'@remirror/core-constants@3.0.0': {}
+ '@remixicon/react@4.5.0(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+
'@remixicon/react@4.5.0(react@19.0.0)':
dependencies:
react: 19.0.0
@@ -4992,6 +5014,11 @@ snapshots:
dependencies:
'@tiptap/core': 2.10.3(@tiptap/pm@2.10.3)
+ '@tiptap/extension-placeholder@2.10.3(@tiptap/core@2.10.3(@tiptap/pm@2.10.3))(@tiptap/pm@2.10.3)':
+ dependencies:
+ '@tiptap/core': 2.10.3(@tiptap/pm@2.10.3)
+ '@tiptap/pm': 2.10.3
+
'@tiptap/extension-strike@2.10.3(@tiptap/core@2.10.3(@tiptap/pm@2.10.3))':
dependencies:
'@tiptap/core': 2.10.3(@tiptap/pm@2.10.3)