From 60bc657ec66b935f719f9f085300151d20dcd0ac Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sun, 8 Feb 2026 18:46:52 +0900 Subject: [PATCH 1/2] refactor(copilot): minimize UI for professional appearance and fix block editor bugs - Remove ASSISTANT badge, @mention hint, and header border from copilot panel - Remove empty state message (How can I help you today?) for cleaner interface - Improve AI message styling: use transparent bg with border instead of filled color - Change AI loader color from accent to tertiary for less visual disruption - Convert progress indicator text from Korean to English for consistency - Fix cursor disappearance bug when re-clicking focused blocks - Remove text selection auto-highlight feature (CodeMirror match highlighting) - Remove IconRobot and related imports (no longer used) These changes create a more professional, minimalist copilot experience while maintaining agent-based architecture. Build passes, all diagnostics clean. --- src/components/copilot/CopilotPanel.tsx | 61 +++++-------------------- src/editor/createEditor.ts | 51 ++++++++++----------- src/outliner/BlockComponent.tsx | 27 +++++++---- 3 files changed, 53 insertions(+), 86 deletions(-) diff --git a/src/components/copilot/CopilotPanel.tsx b/src/components/copilot/CopilotPanel.tsx index 36dadf8c..31557ae4 100644 --- a/src/components/copilot/CopilotPanel.tsx +++ b/src/components/copilot/CopilotPanel.tsx @@ -20,7 +20,6 @@ import { IconChevronDown, IconDeviceDesktop, IconPlayerStop, - IconRobot, IconTrash, IconUser, IconX, @@ -595,24 +594,7 @@ export function CopilotPanel() { ))} {/* Header */} - - - } - > - Assistant - - - Use @ to mention - - + {chatMessages.length > 0 && ( - + - {currentStep === "thinking" && "분석 중..."} + {currentStep === "thinking" && "Analyzing..."} {currentStep === "tool_call" && - `${currentToolName || "도구"} 실행 중...`} - {currentStep === "observation" && "결과 처리 중..."} - {currentStep === "final" && "응답 생성 중..."} + `Running ${currentToolName || "tool"}...`} + {currentStep === "observation" && "Processing..."} + {currentStep === "final" && "Generating..."} )} - {chatMessages.length === 0 && ( - - - - How can I help you today? - - - )} {chatMessages .filter((msg) => msg.content.trim() !== "") .map((msg) => { @@ -714,24 +683,13 @@ export function CopilotPanel() { wrap="nowrap" justify={msg.role === "user" ? "flex-end" : "flex-start"} > - {msg.role === "assistant" && ( - - - - )}
binding.key !== "Tab" && binding.key !== "Shift-Tab" + (binding) => binding.key !== "Tab" && binding.key !== "Shift-Tab", ), ...historyKeymap, ...closeBracketsKeymap, ...searchKeymap, - ]) + ]), ); // Line wrapping @@ -348,7 +345,7 @@ function normalizeWikiTitle(input: string): string { function extractBlockRefAtLinePos( lineText: string, - offsetInLine: number + offsetInLine: number, ): { id: string; isEmbed: boolean } | null { // Match: // - ((uuid)) @@ -471,7 +468,7 @@ function createBlockRefClickHandler(): Extension { if (!target) return false; const el = target.closest?.( - ".cm-block-ref, .cm-block-embed" + ".cm-block-ref, .cm-block-embed", ) as HTMLElement | null; if (!el) return false; @@ -500,7 +497,7 @@ function createBlockRefClickHandler(): Extension { const lineText = line.text; const offsetInLine = Math.max( 0, - Math.min(pos - line.from, lineText.length) + Math.min(pos - line.from, lineText.length), ); const ref = extractBlockRefAtLinePos(lineText, offsetInLine); @@ -518,7 +515,7 @@ function createBlockRefClickHandler(): Extension { function getWikiLinkQueryAtPos( doc: string, - cursorPos: number + cursorPos: number, ): { from: number; to: number; query: string; isEmbed: boolean } | null { // Detect an in-progress wiki link like: // - `[[que` (no closing ]]) @@ -551,7 +548,7 @@ function getWikiLinkQueryAtPos( function getParensLinkQueryAtPos( doc: string, - cursorPos: number + cursorPos: number, ): { from: number; to: number; query: string; isEmbed: boolean } | null { // Detect in-progress block reference: // - `((query` -> normal link @@ -583,7 +580,7 @@ type PageRecord = { id: string; title: string; parentId?: string }; function computePageFullPathTitles( pageId: string, - pagesById: Record + pagesById: Record, ): string[] { const out: string[] = []; let cur: string | undefined = pageId; @@ -605,7 +602,7 @@ function computePageFullPathTitles( function buildWikiPathForPage( pageId: string, - pagesById: Record + pagesById: Record, ): string { return computePageFullPathTitles(pageId, pagesById).join("/"); } @@ -655,7 +652,7 @@ function createUnifiedLinkAutocomplete(): Extension { view: EditorView, _completion: Completion, fromPos: number, - toPos: number + toPos: number, ) => { const state = view.state; const currentDoc = state.doc.toString(); @@ -753,7 +750,7 @@ function createUnifiedLinkAutocomplete(): Extension { _view: EditorView, _completion: Completion, _fromPos: number, - _toPos: number + _toPos: number, ) => { // No-op placeholder; user should keep typing. }, @@ -793,7 +790,7 @@ function createUnifiedLinkAutocomplete(): Extension { view: EditorView, _completion: Completion, fromPos: number, - toPos: number + toPos: number, ) => { const state = view.state; const currentDoc = state.doc.toString(); @@ -920,7 +917,7 @@ async function openOrCreateNoteByTitle(noteTitle: string): Promise { title: string, parentId: string | null, pagesById: typeof pageStore.pagesById, - pageIds: string[] + pageIds: string[], ): string | null => { const t = title.toLowerCase(); for (const id of pageIds) { @@ -936,7 +933,7 @@ async function openOrCreateNoteByTitle(noteTitle: string): Promise { // Ensure a folder-note exists (and isDirectory=true). Return its pageId. const ensureFolderNote = async ( folderTitle: string, - parentId: string | null + parentId: string | null, ): Promise => { // Re-read state each time to avoid stale copies after loadPages() let { pagesById, pageIds } = usePageStore.getState(); @@ -945,7 +942,7 @@ async function openOrCreateNoteByTitle(noteTitle: string): Promise { if (!existingId) { existingId = await pageStore.createPage( folderTitle, - parentId ?? undefined + parentId ?? undefined, ); await pageStore.loadPages(); ({ pagesById, pageIds } = usePageStore.getState()); @@ -1016,7 +1013,7 @@ async function openOrCreateNoteByTitle(noteTitle: string): Promise { } function createWikiLinkClickHandler( - onOpenWikiLink?: (raw: string, noteTitle: string) => void + onOpenWikiLink?: (raw: string, noteTitle: string) => void, ): Extension { const handleClick = (event: MouseEvent, view: EditorView) => { // Only handle left clicks @@ -1042,7 +1039,7 @@ function createWikiLinkClickHandler( // We need to compute the position within the line. const offsetInLine = Math.max( 0, - Math.min(pos - line.from, lineText.length) + Math.min(pos - line.from, lineText.length), ); // Scan for wiki links in this line and pick the match that contains the click position. @@ -1129,7 +1126,7 @@ function createExternalLinkClickHandler(): Extension { const offsetInLine = Math.max( 0, - Math.min(pos - line.from, lineText.length) + Math.min(pos - line.from, lineText.length), ); const linkRegex = /\[([^\]]*)\]\(([^)]+)\)/g; @@ -1167,7 +1164,7 @@ function createExternalLinkClickHandler(): Extension { */ function createFocusListeners( onFocus?: () => void, - onBlur?: () => void + onBlur?: () => void, ): Extension[] { const extensions: Extension[] = []; @@ -1178,7 +1175,7 @@ function createFocusListeners( onFocus(); return false; }, - }) + }), ); } @@ -1189,7 +1186,7 @@ function createFocusListeners( onBlur(); return false; }, - }) + }), ); } @@ -1291,7 +1288,7 @@ function createEditorTheme(theme: "light" | "dark" = "light"): Extension { fontFamily: "var(--font-family)", }, }, - { dark: isDark } + { dark: isDark }, ); } @@ -1300,7 +1297,7 @@ function createEditorTheme(theme: "light" | "dark" = "light"): Extension { */ export function createEditor( parent: HTMLElement, - config: EditorConfig = {} + config: EditorConfig = {}, ): EditorView { const extensions: Extension[] = [ // Basic extensions diff --git a/src/outliner/BlockComponent.tsx b/src/outliner/BlockComponent.tsx index 53f985ce..4b08f83e 100644 --- a/src/outliner/BlockComponent.tsx +++ b/src/outliner/BlockComponent.tsx @@ -1294,14 +1294,26 @@ export const BlockComponent: React.FC = memo( const view = editorRef.current.getView(); if (view?.hasFocus) { console.log( - `[BlockComponent:onMouseDown] Committing draft for blockId=${blockId.slice(0, 8)}`, + `[BlockComponent:onMouseDown] Block already focused, updating cursor position for blockId=${blockId.slice(0, 8)}`, ); - commitDraft().then(() => { - console.log( - `[BlockComponent:onMouseDown] Draft committed, blurring blockId=${blockId.slice(0, 8)}`, + const range = document.caretRangeFromPoint( + e.clientX, + e.clientY, + ); + if (range && e.currentTarget.contains(range.startContainer)) { + const preCaretRange = document.createRange(); + preCaretRange.selectNodeContents(e.currentTarget); + preCaretRange.setEnd( + range.startContainer, + range.startOffset, ); - view.contentDOM.blur(); - }); + const cursorPos = preCaretRange.toString().length; + + view.dispatch({ + selection: { anchor: cursorPos, head: cursorPos }, + }); + view.focus(); + } return; } } @@ -1310,9 +1322,6 @@ export const BlockComponent: React.FC = memo( console.log( `[BlockComponent:onMouseDown] Setting focus to blockId=${blockId.slice(0, 8)}`, ); - // CRITICAL: Do NOT set targetCursorPosition yet - wait for store to be updated - // If we set it now, other blocks' blockContentEffect might fire before the store update completes - // causing them to override their draft with stale values setFocusedBlock(blockId); } }} From f8b9ea4cbd23b1a0f3a1e6df812e1af19fdaa43c Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sun, 8 Feb 2026 18:49:01 +0900 Subject: [PATCH 2/2] refactor(copilot): remove user message icon for minimal design Remove the user icon (IconUser) from user message bubbles in the copilot panel to achieve a cleaner, more minimal appearance. This completes the minimalist UI overhaul - both user and AI messages now display without accompanying icons. --- src/components/copilot/CopilotPanel.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/copilot/CopilotPanel.tsx b/src/components/copilot/CopilotPanel.tsx index 31557ae4..b9999c1c 100644 --- a/src/components/copilot/CopilotPanel.tsx +++ b/src/components/copilot/CopilotPanel.tsx @@ -21,7 +21,6 @@ import { IconDeviceDesktop, IconPlayerStop, IconTrash, - IconUser, IconX, } from "@tabler/icons-react"; import { useEffect, useRef, useState } from "react"; @@ -720,17 +719,6 @@ export function CopilotPanel() { }} /> - {msg.role === "user" && ( - - - - )} ); })}