docs: add UI-rewrite notes, plans, and stream-formatter spec

This commit is contained in:
Mika Kuns
2026-04-21 15:56:19 +02:00
parent a180e8446c
commit 23f8fddc4d
16 changed files with 7165 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
// Diff modal + Worktree modal
const { useState: useStateM, useEffect: useEffectM } = window.React;
// Fake diff hunks per task
const DIFF_HUNKS = {
t1: [
{ file: 'src/middleware/auth.ts', adds: 48, dels: 22, hunks: [
{ header: '@@ -12,7 +12,9 @@ export function authMiddleware(', lines: [
{ k: 'ctx', n1: 12, n2: 12, t: ' const session = await getSession(req);' },
{ k: 'del', n1: 13, n2: null, t: ' if (!session) return unauthorized();' },
{ k: 'del', n1: 14, n2: null, t: ' const user = await lookupUser(session.userId);' },
{ k: 'add', n1: null, n2: 13, t: ' if (!session || session.expired) {' },
{ k: 'add', n1: null, n2: 14, t: ' return unauthorized("expired_or_missing");' },
{ k: 'add', n1: null, n2: 15, t: ' }' },
{ k: 'add', n1: null, n2: 16, t: ' const user = await pool.withConnection(c => lookupUser(c, session.userId));' },
{ k: 'ctx', n1: 15, n2: 17, t: ' req.user = user;' },
{ k: 'ctx', n1: 16, n2: 18, t: ' return next();' },
]},
{ header: '@@ -42,4 +44,6 @@ export function guard(', lines: [
{ k: 'ctx', n1: 42, n2: 44, t: ' return async (req, res, next) => {' },
{ k: 'del', n1: 43, n2: null, t: ' const s = await redis.get(req.cookies.sid);' },
{ k: 'add', n1: null, n2: 45, t: ' const s = await store.get(req.cookies.sid);' },
{ k: 'add', n1: null, n2: 46, t: ' if (s) store.touch(req.cookies.sid);' },
{ k: 'ctx', n1: 44, n2: 47, t: ' next();' },
]},
]},
{ file: 'src/lib/session/index.ts', adds: 31, dels: 14, hunks: [
{ header: '@@ -1,8 +1,14 @@', lines: [
{ k: 'del', n1: 1, n2: null, t: 'import { createClient } from "redis";' },
{ k: 'add', n1: null, n2: 1, t: 'import { SessionStore } from "./store";' },
{ k: 'add', n1: null, n2: 2, t: 'import { Pool } from "./pool";' },
{ k: 'ctx', n1: 2, n2: 3, t: '' },
{ k: 'del', n1: 3, n2: null, t: 'export const redis = createClient({ url: process.env.REDIS_URL });' },
{ k: 'add', n1: null, n2: 4, t: 'export const pool = new Pool({ size: 16 });' },
{ k: 'add', n1: null, n2: 5, t: 'export const store = new SessionStore(pool);' },
]},
]},
{ file: 'src/lib/session/ttl.ts', adds: 12, dels: 4, hunks: [] },
{ file: 'src/lib/session/store.ts', adds: 38, dels: 0, hunks: [] },
],
t2: [
{ file: 'src/pages/settings.tsx', adds: 32, dels: 2, hunks: [
{ header: '@@ -4,6 +4,8 @@ import { Section } from "../ui";', lines: [
{ k: 'ctx', n1: 4, n2: 4, t: 'import { useTheme } from "../hooks/useTheme";' },
{ k: 'add', n1: null, n2: 5, t: 'import { ThemeToggle } from "../ui/ThemeToggle";' },
{ k: 'ctx', n1: 5, n2: 6, t: '' },
{ k: 'ctx', n1: 6, n2: 7, t: 'export default function Settings() {' },
{ k: 'add', n1: null, n2: 8, t: ' const [theme, setTheme] = useTheme();' },
]},
]},
{ file: 'src/hooks/useTheme.ts', adds: 24, dels: 0, hunks: [] },
{ file: 'src/theme/tokens.css', adds: 10, dels: 8, hunks: [] },
{ file: 'src/ui/ThemeToggle.tsx', adds: 26, dels: 2, hunks: [] },
],
};
const DiffModal = ({ task, onClose }) => {
useEffectM(() => {
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [onClose]);
const files = DIFF_HUNKS[task.id] || [
{ file: 'No diff available yet', adds: 0, dels: 0, hunks: [] }
];
const [activeFile, setActiveFile] = useStateM(0);
const current = files[activeFile];
return (
<div className="modal-backdrop" onClick={onClose}>
<div className="modal diff-modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-head">
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<Icon name="diff" size={14} />
<div>
<div className="modal-title">Diff · {task.agent.branch}</div>
<div className="modal-sub">
{task.agent.worktree} · {files.length} files ·
<span className="add" style={{ marginLeft: 6 }}>+{task.agent.diff.additions}</span>
<span className="del" style={{ marginLeft: 6 }}>{task.agent.diff.deletions}</span>
</div>
</div>
</div>
<div style={{ display: 'flex', gap: 6 }}>
<button className="btn"><Icon name="external" size={12} /> Open in editor</button>
<button className="btn primary"><Icon name="check" size={12} /> Approve & merge</button>
<button className="icon-btn" onClick={onClose}><Icon name="close" size={12} /></button>
</div>
</div>
<div className="modal-body diff-body">
<div className="diff-sidebar">
{files.map((f, i) => (
<div key={f.file} className={`diff-file-tab ${i === activeFile ? 'active' : ''}`} onClick={() => setActiveFile(i)}>
<div className="diff-file-name" title={f.file}>{f.file}</div>
<div className="diff-file-stats">
<span className="add">+{f.adds}</span>
<span className="del">{f.dels}</span>
</div>
</div>
))}
</div>
<div className="diff-view">
<div className="diff-file-header">
<Icon name="note" size={12} />
<span>{current.file}</span>
<span style={{ marginLeft: 'auto' }} className="diff-stats">
<span className="add">+{current.adds}</span>
<span className="del">{current.dels}</span>
</span>
</div>
{current.hunks.length === 0 ? (
<div style={{ padding: 40, textAlign: 'center', color: 'var(--text-faint)', fontFamily: 'var(--mono)', fontSize: 11 }}>
Select a hunk no detail preview available for this file.
</div>
) : current.hunks.map((h, hi) => (
<div key={hi} className="diff-hunk">
<div className="diff-hunk-header">{h.header}</div>
{h.lines.map((ln, li) => (
<div key={li} className={`diff-line ${ln.k}`}>
<span className="ln">{ln.n1 ?? ''}</span>
<span className="ln">{ln.n2 ?? ''}</span>
<span className="sign">{ln.k === 'add' ? '+' : ln.k === 'del' ? '' : ' '}</span>
<span className="t">{ln.t}</span>
</div>
))}
</div>
))}
</div>
</div>
</div>
</div>
);
};
const WorktreeModal = ({ task, onClose }) => {
useEffectM(() => {
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [onClose]);
const fakeTree = [
{ kind: 'dir', path: 'src', children: [
{ kind: 'dir', path: 'middleware', children: [
{ kind: 'file', path: 'auth.ts', mod: true },
]},
{ kind: 'dir', path: 'lib/session', children: [
{ kind: 'file', path: 'index.ts', mod: true },
{ kind: 'file', path: 'ttl.ts', mod: true },
{ kind: 'file', path: 'store.ts', added: true },
]},
]},
{ kind: 'file', path: 'package.json' },
{ kind: 'file', path: 'README.md' },
];
const render = (nodes, depth = 0) => nodes.map((n) => (
n.kind === 'dir' ? (
<React.Fragment key={n.path}>
<div className="tree-row" style={{ paddingLeft: 10 + depth * 14 }}>
<Icon name="folder" size={12} /> <span>{n.path}</span>
</div>
{render(n.children, depth + 1)}
</React.Fragment>
) : (
<div key={n.path} className={`tree-row ${n.mod ? 'mod' : ''} ${n.added ? 'added' : ''}`} style={{ paddingLeft: 10 + depth * 14 }}>
<Icon name="note" size={12} />
<span>{n.path}</span>
{n.mod && <span className="tree-badge mod">M</span>}
{n.added && <span className="tree-badge add">A</span>}
</div>
)
));
return (
<div className="modal-backdrop" onClick={onClose}>
<div className="modal worktree-modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-head">
<div>
<div className="modal-title"><Icon name="folder-open" size={14} /> {task.agent.worktree}</div>
<div className="modal-sub">{task.agent.branch} {task.agent.baseBranch}</div>
</div>
<div style={{ display: 'flex', gap: 6 }}>
<button className="btn"><Icon name="terminal" size={12} /> Open terminal</button>
<button className="icon-btn" onClick={onClose}><Icon name="close" size={12} /></button>
</div>
</div>
<div className="modal-body" style={{ padding: 0, display: 'flex', flexDirection: 'column' }}>
<div style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)', fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--text-mute)' }}>
Filesystem preview modified files marked <span style={{ color: 'var(--peat)' }}>M</span>, additions <span style={{ color: 'var(--moss-bright)' }}>A</span>
</div>
<div style={{ flex: 1, overflowY: 'auto', padding: 8, fontFamily: 'var(--mono)', fontSize: 12 }}>
{render(fakeTree)}
</div>
</div>
</div>
</div>
);
};
window.DiffModal = DiffModal;
window.WorktreeModal = WorktreeModal;