aboutsummaryrefslogtreecommitdiffstats
path: root/.claude-flow
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-28 15:26:02 -0800
committerClawd <ai@clawd.bot>2026-02-28 15:26:02 -0800
commit519d6b850e07a0387511a8f024dc394250b1a241 (patch)
tree5ee0a1bbbd7410f93a770e8129c88e29b5a5e5fb /.claude-flow
parentbebbffa890c38f12f04b6fafad4c5c5e46e051a8 (diff)
Clear artifacts when creating new session
Each new session now starts with empty research.md and plan.md files, preventing stale content from previous sessions appearing.
Diffstat (limited to '.claude-flow')
-rw-r--r--.claude-flow/plan.md1090
-rw-r--r--.claude-flow/research.md214
2 files changed, 1304 insertions, 0 deletions
diff --git a/.claude-flow/plan.md b/.claude-flow/plan.md
new file mode 100644
index 0000000..bbcae6a
--- /dev/null
+++ b/.claude-flow/plan.md
@@ -0,0 +1,1090 @@
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
new file mode 100644
index 0000000..30c4832
--- /dev/null
+++ b/.claude-flow/research.md
@@ -0,0 +1,214 @@
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 |