aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-28 18:46:20 -0800
committerClawd <ai@clawd.bot>2026-02-28 18:46:20 -0800
commite2a0bc68726c1b8dca179ee1f6826b88d8dd09f5 (patch)
tree2bb17945208c24bfd8c4b735cde9af732ff921fd
parent3ac34530578b9a6f59bcea6b5aeefd97eb03d588 (diff)
Remove accidentally created .claude-flow/ directory
-rw-r--r--.claude-flow/plan.md1090
-rw-r--r--.claude-flow/research.md214
2 files changed, 0 insertions, 1304 deletions
diff --git a/.claude-flow/plan.md b/.claude-flow/plan.md
deleted file mode 100644
index bbcae6a..0000000
--- a/.claude-flow/plan.md
+++ /dev/null
@@ -1,1090 +0,0 @@
1# Implementation Plan
2
3## Goal
4Restyle the Claude Flow Electron app with:
51. Fix the window/tab title from "minimal" → "Claude Flow"
62. Add a "CLAUDE FLOW" wordmark to the header
73. Apply a cipherpunk aesthetic (full monospace UI font, sharper geometry, electric-blue accent, uppercase meta labels, focus glow)
84. Add light/dark mode with a text toggle (`[dark]` / `[light]`) persisted to `localStorage`
9
10## Approach
11Five files touched in order of dependency. No new npm packages. The CSS rewrite is the biggest change; the JS/TSX changes are small and surgical.
12
13**Order of changes:**
141. `renderer/index.html` — title fix (trivial, isolated)
152. `renderer/src/styles/globals.css` — full restyle (all visual work)
163. `renderer/src/App.tsx` — add theme state, wire it down
174. `renderer/src/components/Header.tsx` — wordmark + toggle button
185. `renderer/src/components/DocumentPane.tsx` — dynamic CodeMirror theme
19
20---
21
22## Changes
23
24### 1. Fix Window Title
25**File:** `renderer/index.html`
26**What:** Change `<title>minimal</title>` to `<title>Claude Flow</title>` on line 10.
27
28```html
29<!-- BEFORE -->
30<title>minimal</title>
31
32<!-- AFTER -->
33<title>Claude Flow</title>
34```
35
36---
37
38### 2. Restyle the CSS
39**File:** `renderer/src/styles/globals.css`
40**What:** Complete replacement of the file. Key changes from the original:
41- `:root` dark-mode accent updated to `#60a5fa` / `#93c5fd`
42- `body` font-family changed to full monospace stack
43- New `html[data-theme="light"]` block added after `:root`
44- `border-radius` reduced to 2px on all interactive controls (4px on chat bubbles)
45- `text-transform: uppercase; letter-spacing: 0.07em` added to all meta/chrome labels
46- Focus glow (`box-shadow`) added on inputs and focused buttons
47- New `.app-wordmark` class added
48- New `.theme-toggle` class added
49- All existing selectors preserved; only values changed
50
51```css
52* {
53 box-sizing: border-box;
54 margin: 0;
55 padding: 0;
56}
57
58/* ── Dark theme (default) ────────────────────────────────────── */
59:root {
60 --bg-primary: #1a1a1a;
61 --bg-secondary: #252525;
62 --bg-tertiary: #333;
63 --border: #444;
64 --text-primary: #e0e0e0;
65 --text-secondary: #888;
66 --accent: #60a5fa; /* electric blue — brighter than original #3b82f6 */
67 --accent-hover: #93c5fd;
68 --success: #10b981;
69 --warning: #f59e0b;
70 --danger: #ef4444;
71}
72
73/* ── Light theme overrides ───────────────────────────────────── */
74html[data-theme="light"] {
75 --bg-primary: #f4f4f2;
76 --bg-secondary: #e8e8e5;
77 --bg-tertiary: #d8d8d4;
78 --border: #b4b4b0;
79 --text-primary: #1a1a18;
80 --text-secondary: #5a5a56;
81 --accent: #2563eb;
82 --accent-hover: #1d4ed8;
83 --success: #059669;
84 --warning: #d97706;
85 --danger: #dc2626;
86}
87
88/* ── Base ────────────────────────────────────────────────────── */
89body {
90 font-family: "SF Mono", "Cascadia Code", "JetBrains Mono", "Fira Code",
91 Monaco, "Courier New", monospace;
92 background: var(--bg-primary);
93 color: var(--text-primary);
94 overflow: hidden;
95 font-size: 13px;
96}
97
98.app {
99 display: flex;
100 flex-direction: column;
101 height: 100vh;
102}
103
104/* ── Header ──────────────────────────────────────────────────── */
105.header {
106 display: flex;
107 justify-content: space-between;
108 align-items: center;
109 padding: 10px 16px;
110 background: var(--bg-secondary);
111 border-bottom: 1px solid var(--border);
112 -webkit-app-region: drag;
113}
114
115.header-left,
116.header-right {
117 display: flex;
118 align-items: center;
119 gap: 8px;
120 -webkit-app-region: no-drag;
121}
122
123/* App wordmark */
124.app-wordmark {
125 font-size: 12px;
126 font-weight: 700;
127 letter-spacing: 0.15em;
128 text-transform: uppercase;
129 color: var(--text-primary);
130 padding-right: 12px;
131 border-right: 1px solid var(--border);
132 margin-right: 4px;
133 user-select: none;
134 white-space: nowrap;
135}
136
137.header select,
138.header button {
139 padding: 5px 10px;
140 background: var(--bg-tertiary);
141 border: 1px solid var(--border);
142 border-radius: 2px;
143 color: var(--text-primary);
144 cursor: pointer;
145 font-size: 12px;
146 font-family: inherit;
147}
148
149.header button:hover {
150 background: var(--border);
151}
152
153.header button.btn-delete {
154 background: transparent;
155 border: 1px solid var(--border);
156 padding: 5px 8px;
157 font-size: 13px;
158}
159
160.header button.btn-delete:hover {
161 background: var(--danger);
162 border-color: var(--danger);
163}
164
165/* Theme toggle */
166.theme-toggle {
167 font-size: 11px;
168 letter-spacing: 0.08em;
169 text-transform: lowercase;
170 opacity: 0.7;
171 transition: opacity 0.15s;
172}
173
174.theme-toggle:hover {
175 opacity: 1;
176 background: var(--bg-tertiary) !important;
177}
178
179/* Phase indicator */
180.phase-indicator {
181 display: flex;
182 gap: 4px;
183}
184
185.phase-step {
186 padding: 3px 10px;
187 font-size: 11px;
188 letter-spacing: 0.07em;
189 text-transform: uppercase;
190 border-radius: 2px;
191 background: var(--bg-tertiary);
192 color: var(--text-secondary);
193}
194
195.phase-step.active {
196 background: var(--accent);
197 color: white;
198}
199
200.phase-step.complete {
201 background: var(--success);
202 color: white;
203}
204
205/* ── Main Content ─────────────────────────────────────────────── */
206.main-content {
207 flex: 1;
208 display: flex;
209 overflow: hidden;
210}
211
212/* ── Document Pane ───────────────────────────────────────────── */
213.document-pane {
214 flex: 1;
215 display: flex;
216 flex-direction: column;
217 border-right: 1px solid var(--border);
218 min-width: 0;
219 overflow: hidden;
220}
221
222.document-header {
223 display: flex;
224 justify-content: space-between;
225 align-items: center;
226 padding: 7px 16px;
227 background: var(--bg-secondary);
228 border-bottom: 1px solid var(--border);
229 font-size: 11px;
230 letter-spacing: 0.07em;
231 text-transform: uppercase;
232 color: var(--text-secondary);
233}
234
235.document-header button {
236 padding: 3px 8px;
237 background: var(--bg-tertiary);
238 border: 1px solid var(--border);
239 border-radius: 2px;
240 color: var(--text-primary);
241 cursor: pointer;
242 font-size: 11px;
243 font-family: inherit;
244 letter-spacing: 0.05em;
245}
246
247.document-header button:hover {
248 background: var(--border);
249}
250
251.document-content {
252 flex: 1;
253 overflow-y: auto;
254 padding: 24px;
255}
256
257.document-content.editing {
258 font-family: inherit;
259 font-size: 13px;
260 line-height: 1.6;
261 background: var(--bg-primary);
262 border: none;
263 resize: none;
264 color: var(--text-primary);
265}
266
267.codemirror-editor {
268 flex: 1;
269 overflow: hidden;
270 min-height: 0;
271}
272
273.codemirror-editor .cm-editor {
274 height: 100%;
275 max-width: 100%;
276}
277
278.codemirror-editor .cm-scroller {
279 overflow: auto !important;
280}
281
282.codemirror-editor .cm-gutters {
283 background: var(--bg-secondary);
284 border-right: 1px solid var(--border);
285}
286
287.document-content.rendered {
288 line-height: 1.7;
289}
290
291.document-content.rendered h1 {
292 font-size: 22px;
293 margin: 24px 0 16px;
294 letter-spacing: -0.01em;
295}
296.document-content.rendered h2 {
297 font-size: 17px;
298 margin: 20px 0 12px;
299 color: var(--text-secondary);
300 text-transform: uppercase;
301 letter-spacing: 0.05em;
302}
303.document-content.rendered h3 {
304 font-size: 14px;
305 margin: 16px 0 8px;
306}
307.document-content.rendered p {
308 margin: 8px 0;
309 line-height: 1.6;
310}
311.document-content.rendered code {
312 background: var(--bg-tertiary);
313 padding: 2px 6px;
314 border-radius: 2px;
315 font-size: 12px;
316 font-family: inherit;
317}
318.document-content.rendered pre {
319 background: var(--bg-tertiary);
320 padding: 16px;
321 border-radius: 2px;
322 overflow-x: auto;
323 margin: 16px 0;
324}
325.document-content.rendered pre code {
326 background: none;
327 padding: 0;
328}
329.document-content.rendered ul,
330.document-content.rendered ol {
331 margin: 12px 0;
332 padding-left: 24px;
333}
334.document-content.rendered li {
335 margin-bottom: 6px;
336 line-height: 1.5;
337}
338.document-content.rendered ul.contains-task-list {
339 list-style: none;
340 padding-left: 0;
341}
342.document-content.rendered li.task-list-item {
343 display: flex;
344 align-items: flex-start;
345 gap: 8px;
346}
347.document-content.rendered li.task-list-item input[type="checkbox"] {
348 margin-top: 4px;
349}
350.document-content.rendered table {
351 width: 100%;
352 border-collapse: collapse;
353 margin: 16px 0;
354 font-size: 12px;
355}
356.document-content.rendered th,
357.document-content.rendered td {
358 padding: 8px 12px;
359 text-align: left;
360 border: 1px solid var(--border);
361}
362.document-content.rendered th {
363 background: var(--bg-tertiary);
364 font-weight: 600;
365 text-transform: uppercase;
366 letter-spacing: 0.06em;
367 font-size: 11px;
368}
369.document-content.rendered tr:nth-child(even) td {
370 background: var(--bg-secondary);
371}
372.document-content.rendered blockquote {
373 border-left: 3px solid var(--accent);
374 margin: 16px 0;
375 padding-left: 16px;
376 color: var(--text-secondary);
377}
378.document-content.rendered hr {
379 border: none;
380 border-top: 1px solid var(--border);
381 margin: 24px 0;
382}
383.document-content.rendered a {
384 color: var(--accent);
385 text-decoration: none;
386}
387.document-content.rendered a:hover {
388 text-decoration: underline;
389}
390.document-content.rendered .empty {
391 color: var(--text-secondary);
392 font-style: italic;
393}
394
395.badge {
396 background: var(--accent);
397 color: white;
398 padding: 2px 8px;
399 border-radius: 2px;
400 font-size: 10px;
401 letter-spacing: 0.08em;
402 text-transform: uppercase;
403}
404
405/* ── Chat Pane ───────────────────────────────────────────────── */
406.chat-pane {
407 width: 380px;
408 display: flex;
409 flex-direction: column;
410 background: var(--bg-secondary);
411}
412
413.chat-messages {
414 flex: 1;
415 overflow-y: auto;
416 padding: 16px;
417}
418
419.message {
420 margin-bottom: 10px;
421 padding: 9px 13px;
422 border-radius: 4px;
423 max-width: 90%;
424 font-size: 13px;
425 line-height: 1.5;
426 white-space: pre-wrap;
427}
428
429.message.user {
430 background: var(--accent);
431 margin-left: auto;
432 color: white;
433}
434
435.message.assistant {
436 background: var(--bg-tertiary);
437}
438
439.message.loading {
440 color: var(--text-secondary);
441 font-style: italic;
442}
443
444.chat-input {
445 display: flex;
446 gap: 8px;
447 padding: 12px;
448 border-top: 1px solid var(--border);
449}
450
451.chat-input input {
452 flex: 1;
453 padding: 9px 13px;
454 background: var(--bg-tertiary);
455 border: 1px solid var(--border);
456 border-radius: 2px;
457 color: var(--text-primary);
458 font-size: 13px;
459 font-family: inherit;
460 transition: border-color 0.15s, box-shadow 0.15s;
461}
462
463.chat-input input:focus {
464 outline: none;
465 border-color: var(--accent);
466 box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
467}
468
469html[data-theme="light"] .chat-input input:focus {
470 box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
471}
472
473.chat-input button {
474 padding: 9px 15px;
475 background: var(--accent);
476 border: none;
477 border-radius: 2px;
478 color: white;
479 cursor: pointer;
480 font-size: 12px;
481 font-family: inherit;
482 letter-spacing: 0.05em;
483 text-transform: uppercase;
484 transition: background 0.15s;
485}
486
487.chat-input button:hover:not(:disabled) {
488 background: var(--accent-hover);
489}
490
491.chat-input button:disabled {
492 opacity: 0.4;
493 cursor: not-allowed;
494}
495
496/* ── Action Bar ──────────────────────────────────────────────── */
497.action-bar {
498 display: flex;
499 justify-content: space-between;
500 align-items: center;
501 padding: 10px 16px;
502 background: var(--bg-secondary);
503 border-top: 1px solid var(--border);
504}
505
506.action-bar-left,
507.action-bar-right {
508 display: flex;
509 align-items: center;
510 gap: 16px;
511}
512
513.token-indicator {
514 display: flex;
515 align-items: center;
516 gap: 8px;
517}
518
519.token-bar {
520 width: 100px;
521 height: 4px;
522 background: var(--bg-tertiary);
523 border-radius: 1px;
524 overflow: hidden;
525}
526
527.token-fill {
528 height: 100%;
529 transition: width 0.3s ease;
530}
531
532.token-label {
533 font-size: 10px;
534 letter-spacing: 0.08em;
535 text-transform: uppercase;
536 color: var(--text-secondary);
537}
538
539.permission-toggle {
540 display: flex;
541 align-items: center;
542 gap: 6px;
543 font-size: 11px;
544 letter-spacing: 0.05em;
545 text-transform: uppercase;
546 color: var(--text-secondary);
547 cursor: pointer;
548}
549
550.permission-toggle input {
551 cursor: pointer;
552}
553
554.btn-secondary {
555 padding: 6px 14px;
556 background: var(--bg-tertiary);
557 border: 1px solid var(--border);
558 border-radius: 2px;
559 color: var(--text-primary);
560 cursor: pointer;
561 font-size: 12px;
562 font-family: inherit;
563 letter-spacing: 0.05em;
564 transition: background 0.15s;
565}
566
567.btn-secondary:hover:not(:disabled) {
568 background: var(--border);
569}
570
571.btn-secondary:disabled {
572 opacity: 0.4;
573 cursor: not-allowed;
574}
575
576.btn-primary {
577 padding: 6px 18px;
578 background: var(--accent);
579 border: none;
580 border-radius: 2px;
581 color: white;
582 cursor: pointer;
583 font-weight: 600;
584 font-size: 12px;
585 font-family: inherit;
586 letter-spacing: 0.07em;
587 text-transform: uppercase;
588 transition: background 0.15s;
589}
590
591.btn-primary:hover:not(:disabled) {
592 background: var(--accent-hover);
593}
594
595.btn-primary:disabled {
596 opacity: 0.4;
597 cursor: not-allowed;
598}
599
600.implementing-status {
601 color: var(--success);
602 font-size: 11px;
603 letter-spacing: 0.1em;
604 text-transform: uppercase;
605}
606
607/* ── Error Bar ───────────────────────────────────────────────── */
608.error-bar {
609 display: flex;
610 justify-content: space-between;
611 align-items: center;
612 padding: 8px 16px;
613 background: var(--danger);
614 color: white;
615 font-size: 12px;
616 letter-spacing: 0.03em;
617}
618
619.error-bar button {
620 background: none;
621 border: none;
622 color: white;
623 font-size: 16px;
624 cursor: pointer;
625 padding: 0 4px;
626}
627
628.error-bar button:hover {
629 opacity: 0.8;
630}
631
632/* ── Onboarding ──────────────────────────────────────────────── */
633.onboarding h1 {
634 font-size: 24px;
635 margin-bottom: 8px;
636 letter-spacing: 0.05em;
637 text-transform: uppercase;
638}
639
640.onboarding h2 {
641 font-size: 13px;
642 margin-top: 28px;
643 margin-bottom: 12px;
644 color: var(--accent);
645 text-transform: uppercase;
646 letter-spacing: 0.1em;
647}
648
649.onboarding p {
650 margin: 12px 0;
651 line-height: 1.6;
652}
653
654.onboarding ol {
655 margin: 12px 0 12px 24px;
656}
657
658.onboarding li {
659 margin: 8px 0;
660 line-height: 1.5;
661}
662
663.onboarding pre {
664 background: var(--bg-tertiary);
665 padding: 12px 16px;
666 border-radius: 2px;
667 margin: 12px 0;
668}
669
670.onboarding code {
671 font-family: inherit;
672 font-size: 12px;
673}
674
675.onboarding a {
676 color: var(--accent);
677 text-decoration: none;
678}
679
680.onboarding a:hover {
681 text-decoration: underline;
682}
683
684.onboarding-tip {
685 margin-top: 28px;
686 padding: 16px;
687 background: var(--bg-tertiary);
688 border-left: 3px solid var(--accent);
689 border-radius: 0 2px 2px 0;
690}
691```
692
693---
694
695### 3. Add Theme State to App
696**File:** `renderer/src/App.tsx`
697**What:** Three additions to the existing file — no existing logic is touched.
698
699**3a. Add `Theme` type and `theme` state** — insert after the existing imports, before the `App` function:
700
701```typescript
702// Add this type alias near the top, alongside other local types if any,
703// or just before the App function:
704type Theme = "dark" | "light";
705```
706
707**3b. Add `theme` state and side-effects** — insert inside `App()`, after the existing `useState` declarations (after `const [error, setError] = useState<string | null>(null)`):
708
709```typescript
710const [theme, setTheme] = useState<Theme>(
711 () => (localStorage.getItem("cf-theme") as Theme) ?? "dark"
712);
713
714// Keep document.documentElement in sync, and persist to localStorage
715useEffect(() => {
716 document.documentElement.setAttribute("data-theme", theme);
717 localStorage.setItem("cf-theme", theme);
718}, [theme]);
719
720const handleToggleTheme = () =>
721 setTheme((t) => (t === "dark" ? "light" : "dark"));
722```
723
724**3c. Pass new props to `Header` and `DocumentPane`** — update the JSX inside the `return`:
725
726```tsx
727// Header — add two new props:
728<Header
729 projects={projects}
730 sessions={sessions}
731 selectedProject={selectedProject}
732 selectedSession={selectedSession}
733 onSelectProject={setSelectedProject}
734 onSelectSession={setSelectedSession}
735 onCreateProject={handleCreateProject}
736 onCreateSession={handleCreateSession}
737 onDeleteProject={handleDeleteProject}
738 onDeleteSession={handleDeleteSession}
739 theme={theme}
740 onToggleTheme={handleToggleTheme}
741/>
742
743// DocumentPane — add one new prop:
744<DocumentPane
745 content={documentContent}
746 onChange={setDocumentContent}
747 phase={selectedSession?.phase || "research"}
748 disabled={!selectedSession || selectedSession.phase === "implement"}
749 showOnboarding={!selectedProject}
750 theme={theme}
751/>
752```
753
754---
755
756### 4. Update Header Component
757**File:** `renderer/src/components/Header.tsx`
758**What:** Add `theme` and `onToggleTheme` to the props interface, insert wordmark element, add toggle button.
759
760**Complete updated file:**
761
762```tsx
763import React from "react";
764import type { Project, Session, Phase } from "../types";
765
766type Theme = "dark" | "light";
767
768interface HeaderProps {
769 projects: Project[];
770 sessions: Session[];
771 selectedProject: Project | null;
772 selectedSession: Session | null;
773 onSelectProject: (project: Project | null) => void;
774 onSelectSession: (session: Session | null) => void;
775 onCreateProject: () => void;
776 onCreateSession: () => void;
777 onDeleteProject?: (id: string) => void;
778 onDeleteSession?: (id: string) => void;
779 theme: Theme;
780 onToggleTheme: () => void;
781}
782
783const phaseLabels: Record<Phase, string> = {
784 research: "Research",
785 plan: "Plan",
786 implement: "Implement",
787};
788
789const phases: Phase[] = ["research", "plan", "implement"];
790
791export function Header({
792 projects,
793 sessions,
794 selectedProject,
795 selectedSession,
796 onSelectProject,
797 onSelectSession,
798 onCreateProject,
799 onCreateSession,
800 onDeleteProject,
801 onDeleteSession,
802 theme,
803 onToggleTheme,
804}: HeaderProps) {
805 const handleDeleteProject = () => {
806 if (!selectedProject || !onDeleteProject) return;
807 if (confirm(`Delete project "${selectedProject.name}"? This cannot be undone.`)) {
808 onDeleteProject(selectedProject.id);
809 }
810 };
811
812 const handleDeleteSession = () => {
813 if (!selectedSession || !onDeleteSession) return;
814 if (confirm(`Delete session "${selectedSession.name}"? This cannot be undone.`)) {
815 onDeleteSession(selectedSession.id);
816 }
817 };
818
819 return (
820 <header className="header">
821 <div className="header-left">
822 {/* ── Wordmark ── */}
823 <span className="app-wordmark">Claude Flow</span>
824
825 <select
826 value={selectedProject?.id || ""}
827 onChange={(e) => {
828 const project = projects.find((p) => p.id === e.target.value);
829 onSelectProject(project || null);
830 onSelectSession(null);
831 }}
832 >
833 <option value="">Select Project...</option>
834 {projects.map((p) => (
835 <option key={p.id} value={p.id}>
836 {p.name}
837 </option>
838 ))}
839 </select>
840 <button onClick={onCreateProject}>+ Project</button>
841 {selectedProject && onDeleteProject && (
842 <button
843 onClick={handleDeleteProject}
844 className="btn-delete"
845 title="Delete project"
846 >
847 🗑️
848 </button>
849 )}
850
851 {selectedProject && (
852 <>
853 <select
854 value={selectedSession?.id || ""}
855 onChange={(e) => {
856 const session = sessions.find((s) => s.id === e.target.value);
857 onSelectSession(session || null);
858 }}
859 >
860 <option value="">Select Session...</option>
861 {sessions.map((s) => (
862 <option key={s.id} value={s.id}>
863 {s.name}
864 </option>
865 ))}
866 </select>
867 <button onClick={onCreateSession}>+ Session</button>
868 {selectedSession && onDeleteSession && (
869 <button
870 onClick={handleDeleteSession}
871 className="btn-delete"
872 title="Delete session"
873 >
874 🗑️
875 </button>
876 )}
877 </>
878 )}
879 </div>
880
881 <div className="header-right">
882 {selectedSession && (
883 <div className="phase-indicator">
884 {phases.map((phase) => {
885 const phaseIndex = phases.indexOf(phase);
886 const currentIndex = phases.indexOf(selectedSession.phase);
887 const isComplete = phaseIndex < currentIndex;
888 const isActive = phase === selectedSession.phase;
889
890 return (
891 <span
892 key={phase}
893 className={`phase-step ${isActive ? "active" : ""} ${
894 isComplete ? "complete" : ""
895 }`}
896 >
897 {phaseLabels[phase]}
898 </span>
899 );
900 })}
901 </div>
902 )}
903
904 {/* ── Theme toggle ── */}
905 <button className="theme-toggle" onClick={onToggleTheme}>
906 {theme === "dark" ? "[light]" : "[dark]"}
907 </button>
908 </div>
909 </header>
910 );
911}
912```
913
914---
915
916### 5. Dynamic CodeMirror Theme in DocumentPane
917**File:** `renderer/src/components/DocumentPane.tsx`
918**What:** Three surgical changes to `MarkdownEditor` — add `theme` prop, update imports, update `useEffect`.
919
920**5a. Update imports** — add `syntaxHighlighting` and `defaultHighlightStyle` to the `@codemirror/language` import:
921
922```typescript
923// BEFORE:
924import { markdown } from "@codemirror/lang-markdown";
925import { languages } from "@codemirror/language-data";
926
927// AFTER:
928import { markdown } from "@codemirror/lang-markdown";
929import { languages } from "@codemirror/language-data";
930import { syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
931```
932
933**5b. Update `MarkdownEditor` props interface** — add `theme`:
934
935```typescript
936// BEFORE:
937function MarkdownEditor({
938 content,
939 onChange,
940 disabled,
941}: {
942 content: string;
943 onChange: (content: string) => void;
944 disabled: boolean;
945})
946
947// AFTER:
948function MarkdownEditor({
949 content,
950 onChange,
951 disabled,
952 theme,
953}: {
954 content: string;
955 onChange: (content: string) => void;
956 disabled: boolean;
957 theme: "dark" | "light";
958})
959```
960
961**5c. Update the `useEffect` inside `MarkdownEditor`** — swap the theme extension and add `theme` to the dependency array:
962
963```typescript
964// BEFORE (inside useEffect):
965extensions: [
966 lineNumbers(),
967 highlightActiveLine(),
968 drawSelection(),
969 history(),
970 keymap.of([...defaultKeymap, ...historyKeymap]),
971 markdown({ codeLanguages: languages }),
972 oneDark, // ← hardcoded
973 updateListener,
974 EditorView.editable.of(!disabled),
975 EditorView.lineWrapping,
976 EditorView.theme({ ... }),
977],
978// dependency array:
979}, [disabled]);
980
981// AFTER:
982extensions: [
983 lineNumbers(),
984 highlightActiveLine(),
985 drawSelection(),
986 history(),
987 keymap.of([...defaultKeymap, ...historyKeymap]),
988 markdown({ codeLanguages: languages }),
989 theme === "dark" ? oneDark : syntaxHighlighting(defaultHighlightStyle), // ← dynamic
990 updateListener,
991 EditorView.editable.of(!disabled),
992 EditorView.lineWrapping,
993 EditorView.theme({ ... }),
994],
995// dependency array:
996}, [disabled, theme]); // ← theme added
997```
998
999**5d. Update `DocumentPaneProps` interface and pass `theme` through** — add to the interface and forward to `MarkdownEditor`:
1000
1001```typescript
1002// BEFORE:
1003interface DocumentPaneProps {
1004 content: string;
1005 onChange: (content: string) => void;
1006 phase: Phase;
1007 disabled: boolean;
1008 showOnboarding?: boolean;
1009}
1010
1011// AFTER:
1012interface DocumentPaneProps {
1013 content: string;
1014 onChange: (content: string) => void;
1015 phase: Phase;
1016 disabled: boolean;
1017 showOnboarding?: boolean;
1018 theme: "dark" | "light";
1019}
1020```
1021
1022And in the `DocumentPane` function body, destructure `theme` and pass it to `MarkdownEditor`:
1023
1024```tsx
1025// BEFORE:
1026export function DocumentPane({
1027 content,
1028 onChange,
1029 phase,
1030 disabled,
1031 showOnboarding,
1032}: DocumentPaneProps)
1033
1034// AFTER:
1035export function DocumentPane({
1036 content,
1037 onChange,
1038 phase,
1039 disabled,
1040 showOnboarding,
1041 theme,
1042}: DocumentPaneProps)
1043```
1044
1045```tsx
1046// BEFORE (in JSX):
1047<MarkdownEditor
1048 content={content}
1049 onChange={onChange}
1050 disabled={disabled}
1051/>
1052
1053// AFTER:
1054<MarkdownEditor
1055 content={content}
1056 onChange={onChange}
1057 disabled={disabled}
1058 theme={theme}
1059/>
1060```
1061
1062---
1063
1064## TODO
1065- [x] **1.** `renderer/index.html` — change `<title>minimal</title>` to `<title>Claude Flow</title>`
1066- [x] **2.** `renderer/src/styles/globals.css` — full replacement with new CSS (monospace body, electric accent, 2px radii, uppercase labels, light theme block, focus glow, `.app-wordmark`, `.theme-toggle`)
1067- [x] **3a.** `renderer/src/App.tsx` — add `type Theme` alias
1068- [x] **3b.** `renderer/src/App.tsx` — add `theme` state + `useEffect` + `handleToggleTheme`
1069- [x] **3c.** `renderer/src/App.tsx` — pass `theme`/`onToggleTheme` to `Header`, pass `theme` to `DocumentPane`
1070- [x] **4.** `renderer/src/components/Header.tsx` — full replacement (new props, wordmark, toggle button)
1071- [x] **5a.** `renderer/src/components/DocumentPane.tsx` — add `syntaxHighlighting, defaultHighlightStyle` import
1072- [x] **5b.** `renderer/src/components/DocumentPane.tsx` — add `theme` to `MarkdownEditor` props
1073- [x] **5c.** `renderer/src/components/DocumentPane.tsx` — dynamic theme extension + `theme` in dep array
1074- [x] **5d.** `renderer/src/components/DocumentPane.tsx` — add `theme` to `DocumentPaneProps`, destructure, forward to `MarkdownEditor`
1075
1076---
1077
1078## Risks / Considerations
1079
1080**CodeMirror reinitialization on theme switch**: Adding `theme` to the `useEffect` dependency array means the entire editor is torn down and recreated when the theme changes. This is intentional and correct — CodeMirror extensions are baked into the `EditorState` at creation time and can't be hot-swapped. The existing `content` sync `useEffect` will immediately restore the document contents after reinit, so no text loss occurs. There is a brief visual flash on theme toggle; this is acceptable.
1081
1082**`data-theme` initial state and flash of wrong theme**: The `useState` initializer reads `localStorage` synchronously, and the `useEffect` sets `data-theme` on first render. Because this happens before paint (Electron loads the renderer in a hidden window and only shows it after load), there should be no flash of the wrong theme in production. In dev the Vite HMR setup may briefly show unstyled content; this is not a concern for shipping.
1083
1084**Monospace font rendering differences per OS**: "SF Mono" ships with macOS/Xcode; "Cascadia Code" ships with Windows Terminal; "JetBrains Mono" and "Fira Code" are user-installed. The fallback chain is safe — `Monaco` is widely available on macOS, and `"Courier New", monospace` are universal final fallbacks. No font files are bundled; this is system-font-only.
1085
1086**`Theme` type duplication**: The `type Theme = "dark" | "light"` alias is defined in both `App.tsx` and `Header.tsx`. This is a minor smell. If it grows to more places, move it into `renderer/src/types.ts`. For this task (2 files) the duplication is acceptable.
1087
1088**Light mode CodeMirror gutter background**: The `.codemirror-editor .cm-gutters` rule uses `var(--bg-secondary)` which will automatically pick up the light-mode value — no extra change needed there.
1089
1090**`select` element styling in light mode**: Native `<select>` elements on macOS render with a system style that may look slightly inconsistent in light mode. This is an existing limitation of the app's native select usage and is out of scope for this task.
diff --git a/.claude-flow/research.md b/.claude-flow/research.md
deleted file mode 100644
index 30c4832..0000000
--- a/.claude-flow/research.md
+++ /dev/null
@@ -1,214 +0,0 @@
1# Research Findings
2
3## Overview
4
5This is an Electron + React + TypeScript app called "Claude Flow" that provides a structured AI-assisted coding workflow (Research → Plan → Implement). The styling is managed through a single monolithic CSS file using CSS custom properties. The current window title is "minimal" (a leftover from the starter template). There is no light/dark mode — only dark. The goal is to: rename the title, add a header wordmark, modernize the UI with a cipherpunk aesthetic (full monospace font, sharper borders, electric-blue accent), and add a text-based light/dark mode toggle.
6
7---
8
9## Architecture
10
11### Tech Stack
12- **Electron 38** — main process, window chrome, IPC
13- **React 19 + Vite** — renderer process
14- **TypeScript** throughout
15- **CodeMirror 6** — markdown editor in DocumentPane
16- **better-sqlite3** — local DB for projects/sessions/messages
17
18### Renderer Structure
19```
20renderer/
21 index.html ← <title>minimal</title> — MUST CHANGE to "Claude Flow"
22 src/
23 main.tsx ← React root mount
24 App.tsx ← Top-level state, layout
25 types.ts ← Shared TS types
26 styles/globals.css ← ALL styling lives here (CSS custom properties)
27 components/
28 Header.tsx ← Top bar: project/session selectors, phase indicator
29 DocumentPane.tsx ← Left pane: CodeMirror editor + ReactMarkdown preview
30 ChatPane.tsx ← Right pane: message list + text input
31 ActionBar.tsx ← Bottom bar: token usage bar, Review/Submit buttons
32```
33
34### Main Process Structure
35```
36src/main/
37 index.ts ← BrowserWindow creation (titleBarStyle: "hiddenInset"), IPC setup
38 preload.ts ← Exposes window.api bridge
39 ipc/handlers.ts
40 db/ ← SQLite schema/queries
41 claude/ ← Claude SDK integration
42```
43
44---
45
46## Relevant Files
47
48| File | Relevance to Task |
49|------|-------------------|
50| `renderer/index.html` | **Line 10**: `<title>minimal</title>` → change to `Claude Flow` |
51| `renderer/src/styles/globals.css` | **All CSS** — CSS custom properties `:root {}` define the entire theme. Single file, ~543 lines |
52| `renderer/src/components/Header.tsx` | Add app wordmark + text theme toggle button (header-right div) |
53| `renderer/src/App.tsx` | Add theme state, persist to localStorage, set `data-theme` on `document.documentElement`, pass props down |
54| `renderer/src/components/DocumentPane.tsx` | Make CodeMirror theme dynamic — swap `oneDark` ↔ `syntaxHighlighting(defaultHighlightStyle)` |
55| `src/main/index.ts` | `titleBarStyle: "hiddenInset"` — macOS frameless, HTML title used in Dock/app switcher |
56
57---
58
59## Key Insights
60
61### 1. Title Change — Two Locations
62The title "minimal" appears in:
63- `renderer/index.html` line 10: `<title>minimal</title>` — the browser/OS-level window title
64- `package.json` line 1: `"name": "minimal-electron-vite-react-better-sqlite"` — package name (lower-priority)
65- `package.json` line 19: `"appId": "com.example.minimalbsqlite"` — build identifier (lower-priority)
66
67**Primary fix**: change `<title>minimal</title>` in `renderer/index.html`.
68
69### 2. Current Color Palette (Dark Only)
70```css
71:root {
72 --bg-primary: #1a1a1a /* near-black background */
73 --bg-secondary: #252525 /* panels, header */
74 --bg-tertiary: #333 /* inputs, code blocks */
75 --border: #444 /* dividers */
76 --text-primary: #e0e0e0 /* main text */
77 --text-secondary:#888 /* muted text, labels */
78 --accent: #3b82f6 /* blue — buttons, links */
79 --accent-hover: #2563eb
80 --success: #10b981 /* green */
81 --warning: #f59e0b /* amber */
82 --danger: #ef4444 /* red */
83}
84```
85No light mode variables exist. The mechanism for light/dark is straightforward: add `html[data-theme="light"]` overrides.
86
87### 3. Light/Dark Mode — Implementation Path
88**Theme storage**: `localStorage` — persist across sessions
89**Toggle mechanism**: Set `data-theme="light"` or `data-theme="dark"` on `document.documentElement`
90**CSS**: Override variables under `html[data-theme="light"]` selector
91**State**: `useState` + `useEffect` in `App.tsx` — no Context needed, just pass `theme` + `onToggleTheme` as props
92**Toggle style**: Text-only, bracket notation: `[dark]` / `[light]` — fits the cipherpunk chrome feel
93
94The `Header` will receive `theme: "dark" | "light"` and `onToggleTheme: () => void` as new props.
95
96**CodeMirror — RESOLVED (no new packages needed)**:
97`@codemirror/language` is already installed as a transitive dependency and exports both `defaultHighlightStyle` and `syntaxHighlighting`. The `defaultHighlightStyle` provides a full light-mode syntax highlighting scheme (keywords in purple, strings in red, literals in green, etc.) — identical coverage to `oneDark`, just different colors.
98
99Strategy:
100- **Dark** → use `[oneDark]` as today
101- **Light** → use `[syntaxHighlighting(defaultHighlightStyle)]` from `@codemirror/language`
102
103`DocumentPane` will receive a `theme` prop. The CodeMirror `useEffect` dependency array will include `theme` (alongside the existing `disabled`) so the editor reinitializes with the correct extension set when the theme changes.
104
105### 4. Accent Color — RESOLVED
106
107**Keep blue, go more electric. No green/cyan.**
108
109The cipherpunk feel will come from sharpness and monospace typography — not a color gimmick.
110
111Proposed accent colors:
112```
113Dark mode: #60a5fa (bright electric blue — more vivid than current #3b82f6)
114 hover: #93c5fd
115Light mode: #2563eb (deeper blue — maintains contrast on light backgrounds)
116 hover: #1d4ed8
117```
118
119### 5. Font — RESOLVED: Full Monospace
120
121Apply a monospace font stack to `body` — the entire UI goes mono. The codebase already uses this stack for code blocks, so extending it to the whole UI creates a unified terminal aesthetic without importing any font files.
122
123```css
124body {
125 font-family: "SF Mono", "Cascadia Code", "JetBrains Mono", "Fira Code", Monaco, "Courier New", monospace;
126}
127```
128
129### 6. Header Wordmark — RESOLVED: Yes, Add It
130
131Add a styled `<span className="app-wordmark">CLAUDE FLOW</span>` as the **first element** inside `.header-left`, before the project dropdown. Separated from the controls by a subtle right border.
132
133```css
134.app-wordmark {
135 font-size: 13px;
136 font-weight: 700;
137 letter-spacing: 0.15em;
138 text-transform: uppercase;
139 color: var(--text-primary);
140 padding-right: 12px;
141 border-right: 1px solid var(--border);
142 margin-right: 4px;
143 user-select: none;
144 white-space: nowrap;
145}
146```
147
148### 7. Other Cipherpunk Touches — All CSS-Only
149
150- **Sharper borders**: reduce `border-radius` from 4–8px → **2px** on all controls (buttons, inputs, messages, badges). Phase steps → 2px. Chat messages → 4px.
151- **Uppercase meta labels**: `text-transform: uppercase; letter-spacing: 0.07em` on `.token-label`, `.phase-step`, `.document-header span`, `.badge`, `.implementing-status`
152- **Subtle focus glow**: `box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.3)` on focused inputs and buttons
153- **Phase brackets**: Phase step text to be updated in `Header.tsx` JSX: `[Research]` → `[RESEARCH]` etc., with CSS uppercase doing the work so the component just renders the labels naturally
154
155### 8. Light Mode Color Palette (Proposed)
156
157```css
158html[data-theme="light"] {
159 --bg-primary: #f4f4f2; /* warm off-white — not stark, feels terminal-adjacent */
160 --bg-secondary: #e8e8e5; /* panels, header */
161 --bg-tertiary: #d8d8d4; /* inputs, code blocks */
162 --border: #b4b4b0; /* dividers */
163 --text-primary: #1a1a18; /* near-black */
164 --text-secondary:#5a5a56; /* muted */
165 --accent: #2563eb; /* deep blue */
166 --accent-hover: #1d4ed8;
167 --success: #059669;
168 --warning: #d97706;
169 --danger: #dc2626;
170}
171```
172
173Warm stone tones rather than pure white — keeps the app from feeling like a generic SaaS product.
174
175### 9. No Existing Theme Infrastructure
176There is currently **no ThemeContext, no localStorage usage, no `data-theme` attribute**. Fully greenfield. Simple prop-passing from `App.tsx` is sufficient.
177
178### 10. Window Styling Note
179- **macOS** (`titleBarStyle: "hiddenInset"`): traffic lights appear, no visible title text in window frame. HTML `<title>` shows in Dock tooltip and app switcher.
180- **Linux/Windows**: default Electron title bar shows `<title>` in window chrome. Critical to fix.
181
182---
183
184## Resolved Decisions
185
186| Question | Decision |
187|----------|----------|
188| Accent color | Electric blue: `#60a5fa` (dark) / `#2563eb` (light). No green/cyan. |
189| CodeMirror light theme | `syntaxHighlighting(defaultHighlightStyle)` from already-installed `@codemirror/language`. Zero new packages. |
190| Font scope | **Full mono** — `body` font-family. Entire UI uses monospace stack. |
191| Header wordmark | **Yes** — `CLAUDE FLOW` in `.header-left`, uppercase + letter-spacing + right-border separator |
192| Theme toggle style | **Text toggle**: `[dark]` / `[light]` bracket notation |
193
194---
195
196## Proposed Change Scope
197
198| Change | File(s) | Effort |
199|--------|---------|--------|
200| Fix `<title>` | `renderer/index.html` | Trivial |
201| Apply monospace body font | `globals.css` | Trivial |
202| Sharpen border-radius across all controls | `globals.css` | Small |
203| Uppercase + letter-spacing on meta labels | `globals.css` | Small |
204| Brighter dark-mode accent (#60a5fa) | `globals.css` | Trivial |
205| Add `html[data-theme="light"]` color block | `globals.css` | Small |
206| Add focus glow styles | `globals.css` | Small |
207| Add `.app-wordmark` styles | `globals.css` | Trivial |
208| Add `[dark]`/`[light]` toggle button styles | `globals.css` | Trivial |
209| Add theme state + `localStorage` init | `App.tsx` | Small |
210| Pass `theme` + `onToggleTheme` props to Header | `App.tsx` → `Header.tsx` | Small |
211| Pass `theme` prop to DocumentPane | `App.tsx` → `DocumentPane.tsx` | Small |
212| Add `CLAUDE FLOW` wordmark element | `Header.tsx` | Trivial |
213| Add `[dark]`/`[light]` toggle button | `Header.tsx` | Small |
214| Make CodeMirror theme dynamic | `DocumentPane.tsx` | Small |