diff options
Diffstat (limited to 'renderer/src/App.tsx')
| -rw-r--r-- | renderer/src/App.tsx | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/renderer/src/App.tsx b/renderer/src/App.tsx index 7d75196..c3eafd4 100644 --- a/renderer/src/App.tsx +++ b/renderer/src/App.tsx | |||
| @@ -63,15 +63,47 @@ export function App() { | |||
| 63 | () => (localStorage.getItem("cf-theme") as Theme) ?? "dark" | 63 | () => (localStorage.getItem("cf-theme") as Theme) ?? "dark" |
| 64 | ); | 64 | ); |
| 65 | 65 | ||
| 66 | const [chatWidth, setChatWidth] = useState<number>( | ||
| 67 | () => Number(localStorage.getItem("cf-chat-width")) || 380 | ||
| 68 | ); | ||
| 69 | const [chatCollapsed, setChatCollapsed] = useState<boolean>( | ||
| 70 | () => localStorage.getItem("cf-chat-collapsed") === "true" | ||
| 71 | ); | ||
| 72 | |||
| 66 | // Keep document.documentElement in sync and persist to localStorage | 73 | // Keep document.documentElement in sync and persist to localStorage |
| 67 | useEffect(() => { | 74 | useEffect(() => { |
| 68 | document.documentElement.setAttribute("data-theme", theme); | 75 | document.documentElement.setAttribute("data-theme", theme); |
| 69 | localStorage.setItem("cf-theme", theme); | 76 | localStorage.setItem("cf-theme", theme); |
| 70 | }, [theme]); | 77 | }, [theme]); |
| 71 | 78 | ||
| 79 | useEffect(() => { | ||
| 80 | localStorage.setItem("cf-chat-width", String(chatWidth)); | ||
| 81 | }, [chatWidth]); | ||
| 82 | |||
| 83 | useEffect(() => { | ||
| 84 | localStorage.setItem("cf-chat-collapsed", String(chatCollapsed)); | ||
| 85 | }, [chatCollapsed]); | ||
| 86 | |||
| 72 | const handleToggleTheme = () => | 87 | const handleToggleTheme = () => |
| 73 | setTheme((t) => (t === "dark" ? "light" : "dark")); | 88 | setTheme((t) => (t === "dark" ? "light" : "dark")); |
| 74 | 89 | ||
| 90 | const handleResizeMouseDown = (e: React.MouseEvent) => { | ||
| 91 | e.preventDefault(); | ||
| 92 | const startX = e.clientX; | ||
| 93 | const startWidth = chatWidth; | ||
| 94 | const onMove = (ev: MouseEvent) => { | ||
| 95 | const delta = startX - ev.clientX; // drag left = wider chat | ||
| 96 | const next = Math.max(180, Math.min(700, startWidth + delta)); | ||
| 97 | setChatWidth(next); | ||
| 98 | }; | ||
| 99 | const onUp = () => { | ||
| 100 | document.removeEventListener("mousemove", onMove); | ||
| 101 | document.removeEventListener("mouseup", onUp); | ||
| 102 | }; | ||
| 103 | document.addEventListener("mousemove", onMove); | ||
| 104 | document.addEventListener("mouseup", onUp); | ||
| 105 | }; | ||
| 106 | |||
| 75 | const hasChanges = documentContent !== originalContent; | 107 | const hasChanges = documentContent !== originalContent; |
| 76 | 108 | ||
| 77 | // Clear error after 5 seconds | 109 | // Clear error after 5 seconds |
| @@ -380,6 +412,13 @@ export function App() { | |||
| 380 | theme={theme} | 412 | theme={theme} |
| 381 | /> | 413 | /> |
| 382 | 414 | ||
| 415 | {!chatCollapsed && ( | ||
| 416 | <div | ||
| 417 | className="chat-resize-handle" | ||
| 418 | onMouseDown={handleResizeMouseDown} | ||
| 419 | /> | ||
| 420 | )} | ||
| 421 | |||
| 383 | <ChatPane | 422 | <ChatPane |
| 384 | messages={messages} | 423 | messages={messages} |
| 385 | onSend={handleSendMessage} | 424 | onSend={handleSendMessage} |
| @@ -390,6 +429,9 @@ export function App() { | |||
| 390 | ? `Chat with Claude (${selectedSession.phase})...` | 429 | ? `Chat with Claude (${selectedSession.phase})...` |
| 391 | : "Select a session to start" | 430 | : "Select a session to start" |
| 392 | } | 431 | } |
| 432 | collapsed={chatCollapsed} | ||
| 433 | chatWidth={chatWidth} | ||
| 434 | onToggleCollapse={() => setChatCollapsed((c) => !c)} | ||
| 393 | /> | 435 | /> |
| 394 | </div> | 436 | </div> |
| 395 | 437 | ||
