@dendelion/paper-camp 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/assets/index-BMsByqaB.js +3 -0
- package/dist/app/assets/index-BMsByqaB.js.map +1 -0
- package/dist/app/assets/index-Bwc1cGPX.js +6 -0
- package/dist/app/assets/index-Bwc1cGPX.js.map +1 -0
- package/dist/app/assets/index-CQB12bDI.js +2 -0
- package/dist/app/assets/index-CQB12bDI.js.map +1 -0
- package/dist/app/assets/index-D-k_o5eq.js +2 -0
- package/dist/app/assets/index-D-k_o5eq.js.map +1 -0
- package/dist/app/assets/index-D6bamXbC.js +2 -0
- package/dist/app/assets/index-D6bamXbC.js.map +1 -0
- package/dist/app/assets/index-V3ou3lzT.js +4 -0
- package/dist/app/assets/index-V3ou3lzT.js.map +1 -0
- package/dist/app/assets/main-0sfOZaxl.js +207 -0
- package/dist/app/assets/main-0sfOZaxl.js.map +1 -0
- package/dist/app/assets/main-CZ1b3UwB.css +1 -0
- package/dist/app/assets/sidebar-section-DLMrMf9C.js +2 -0
- package/dist/app/assets/sidebar-section-DLMrMf9C.js.map +1 -0
- package/dist/app/assets/tokens-C9JyAXxm.js +2 -0
- package/dist/app/assets/tokens-C9JyAXxm.js.map +1 -0
- package/dist/app/index.html +2 -2
- package/dist/chunks/{scaffold.B_3sY6Rg.js → scaffold.CuJP4koY.js} +1832 -1914
- package/dist/chunks/scaffold.CuJP4koY.js.map +1 -0
- package/dist/cli/index.js +8845 -8571
- package/dist/cli/index.js.map +1 -1
- package/dist/core/git-pr/pr-lookup.d.ts.map +1 -1
- package/dist/core/git-pr/pr.d.ts.map +1 -1
- package/dist/core/index.js +74 -82
- package/dist/core/parse/parser.d.ts +2 -4
- package/dist/core/parse/parser.d.ts.map +1 -1
- package/dist/core/parse/schemas.d.ts +0 -20
- package/dist/core/parse/schemas.d.ts.map +1 -1
- package/dist/core/readers.d.ts.map +1 -1
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/scaffold/templates.d.ts +1 -1
- package/dist/core/scaffold/templates.d.ts.map +1 -1
- package/dist/core/sections.d.ts +5 -1
- package/dist/core/sections.d.ts.map +1 -1
- package/dist/core/serialize/serializer.d.ts +4 -26
- package/dist/core/serialize/serializer.d.ts.map +1 -1
- package/dist/core/stats.d.ts +10 -0
- package/dist/core/stats.d.ts.map +1 -0
- package/dist/core/stats.test.d.ts +2 -0
- package/dist/core/stats.test.d.ts.map +1 -0
- package/dist/core/status/status.d.ts +1 -0
- package/dist/core/status/status.d.ts.map +1 -1
- package/dist/core/thread.d.ts +15 -0
- package/dist/core/thread.d.ts.map +1 -0
- package/dist/types/index.d.ts +92 -45
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +9 -4
- package/dist/app/assets/index-B6eOrr41.js +0 -4
- package/dist/app/assets/index-B6eOrr41.js.map +0 -1
- package/dist/app/assets/index-BajRXbXg.js +0 -2
- package/dist/app/assets/index-BajRXbXg.js.map +0 -1
- package/dist/app/assets/index-DAexv1XS.js +0 -3
- package/dist/app/assets/index-DAexv1XS.js.map +0 -1
- package/dist/app/assets/index-DFJlDY3W.js +0 -2
- package/dist/app/assets/index-DFJlDY3W.js.map +0 -1
- package/dist/app/assets/main-Bno8vU-0.js +0 -288
- package/dist/app/assets/main-Bno8vU-0.js.map +0 -1
- package/dist/app/assets/main-RP3LpwgH.css +0 -1
- package/dist/app/assets/sidebar-section-BrJbBK_b.js +0 -2
- package/dist/app/assets/sidebar-section-BrJbBK_b.js.map +0 -1
- package/dist/chunks/scaffold.B_3sY6Rg.js.map +0 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{u as h,z as v,r,j as e,P as y,w as u,x as L,U as S,B as T,J as A}from"./main-0sfOZaxl.js";import{c as j}from"./tokens-C9JyAXxm.js";const _={phase:"Phase run",audit:"Audit","batch-reconcile":"Batch reconcile","run-all":"Run all phases",draft:"Draft",extend:"Extend",suggest:"Suggest ideas","commit-suggest":"Commit suggest","overlap-check":"Overlap check",prioritise:"Prioritise queue",sync:"Sync",reconcile:"Reconcile","fix-review":"Fix review","resolve-conflict":"Resolve conflict",feedback:"Feedback reply"},m=s=>String(s).padStart(2,"0"),b=s=>{const a=new Date(s);return`${a.getFullYear()}-${m(a.getMonth()+1)}-${m(a.getDate())}`},D=s=>new Date(s).toLocaleDateString(void 0,{weekday:"long",year:"numeric",month:"long",day:"numeric"}),k=s=>{const a=new Date(s);return`${m(a.getHours())}:${m(a.getMinutes())}:${m(a.getSeconds())}`},p="text-sm font-semibold whitespace-nowrap overflow-hidden",N="grid grid-cols-[20px_116px_minmax(0,1fr)_88px_150px_72px] gap-2.5 items-center max-lg:grid-cols-[20px_116px_minmax(0,1fr)_88px_72px] max-[480px]:grid-cols-1 max-[480px]:gap-1",E=({id:s})=>{const[a,c]=r.useState(null),[l,i]=r.useState(!1),[o,x]=r.useState(0);r.useEffect(()=>{let n=!1,d;c(null),i(!1);const f=g=>{T(s).then(w=>{n||c(w.lines??[])}).catch(()=>{if(!n){if(g>0){d=setTimeout(()=>f(g-1),700);return}i(!0),c([])}})};return f(3),()=>{n=!0,d&&clearTimeout(d)}},[s,o]);const t=e.jsx(A,{variant:"ghost",size:"small",onClick:()=>x(n=>n+1),children:"Retry"});return a===null?e.jsx("p",{className:"opacity-50 m-0",children:"Loading…"}):l?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("p",{className:"opacity-50 m-0",children:"Couldn't load this task's output."}),t]}):a.length===0?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("p",{className:"opacity-50 m-0",children:"No output recorded."}),t]}):e.jsx("pre",{className:"font-mono text-xs m-0 max-h-[320px] overflow-y-auto whitespace-pre-wrap",children:a.join(`
|
|
2
|
+
`)})},R=({size:s=14,className:a})=>e.jsx("svg",{width:s,height:s,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",className:a,children:e.jsx("polyline",{points:"9 18 15 12 9 6"})}),$=({entry:s,highlighted:a})=>{const[c,l]=r.useState(a);r.useEffect(()=>{a&&l(!0)},[a]);const i=()=>l(o=>!o);return e.jsxs("div",{className:a?"task-row-highlighted flex flex-col gap-1 rounded-[10px] outline outline-2 outline-offset-[-2px] outline-[rgba(200,154,90,0.5)]":"flex flex-col gap-1 rounded-[10px]",children:[e.jsx("div",{role:"button",tabIndex:0,"aria-expanded":c,onClick:i,onKeyDown:o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),i())},className:"group cursor-pointer rounded-[10px]",children:e.jsx(u,{size:"small",texture:"canvas",className:"plan-row-card",children:e.jsxs("div",{className:N,children:[e.jsx("span",{className:"inline-flex items-center opacity-50",children:e.jsx(R,{className:"transition-transform duration-150 ease-out group-aria-expanded:rotate-90"})}),e.jsx("span",{className:"font-semibold whitespace-nowrap overflow-hidden",children:_[s.taskKind]??s.taskKind}),e.jsx("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap opacity-70",children:s.planTitle}),e.jsx("span",{className:"text-sm opacity-50 whitespace-nowrap",children:L[s.agentId]}),e.jsxs("span",{className:"max-lg:hidden font-mono text-xs opacity-[0.55] whitespace-nowrap",children:[k(s.startedAt),"–",k(s.endedAt)]}),e.jsx("div",{className:"flex items-center",children:e.jsx(S,{size:"small",fillColor:s.outcome==="done"?"rgba(143, 185, 150, 0.25)":"rgba(201, 139, 139, 0.25)",textColor:s.outcome==="done"?j.accentGreenDark:j.accentRoseDark,children:s.outcome})})]})})}),c&&e.jsx(u,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsx(E,{id:s.id})})]})},K=()=>{const s=h(t=>t.taskLog),a=h(t=>t.taskLogLoading),c=h(t=>t.loadTaskLog),{taskId:l}=v({from:"/tasks"}),i=r.useRef(null);r.useEffect(()=>{c()},[c]),r.useEffect(()=>{var n;if(!l)return;const t=(n=i.current)==null?void 0:n.querySelector(".task-row-highlighted");t==null||t.scrollIntoView({behavior:"smooth",block:"center"})},[l]);const o=[...s].sort((t,n)=>n.endedAt.localeCompare(t.endedAt)),x=[];for(const t of o){const n=b(t.endedAt),d=x.at(-1);(d==null?void 0:d.key)===n?d.entries.push(t):x.push({key:n,entries:[t]})}return e.jsxs("div",{ref:i,children:[e.jsx(y,{children:"Tasks"}),a&&e.jsx("p",{className:"opacity-50",children:"Loading…"}),!a&&o.length===0&&e.jsx("p",{className:"opacity-50",children:"No tasks have run yet."}),!a&&o.length>0&&e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(u,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:N,children:[e.jsx("span",{}),e.jsx("span",{className:`${p} opacity-60`,children:"Task"}),e.jsx("span",{className:`${p} opacity-60`,children:"Plan"}),e.jsx("span",{className:`${p} opacity-60`,children:"Agent"}),e.jsx("span",{className:`max-lg:hidden ${p} opacity-60`,children:"Time"}),e.jsx("span",{className:`${p} opacity-60`,children:"Outcome"})]})}),x.map(t=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(u,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsx("span",{className:`${p} opacity-75`,children:D(t.entries[0].endedAt)})}),t.entries.map(n=>e.jsx($,{entry:n,highlighted:n.id===l},n.id))]},t.key))]})]})};export{K as TasksPage};
|
|
3
|
+
//# sourceMappingURL=index-BMsByqaB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BMsByqaB.js","sources":["../../../src/app/features/tasks/tasks-page.tsx"],"sourcesContent":["import { PageTitle } from '@/app/components/page-title';\nimport { fetchTaskLogLines } from '@/app/services/content/docs-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { color } from '@/app/styles/tokens';\nimport { AGENT_LABELS, type TaskKind, type TaskLogEntry } from '@/types/index';\nimport { Button, Card, Stamp } from '@dendelion/paper-ui';\nimport { useSearch } from '@tanstack/react-router';\nimport { useEffect, useRef, useState } from 'react';\n\nconst TASK_KIND_LABELS: Record<TaskKind, string> = {\n phase: 'Phase run',\n audit: 'Audit',\n 'batch-reconcile': 'Batch reconcile',\n 'run-all': 'Run all phases',\n draft: 'Draft',\n extend: 'Extend',\n suggest: 'Suggest ideas',\n 'commit-suggest': 'Commit suggest',\n 'overlap-check': 'Overlap check',\n prioritise: 'Prioritise queue',\n sync: 'Sync',\n reconcile: 'Reconcile',\n 'fix-review': 'Fix review',\n 'resolve-conflict': 'Resolve conflict',\n feedback: 'Feedback reply',\n};\n\nconst pad = (n: number) => String(n).padStart(2, '0');\n\nconst dayKey = (iso: string) => {\n const d = new Date(iso);\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n};\n\nconst formatDayHeader = (iso: string) =>\n new Date(iso).toLocaleDateString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n\nconst formatTime = (iso: string) => {\n const d = new Date(iso);\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;\n};\n\nconst headerLabelClassName = 'text-sm font-semibold whitespace-nowrap overflow-hidden';\n\nconst TASK_ROWS_GRID_CLASS =\n 'grid grid-cols-[20px_116px_minmax(0,1fr)_88px_150px_72px] gap-2.5 items-center max-lg:grid-cols-[20px_116px_minmax(0,1fr)_88px_72px] max-[480px]:grid-cols-1 max-[480px]:gap-1';\n\nconst TaskLogLines = ({ id }: { id: string }) => {\n const [lines, setLines] = useState<string[] | null>(null);\n const [failed, setFailed] = useState(false);\n const [attempt, setAttempt] = useState(0);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: attempt is the retry trigger (re-runs the fetch), not a value read in the body.\n useEffect(() => {\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n setLines(null);\n setFailed(false);\n // Auto-retry first (dev server briefly drops requests on hot reload); manual Retry only if it stays down.\n const load = (remaining: number) => {\n fetchTaskLogLines(id)\n .then((data) => {\n if (cancelled) return;\n setLines(data.lines ?? []);\n })\n .catch(() => {\n if (cancelled) return;\n if (remaining > 0) {\n timer = setTimeout(() => load(remaining - 1), 700);\n return;\n }\n // Empty result != failed request: conflating them mislabels a fetch error as \"no output\".\n setFailed(true);\n setLines([]);\n });\n };\n load(3);\n return () => {\n cancelled = true;\n if (timer) clearTimeout(timer);\n };\n }, [id, attempt]);\n\n const retry = (\n <Button variant=\"ghost\" size=\"small\" onClick={() => setAttempt((n) => n + 1)}>\n Retry\n </Button>\n );\n\n if (lines === null) return <p className=\"opacity-50 m-0\">Loading…</p>;\n if (failed)\n return (\n <div className=\"flex items-center gap-2\">\n <p className=\"opacity-50 m-0\">Couldn't load this task's output.</p>\n {retry}\n </div>\n );\n if (lines.length === 0)\n return (\n <div className=\"flex items-center gap-2\">\n <p className=\"opacity-50 m-0\">No output recorded.</p>\n {retry}\n </div>\n );\n return (\n <pre className=\"font-mono text-xs m-0 max-h-[320px] overflow-y-auto whitespace-pre-wrap\">\n {lines.join('\\n')}\n </pre>\n );\n};\n\nconst ChevronRightIcon = ({ size = 14, className }: { size?: number; className?: string }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className={className}\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\nconst TaskRow = ({ entry, highlighted }: { entry: TaskLogEntry; highlighted: boolean }) => {\n const [expanded, setExpanded] = useState(highlighted);\n useEffect(() => {\n if (highlighted) setExpanded(true);\n }, [highlighted]);\n const toggle = () => setExpanded((v) => !v);\n\n return (\n <div\n className={\n highlighted\n ? 'task-row-highlighted flex flex-col gap-1 rounded-[10px] outline outline-2 outline-offset-[-2px] outline-[rgba(200,154,90,0.5)]'\n : 'flex flex-col gap-1 rounded-[10px]'\n }\n >\n {/* biome-ignore lint/a11y/useSemanticElements: the clickable row wraps a Card whose block layout a native <button> would break; keyboard toggling is wired on the div. */}\n <div\n role=\"button\"\n tabIndex={0}\n aria-expanded={expanded}\n onClick={toggle}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle();\n }\n }}\n className=\"group cursor-pointer rounded-[10px]\"\n >\n <Card size=\"small\" texture=\"canvas\" className=\"plan-row-card\">\n <div className={TASK_ROWS_GRID_CLASS}>\n <span className=\"inline-flex items-center opacity-50\">\n <ChevronRightIcon className=\"transition-transform duration-150 ease-out group-aria-expanded:rotate-90\" />\n </span>\n <span className=\"font-semibold whitespace-nowrap overflow-hidden\">\n {TASK_KIND_LABELS[entry.taskKind] ?? entry.taskKind}\n </span>\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap opacity-70\">\n {entry.planTitle}\n </span>\n <span className=\"text-sm opacity-50 whitespace-nowrap\">\n {AGENT_LABELS[entry.agentId]}\n </span>\n <span className=\"max-lg:hidden font-mono text-xs opacity-[0.55] whitespace-nowrap\">\n {formatTime(entry.startedAt)}–{formatTime(entry.endedAt)}\n </span>\n <div className=\"flex items-center\">\n <Stamp\n size=\"small\"\n fillColor={\n entry.outcome === 'done'\n ? 'rgba(143, 185, 150, 0.25)'\n : 'rgba(201, 139, 139, 0.25)'\n }\n textColor={entry.outcome === 'done' ? color.accentGreenDark : color.accentRoseDark}\n >\n {entry.outcome}\n </Stamp>\n </div>\n </div>\n </Card>\n </div>\n {expanded && (\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <TaskLogLines id={entry.id} />\n </Card>\n )}\n </div>\n );\n};\n\nexport const TasksPage = () => {\n const taskLog = useAppStore((s) => s.taskLog);\n const taskLogLoading = useAppStore((s) => s.taskLogLoading);\n const loadTaskLog = useAppStore((s) => s.loadTaskLog);\n const { taskId } = useSearch({ from: '/tasks' });\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n loadTaskLog();\n }, [loadTaskLog]);\n\n useEffect(() => {\n if (!taskId) return;\n const row = containerRef.current?.querySelector('.task-row-highlighted');\n row?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, [taskId]);\n\n const sorted = [...taskLog].sort((a, b) => b.endedAt.localeCompare(a.endedAt));\n\n const groups: { key: string; entries: TaskLogEntry[] }[] = [];\n for (const entry of sorted) {\n const key = dayKey(entry.endedAt);\n const group = groups.at(-1);\n if (group?.key === key) group.entries.push(entry);\n else groups.push({ key, entries: [entry] });\n }\n\n return (\n <div ref={containerRef}>\n <PageTitle>Tasks</PageTitle>\n {taskLogLoading && <p className=\"opacity-50\">Loading…</p>}\n {!taskLogLoading && sorted.length === 0 && (\n <p className=\"opacity-50\">No tasks have run yet.</p>\n )}\n {!taskLogLoading && sorted.length > 0 && (\n <div className=\"flex flex-col gap-1\">\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <div className={TASK_ROWS_GRID_CLASS}>\n <span />\n <span className={`${headerLabelClassName} opacity-60`}>Task</span>\n <span className={`${headerLabelClassName} opacity-60`}>Plan</span>\n <span className={`${headerLabelClassName} opacity-60`}>Agent</span>\n <span className={`max-lg:hidden ${headerLabelClassName} opacity-60`}>Time</span>\n <span className={`${headerLabelClassName} opacity-60`}>Outcome</span>\n </div>\n </Card>\n {groups.map((group) => (\n <div key={group.key} className=\"flex flex-col gap-1\">\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <span className={`${headerLabelClassName} opacity-75`}>\n {formatDayHeader(group.entries[0].endedAt)}\n </span>\n </Card>\n {group.entries.map((entry) => (\n <TaskRow key={entry.id} entry={entry} highlighted={entry.id === taskId} />\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n};\n"],"names":["TASK_KIND_LABELS","pad","n","dayKey","iso","d","formatDayHeader","formatTime","headerLabelClassName","TASK_ROWS_GRID_CLASS","TaskLogLines","id","lines","setLines","useState","failed","setFailed","attempt","setAttempt","useEffect","cancelled","timer","load","remaining","fetchTaskLogLines","data","retry","jsx","Button","jsxs","ChevronRightIcon","size","className","TaskRow","entry","highlighted","expanded","setExpanded","toggle","v","e","Card","AGENT_LABELS","Stamp","color","TasksPage","taskLog","useAppStore","s","taskLogLoading","loadTaskLog","taskId","useSearch","containerRef","useRef","row","_a","sorted","a","b","groups","key","group","PageTitle"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{j as e,w as m,U as h,i as f,a as j,r as d,O as u,y as g}from"./main-0sfOZaxl.js";function N(s){if(!s)return[];const t=(s.endsWith(`
|
|
2
|
+
`)?s.slice(0,-1):s).split(`
|
|
3
|
+
`),a=[];let l=null;for(const i of t){if(i.startsWith("@@ ")){l={header:i,lines:[]},a.push(l);continue}l&&(i.startsWith("+")?l.lines.push({type:"add",text:i.slice(1)}):i.startsWith("-")?l.lines.push({type:"remove",text:i.slice(1)}):l.lines.push({type:"context",text:i.startsWith(" ")?i.slice(1):i}))}return a}function w(s){return s?[{header:"",lines:(s.endsWith(`
|
|
4
|
+
`)?s.slice(0,-1):s).split(`
|
|
5
|
+
`).map(a=>({type:"add",text:a}))}]:[]}const v={add:"bg-watercolor-green/[18%] text-watercolor-green-dark",remove:"bg-watercolor-rose/[18%] text-watercolor-rose-dark",context:""},b={add:"+",remove:"-",context:" "},k=({additions:s,deletions:t})=>e.jsxs("span",{className:"inline-flex shrink-0 gap-2 font-mono text-2xs",children:[e.jsxs("span",{className:"text-watercolor-green-dark",children:["+",s]}),e.jsxs("span",{className:"text-watercolor-rose-dark",children:["-",t]})]}),C=({entry:s})=>e.jsxs("div",{className:"flex w-full min-w-0 items-center justify-between gap-3",children:[e.jsx("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap font-mono text-xs",children:s.renameSource?`${s.renameSource} → ${s.path}`:s.path}),e.jsxs("span",{className:"flex shrink-0 items-center gap-2",children:[s.staged&&e.jsx(h,{size:"small",children:"staged"}),!s.binary&&e.jsx(k,{additions:s.additions,deletions:s.deletions})]})]}),S=({entry:s})=>{if(s.binary)return e.jsx("p",{className:"m-0 opacity-60",children:"Binary file not shown."});if(s.contentKind==="too-large")return e.jsx("p",{className:"m-0 opacity-60",children:"File too large to preview."});const t=s.contentKind==="raw"?w(s.patch):N(s.patch);return t.length===0?s.renameSource?e.jsx("p",{className:"m-0 opacity-60",children:s.additions===0&&s.deletions===0?"Renamed, no content changes.":"Renamed with unrelated content — diff omitted."}):e.jsx("p",{className:"m-0 opacity-60",children:"No changes to preview."}):e.jsx("div",{className:"flex flex-col gap-3",children:t.map((a,l)=>e.jsxs("div",{children:[a.header&&e.jsx("div",{className:"mb-1 font-mono text-2xs opacity-50",children:a.header}),e.jsx("pre",{className:"m-0 overflow-x-auto font-mono text-2xs",children:a.lines.map((i,c)=>e.jsxs("span",{className:`block ${v[i.type]}`,children:[b[i.type],i.text]},`${i.type}-${c}`))})]},`${a.header}-${l}`))})},F=({entry:s,sectionRef:t})=>e.jsx("div",{ref:t,className:"scroll-mt-4",children:e.jsxs(m,{children:[e.jsx("div",{className:"mb-3",children:e.jsx(C,{entry:s})}),e.jsx(S,{entry:s})]})}),$=({additions:s,deletions:t})=>e.jsxs("span",{className:"inline-flex gap-2 font-mono text-2xs",children:[e.jsxs("span",{className:"text-watercolor-green-dark",children:["+",s]}),e.jsxs("span",{className:"text-watercolor-rose-dark",children:["-",t]})]}),R=({files:s,onSelect:t})=>e.jsx("div",{className:"sticky top-4 flex w-[288px] shrink-0 flex-col gap-1 max-h-page overflow-y-auto",children:s.map(a=>e.jsx(f,{size:"small",onClick:()=>t(a.path),action:e.jsxs("span",{className:"flex items-center gap-2",children:[a.staged&&e.jsx(h,{size:"small",children:"staged"}),!a.binary&&e.jsx($,{additions:a.additions,deletions:a.deletions})]}),children:e.jsx("span",{className:"block overflow-hidden text-ellipsis whitespace-nowrap font-mono text-2xs",children:a.path})},a.path))}),L=()=>{const s=j(),[t,a]=d.useState(null),[l,i]=d.useState(!1),c=d.useRef(new Map);d.useEffect(()=>{u().then(a).catch(()=>i(!0))},[]);const x=e.jsx("div",{className:"mb-4",children:e.jsx(g,{items:[{id:"plans",label:"Plans",onClick:()=>s({to:"/"})},{id:"changes",label:"Changes"}]})}),r="min-h-page";if(l)return e.jsxs("div",{children:[x,e.jsx("div",{className:r,children:e.jsx("p",{className:"opacity-50",children:"Couldn't load the working-tree diff."})})]});if(!t)return e.jsxs("div",{children:[x,e.jsx("div",{className:r,children:e.jsx("p",{className:"opacity-50",children:"Loading…"})})]});const p=n=>{var o;(o=c.current.get(n))==null||o.scrollIntoView({behavior:"smooth",block:"start"})};return e.jsxs("div",{children:[x,t.length===0?e.jsx("div",{className:r,children:e.jsx("p",{className:"opacity-50",children:"No changed files."})}):e.jsxs("div",{className:`flex items-start gap-6 ${r}`,children:[e.jsx(R,{files:t,onSelect:p}),e.jsx("div",{className:"flex min-w-0 flex-1 flex-col gap-6",children:t.map(n=>e.jsx(F,{entry:n,sectionRef:o=>{o?c.current.set(n.path,o):c.current.delete(n.path)}},n.path))})]})]})};export{L as DiffPage};
|
|
6
|
+
//# sourceMappingURL=index-Bwc1cGPX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Bwc1cGPX.js","sources":["../../../src/app/utils/parse-diff.ts","../../../src/app/features/diff/file-diff-card.tsx","../../../src/app/features/diff/file-list-sidebar.tsx","../../../src/app/features/diff/diff-page.tsx"],"sourcesContent":["export type DiffLineType = 'add' | 'remove' | 'context';\n\nexport interface DiffLine {\n type: DiffLineType;\n text: string;\n}\n\nexport interface DiffHunk {\n header: string;\n lines: DiffLine[];\n}\n\n// Parses a unified diff (git.ts getWorkingDiff, contentKind: 'diff'). A real diff\n// with no hunks (pure rename, mode-only change) is just the preamble, so it yields [].\nexport function parsePatch(patch: string): DiffHunk[] {\n if (!patch) return [];\n const lines = (patch.endsWith('\\n') ? patch.slice(0, -1) : patch).split('\\n');\n\n const hunks: DiffHunk[] = [];\n let current: DiffHunk | null = null;\n for (const line of lines) {\n if (line.startsWith('@@ ')) {\n current = { header: line, lines: [] };\n hunks.push(current);\n continue;\n }\n if (!current) continue; // preamble: diff --git / index / --- / +++\n if (line.startsWith('+')) current.lines.push({ type: 'add', text: line.slice(1) });\n else if (line.startsWith('-')) current.lines.push({ type: 'remove', text: line.slice(1) });\n else current.lines.push({ type: 'context', text: line.startsWith(' ') ? line.slice(1) : line });\n }\n return hunks;\n}\n\n// Untracked files store raw content, not a diff (git.ts getWorkingDiff, contentKind:\n// 'raw') — synthesize a single all-added hunk so the renderer only has one shape to draw.\nexport function rawContentHunks(content: string): DiffHunk[] {\n if (!content) return [];\n const lines = (content.endsWith('\\n') ? content.slice(0, -1) : content).split('\\n');\n return [{ header: '', lines: lines.map((text) => ({ type: 'add', text })) }];\n}\n","import { type DiffLineType, parsePatch, rawContentHunks } from '@/app/utils/parse-diff';\nimport type { FileDiffEntry } from '@/types/index';\nimport { Card, Stamp } from '@dendelion/paper-ui';\n\nconst LINE_CLASS: Record<DiffLineType, string> = {\n add: 'bg-watercolor-green/[18%] text-watercolor-green-dark',\n remove: 'bg-watercolor-rose/[18%] text-watercolor-rose-dark',\n context: '',\n};\n\nconst LINE_PREFIX: Record<DiffLineType, string> = { add: '+', remove: '-', context: ' ' };\n\ninterface CountBadgeProps {\n additions: number;\n deletions: number;\n}\n\nconst CountBadge = ({ additions, deletions }: CountBadgeProps) => (\n <span className=\"inline-flex shrink-0 gap-2 font-mono text-2xs\">\n <span className=\"text-watercolor-green-dark\">+{additions}</span>\n <span className=\"text-watercolor-rose-dark\">-{deletions}</span>\n </span>\n);\n\ninterface CardTitleProps {\n entry: FileDiffEntry;\n}\n\nconst CardTitle = ({ entry }: CardTitleProps) => (\n <div className=\"flex w-full min-w-0 items-center justify-between gap-3\">\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap font-mono text-xs\">\n {entry.renameSource ? `${entry.renameSource} → ${entry.path}` : entry.path}\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {entry.staged && <Stamp size=\"small\">staged</Stamp>}\n {!entry.binary && <CountBadge additions={entry.additions} deletions={entry.deletions} />}\n </span>\n </div>\n);\n\ninterface DiffBodyProps {\n entry: FileDiffEntry;\n}\n\nconst DiffBody = ({ entry }: DiffBodyProps) => {\n if (entry.binary) {\n return <p className=\"m-0 opacity-60\">Binary file not shown.</p>;\n }\n if (entry.contentKind === 'too-large') {\n return <p className=\"m-0 opacity-60\">File too large to preview.</p>;\n }\n const hunks =\n entry.contentKind === 'raw' ? rawContentHunks(entry.patch) : parsePatch(entry.patch);\n if (hunks.length === 0) {\n if (entry.renameSource) {\n return (\n <p className=\"m-0 opacity-60\">\n {entry.additions === 0 && entry.deletions === 0\n ? 'Renamed, no content changes.'\n : 'Renamed with unrelated content — diff omitted.'}\n </p>\n );\n }\n return <p className=\"m-0 opacity-60\">No changes to preview.</p>;\n }\n return (\n <div className=\"flex flex-col gap-3\">\n {hunks.map((hunk, i) => (\n <div key={`${hunk.header}-${i}`}>\n {hunk.header && <div className=\"mb-1 font-mono text-2xs opacity-50\">{hunk.header}</div>}\n {/* paper-ui's CodeBlock has no per-line add/remove styling. */}\n <pre className=\"m-0 overflow-x-auto font-mono text-2xs\">\n {hunk.lines.map((line, j) => (\n <span key={`${line.type}-${j}`} className={`block ${LINE_CLASS[line.type]}`}>\n {LINE_PREFIX[line.type]}\n {line.text}\n </span>\n ))}\n </pre>\n </div>\n ))}\n </div>\n );\n};\n\ninterface FileDiffSectionProps {\n entry: FileDiffEntry;\n sectionRef: (el: HTMLDivElement | null) => void;\n}\n\nexport const FileDiffSection = ({ entry, sectionRef }: FileDiffSectionProps) => (\n <div ref={sectionRef} className=\"scroll-mt-4\">\n <Card>\n <div className=\"mb-3\">\n <CardTitle entry={entry} />\n </div>\n <DiffBody entry={entry} />\n </Card>\n </div>\n);\n","import type { FileDiffEntry } from '@/types/index';\nimport { ListItem, Stamp } from '@dendelion/paper-ui';\n\ninterface CountBadgeProps {\n additions: number;\n deletions: number;\n}\n\nconst CountBadge = ({ additions, deletions }: CountBadgeProps) => (\n <span className=\"inline-flex gap-2 font-mono text-2xs\">\n <span className=\"text-watercolor-green-dark\">+{additions}</span>\n <span className=\"text-watercolor-rose-dark\">-{deletions}</span>\n </span>\n);\n\ninterface FileListSidebarProps {\n files: FileDiffEntry[];\n onSelect: (path: string) => void;\n}\n\nexport const FileListSidebar = ({ files, onSelect }: FileListSidebarProps) => (\n <div className=\"sticky top-4 flex w-[288px] shrink-0 flex-col gap-1 max-h-page overflow-y-auto\">\n {files.map((entry) => (\n <ListItem\n key={entry.path}\n size=\"small\"\n onClick={() => onSelect(entry.path)}\n action={\n <span className=\"flex items-center gap-2\">\n {entry.staged && <Stamp size=\"small\">staged</Stamp>}\n {!entry.binary && (\n <CountBadge additions={entry.additions} deletions={entry.deletions} />\n )}\n </span>\n }\n >\n <span className=\"block overflow-hidden text-ellipsis whitespace-nowrap font-mono text-2xs\">\n {entry.path}\n </span>\n </ListItem>\n ))}\n </div>\n);\n","import { fetchFileDiffs } from '@/app/services/git-api';\nimport type { FileDiffEntry } from '@/types/index';\nimport { Breadcrumb } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\nimport { useEffect, useRef, useState } from 'react';\nimport { FileDiffSection } from './file-diff-card';\nimport { FileListSidebar } from './file-list-sidebar';\n\nexport const DiffPage = () => {\n const navigate = useNavigate();\n const [files, setFiles] = useState<FileDiffEntry[] | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n const sectionRefs = useRef(new Map<string, HTMLDivElement>());\n\n useEffect(() => {\n fetchFileDiffs()\n .then(setFiles)\n .catch(() => setLoadFailed(true));\n }, []);\n\n const breadcrumb = (\n <div className=\"mb-4\">\n <Breadcrumb\n items={[\n { id: 'plans', label: 'Plans', onClick: () => navigate({ to: '/' }) },\n { id: 'changes', label: 'Changes' },\n ]}\n />\n </div>\n );\n\n const contentClass = 'min-h-page';\n\n if (loadFailed) {\n return (\n <div>\n {breadcrumb}\n <div className={contentClass}>\n <p className=\"opacity-50\">Couldn't load the working-tree diff.</p>\n </div>\n </div>\n );\n }\n\n if (!files) {\n return (\n <div>\n {breadcrumb}\n <div className={contentClass}>\n <p className=\"opacity-50\">Loading…</p>\n </div>\n </div>\n );\n }\n\n const scrollToFile = (path: string) => {\n sectionRefs.current.get(path)?.scrollIntoView({ behavior: 'smooth', block: 'start' });\n };\n\n return (\n <div>\n {breadcrumb}\n {files.length === 0 ? (\n <div className={contentClass}>\n <p className=\"opacity-50\">No changed files.</p>\n </div>\n ) : (\n <div className={`flex items-start gap-6 ${contentClass}`}>\n <FileListSidebar files={files} onSelect={scrollToFile} />\n <div className=\"flex min-w-0 flex-1 flex-col gap-6\">\n {files.map((entry) => (\n <FileDiffSection\n key={entry.path}\n entry={entry}\n sectionRef={(el) => {\n if (el) sectionRefs.current.set(entry.path, el);\n else sectionRefs.current.delete(entry.path);\n }}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"names":["parsePatch","patch","lines","hunks","current","line","rawContentHunks","content","text","LINE_CLASS","LINE_PREFIX","CountBadge","additions","deletions","jsxs","CardTitle","entry","jsx","Stamp","DiffBody","hunk","i","j","FileDiffSection","sectionRef","Card","FileListSidebar","files","onSelect","ListItem","DiffPage","navigate","useNavigate","setFiles","useState","loadFailed","setLoadFailed","sectionRefs","useRef","useEffect","fetchFileDiffs","breadcrumb","Breadcrumb","contentClass","scrollToFile","path","_a","el"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{u as i,a as p,j as e,J as D,d as j,M as v,b as x,y as f,P as g,r as S,o as b,i as N}from"./main-0sfOZaxl.js";import{S as y}from"./sidebar-section-DLMrMf9C.js";const h=(s,r)=>s.toLowerCase().includes(r.toLowerCase()),w=(s,r,t=120)=>{const l=s.toLowerCase().indexOf(r.toLowerCase());if(l===-1)return s.slice(0,t);const a=Math.max(0,l-40),n=Math.min(s.length,l+r.length+40);let d=s.slice(a,n);return a>0&&(d=`…${d}`),n<s.length&&(d=`${d}…`),d},T=({query:s})=>{const r=i(o=>o.repoDocs),t=i(o=>o.setActiveDocTitle),l=i(o=>o.setDocSearchQuery),a=p(),n=[];for(const o of r)(h(o.name,s)||h(o.content,s))&&n.push({title:o.name,snippet:w(o.content,s)});const d=o=>{a({to:"/docs/$section",params:{section:"repo-docs"}}),t(o),l("")};return n.length===0?e.jsxs("p",{className:"opacity-50",children:['No results found for "',s,'".']}):e.jsxs("div",{children:[e.jsxs("p",{className:"mb-3 text-sm opacity-50",children:[n.length," result",n.length===1?"":"s",' for "',s,'"']}),e.jsx("div",{className:"flex flex-col gap-1",children:n.map(o=>e.jsx(D,{variant:"ghost",size:"small",fullWidth:!0,onClick:()=>d(o.title),className:"justify-start px-3 py-2",children:e.jsxs("span",{className:"text-left",children:[e.jsx("span",{className:"mb-[0.2rem] block font-display-luminari text-[0.95rem] font-semibold",children:o.title}),e.jsx("span",{className:"text-sm opacity-60",children:o.snippet})]})},o.title))})]})},k=()=>{const s=i(a=>a.repoDocs),r=i(a=>a.activeDocTitle),t=s.find(a=>a.name===r);if(!t)return null;const l=t.name.endsWith(".md");return e.jsxs("div",{children:[e.jsx("h2",{className:`${j} m-0 mb-4`,children:t.name}),l?e.jsx("div",{className:"text-base leading-[1.7] text-ink-900",children:e.jsx(v,{children:t.content})}):e.jsx("pre",{className:"overflow-x-auto whitespace-pre-wrap rounded-md bg-black/[4%] p-4 font-mono text-xs leading-normal",children:t.content})]})},A=()=>{const s=i(n=>n.docSearchQuery),r=i(n=>n.activeDocTitle),t=p(),l=x(),a=()=>t({to:"/docs"});return s.trim()?e.jsx("div",{children:e.jsx(T,{query:s})}):l==="repo-docs"&&r?e.jsxs("div",{children:[e.jsx("div",{className:"mb-4",children:e.jsx(f,{items:[{id:"docs",label:"Docs",onClick:a},{id:"repo-doc",label:r}]})}),e.jsx(k,{})]}):e.jsxs("div",{children:[e.jsx(g,{children:"Docs"}),e.jsx("p",{className:"opacity-50",children:"Pick a doc from the sidebar — start with a repo doc if you're new here."})]})},C=s=>s.charAt(0).toUpperCase()+s.slice(1).toLowerCase(),m=({children:s})=>e.jsx("span",{className:"block px-3 py-1 text-sm italic opacity-[0.35]",children:s}),R=()=>{const s=i(c=>c.repoDocs),r=i(c=>c.repoDocsLoading),t=i(c=>c.loadRepoDocs),l=i(c=>c.activeDocTitle),a=i(c=>c.setActiveDocTitle),n=i(c=>c.docSearchQuery),d=i(c=>c.setDocSearchQuery),o=x(),u=p();return S.useEffect(()=>{t()},[t]),e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"mb-4",children:e.jsx(b,{size:"small","aria-label":"Search docs",placeholder:"Search docs…",value:n,onChange:c=>d(c.target.value)})}),e.jsx(y,{label:"Repo Docs",children:r&&s.length===0?e.jsx(m,{children:"Loading…"}):s.length>0?s.map(c=>e.jsx(N,{size:"small",active:o==="repo-docs"&&l===c.name,onClick:()=>{u({to:"/docs/$section",params:{section:"repo-docs"}}),a(c.name)},children:C(c.name)},c.name)):e.jsx(m,{children:"No repo docs found"})})]})};export{A as DocsPage,R as DocsSidebar};
|
|
2
|
+
//# sourceMappingURL=index-CQB12bDI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CQB12bDI.js","sources":["../../../src/app/features/docs/components/docs-search.tsx","../../../src/app/features/docs/components/repo-doc-detail.tsx","../../../src/app/features/docs/docs-page.tsx","../../../src/app/features/docs/components/docs-sidebar.tsx"],"sourcesContent":["import { useAppStore } from '@/app/stores/app-store';\nimport { Button } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\n\ninterface SearchMatch {\n title: string;\n snippet: string;\n}\n\ninterface DocsSearchProps {\n query: string;\n}\n\nconst searchIn = (text: string, query: string): boolean =>\n text.toLowerCase().includes(query.toLowerCase());\n\nconst snippet = (text: string, query: string, maxLen = 120): string => {\n const idx = text.toLowerCase().indexOf(query.toLowerCase());\n if (idx === -1) return text.slice(0, maxLen);\n const start = Math.max(0, idx - 40);\n const end = Math.min(text.length, idx + query.length + 40);\n let s = text.slice(start, end);\n if (start > 0) s = `…${s}`;\n if (end < text.length) s = `${s}…`;\n return s;\n};\n\nexport const DocsSearch = ({ query }: DocsSearchProps) => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const navigate = useNavigate();\n\n const results: SearchMatch[] = [];\n\n for (const f of repoDocs) {\n if (searchIn(f.name, query) || searchIn(f.content, query)) {\n results.push({\n title: f.name,\n snippet: snippet(f.content, query),\n });\n }\n }\n\n const handleSelect = (title: string) => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(title);\n setDocSearchQuery('');\n };\n\n if (results.length === 0) {\n return <p className=\"opacity-50\">No results found for \"{query}\".</p>;\n }\n\n return (\n <div>\n <p className=\"mb-3 text-sm opacity-50\">\n {results.length} result{results.length === 1 ? '' : 's'} for \"{query}\"\n </p>\n <div className=\"flex flex-col gap-1\">\n {results.map((m) => (\n <Button\n key={m.title}\n variant=\"ghost\"\n size=\"small\"\n fullWidth\n onClick={() => handleSelect(m.title)}\n className=\"justify-start px-3 py-2\"\n >\n <span className=\"text-left\">\n <span className=\"mb-[0.2rem] block font-display-luminari text-[0.95rem] font-semibold\">\n {m.title}\n </span>\n <span className=\"text-sm opacity-60\">{m.snippet}</span>\n </span>\n </Button>\n ))}\n </div>\n </div>\n );\n};\n","import { detailHeadingClassName } from '@/app/components/detail-heading-style';\nimport { Markdown } from '@/app/components/markdown';\nimport { useAppStore } from '@/app/stores/app-store';\n\nexport const RepoDocDetail = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n\n const file = repoDocs.find((f) => f.name === activeDocTitle);\n if (!file) return null;\n\n const isMarkdown = file.name.endsWith('.md');\n\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{file.name}</h2>\n\n {isMarkdown ? (\n <div className=\"text-base leading-[1.7] text-ink-900\">\n <Markdown>{file.content}</Markdown>\n </div>\n ) : (\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-black/[4%] p-4 font-mono text-xs leading-normal\">\n {file.content}\n </pre>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { Breadcrumb } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\nimport { DocsSearch } from './components/docs-search';\nimport { RepoDocDetail } from './components/repo-doc-detail';\n\nexport const DocsPage = () => {\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const navigate = useNavigate();\n const activeDocSection = useResolvedDocSection();\n\n const handleBackToDocs = () => navigate({ to: '/docs' });\n\n if (docSearchQuery.trim()) {\n return (\n <div>\n <DocsSearch query={docSearchQuery} />\n </div>\n );\n }\n\n if (activeDocSection === 'repo-docs' && activeDocTitle) {\n return (\n <div>\n <div className=\"mb-4\">\n <Breadcrumb\n items={[\n { id: 'docs', label: 'Docs', onClick: handleBackToDocs },\n { id: 'repo-doc', label: activeDocTitle },\n ]}\n />\n </div>\n <RepoDocDetail />\n </div>\n );\n }\n\n return (\n <div>\n <PageTitle>Docs</PageTitle>\n <p className=\"opacity-50\">\n Pick a doc from the sidebar — start with a repo doc if you're new here.\n </p>\n </div>\n );\n};\n","import { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { Input, ListItem } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\nimport { useEffect } from 'react';\nimport { SidebarSection } from '../../plans/components/sidebar-section';\n\nconst simplecaseLabel = (name: string) =>\n name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();\n\nconst EmptyState = ({ children }: { children: React.ReactNode }) => (\n <span className=\"block px-3 py-1 text-sm italic opacity-[0.35]\">{children}</span>\n);\n\nexport const DocsSidebar = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const repoDocsLoading = useAppStore((s) => s.repoDocsLoading);\n const loadRepoDocs = useAppStore((s) => s.loadRepoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const activeDocSection = useResolvedDocSection();\n const navigate = useNavigate();\n\n useEffect(() => {\n loadRepoDocs();\n }, [loadRepoDocs]);\n\n return (\n <>\n <div className=\"mb-4\">\n <Input\n size=\"small\"\n aria-label=\"Search docs\"\n placeholder=\"Search docs…\"\n value={docSearchQuery}\n onChange={(e) => setDocSearchQuery(e.target.value)}\n />\n </div>\n\n <SidebarSection label=\"Repo Docs\">\n {repoDocsLoading && repoDocs.length === 0 ? (\n <EmptyState>Loading…</EmptyState>\n ) : repoDocs.length > 0 ? (\n repoDocs.map((f) => (\n <ListItem\n key={f.name}\n size=\"small\"\n active={activeDocSection === 'repo-docs' && activeDocTitle === f.name}\n onClick={() => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(f.name);\n }}\n >\n {simplecaseLabel(f.name)}\n </ListItem>\n ))\n ) : (\n <EmptyState>No repo docs found</EmptyState>\n )}\n </SidebarSection>\n </>\n );\n};\n"],"names":["searchIn","text","query","snippet","maxLen","idx","start","end","s","DocsSearch","repoDocs","useAppStore","setActiveDocTitle","setDocSearchQuery","navigate","useNavigate","results","f","handleSelect","title","jsxs","m","jsx","Button","RepoDocDetail","activeDocTitle","file","isMarkdown","detailHeadingClassName","Markdown","DocsPage","docSearchQuery","activeDocSection","useResolvedDocSection","handleBackToDocs","Breadcrumb","PageTitle","simplecaseLabel","name","EmptyState","children","DocsSidebar","repoDocsLoading","loadRepoDocs","useEffect","Fragment","Input","e","SidebarSection","ListItem"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as o,j as e,P as x,Q as c,R as d,L as m,w as j}from"./main-0sfOZaxl.js";import{c as i}from"./tokens-C9JyAXxm.js";const p=async()=>{const s=await fetch("/api/stats");if(!s.ok)throw new Error(`Failed to fetch stats: ${s.status}`);return s.json()},h=["open",...d],u={...m,open:"Open"},f=s=>`${Math.round(s*100)}%`,n=({title:s,children:t})=>e.jsx("div",{className:"flex-[1_1_260px] max-w-[360px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none",children:e.jsx(j,{size:"small",texture:"kraft",className:"h-full",children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("span",{className:"font-handwritten text-xs font-semibold opacity-[0.55]",children:s}),t]})})}),a=({label:s,value:t})=>e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"opacity-70",children:s}),e.jsx("span",{className:"font-semibold",children:t})]}),v=({comments:s})=>e.jsxs(n,{title:"Comment ratio",children:[e.jsx(c,{value:s.ratio*100,color:i.accentSlate}),e.jsx(a,{label:"Ratio",value:f(s.ratio)}),e.jsx(a,{label:"Comment lines",value:s.commentLines}),e.jsx(a,{label:"Source lines",value:s.sourceLines})]}),N=({testCoveragePct:s})=>e.jsx(n,{title:"Test coverage",children:s===null?e.jsx("p",{className:"opacity-50 m-0",children:"Run `pnpm test` to generate a coverage report."}):e.jsxs(e.Fragment,{children:[e.jsx(c,{value:s,color:i.accentGreen}),e.jsx(a,{label:"Line coverage",value:`${Math.round(s)}%`})]})}),S=({sourceLines:s,testLines:t})=>e.jsxs(n,{title:"Codebase size",children:[e.jsx(a,{label:"Source lines",value:s}),e.jsx(a,{label:"Test lines",value:t})]}),g=({entitiesByStatus:s})=>e.jsxs(n,{title:"Entities by status",children:[h.filter(t=>s[t]).map(t=>e.jsx(a,{label:u[t],value:s[t]},t)),Object.keys(s).length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No entities yet."})]}),b=({openQuestions:s,decisions:t})=>e.jsxs(n,{title:"Notes",children:[e.jsx(a,{label:"Open questions",value:s}),e.jsx(a,{label:"Decisions",value:t})]}),y=({tasksPerWeek:s})=>{const t=Math.max(1,...s.map(l=>l.count));return e.jsxs(n,{title:"Tasks per week",children:[s.length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No tasks run yet."}),s.map(l=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(a,{label:l.week,value:l.count}),e.jsx(c,{value:l.count,max:t,color:i.accentAmber,height:4})]},l.week))]})},C=()=>{const[s,t]=o.useState(null),[l,r]=o.useState(!1);return o.useEffect(()=>{p().then(t).catch(()=>r(!0))},[]),e.jsxs("div",{children:[e.jsx(x,{children:"Stats"}),e.jsx("p",{className:"opacity-50 mb-6",children:"A read-only look at project health — every number here is informational, none of them gate anything."}),l&&e.jsx("p",{className:"opacity-50",children:"Couldn't load stats."}),!l&&!s&&e.jsx("p",{className:"opacity-50",children:"Loading…"}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-wrap items-start gap-2.5",children:[e.jsx(v,{comments:s.comments}),e.jsx(N,{testCoveragePct:s.testCoveragePct}),e.jsx(S,{sourceLines:s.comments.sourceLines,testLines:s.testLines}),e.jsx(g,{entitiesByStatus:s.entitiesByStatus}),e.jsx(b,{openQuestions:s.openQuestions,decisions:s.decisions}),e.jsx(y,{tasksPerWeek:s.tasksPerWeek})]}),e.jsxs("p",{className:"opacity-[0.4] text-2xs mt-6",children:["Generated ",new Date(s.generatedAt).toLocaleString()]})]})]})};export{C as StatsPage};
|
|
2
|
+
//# sourceMappingURL=index-D-k_o5eq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D-k_o5eq.js","sources":["../../../src/app/services/content/stats-api.ts","../../../src/app/features/stats/stats-page.tsx"],"sourcesContent":["import type { ProjectStats } from '@/types/index';\n\nexport const fetchStats = async () => {\n const res = await fetch('/api/stats');\n if (!res.ok) throw new Error(`Failed to fetch stats: ${res.status}`);\n return res.json() as Promise<ProjectStats>;\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { STATUS_LABEL } from '@/app/features/plans/constants';\nimport { fetchStats } from '@/app/services/content';\nimport { color } from '@/app/styles/tokens';\nimport { type EntityStatus, PLAN_STATUSES, type ProjectStats } from '@/types/index';\nimport { Card, Progress } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\n\nconst ENTITY_STATUS_ORDER: EntityStatus[] = ['open', ...PLAN_STATUSES];\n\nconst ENTITY_STATUS_LABEL: Record<EntityStatus, string> = { ...STATUS_LABEL, open: 'Open' };\n\nconst pct = (value: number) => `${Math.round(value * 100)}%`;\n\nconst StatCard = ({ title, children }: { title: string; children: React.ReactNode }) => (\n <div className=\"flex-[1_1_260px] max-w-[360px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\">\n <Card size=\"small\" texture=\"kraft\" className=\"h-full\">\n <div className=\"flex flex-col gap-3\">\n <span className=\"font-handwritten text-xs font-semibold opacity-[0.55]\">{title}</span>\n {children}\n </div>\n </Card>\n </div>\n);\n\nconst StatRow = ({ label, value }: { label: string; value: React.ReactNode }) => (\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"opacity-70\">{label}</span>\n <span className=\"font-semibold\">{value}</span>\n </div>\n);\n\nconst CommentRatioCard = ({ comments }: { comments: ProjectStats['comments'] }) => (\n <StatCard title=\"Comment ratio\">\n <Progress value={comments.ratio * 100} color={color.accentSlate} />\n <StatRow label=\"Ratio\" value={pct(comments.ratio)} />\n <StatRow label=\"Comment lines\" value={comments.commentLines} />\n <StatRow label=\"Source lines\" value={comments.sourceLines} />\n </StatCard>\n);\n\nconst TestCoverageCard = ({ testCoveragePct }: { testCoveragePct: number | null }) => (\n <StatCard title=\"Test coverage\">\n {testCoveragePct === null ? (\n <p className=\"opacity-50 m-0\">Run `pnpm test` to generate a coverage report.</p>\n ) : (\n <>\n <Progress value={testCoveragePct} color={color.accentGreen} />\n <StatRow label=\"Line coverage\" value={`${Math.round(testCoveragePct)}%`} />\n </>\n )}\n </StatCard>\n);\n\nconst CodebaseSizeCard = ({\n sourceLines,\n testLines,\n}: { sourceLines: number; testLines: number }) => (\n <StatCard title=\"Codebase size\">\n <StatRow label=\"Source lines\" value={sourceLines} />\n <StatRow label=\"Test lines\" value={testLines} />\n </StatCard>\n);\n\nconst EntitiesByStatusCard = ({\n entitiesByStatus,\n}: { entitiesByStatus: ProjectStats['entitiesByStatus'] }) => (\n <StatCard title=\"Entities by status\">\n {ENTITY_STATUS_ORDER.filter((status) => entitiesByStatus[status]).map((status) => (\n <StatRow key={status} label={ENTITY_STATUS_LABEL[status]} value={entitiesByStatus[status]} />\n ))}\n {Object.keys(entitiesByStatus).length === 0 && (\n <p className=\"opacity-50 m-0\">No entities yet.</p>\n )}\n </StatCard>\n);\n\nconst NotesCard = ({ openQuestions, decisions }: { openQuestions: number; decisions: number }) => (\n <StatCard title=\"Notes\">\n <StatRow label=\"Open questions\" value={openQuestions} />\n <StatRow label=\"Decisions\" value={decisions} />\n </StatCard>\n);\n\nconst TasksPerWeekCard = ({ tasksPerWeek }: { tasksPerWeek: ProjectStats['tasksPerWeek'] }) => {\n const max = Math.max(1, ...tasksPerWeek.map((w) => w.count));\n return (\n <StatCard title=\"Tasks per week\">\n {tasksPerWeek.length === 0 && <p className=\"opacity-50 m-0\">No tasks run yet.</p>}\n {tasksPerWeek.map((week) => (\n <div key={week.week} className=\"flex flex-col gap-1\">\n <StatRow label={week.week} value={week.count} />\n <Progress value={week.count} max={max} color={color.accentAmber} height={4} />\n </div>\n ))}\n </StatCard>\n );\n};\n\nexport const StatsPage = () => {\n const [stats, setStats] = useState<ProjectStats | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n\n useEffect(() => {\n fetchStats()\n .then(setStats)\n .catch(() => setLoadFailed(true));\n }, []);\n\n return (\n <div>\n <PageTitle>Stats</PageTitle>\n <p className=\"opacity-50 mb-6\">\n A read-only look at project health — every number here is informational, none of them gate\n anything.\n </p>\n {loadFailed && <p className=\"opacity-50\">Couldn't load stats.</p>}\n {!loadFailed && !stats && <p className=\"opacity-50\">Loading…</p>}\n {stats && (\n <>\n <div className=\"flex flex-wrap items-start gap-2.5\">\n <CommentRatioCard comments={stats.comments} />\n <TestCoverageCard testCoveragePct={stats.testCoveragePct} />\n <CodebaseSizeCard\n sourceLines={stats.comments.sourceLines}\n testLines={stats.testLines}\n />\n <EntitiesByStatusCard entitiesByStatus={stats.entitiesByStatus} />\n <NotesCard openQuestions={stats.openQuestions} decisions={stats.decisions} />\n <TasksPerWeekCard tasksPerWeek={stats.tasksPerWeek} />\n </div>\n <p className=\"opacity-[0.4] text-2xs mt-6\">\n Generated {new Date(stats.generatedAt).toLocaleString()}\n </p>\n </>\n )}\n </div>\n );\n};\n"],"names":["fetchStats","res","ENTITY_STATUS_ORDER","PLAN_STATUSES","ENTITY_STATUS_LABEL","STATUS_LABEL","pct","value","StatCard","title","children","jsx","Card","jsxs","StatRow","label","CommentRatioCard","comments","Progress","color","TestCoverageCard","testCoveragePct","Fragment","CodebaseSizeCard","sourceLines","testLines","EntitiesByStatusCard","entitiesByStatus","status","NotesCard","openQuestions","decisions","TasksPerWeekCard","tasksPerWeek","max","w","week","StatsPage","stats","setStats","useState","loadFailed","setLoadFailed","useEffect","PageTitle"],"mappings":"0HAEO,MAAMA,EAAa,SAAY,CACpC,MAAMC,EAAM,MAAM,MAAM,YAAY,EACpC,GAAI,CAACA,EAAI,GAAI,MAAM,IAAI,MAAM,0BAA0BA,EAAI,MAAM,EAAE,EACnE,OAAOA,EAAI,KAAA,CACb,ECEMC,EAAsC,CAAC,OAAQ,GAAGC,CAAa,EAE/DC,EAAoD,CAAE,GAAGC,EAAc,KAAM,MAAA,EAE7EC,EAAOC,GAAkB,GAAG,KAAK,MAAMA,EAAQ,GAAG,CAAC,IAEnDC,EAAW,CAAC,CAAE,MAAAC,EAAO,SAAAC,KACzBC,EAAAA,IAAC,OAAI,UAAU,oFACb,eAACC,EAAA,CAAK,KAAK,QAAQ,QAAQ,QAAQ,UAAU,SAC3C,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACb,SAAA,CAAAF,EAAAA,IAAC,OAAA,CAAK,UAAU,wDAAyD,SAAAF,EAAM,EAC9EC,CAAA,CAAA,CACH,EACF,EACF,EAGII,EAAU,CAAC,CAAE,MAAAC,EAAO,MAAAR,KACxBM,OAAC,MAAA,CAAI,UAAU,0CACb,SAAA,CAAAF,EAAAA,IAAC,OAAA,CAAK,UAAU,aAAc,SAAAI,EAAM,EACpCJ,EAAAA,IAAC,OAAA,CAAK,UAAU,gBAAiB,SAAAJ,CAAA,CAAM,CAAA,EACzC,EAGIS,EAAmB,CAAC,CAAE,SAAAC,KAC1BJ,EAAAA,KAACL,EAAA,CAAS,MAAM,gBACd,SAAA,CAAAG,MAACO,GAAS,MAAOD,EAAS,MAAQ,IAAK,MAAOE,EAAM,YAAa,EACjER,MAACG,GAAQ,MAAM,QAAQ,MAAOR,EAAIW,EAAS,KAAK,EAAG,QAClDH,EAAA,CAAQ,MAAM,gBAAgB,MAAOG,EAAS,aAAc,QAC5DH,EAAA,CAAQ,MAAM,eAAe,MAAOG,EAAS,WAAA,CAAa,CAAA,EAC7D,EAGIG,EAAmB,CAAC,CAAE,gBAAAC,CAAA,UACzBb,EAAA,CAAS,MAAM,gBACb,SAAAa,IAAoB,KACnBV,EAAAA,IAAC,IAAA,CAAE,UAAU,iBAAiB,SAAA,gDAAA,CAA8C,EAE5EE,EAAAA,KAAAS,WAAA,CACE,SAAA,CAAAX,EAAAA,IAACO,EAAA,CAAS,MAAOG,EAAiB,MAAOF,EAAM,YAAa,EAC5DR,EAAAA,IAACG,EAAA,CAAQ,MAAM,gBAAgB,MAAO,GAAG,KAAK,MAAMO,CAAe,CAAC,GAAA,CAAK,CAAA,CAAA,CAC3E,CAAA,CAEJ,EAGIE,EAAmB,CAAC,CACxB,YAAAC,EACA,UAAAC,CACF,IACEZ,EAAAA,KAACL,EAAA,CAAS,MAAM,gBACd,SAAA,CAAAG,EAAAA,IAACG,EAAA,CAAQ,MAAM,eAAe,MAAOU,EAAa,EAClDb,EAAAA,IAACG,EAAA,CAAQ,MAAM,aAAa,MAAOW,CAAA,CAAW,CAAA,EAChD,EAGIC,EAAuB,CAAC,CAC5B,iBAAAC,CACF,IACEd,EAAAA,KAACL,EAAA,CAAS,MAAM,qBACb,SAAA,CAAAN,EAAoB,OAAQ0B,GAAWD,EAAiBC,CAAM,CAAC,EAAE,IAAKA,SACpEd,EAAA,CAAqB,MAAOV,EAAoBwB,CAAM,EAAG,MAAOD,EAAiBC,CAAM,GAA1EA,CAA6E,CAC5F,EACA,OAAO,KAAKD,CAAgB,EAAE,SAAW,GACxChB,MAAC,IAAA,CAAE,UAAU,iBAAiB,SAAA,kBAAA,CAAgB,CAAA,EAElD,EAGIkB,EAAY,CAAC,CAAE,cAAAC,EAAe,UAAAC,KAClClB,OAACL,EAAA,CAAS,MAAM,QACd,SAAA,CAAAG,EAAAA,IAACG,EAAA,CAAQ,MAAM,iBAAiB,MAAOgB,EAAe,EACtDnB,EAAAA,IAACG,EAAA,CAAQ,MAAM,YAAY,MAAOiB,CAAA,CAAW,CAAA,EAC/C,EAGIC,EAAmB,CAAC,CAAE,aAAAC,KAAmE,CAC7F,MAAMC,EAAM,KAAK,IAAI,EAAG,GAAGD,EAAa,IAAKE,GAAMA,EAAE,KAAK,CAAC,EAC3D,OACEtB,EAAAA,KAACL,EAAA,CAAS,MAAM,iBACb,SAAA,CAAAyB,EAAa,SAAW,GAAKtB,MAAC,IAAA,CAAE,UAAU,iBAAiB,SAAA,oBAAiB,EAC5EsB,EAAa,IAAKG,GACjBvB,EAAAA,KAAC,MAAA,CAAoB,UAAU,sBAC7B,SAAA,CAAAF,MAACG,GAAQ,MAAOsB,EAAK,KAAM,MAAOA,EAAK,MAAO,EAC9CzB,EAAAA,IAACO,EAAA,CAAS,MAAOkB,EAAK,MAAO,IAAAF,EAAU,MAAOf,EAAM,YAAa,OAAQ,CAAA,CAAG,CAAA,CAAA,EAFpEiB,EAAK,IAGf,CACD,CAAA,EACH,CAEJ,EAEaC,EAAY,IAAM,CAC7B,KAAM,CAACC,EAAOC,CAAQ,EAAIC,EAAAA,SAA8B,IAAI,EACtD,CAACC,EAAYC,CAAa,EAAIF,EAAAA,SAAS,EAAK,EAElDG,OAAAA,EAAAA,UAAU,IAAM,CACd3C,EAAA,EACG,KAAKuC,CAAQ,EACb,MAAM,IAAMG,EAAc,EAAI,CAAC,CACpC,EAAG,CAAA,CAAE,SAGF,MAAA,CACC,SAAA,CAAA/B,EAAAA,IAACiC,GAAU,SAAA,OAAA,CAAK,EAChBjC,EAAAA,IAAC,IAAA,CAAE,UAAU,kBAAkB,SAAA,uGAG/B,EACC8B,GAAc9B,EAAAA,IAAC,IAAA,CAAE,UAAU,aAAa,SAAA,uBAAoB,EAC5D,CAAC8B,GAAc,CAACH,SAAU,IAAA,CAAE,UAAU,aAAa,SAAA,WAAQ,EAC3DA,GACCzB,EAAAA,KAAAS,WAAA,CACE,SAAA,CAAAT,EAAAA,KAAC,MAAA,CAAI,UAAU,qCACb,SAAA,CAAAF,EAAAA,IAACK,EAAA,CAAiB,SAAUsB,EAAM,QAAA,CAAU,EAC5C3B,EAAAA,IAACS,EAAA,CAAiB,gBAAiBkB,EAAM,eAAA,CAAiB,EAC1D3B,EAAAA,IAACY,EAAA,CACC,YAAae,EAAM,SAAS,YAC5B,UAAWA,EAAM,SAAA,CAAA,EAEnB3B,EAAAA,IAACe,EAAA,CAAqB,iBAAkBY,EAAM,gBAAA,CAAkB,QAC/DT,EAAA,CAAU,cAAeS,EAAM,cAAe,UAAWA,EAAM,UAAW,EAC3E3B,EAAAA,IAACqB,EAAA,CAAiB,aAAcM,EAAM,YAAA,CAAc,CAAA,EACtD,EACAzB,EAAAA,KAAC,IAAA,CAAE,UAAU,8BAA8B,SAAA,CAAA,aAC9B,IAAI,KAAKyB,EAAM,WAAW,EAAE,eAAA,CAAe,CAAA,CACxD,CAAA,CAAA,CACF,CAAA,EAEJ,CAEJ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as d,k as L,j as e,l as E,U as T,w as R,J as g,p as N,f as G,c as V,g as X,e as Q,s as M,u as D,V as W,h as Z,a as $,m as J,P as ee,n as se,o as O,D as C,q as te,A as F,t as U,v as ae,x as ne,i as z}from"./main-0sfOZaxl.js";import{c as le}from"./tokens-C9JyAXxm.js";import{S as B}from"./sidebar-section-DLMrMf9C.js";const ie=async()=>{try{const s=await fetch("/api/merge-policy");return s.ok?await s.json():null}catch{return null}},oe=async()=>{try{const s=await fetch("/api/merge-policy/apply",{method:"POST"});return s.ok?await s.json():null}catch{return null}},K={allowSquashMerge:!0,allowMergeCommit:!1,allowRebaseMerge:!1,squashMergeCommitTitle:"PR_TITLE",squashMergeCommitMessage:"PR_BODY"},q=[{key:"allowSquashMerge",label:"Allow squash merge"},{key:"allowMergeCommit",label:"Allow merge commit"},{key:"allowRebaseMerge",label:"Allow rebase merge"},{key:"squashMergeCommitTitle",label:"Squash commit title"},{key:"squashMergeCommitMessage",label:"Squash commit message"}];function Y(s){return typeof s=="boolean"?s?"Yes":"No":s}function re(s){return q.every(t=>s[t.key]===K[t.key])}const ce=({label:s,current:t,recommended:o,isLast:l})=>{const u=t===o;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-3 pb-2 pt-2",children:[e.jsx("span",{className:"flex-1",children:s}),e.jsx("span",{className:u?"opacity-[0.65] font-normal":"font-semibold",children:Y(t)}),!u&&e.jsxs("span",{className:"opacity-[0.45]",children:["→ ",Y(o)]})]}),!l&&e.jsx(N,{})]})},de=()=>{const[s,t]=d.useState(void 0),[o,l]=d.useState(!1),{toast:u}=L();d.useEffect(()=>{ie().then(t)},[]);const h=async()=>{l(!0);const n=await oe();l(!1),(n==null?void 0:n.status)==="ok"?(t(n),u({title:"Merge policy applied",variant:"success"})):u({title:"Failed to apply merge policy",description:(n==null?void 0:n.status)==="unavailable"?n.reason:void 0,variant:"error"})},p=(s==null?void 0:s.status)==="ok"&&re(s.policy);return e.jsxs("div",{children:[e.jsxs("div",{className:"mb-6",children:[e.jsx("h2",{className:"m-0",children:"Merge Policy"}),e.jsx("p",{className:"opacity-50 mt-1",children:"Paper Camp's canonical policy: squash-only merges, with the commit title and body taken from the PR."})]}),s===void 0&&e.jsx("p",{children:"Loading…"}),s===null&&e.jsx(E,{variant:"warning",children:"Failed to load merge policy."}),(s==null?void 0:s.status)==="unavailable"&&e.jsx(E,{variant:"warning",children:s.reason}),(s==null?void 0:s.status)==="ok"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[e.jsx("span",{className:"font-medium",children:s.repo}),e.jsx(T,{size:"small",fillColor:p?"rgba(143, 185, 150, 0.25)":"rgba(212, 163, 115, 0.25)",textColor:p?"#5E8A66":"#A67B4F",children:p?"Matches recommended policy":"Differs from recommended policy"})]}),e.jsx(R,{size:"small",texture:"kraft",children:q.map((n,r)=>e.jsx(ce,{label:n.label,current:s.policy[n.key],recommended:K[n.key],isLast:r===q.length-1},n.key))}),e.jsx("div",{className:"mt-4",children:e.jsx(g,{size:"small",onClick:h,disabled:o||p,children:o?"Applying…":"Apply recommended policy"})})]})]})},me=2e3,ue="agent:claude-code",pe=["claude auth login","claude setup-token"],he=()=>e.jsxs("div",{className:"mt-2 flex flex-col gap-2",children:[e.jsx("p",{className:"opacity-[0.65] text-sm m-0",children:"The in-app relay couldn't complete. Run one of these in a terminal instead:"}),pe.map(s=>e.jsx(W,{code:s},s)),e.jsxs("p",{className:"opacity-50 text-sm m-0",children:["Alternatively, set ",e.jsx("code",{children:"ANTHROPIC_API_KEY"})," in the server's environment — that bills as API usage rather than your Max subscription, so treat it as a fallback, not the default."]})]}),xe=({onSignedIn:s})=>{const t=D(r=>r.loginRelay),o=D(r=>r.startLoginRelay),l=D(r=>r.loadLoginRelayStatus),u=D(r=>r.cancelLoginRelay),{toast:h}=L(),p=d.useRef(null),n=t==null?void 0:t.phase;return d.useEffect(()=>{if(n!=="starting"&&n!=="awaiting-authorization")return;const r=window.setInterval(()=>l(),me);return()=>window.clearInterval(r)},[n,l]),d.useEffect(()=>{const r=t==null?void 0:t.authorizeUrl;n!=="awaiting-authorization"||!r||p.current===r||(p.current=r,window.open(r,"_blank","noopener,noreferrer"))},[n,t==null?void 0:t.authorizeUrl]),d.useEffect(()=>{n==="success"?(h({title:"Signed in",variant:"success"}),s()):n==="error"&&h({title:"Sign-in failed",description:t==null?void 0:t.error,variant:"error"})},[n]),n==="starting"?e.jsx(g,{size:"small",disabled:!0,children:"Starting…"}):n==="awaiting-authorization"?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(g,{size:"small",onClick:()=>(t==null?void 0:t.authorizeUrl)&&window.open(t.authorizeUrl,"_blank","noopener,noreferrer"),children:"Reopen sign-in tab"}),e.jsx(g,{size:"small",onClick:()=>u(),children:"Cancel"})]}):e.jsxs("div",{children:[e.jsx(g,{size:"small",onClick:()=>o(),children:"Sign in"}),n==="error"&&e.jsx(he,{})]})},je={ok:{fill:"rgba(143, 185, 150, 0.25)",text:"#5E8A66",label:"Ready"},warn:{fill:"rgba(212, 163, 115, 0.25)",text:"#A67B4F",label:"Needs attention"},missing:{fill:"rgba(201, 139, 139, 0.25)",text:"#6E3A3A",label:"Missing"}},fe=({connection:s,onConnect:t,connecting:o})=>{const{connect:l}=s;return l?l.kind==="command"&&l.runnable?e.jsx(g,{size:"small",onClick:()=>t(s.id),disabled:o,children:o?"Running…":`Run \`${l.command}\``}):l.kind==="command"?e.jsx("div",{className:"mt-2",children:e.jsx(W,{code:l.command})}):l.kind==="link"?e.jsx(g,{size:"small",onClick:()=>window.open(l.url,"_blank","noopener,noreferrer"),children:l.label}):e.jsx("p",{className:"opacity-[0.65] text-sm mt-2 mx-0 mb-0",children:l.message}):null},ge=({connection:s,isLast:t,onRecheck:o,rechecking:l,onConnect:u,connecting:h})=>{const p=je[s.status];return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pb-3 pt-3",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex-1 font-medium",children:s.label}),s.authenticated===!1&&e.jsx(X,{content:"Installed, but not signed in",children:e.jsx(T,{size:"small",variant:"warning",children:"Not signed in"})}),e.jsx(T,{size:"small",fillColor:p.fill,textColor:p.text,children:p.label}),e.jsx(g,{size:"small",onClick:()=>o(s.id),disabled:l,children:l?"Checking…":"Recheck"})]}),e.jsxs("p",{className:"opacity-[0.65] text-sm mt-1 mx-0 mb-0",children:["Unlocks: ",s.unlocks]}),e.jsx("p",{className:"opacity-50 text-sm mt-1 mx-0 mb-0",children:s.detail}),e.jsx("div",{className:"mt-2",children:s.id===ue&&s.authenticated===!1?e.jsx(xe,{onSignedIn:()=>o(s.id)}):e.jsx(fe,{connection:s,onConnect:u,connecting:h})})]}),!t&&e.jsx(N,{})]})},ve=()=>{const[s,t]=d.useState(null),[o,l]=d.useState(!1),[u,h]=d.useState(null),[p,n]=d.useState(null),[r,S]=d.useState(!1),{toast:b}=L(),y=d.useCallback(i=>{if(i===null){l(!0);return}l(!1),t(i)},[]);d.useEffect(()=>{G().then(y),V().then(i=>S((i==null?void 0:i.setupDismissed)??!1))},[y]);const w=async i=>{h(i),y(await G()),h(null)},m=async i=>{n(i);const f=await Q(i);f?t(A=>(A==null?void 0:A.map(a=>a.id===i?f:a))??A):b({title:"Failed to connect",variant:"error"}),n(null)},x=async()=>{const i=!r,{ok:f}=await M({setupDismissed:i});f?(S(i),b({title:"Saved",variant:"success"})):b({title:"Failed to save",variant:"error"})},k=(s==null?void 0:s.every(i=>i.status==="ok"))??!0;return e.jsxs("div",{children:[e.jsx("div",{className:"mb-6",children:e.jsx("h2",{className:"m-0",children:"Setup"})}),s===null&&!o&&e.jsx("p",{children:"Loading…"}),o&&e.jsx("div",{className:"mb-4",children:e.jsx(E,{variant:"warning",children:"Failed to load connections. Try refreshing."})}),s&&e.jsxs(e.Fragment,{children:[!k&&e.jsx("div",{className:"mb-4",children:e.jsx(E,{variant:"warning",children:"Some connections are incomplete — features that depend on them stay disabled until fixed. Run the connect command below, then recheck."})}),e.jsx(R,{size:"small",texture:"kraft",children:s.map((i,f)=>e.jsx(ge,{connection:i,isLast:f===s.length-1,onRecheck:w,rechecking:u===i.id,onConnect:m,connecting:p===i.id},i.id))}),e.jsx("div",{className:"mt-4",children:e.jsx(g,{size:"small",onClick:x,children:r?"Show Setup on open again":"Don't show Setup on open"})})]})]})},be=()=>{const{subjects:s,loading:t,available:o}=Z(),l=$();return e.jsxs("div",{children:[e.jsxs("div",{className:"mb-6",children:[e.jsx("h2",{className:"m-0",children:"Subjects"}),e.jsx("p",{className:"opacity-50 mt-1",children:"Subjects are the roadmap's bets and standing concerns. Edit the vocabulary on the Roadmap page."})]}),t&&e.jsx("p",{children:"Loading…"}),!t&&!o&&e.jsx("p",{className:"opacity-60 m-0",children:"Couldn't load subjects — check the server config."}),!t&&o&&e.jsxs(R,{size:"small",texture:"kraft",children:[s.length===0&&e.jsx("p",{className:"opacity-[0.45] m-0 pb-2",children:"No subjects yet."}),s.map((u,h)=>e.jsxs("div",{children:[e.jsx("div",{className:"pb-2 pt-2",children:u}),h<s.length-1&&e.jsx(N,{})]},u)),s.length>0&&e.jsx(N,{}),e.jsx("div",{className:"pt-3",children:e.jsx(g,{size:"small",onClick:()=>l({to:"/roadmap"}),children:"Go to Roadmap"})})]})]})},H=["phase","planDraft","ideaExtend","commitSuggest"],ye={phase:"Phase run",planDraft:"Plan draft",ideaExtend:"Idea extend",commitSuggest:"Commit suggest"},Ne=140,Se=160,ke=110,we=()=>e.jsxs("div",{className:"flex items-center gap-3 pb-1",children:[e.jsx("span",{className:"w-[110px] shrink-0 text-sm opacity-[0.45]",children:"Task"}),e.jsx("span",{className:"w-[140px] shrink-0 text-sm opacity-[0.45]",children:"Agent"}),e.jsx("span",{className:"w-[160px] shrink-0 text-sm opacity-[0.45]",children:"Model"}),e.jsx("span",{className:"w-[110px] shrink-0 text-sm opacity-[0.45]",children:"Effort"})]}),Ae=({taskKey:s,agentConfig:t,isLast:o,onSave:l})=>{const u=F[t.agent]??F["claude-code"],h=u.model,p=u.effort,[n,r]=d.useState(t.model??"");d.useEffect(()=>{r(t.model??"")},[t.model]);const S=m=>{var f;const x=m,k=F[x],i={agent:x};t.model&&(k.model===null||(f=k.model)!=null&&f.includes(t.model))&&(i.model=t.model),t.effort&&Array.isArray(k.effort)&&(i.effort=t.effort),l(s,i)},b=m=>{l(s,{...t,model:m||void 0})},y=()=>{l(s,{...t,model:n||void 0})},w=m=>{l(s,{...t,effort:m||void 0})};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-3 pb-2 pt-2",children:[e.jsx("span",{className:"w-[110px] shrink-0 text-sm opacity-[0.65]",children:ye[s]}),e.jsx(U,{size:"small",width:Ne,value:t.agent,onChange:S,options:ae.map(m=>({value:m,label:ne[m]}))}),Array.isArray(h)?e.jsx(U,{size:"small",width:Se,value:t.model??"",onChange:b,options:[{value:"",label:"Default"},...h.map(m=>({value:m,label:m}))]}):h===null?e.jsx(O,{size:"small",className:"w-[160px]",value:n,placeholder:"Default model",onChange:m=>r(m.target.value),onBlur:y}):null,e.jsx("div",{className:Array.isArray(p)?"visible":"invisible",children:e.jsx(U,{size:"small",width:ke,value:t.effort??"",onChange:w,options:[{value:"",label:"Default"},...(Array.isArray(p)?p:[]).map(m=>({value:m,label:m}))]})})]}),!o&&e.jsx(N,{})]})},Ce=()=>{const s=d.useRef(null),[t,o]=d.useState(void 0),{iconDataUri:l,loading:u}=se(),[h,p]=d.useState(null),n=h??l,[r,S]=d.useState(!1),[b,y]=d.useState(""),[w,m]=d.useState(""),{toast:x}=L();d.useEffect(()=>{V().then(a=>{o(a),(a==null?void 0:a.port)!==void 0&&y(String(a.port)),(a==null?void 0:a.projectName)!==void 0&&m(a.projectName)})},[]);const k=async(a,v)=>{const c=t==null?void 0:t.defaultAgents,j={phase:(c==null?void 0:c.phase)??C.phase,planDraft:(c==null?void 0:c.planDraft)??C.planDraft,ideaExtend:(c==null?void 0:c.ideaExtend)??C.ideaExtend,commitSuggest:(c==null?void 0:c.commitSuggest)??C.commitSuggest,[a]:v},{ok:I,error:P}=await M({defaultAgents:j});I?(o(_=>_&&{..._,defaultAgents:j}),x({title:"Saved",variant:"success"})):x({title:"Failed to save",description:P,variant:"error"})},i=async()=>{const a=Number(b);if(!t||!Number.isInteger(a)||a<=0||a===t.port)return;const{ok:v,error:c}=await M({port:a});v?(o(j=>j&&{...j,port:a}),x({title:"Saved",variant:"success"})):x({title:"Failed to save",description:c,variant:"error"})},f=async()=>{const a=w.trim();if(!t||!a||a===t.projectName)return;const{ok:v,error:c}=await M({projectName:a});v?(o(j=>j&&{...j,projectName:a}),m(a),x({title:"Saved",variant:"success"})):x({title:"Failed to save",description:c,variant:"error"})},A=async a=>{var j;const v=(j=a.target.files)==null?void 0:j[0];if(!v)return;S(!0);const c=new FileReader;c.onloadend=async()=>{const I=c.result,P=await te(I);S(!1),P?(p(I),x({title:"Saved",variant:"success"})):x({title:"Failed to save",variant:"error"})},c.readAsDataURL(v)};return e.jsxs("div",{children:[e.jsx("div",{className:"mb-6",children:e.jsx("h2",{className:"m-0",children:"Project Info"})}),t===void 0&&e.jsx("p",{children:"Loading…"}),t===null&&e.jsxs(E,{variant:"warning",children:["No papercamp/config.json found — run ",e.jsx("code",{children:"paper-camp init"})," in this directory first."]}),t&&e.jsxs(R,{size:"small",texture:"kraft",children:[e.jsxs("div",{className:"flex items-end gap-3 pb-3",children:[e.jsx(O,{value:w,onChange:a=>m(a.target.value),onBlur:f,label:"Project Name"}),e.jsxs(T,{size:"small",fillColor:"rgba(143, 185, 150, 0.25)",textColor:le.accentGreenDark,children:["v",t.version]})]}),e.jsx(N,{}),e.jsxs("div",{className:"flex items-center gap-3 pb-3 pt-3",children:[n&&e.jsx("img",{src:n,alt:"Project icon",className:"w-10 h-10 object-contain shrink-0 rounded"}),e.jsxs("div",{children:[e.jsx("input",{ref:s,type:"file",accept:".svg,.png,.jpg,.jpeg,.gif,.webp",onChange:A,className:"hidden"}),e.jsx(g,{size:"small",onClick:()=>{var a;return(a=s.current)==null?void 0:a.click()},disabled:r,children:r?"Uploading…":"Choose File"}),u&&e.jsx("p",{className:"text-sm opacity-50 mt-1 mx-0 mb-0",children:"Loading…"}),!u&&!n&&!r&&e.jsx("p",{className:"text-sm opacity-[0.45] mt-1 mx-0 mb-0",children:"No icon set."})]})]}),e.jsx(N,{}),e.jsx("div",{className:"flex items-end gap-3 pb-3 pt-3",children:e.jsx(O,{type:"number",value:b,onChange:a=>y(a.target.value),onBlur:i,label:"Port",helperText:"Default for `paper-camp dev`. Does not affect the running server."})}),e.jsx(N,{}),e.jsx(we,{}),H.map((a,v)=>{var c;return e.jsx(Ae,{taskKey:a,agentConfig:((c=t.defaultAgents)==null?void 0:c[a])??C[a],isLast:v===H.length-1,onSave:k},a)})]}),e.jsxs("p",{className:"opacity-[0.45] text-sm mt-4",children:[e.jsx("strong",{children:"Initialized:"})," ",t?new Date(t.initializedAt).toLocaleString():"—"]})]})},ze=()=>{const s=J();return e.jsxs("div",{children:[e.jsx(ee,{children:"Settings"}),s==="subjects"?e.jsx(be,{}):s==="setup"?e.jsx(ve,{}):s==="merge-policy"?e.jsx(de,{}):e.jsx(Ce,{})]})},Me=()=>{const s=J(),t=$();return e.jsxs(e.Fragment,{children:[e.jsxs(B,{label:"General",children:[e.jsx(z,{size:"small",active:s===null,onClick:()=>t({to:"/settings"}),children:"Project Info"}),e.jsx(z,{size:"small",active:s==="setup",onClick:()=>t({to:"/settings/$section",params:{section:"setup"}}),children:"Setup"}),e.jsx(z,{size:"small",active:s==="merge-policy",onClick:()=>t({to:"/settings/$section",params:{section:"merge-policy"}}),children:"Merge Policy"})]}),e.jsx(B,{label:"Ideas",children:e.jsx(z,{size:"small",active:s==="subjects",onClick:()=>t({to:"/settings/$section",params:{section:"subjects"}}),children:"Subjects"})})]})};export{ze as SettingsPage,Me as SettingsSidebar};
|
|
2
|
+
//# sourceMappingURL=index-D6bamXbC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D6bamXbC.js","sources":["../../../src/app/services/system/merge-policy-api.ts","../../../src/app/features/settings/components/merge-policy-section.tsx","../../../src/app/features/settings/components/setup-section.tsx","../../../src/app/features/settings/components/subjects-section.tsx","../../../src/app/features/settings/settings-page.tsx","../../../src/app/features/settings/components/settings-sidebar.tsx"],"sourcesContent":["import type { MergePolicyResult } from '@/types/index';\n\nexport const fetchMergePolicy = async (): Promise<MergePolicyResult | null> => {\n try {\n const response = await fetch('/api/merge-policy');\n if (!response.ok) return null;\n return (await response.json()) as MergePolicyResult;\n } catch {\n return null;\n }\n};\n\nexport const applyMergePolicy = async (): Promise<MergePolicyResult | null> => {\n try {\n const response = await fetch('/api/merge-policy/apply', { method: 'POST' });\n if (!response.ok) return null;\n return (await response.json()) as MergePolicyResult;\n } catch {\n return null;\n }\n};\n","import { applyMergePolicy, fetchMergePolicy } from '@/app/services/system';\nimport type { MergePolicy, MergePolicyResult } from '@/types/index';\nimport { Alert, Button, Card, Divider, Stamp, useToast } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\n\nconst RECOMMENDED: MergePolicy = {\n allowSquashMerge: true,\n allowMergeCommit: false,\n allowRebaseMerge: false,\n squashMergeCommitTitle: 'PR_TITLE',\n squashMergeCommitMessage: 'PR_BODY',\n};\n\nconst POLICY_ROWS: { key: keyof MergePolicy; label: string }[] = [\n { key: 'allowSquashMerge', label: 'Allow squash merge' },\n { key: 'allowMergeCommit', label: 'Allow merge commit' },\n { key: 'allowRebaseMerge', label: 'Allow rebase merge' },\n { key: 'squashMergeCommitTitle', label: 'Squash commit title' },\n { key: 'squashMergeCommitMessage', label: 'Squash commit message' },\n];\n\nfunction formatValue(value: boolean | string): string {\n return typeof value === 'boolean' ? (value ? 'Yes' : 'No') : value;\n}\n\nfunction matchesRecommended(policy: MergePolicy): boolean {\n return POLICY_ROWS.every((row) => policy[row.key] === RECOMMENDED[row.key]);\n}\n\ninterface PolicyRowProps {\n label: string;\n current: boolean | string;\n recommended: boolean | string;\n isLast: boolean;\n}\n\nconst PolicyRow = ({ label, current, recommended, isLast }: PolicyRowProps) => {\n const matches = current === recommended;\n return (\n <>\n <div className=\"flex items-center gap-3 pb-2 pt-2\">\n <span className=\"flex-1\">{label}</span>\n <span className={matches ? 'opacity-[0.65] font-normal' : 'font-semibold'}>\n {formatValue(current)}\n </span>\n {!matches && <span className=\"opacity-[0.45]\">→ {formatValue(recommended)}</span>}\n </div>\n {!isLast && <Divider />}\n </>\n );\n};\n\nexport const MergePolicySection = () => {\n const [result, setResult] = useState<MergePolicyResult | null | undefined>(undefined);\n const [applying, setApplying] = useState(false);\n const { toast } = useToast();\n\n useEffect(() => {\n fetchMergePolicy().then(setResult);\n }, []);\n\n const handleApply = async () => {\n setApplying(true);\n const applied = await applyMergePolicy();\n setApplying(false);\n if (applied?.status === 'ok') {\n setResult(applied);\n toast({ title: 'Merge policy applied', variant: 'success' });\n } else {\n toast({\n title: 'Failed to apply merge policy',\n description: applied?.status === 'unavailable' ? applied.reason : undefined,\n variant: 'error',\n });\n }\n };\n\n const upToDate = result?.status === 'ok' && matchesRecommended(result.policy);\n\n return (\n <div>\n <div className=\"mb-6\">\n <h2 className=\"m-0\">Merge Policy</h2>\n <p className=\"opacity-50 mt-1\">\n Paper Camp's canonical policy: squash-only merges, with the commit title and body taken\n from the PR.\n </p>\n </div>\n {result === undefined && <p>Loading…</p>}\n {result === null && <Alert variant=\"warning\">Failed to load merge policy.</Alert>}\n {result?.status === 'unavailable' && <Alert variant=\"warning\">{result.reason}</Alert>}\n {result?.status === 'ok' && (\n <>\n <div className=\"flex items-center gap-3 mb-3\">\n <span className=\"font-medium\">{result.repo}</span>\n <Stamp\n size=\"small\"\n fillColor={upToDate ? 'rgba(143, 185, 150, 0.25)' : 'rgba(212, 163, 115, 0.25)'}\n textColor={upToDate ? '#5E8A66' : '#A67B4F'}\n >\n {upToDate ? 'Matches recommended policy' : 'Differs from recommended policy'}\n </Stamp>\n </div>\n <Card size=\"small\" texture=\"kraft\">\n {POLICY_ROWS.map((row, idx) => (\n <PolicyRow\n key={row.key}\n label={row.label}\n current={result.policy[row.key]}\n recommended={RECOMMENDED[row.key]}\n isLast={idx === POLICY_ROWS.length - 1}\n />\n ))}\n </Card>\n <div className=\"mt-4\">\n <Button size=\"small\" onClick={handleApply} disabled={applying || upToDate}>\n {applying ? 'Applying…' : 'Apply recommended policy'}\n </Button>\n </div>\n </>\n )}\n </div>\n );\n};\n","import { connectService, fetchConfig, fetchConnections, saveConfig } from '@/app/services/system';\nimport { useAppStore } from '@/app/stores/app-store';\nimport type { CapabilityStatus, ConnectionResult } from '@/types/index';\nimport {\n Alert,\n Button,\n Card,\n CodeBlock,\n Divider,\n Stamp,\n Tooltip,\n useToast,\n} from '@dendelion/paper-ui';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nconst RELAY_POLL_MS = 2000;\n\n// Only claude-code exposes `auth login`/`auth status` (see agentAuthenticated in server/services.ts).\nconst RELAY_CONNECTION_ID: ConnectionResult['id'] = 'agent:claude-code';\n\n// Same two commands the relay drives interactively — shown as copy-paste fallback\n// when the relay itself can't run (no PTY, offline, unsupported CLI version).\nconst AUTH_FIX_COMMANDS = ['claude auth login', 'claude setup-token'] as const;\n\nconst RelayFallbackGuide = () => (\n <div className=\"mt-2 flex flex-col gap-2\">\n <p className=\"opacity-[0.65] text-sm m-0\">\n The in-app relay couldn't complete. Run one of these in a terminal instead:\n </p>\n {AUTH_FIX_COMMANDS.map((cmd) => (\n <CodeBlock key={cmd} code={cmd} />\n ))}\n <p className=\"opacity-50 text-sm m-0\">\n {/* paper-ui has no inline-code component, only the block-level CodeBlock */}\n Alternatively, set <code>ANTHROPIC_API_KEY</code> in the server's environment — that bills as\n API usage rather than your Max subscription, so treat it as a fallback, not the default.\n </p>\n </div>\n);\n\nconst SignInAction = ({ onSignedIn }: { onSignedIn: () => void }) => {\n const loginRelay = useAppStore((s) => s.loginRelay);\n const startLoginRelay = useAppStore((s) => s.startLoginRelay);\n const loadLoginRelayStatus = useAppStore((s) => s.loadLoginRelayStatus);\n const cancelLoginRelay = useAppStore((s) => s.cancelLoginRelay);\n const { toast } = useToast();\n const openedUrlRef = useRef<string | null>(null);\n const phase = loginRelay?.phase;\n\n useEffect(() => {\n if (phase !== 'starting' && phase !== 'awaiting-authorization') return;\n const id = window.setInterval(() => loadLoginRelayStatus(), RELAY_POLL_MS);\n return () => window.clearInterval(id);\n }, [phase, loadLoginRelayStatus]);\n\n useEffect(() => {\n const url = loginRelay?.authorizeUrl;\n if (phase !== 'awaiting-authorization' || !url || openedUrlRef.current === url) return;\n openedUrlRef.current = url;\n window.open(url, '_blank', 'noopener,noreferrer');\n }, [phase, loginRelay?.authorizeUrl]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed on phase transitions only — toast/onSignedIn aren't stable across renders, and including them would refire on every relay poll\n useEffect(() => {\n if (phase === 'success') {\n toast({ title: 'Signed in', variant: 'success' });\n onSignedIn();\n } else if (phase === 'error') {\n toast({ title: 'Sign-in failed', description: loginRelay?.error, variant: 'error' });\n }\n }, [phase]);\n\n if (phase === 'starting') {\n return (\n <Button size=\"small\" disabled>\n Starting…\n </Button>\n );\n }\n if (phase === 'awaiting-authorization') {\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"small\"\n onClick={() =>\n loginRelay?.authorizeUrl &&\n window.open(loginRelay.authorizeUrl, '_blank', 'noopener,noreferrer')\n }\n >\n Reopen sign-in tab\n </Button>\n <Button size=\"small\" onClick={() => cancelLoginRelay()}>\n Cancel\n </Button>\n </div>\n );\n }\n return (\n <div>\n <Button size=\"small\" onClick={() => startLoginRelay()}>\n Sign in\n </Button>\n {phase === 'error' && <RelayFallbackGuide />}\n </div>\n );\n};\n\nconst STATUS_STAMP: Record<CapabilityStatus, { fill: string; text: string; label: string }> = {\n ok: { fill: 'rgba(143, 185, 150, 0.25)', text: '#5E8A66', label: 'Ready' },\n warn: { fill: 'rgba(212, 163, 115, 0.25)', text: '#A67B4F', label: 'Needs attention' },\n missing: { fill: 'rgba(201, 139, 139, 0.25)', text: '#6E3A3A', label: 'Missing' },\n};\n\nconst ConnectActionView = ({\n connection,\n onConnect,\n connecting,\n}: {\n connection: ConnectionResult;\n onConnect: (id: string) => void;\n connecting: boolean;\n}) => {\n const { connect } = connection;\n if (!connect) return null;\n if (connect.kind === 'command' && connect.runnable) {\n return (\n <Button size=\"small\" onClick={() => onConnect(connection.id)} disabled={connecting}>\n {connecting ? 'Running…' : `Run \\`${connect.command}\\``}\n </Button>\n );\n }\n if (connect.kind === 'command') {\n return (\n <div className=\"mt-2\">\n <CodeBlock code={connect.command} />\n </div>\n );\n }\n if (connect.kind === 'link') {\n return (\n <Button\n size=\"small\"\n onClick={() => window.open(connect.url, '_blank', 'noopener,noreferrer')}\n >\n {connect.label}\n </Button>\n );\n }\n return <p className=\"opacity-[0.65] text-sm mt-2 mx-0 mb-0\">{connect.message}</p>;\n};\n\nconst ConnectionRow = ({\n connection,\n isLast,\n onRecheck,\n rechecking,\n onConnect,\n connecting,\n}: {\n connection: ConnectionResult;\n isLast: boolean;\n onRecheck: (id: string) => void;\n rechecking: boolean;\n onConnect: (id: string) => void;\n connecting: boolean;\n}) => {\n const stamp = STATUS_STAMP[connection.status];\n return (\n <>\n <div className=\"pb-3 pt-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex-1 font-medium\">{connection.label}</span>\n {connection.authenticated === false && (\n <Tooltip content=\"Installed, but not signed in\">\n <Stamp size=\"small\" variant=\"warning\">\n Not signed in\n </Stamp>\n </Tooltip>\n )}\n <Stamp size=\"small\" fillColor={stamp.fill} textColor={stamp.text}>\n {stamp.label}\n </Stamp>\n <Button size=\"small\" onClick={() => onRecheck(connection.id)} disabled={rechecking}>\n {rechecking ? 'Checking…' : 'Recheck'}\n </Button>\n </div>\n <p className=\"opacity-[0.65] text-sm mt-1 mx-0 mb-0\">Unlocks: {connection.unlocks}</p>\n <p className=\"opacity-50 text-sm mt-1 mx-0 mb-0\">{connection.detail}</p>\n <div className=\"mt-2\">\n {connection.id === RELAY_CONNECTION_ID && connection.authenticated === false ? (\n <SignInAction onSignedIn={() => onRecheck(connection.id)} />\n ) : (\n <ConnectActionView\n connection={connection}\n onConnect={onConnect}\n connecting={connecting}\n />\n )}\n </div>\n </div>\n {!isLast && <Divider />}\n </>\n );\n};\n\nexport const SetupSection = () => {\n const [connections, setConnections] = useState<ConnectionResult[] | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n const [reloadingId, setReloadingId] = useState<string | null>(null);\n const [connectingId, setConnectingId] = useState<string | null>(null);\n const [setupDismissed, setSetupDismissed] = useState(false);\n const { toast } = useToast();\n\n const applyConnections = useCallback((result: ConnectionResult[] | null) => {\n if (result === null) {\n setLoadFailed(true);\n return;\n }\n setLoadFailed(false);\n setConnections(result);\n }, []);\n\n useEffect(() => {\n fetchConnections().then(applyConnections);\n fetchConfig().then((c) => setSetupDismissed(c?.setupDismissed ?? false));\n }, [applyConnections]);\n\n const handleRecheck = async (id: string) => {\n setReloadingId(id);\n applyConnections(await fetchConnections());\n setReloadingId(null);\n };\n\n const handleConnect = async (id: string) => {\n setConnectingId(id);\n const updated = await connectService(id);\n if (updated) {\n setConnections((prev) => prev?.map((c) => (c.id === id ? updated : c)) ?? prev);\n } else {\n toast({ title: 'Failed to connect', variant: 'error' });\n }\n setConnectingId(null);\n };\n\n const handleDismissToggle = async () => {\n const next = !setupDismissed;\n const { ok } = await saveConfig({ setupDismissed: next });\n if (ok) {\n setSetupDismissed(next);\n toast({ title: 'Saved', variant: 'success' });\n } else {\n toast({ title: 'Failed to save', variant: 'error' });\n }\n };\n\n const allOk = connections?.every((c) => c.status === 'ok') ?? true;\n\n return (\n <div>\n <div className=\"mb-6\">\n <h2 className=\"m-0\">Setup</h2>\n </div>\n {connections === null && !loadFailed && <p>Loading…</p>}\n {loadFailed && (\n <div className=\"mb-4\">\n <Alert variant=\"warning\">Failed to load connections. Try refreshing.</Alert>\n </div>\n )}\n {connections && (\n <>\n {!allOk && (\n <div className=\"mb-4\">\n <Alert variant=\"warning\">\n Some connections are incomplete — features that depend on them stay disabled until\n fixed. Run the connect command below, then recheck.\n </Alert>\n </div>\n )}\n <Card size=\"small\" texture=\"kraft\">\n {connections.map((c, idx) => (\n <ConnectionRow\n key={c.id}\n connection={c}\n isLast={idx === connections.length - 1}\n onRecheck={handleRecheck}\n rechecking={reloadingId === c.id}\n onConnect={handleConnect}\n connecting={connectingId === c.id}\n />\n ))}\n </Card>\n <div className=\"mt-4\">\n <Button size=\"small\" onClick={handleDismissToggle}>\n {setupDismissed ? 'Show Setup on open again' : \"Don't show Setup on open\"}\n </Button>\n </div>\n </>\n )}\n </div>\n );\n};\n","import { useSubjectVocabulary } from '@/app/hooks';\nimport { Button, Card, Divider } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\n\nexport const SubjectsSection = () => {\n const { subjects, loading, available } = useSubjectVocabulary();\n const navigate = useNavigate();\n\n return (\n <div>\n <div className=\"mb-6\">\n <h2 className=\"m-0\">Subjects</h2>\n <p className=\"opacity-50 mt-1\">\n Subjects are the roadmap's bets and standing concerns. Edit the vocabulary on the Roadmap\n page.\n </p>\n </div>\n {loading && <p>Loading…</p>}\n {!loading && !available && (\n <p className=\"opacity-60 m-0\">Couldn't load subjects — check the server config.</p>\n )}\n {!loading && available && (\n <Card size=\"small\" texture=\"kraft\">\n {subjects.length === 0 && <p className=\"opacity-[0.45] m-0 pb-2\">No subjects yet.</p>}\n {subjects.map((name, idx) => (\n <div key={name}>\n <div className=\"pb-2 pt-2\">{name}</div>\n {idx < subjects.length - 1 && <Divider />}\n </div>\n ))}\n {subjects.length > 0 && <Divider />}\n <div className=\"pt-3\">\n <Button size=\"small\" onClick={() => navigate({ to: '/roadmap' })}>\n Go to Roadmap\n </Button>\n </div>\n </Card>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { useActiveSettingsSection, useProjectIdentity } from '@/app/hooks';\nimport { fetchConfig, saveConfig, uploadIcon } from '@/app/services/system';\nimport { color } from '@/app/styles/tokens';\nimport {\n AGENT_IDS,\n AGENT_LABELS,\n AGENT_OPTIONS,\n type AgentConfig,\n type AgentId,\n DEFAULT_AGENTS,\n type DefaultAgentsMap,\n type PaperCampConfig,\n} from '@/types/index';\nimport { Alert, Button, Card, Divider, Input, Select, Stamp, useToast } from '@dendelion/paper-ui';\nimport { useEffect, useRef, useState } from 'react';\nimport { MergePolicySection } from './components/merge-policy-section';\nimport { SetupSection } from './components/setup-section';\nimport { SubjectsSection } from './components/subjects-section';\n\nconst TASK_TYPE_KEYS = ['phase', 'planDraft', 'ideaExtend', 'commitSuggest'] as const;\ntype TaskTypeKey = (typeof TASK_TYPE_KEYS)[number];\n\nconst TASK_TYPE_LABELS: Record<TaskTypeKey, string> = {\n phase: 'Phase run',\n planDraft: 'Plan draft',\n ideaExtend: 'Idea extend',\n commitSuggest: 'Commit suggest',\n};\n\ninterface AgentTaskRowProps {\n taskKey: TaskTypeKey;\n agentConfig: AgentConfig;\n isLast: boolean;\n onSave: (key: TaskTypeKey, config: AgentConfig) => Promise<void>;\n}\n\nconst TASK_COLUMN_WIDTH = 110;\nconst AGENT_COLUMN_WIDTH = 140;\nconst MODEL_COLUMN_WIDTH = 160;\nconst EFFORT_COLUMN_WIDTH = 110;\n\nconst AgentTaskRowHeader = () => (\n <div className=\"flex items-center gap-3 pb-1\">\n <span className=\"w-[110px] shrink-0 text-sm opacity-[0.45]\">Task</span>\n <span className=\"w-[140px] shrink-0 text-sm opacity-[0.45]\">Agent</span>\n <span className=\"w-[160px] shrink-0 text-sm opacity-[0.45]\">Model</span>\n <span className=\"w-[110px] shrink-0 text-sm opacity-[0.45]\">Effort</span>\n </div>\n);\n\nconst AgentTaskRow = ({ taskKey, agentConfig, isLast, onSave }: AgentTaskRowProps) => {\n // Fall back if the config carries an unknown agent id — never white-screen the page.\n const opts = AGENT_OPTIONS[agentConfig.agent] ?? AGENT_OPTIONS['claude-code'];\n const modelOpts = opts.model;\n const effortOpts = opts.effort;\n const [localModel, setLocalModel] = useState(agentConfig.model ?? '');\n\n useEffect(() => {\n setLocalModel(agentConfig.model ?? '');\n }, [agentConfig.model]);\n\n const handleAgentChange = (v: string) => {\n const newId = v as AgentId;\n const newOpts = AGENT_OPTIONS[newId];\n const newConfig: AgentConfig = { agent: newId };\n // Only carry the model over if the new agent accepts it — otherwise a claude\n // model like 'opus' would leak into opencode and fail at launch.\n if (\n agentConfig.model &&\n (newOpts.model === null || newOpts.model?.includes(agentConfig.model))\n ) {\n newConfig.model = agentConfig.model;\n }\n if (agentConfig.effort && Array.isArray(newOpts.effort)) newConfig.effort = agentConfig.effort;\n onSave(taskKey, newConfig);\n };\n\n const handleModelSelectChange = (v: string) => {\n onSave(taskKey, { ...agentConfig, model: v || undefined });\n };\n\n const handleModelInputBlur = () => {\n onSave(taskKey, { ...agentConfig, model: localModel || undefined });\n };\n\n const handleEffortChange = (v: string) => {\n onSave(taskKey, { ...agentConfig, effort: v || undefined });\n };\n\n return (\n <>\n <div className=\"flex items-center gap-3 pb-2 pt-2\">\n <span className=\"w-[110px] shrink-0 text-sm opacity-[0.65]\">\n {TASK_TYPE_LABELS[taskKey]}\n </span>\n <Select\n size=\"small\"\n width={AGENT_COLUMN_WIDTH}\n value={agentConfig.agent}\n onChange={handleAgentChange}\n options={AGENT_IDS.map((id) => ({ value: id, label: AGENT_LABELS[id] }))}\n />\n {Array.isArray(modelOpts) ? (\n <Select\n size=\"small\"\n width={MODEL_COLUMN_WIDTH}\n value={agentConfig.model ?? ''}\n onChange={handleModelSelectChange}\n options={[\n { value: '', label: 'Default' },\n ...modelOpts.map((m) => ({ value: m, label: m })),\n ]}\n />\n ) : modelOpts === null ? (\n <Input\n size=\"small\"\n className=\"w-[160px]\"\n value={localModel}\n placeholder=\"Default model\"\n onChange={(e) => setLocalModel(e.target.value)}\n onBlur={handleModelInputBlur}\n />\n ) : null}\n {/* Reserve the effort slot even when the agent has no effort options, so\n switching agents doesn't change the control count and shift the row. */}\n <div className={Array.isArray(effortOpts) ? 'visible' : 'invisible'}>\n <Select\n size=\"small\"\n width={EFFORT_COLUMN_WIDTH}\n value={agentConfig.effort ?? ''}\n onChange={handleEffortChange}\n options={[\n { value: '', label: 'Default' },\n ...(Array.isArray(effortOpts) ? effortOpts : []).map((e) => ({ value: e, label: e })),\n ]}\n />\n </div>\n </div>\n {!isLast && <Divider />}\n </>\n );\n};\n\nconst GeneralSection = () => {\n const fileRef = useRef<HTMLInputElement>(null);\n const [config, setConfig] = useState<PaperCampConfig | null | undefined>(undefined);\n const { iconDataUri: fetchedIconDataUri, loading: identityLoading } = useProjectIdentity();\n const [uploadedIconDataUri, setUploadedIconDataUri] = useState<string | null>(null);\n const iconDataUri = uploadedIconDataUri ?? fetchedIconDataUri;\n const [uploading, setUploading] = useState(false);\n const [portInput, setPortInput] = useState('');\n const [nameInput, setNameInput] = useState('');\n const { toast } = useToast();\n\n useEffect(() => {\n fetchConfig().then((c) => {\n setConfig(c);\n if (c?.port !== undefined) setPortInput(String(c.port));\n if (c?.projectName !== undefined) setNameInput(c.projectName);\n });\n }, []);\n\n const handleSaveAgentConfig = async (key: TaskTypeKey, newEntry: AgentConfig) => {\n const current = config?.defaultAgents;\n const updated: DefaultAgentsMap = {\n phase: current?.phase ?? DEFAULT_AGENTS.phase,\n planDraft: current?.planDraft ?? DEFAULT_AGENTS.planDraft,\n ideaExtend: current?.ideaExtend ?? DEFAULT_AGENTS.ideaExtend,\n commitSuggest: current?.commitSuggest ?? DEFAULT_AGENTS.commitSuggest,\n [key]: newEntry,\n };\n const { ok, error } = await saveConfig({ defaultAgents: updated });\n if (ok) {\n setConfig((prev) => (prev ? { ...prev, defaultAgents: updated } : prev));\n toast({ title: 'Saved', variant: 'success' });\n } else {\n toast({ title: 'Failed to save', description: error, variant: 'error' });\n }\n };\n\n const handleSavePort = async () => {\n const port = Number(portInput);\n if (!config || !Number.isInteger(port) || port <= 0 || port === config.port) return;\n const { ok, error } = await saveConfig({ port });\n if (ok) {\n setConfig((prev) => (prev ? { ...prev, port } : prev));\n toast({ title: 'Saved', variant: 'success' });\n } else {\n toast({ title: 'Failed to save', description: error, variant: 'error' });\n }\n };\n\n const handleSaveName = async () => {\n const projectName = nameInput.trim();\n if (!config || !projectName || projectName === config.projectName) return;\n const { ok, error } = await saveConfig({ projectName });\n if (ok) {\n setConfig((prev) => (prev ? { ...prev, projectName } : prev));\n setNameInput(projectName);\n toast({ title: 'Saved', variant: 'success' });\n } else {\n toast({ title: 'Failed to save', description: error, variant: 'error' });\n }\n };\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n setUploading(true);\n const reader = new FileReader();\n reader.onloadend = async () => {\n const dataUri = reader.result as string;\n const ok = await uploadIcon(dataUri);\n setUploading(false);\n if (ok) {\n setUploadedIconDataUri(dataUri);\n toast({ title: 'Saved', variant: 'success' });\n } else {\n toast({ title: 'Failed to save', variant: 'error' });\n }\n };\n reader.readAsDataURL(file);\n };\n\n return (\n <div>\n <div className=\"mb-6\">\n <h2 className=\"m-0\">Project Info</h2>\n </div>\n {config === undefined && <p>Loading…</p>}\n {config === null && (\n <Alert variant=\"warning\">\n No papercamp/config.json found — run <code>paper-camp init</code> in this directory first.\n </Alert>\n )}\n {config && (\n <Card size=\"small\" texture=\"kraft\">\n <div className=\"flex items-end gap-3 pb-3\">\n <Input\n value={nameInput}\n onChange={(e) => setNameInput(e.target.value)}\n onBlur={handleSaveName}\n label=\"Project Name\"\n />\n <Stamp\n size=\"small\"\n fillColor=\"rgba(143, 185, 150, 0.25)\"\n textColor={color.accentGreenDark}\n >\n v{config.version}\n </Stamp>\n </div>\n <Divider />\n\n <div className=\"flex items-center gap-3 pb-3 pt-3\">\n {iconDataUri && (\n <img\n src={iconDataUri}\n alt=\"Project icon\"\n className=\"w-10 h-10 object-contain shrink-0 rounded\"\n />\n )}\n <div>\n {/* paper-ui has no file-input component, so this raw input is intentional */}\n <input\n ref={fileRef}\n type=\"file\"\n accept=\".svg,.png,.jpg,.jpeg,.gif,.webp\"\n onChange={handleFile}\n className=\"hidden\"\n />\n <Button size=\"small\" onClick={() => fileRef.current?.click()} disabled={uploading}>\n {uploading ? 'Uploading…' : 'Choose File'}\n </Button>\n {identityLoading && <p className=\"text-sm opacity-50 mt-1 mx-0 mb-0\">Loading…</p>}\n {!identityLoading && !iconDataUri && !uploading && (\n <p className=\"text-sm opacity-[0.45] mt-1 mx-0 mb-0\">No icon set.</p>\n )}\n </div>\n </div>\n <Divider />\n\n <div className=\"flex items-end gap-3 pb-3 pt-3\">\n <Input\n type=\"number\"\n value={portInput}\n onChange={(e) => setPortInput(e.target.value)}\n onBlur={handleSavePort}\n label=\"Port\"\n helperText=\"Default for `paper-camp dev`. Does not affect the running server.\"\n />\n </div>\n <Divider />\n\n <AgentTaskRowHeader />\n {TASK_TYPE_KEYS.map((key, idx) => (\n <AgentTaskRow\n key={key}\n taskKey={key}\n agentConfig={config.defaultAgents?.[key] ?? DEFAULT_AGENTS[key]}\n isLast={idx === TASK_TYPE_KEYS.length - 1}\n onSave={handleSaveAgentConfig}\n />\n ))}\n </Card>\n )}\n\n <p className=\"opacity-[0.45] text-sm mt-4\">\n <strong>Initialized:</strong>{' '}\n {config ? new Date(config.initializedAt).toLocaleString() : '—'}\n </p>\n </div>\n );\n};\n\nexport const SettingsPage = () => {\n const section = useActiveSettingsSection();\n return (\n <div>\n <PageTitle>Settings</PageTitle>\n {section === 'subjects' ? (\n <SubjectsSection />\n ) : section === 'setup' ? (\n <SetupSection />\n ) : section === 'merge-policy' ? (\n <MergePolicySection />\n ) : (\n <GeneralSection />\n )}\n </div>\n );\n};\n","import { useActiveSettingsSection } from '@/app/hooks';\nimport { ListItem } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\nimport { SidebarSection } from '../../plans/components/sidebar-section';\n\nexport const SettingsSidebar = () => {\n const section = useActiveSettingsSection();\n const navigate = useNavigate();\n\n return (\n <>\n <SidebarSection label=\"General\">\n <ListItem\n size=\"small\"\n active={section === null}\n onClick={() => navigate({ to: '/settings' })}\n >\n Project Info\n </ListItem>\n <ListItem\n size=\"small\"\n active={section === 'setup'}\n onClick={() => navigate({ to: '/settings/$section', params: { section: 'setup' } })}\n >\n Setup\n </ListItem>\n <ListItem\n size=\"small\"\n active={section === 'merge-policy'}\n onClick={() =>\n navigate({ to: '/settings/$section', params: { section: 'merge-policy' } })\n }\n >\n Merge Policy\n </ListItem>\n </SidebarSection>\n <SidebarSection label=\"Ideas\">\n <ListItem\n size=\"small\"\n active={section === 'subjects'}\n onClick={() => navigate({ to: '/settings/$section', params: { section: 'subjects' } })}\n >\n Subjects\n </ListItem>\n </SidebarSection>\n </>\n );\n};\n"],"names":["fetchMergePolicy","response","applyMergePolicy","RECOMMENDED","POLICY_ROWS","formatValue","value","matchesRecommended","policy","row","PolicyRow","label","current","recommended","isLast","matches","jsxs","Fragment","jsx","Divider","MergePolicySection","result","setResult","useState","applying","setApplying","toast","useToast","useEffect","handleApply","applied","upToDate","Alert","Stamp","Card","idx","Button","RELAY_POLL_MS","RELAY_CONNECTION_ID","AUTH_FIX_COMMANDS","RelayFallbackGuide","cmd","CodeBlock","SignInAction","onSignedIn","loginRelay","useAppStore","s","startLoginRelay","loadLoginRelayStatus","cancelLoginRelay","openedUrlRef","useRef","phase","id","url","STATUS_STAMP","ConnectActionView","connection","onConnect","connecting","connect","ConnectionRow","onRecheck","rechecking","stamp","Tooltip","SetupSection","connections","setConnections","loadFailed","setLoadFailed","reloadingId","setReloadingId","connectingId","setConnectingId","setupDismissed","setSetupDismissed","applyConnections","useCallback","fetchConnections","fetchConfig","c","handleRecheck","handleConnect","updated","connectService","prev","handleDismissToggle","next","ok","saveConfig","allOk","SubjectsSection","subjects","loading","available","useSubjectVocabulary","navigate","useNavigate","name","TASK_TYPE_KEYS","TASK_TYPE_LABELS","AGENT_COLUMN_WIDTH","MODEL_COLUMN_WIDTH","EFFORT_COLUMN_WIDTH","AgentTaskRowHeader","AgentTaskRow","taskKey","agentConfig","onSave","opts","AGENT_OPTIONS","modelOpts","effortOpts","localModel","setLocalModel","handleAgentChange","v","newId","newOpts","newConfig","_a","handleModelSelectChange","handleModelInputBlur","handleEffortChange","Select","AGENT_IDS","AGENT_LABELS","Input","e","GeneralSection","fileRef","config","setConfig","fetchedIconDataUri","identityLoading","useProjectIdentity","uploadedIconDataUri","setUploadedIconDataUri","iconDataUri","uploading","setUploading","portInput","setPortInput","nameInput","setNameInput","handleSaveAgentConfig","key","newEntry","DEFAULT_AGENTS","error","handleSavePort","port","handleSaveName","projectName","handleFile","file","reader","dataUri","uploadIcon","color","SettingsPage","section","useActiveSettingsSection","PageTitle","SettingsSidebar","SidebarSection","ListItem"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{u as S,h as z,r as d,k as L,j as e,C as T,t as D,J as v,E as O,F as M,a as B,z as U,G as C,P as E,M as $,w as _,U as y,S as b,H as V,o as I,I as F,g as G,K as H,L as q}from"./main-0sfOZaxl.js";import{N as fe}from"./main-0sfOZaxl.js";const w="__no-subject__",K=({horizonTitle:a,item:s,candidateName:t,onClose:r,onPromoted:l})=>{const c=S(n=>n.promoteRoadmapItem),o=S(n=>n.launchIdeaExtend),{subjects:f,available:p}=z(),[h,u]=d.useState(w),[j,g]=d.useState(!1),[x,N]=d.useState(null),{toast:R}=L();d.useEffect(()=>{s&&(u(t?s.name:w),g(!1),N(null))},[s,t]);const k=async()=>{if(!(!s||!a)){g(!0),N(null);try{const n=await c(a,s,h===w?void 0:h,t??void 0),i=S.getState().ideaEntries.find(m=>m.id===n);if(i)try{await o(n,O(i,a))}catch(m){R({title:"Idea created, but the refine agent failed to launch",description:M(m.message),variant:"error"})}l(),r()}catch(n){N(n.message)}finally{g(!1)}}};return e.jsx(T,{open:s!==null,onClose:r,title:t??(s==null?void 0:s.name)??"",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[!t&&e.jsx("p",{className:"m-0 opacity-80",children:s==null?void 0:s.description}),e.jsx("div",{children:e.jsx(D,{size:"small",value:h,onChange:u,disabled:j||!p,options:[{value:w,label:"No subject"},...!p&&h!==w?[{value:h,label:h}]:[],...f.map(n=>({value:n,label:n}))]})}),x&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:x}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(v,{type:"button",variant:"ghost",onClick:r,disabled:j,children:"Cancel"}),e.jsx(v,{type:"button",variant:"primary",onClick:k,disabled:j,children:j?"Promoting…":"Promote to idea"})]})]})})},J=a=>({shipped:a.filter(s=>s.status==="done").length,queued:a.filter(s=>s.status!=="done"&&s.status!=="dropped").length}),A="font-handwritten text-md font-semibold opacity-70 leading-none pt-2 px-1 pb-0",W="text-2xs font-normal px-1 py-0 opacity-50",Z=({size:a=14})=>e.jsx("svg",{width:a,height:a,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:e.jsx("polyline",{points:"9 18 15 12 9 6"})}),Q=({name:a,onPromote:s})=>e.jsx(_,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex-1",children:a}),e.jsx(v,{type:"button",variant:"ghost",size:"small",onClick:s,children:"Promote to idea"})]})}),X=({onAdd:a})=>{const[s,t]=d.useState(""),[r,l]=d.useState(!1),c=async()=>{if(s.trim()){l(!0);try{await a(s.trim()),t("")}finally{l(!1)}}};return e.jsxs("div",{className:"flex gap-2",children:[e.jsx(I,{size:"small",value:s,onChange:o=>t(o.target.value),onKeyDown:o=>{o.key==="Enter"&&c()},placeholder:"Add option…",disabled:r,className:"flex-1"}),e.jsx(v,{type:"button",variant:"ghost",size:"small",onClick:c,disabled:r||!s.trim(),children:"Add"})]})},Y=({onAdd:a})=>{const[s,t]=d.useState(!1),[r,l]=d.useState(""),[c,o]=d.useState(""),[f,p]=d.useState(!1),h=async()=>{if(r.trim()){p(!0);try{await a(r.trim(),c.trim()),l(""),o(""),t(!1)}finally{p(!1)}}};return s?e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(I,{size:"small",value:r,onChange:u=>l(u.target.value),placeholder:"Item name…",disabled:f,autoFocus:!0}),e.jsx(I,{size:"small",value:c,onChange:u=>o(u.target.value),onKeyDown:u=>{u.key==="Enter"&&h()},placeholder:"Description…",disabled:f}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(v,{type:"button",variant:"ghost",size:"small",onClick:()=>t(!1),disabled:f,children:"Cancel"}),e.jsx(v,{type:"button",size:"small",onClick:h,disabled:f||!r.trim(),children:"Add"})]})]}):e.jsx(v,{type:"button",variant:"ghost",size:"small",onClick:()=>t(!0),children:"+ Add item"})},ee=(a,s)=>`${a} ${s}${a===1?"":"s"}`,ae=({link:a})=>e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(G,{content:`${ee(a.taskRuns,"task run")}${a.released?" · released":""}`,children:e.jsx(y,{size:"small",fillColor:b[a.status].fill,textColor:b[a.status].text,children:a.id})}),a.pr&&e.jsx(H,{pr:a.pr})]}),se=({plan:a,onOpen:s})=>e.jsx(_,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("button",{type:"button",onClick:s,className:"flex items-center gap-3 w-full bg-transparent border-none p-0 cursor-pointer [font:inherit] text-inherit text-left",children:[e.jsx("span",{className:"flex-1 overflow-hidden text-ellipsis whitespace-nowrap",children:a.title}),e.jsx(y,{size:"small",fillColor:b[a.status].fill,textColor:b[a.status].text,children:q[a.status]})]})}),te=({item:a,graduated:s,highlighted:t,onPromote:r,onPromoteCandidate:l,onAddCandidate:c,onViewGraduated:o,onOpenGraduated:f})=>{const[p,h]=d.useState(t),u=a.candidates.length>0,{shipped:j,queued:g}=J(s);return e.jsxs("div",{className:`flex flex-col gap-1 ${t?"roadmap-item-highlighted outline outline-2 outline-offset-[-2px] outline-[rgba(200,154,90,0.5)]":""}`,children:[e.jsx(_,{size:"small",texture:"canvas",className:"plan-row-card",children:e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button","aria-expanded":p,"aria-label":p?"Collapse item":"Expand item",onClick:()=>h(x=>!x),className:`inline-flex items-center bg-transparent border-none cursor-pointer opacity-50 p-0 ${p?"rotate-90":""}`,children:e.jsx(Z,{})}),e.jsx("span",{className:"font-semibold flex-1",children:a.name}),e.jsx(v,{type:"button",variant:"ghost",size:"small",onClick:r,children:"Promote to idea"})]}),e.jsx("span",{className:`text-sm opacity-70 ${p?"":"line-clamp-2"}`,children:a.description}),e.jsxs("div",{className:"flex items-center gap-1",children:[(g>0||j>0)&&e.jsxs("button",{type:"button",onClick:o,className:"flex items-center gap-1 bg-transparent border-none p-0 cursor-pointer",children:[g>0&&e.jsxs(y,{size:"small",fillColor:b.planned.fill,textColor:b.planned.text,children:[g," in queue"]}),j>0&&e.jsxs(y,{size:"small",fillColor:b.done.fill,textColor:b.done.text,children:[j," shipped"]})]}),u&&e.jsxs(y,{size:"small",fillColor:"rgba(0, 0, 0, 0.06)",textColor:"rgba(0, 0, 0, 0.55)",children:[a.candidates.length," candidate",a.candidates.length===1?"":"s"]}),a.rollup.total>0&&e.jsxs(y,{size:"small",fillColor:b.planned.fill,textColor:b.planned.text,children:[a.rollup.done,"/",a.rollup.total," done"]})]}),a.links.length>0&&e.jsx("div",{className:"flex items-center gap-1 flex-wrap",children:a.links.map(x=>e.jsx(ae,{link:x},x.id))})]})}),p&&e.jsxs("div",{className:"flex flex-col gap-1 pl-6",children:[a.candidates.map(x=>e.jsx(Q,{name:x,onPromote:()=>l(x)},x)),s.map(x=>e.jsx(se,{plan:x,onOpen:()=>f(x.title)},x.title)),e.jsx(X,{onAdd:c})]})]})},le=({goal:a})=>{const[s,t]=d.useState(!1),[r,...l]=a.split(/\n{2,}/),c=l.length>0;return e.jsxs("div",{className:"mb-6 pb-4 border-b border-black/[8%]",children:[e.jsx("span",{className:"font-handwritten text-xs font-semibold opacity-[0.55]",children:"The goal"}),e.jsxs("div",{className:"font-display-luminari text-lg leading-[1.4] mt-2",children:[e.jsx("div",{className:s?void 0:"line-clamp-1",children:e.jsx($,{children:r})}),s&&c&&e.jsx($,{children:l.join(`
|
|
2
|
+
|
|
3
|
+
`)})]}),c&&e.jsx("button",{type:"button",onClick:()=>t(o=>!o),className:"bg-transparent border-none p-0 mt-2 cursor-pointer text-2xs opacity-[0.65] underline",children:s?"Show less":"Show more"})]})},ne=[{id:"tree",label:"Outline"},{id:"map",label:"Board"},{id:"timeline",label:"Timeline"}],re=({mode:a,onChange:s})=>e.jsx("nav",{"aria-label":"Roadmap view",className:"flex gap-1 mb-4",children:ne.map(t=>e.jsx(v,{type:"button",variant:"ghost",size:"small",isActive:a===t.id,"aria-pressed":a===t.id,onClick:()=>s(t.id),children:t.label},t.id))}),P=({items:a,graduatedByItem:s})=>{const t=a.filter(l=>s(l).length>0).length,r=a.length-t;return e.jsxs("div",{className:W,children:[t," graduated · ",r," charted"]})},oe={created:"bg-watercolor-slate","task-run":"bg-watercolor-amber"},ie=({events:a,rangeStart:s,rangeEnd:t})=>{const r=t-s||1;return e.jsx("div",{className:"relative h-8 mx-1 border-b border-black/10",children:a.map((l,c)=>e.jsx("button",{type:"button",className:`absolute top-1/2 w-2.5 h-2.5 p-0 border-none rounded-full -translate-x-1/2 -translate-y-1/2 cursor-pointer ${oe[l.kind]}`,style:{left:`${(Date.parse(l.date)-s)/r*100}%`},title:`${l.itemName} — ${l.label} (${l.date})`,"aria-label":`${l.itemName} — ${l.label} (${l.date})`},`${l.entityId}-${l.kind}-${c}`))})},de=({item:a})=>{const s=a.rollup.total>0?Math.round(a.rollup.done/a.rollup.total*100):0;return e.jsxs("div",{className:"flex flex-col gap-1 py-2 px-2.5",children:[e.jsx("div",{className:"font-semibold text-sm",children:a.name}),e.jsx("div",{className:"h-1.5 rounded-full bg-black/[0.08] overflow-hidden",children:e.jsx("div",{className:`h-full rounded-full ${a.rollup.total>0?"bg-watercolor-green":"bg-transparent"}`,style:{width:`${s}%`}})}),e.jsx("div",{className:"text-2xs opacity-50",children:a.rollup.total>0?`${a.rollup.done}/${a.rollup.total} done`:"not started"})]})},ce=({roadmap:a,graduatedByItem:s})=>e.jsx("div",{className:"flex flex-col gap-6",children:a.horizons.map(t=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-baseline gap-2",children:[e.jsx("div",{className:A,children:t.title}),e.jsx(P,{items:t.items,graduatedByItem:s})]}),e.jsx("div",{className:"flex flex-wrap items-start gap-2.5",children:t.items.map(r=>e.jsx("div",{className:"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none",children:e.jsx(de,{item:r})},r.name))})]},t.title))}),xe=({roadmap:a})=>{if(a.events.length===0)return e.jsx("p",{className:"opacity-50",children:"No dated events yet."});const s=a.events.map(l=>Date.parse(l.date)),t=Math.min(...s),r=Math.max(...s);return e.jsxs("div",{className:"flex flex-col gap-6",children:[a.horizons.map(l=>{const c=a.events.filter(o=>o.horizonTitle===l.title);return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("div",{className:A,children:l.title}),c.length===0?e.jsx("div",{className:"text-2xs font-normal px-1 py-0 opacity-40",children:"No events yet"}):e.jsx(ie,{events:c,rangeStart:t,rangeEnd:r})]},l.title)}),e.jsxs("div",{className:"flex justify-between text-2xs opacity-50",children:[e.jsx("span",{children:new Date(t).toLocaleDateString()}),e.jsx("span",{children:new Date(r).toLocaleDateString()})]})]})},me=()=>{const[a,s]=d.useState(null),[t,r]=d.useState(!0),[l,c]=d.useState(!1),[o,f]=d.useState(null),[p,h]=d.useState("tree"),u=S(n=>n.plans),j=B(),{item:g}=U({from:"/roadmap"}),x=d.useRef(null);if(d.useEffect(()=>{C().then(s).catch(()=>c(!0)).finally(()=>r(!1))},[]),d.useEffect(()=>{var i;if(!g||t)return;const n=(i=x.current)==null?void 0:i.querySelector(".roadmap-item-highlighted");n==null||n.scrollIntoView({behavior:"smooth",block:"center"})},[g,t]),t)return e.jsxs("div",{children:[e.jsx(E,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"Loading…"})]});if(l)return e.jsxs("div",{children:[e.jsx(E,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"Couldn't load the roadmap — the server may need a restart to pick up new routes."})]});if(!a||a.horizons.length===0)return e.jsxs("div",{children:[e.jsx(E,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"No `ROADMAP.md` found at the project root."})]});const N=n=>(u==null?void 0:u.entries.filter(i=>i.subject===n.name))??[],R=async(n,i,m)=>{await F(n,i,m),s(await C())},k=async(n,i,m)=>{await V(n,i,m),s(await C())};return e.jsxs("div",{ref:x,children:[e.jsx(le,{goal:a.goal}),e.jsx(re,{mode:p,onChange:h}),p==="tree"&&e.jsx("div",{className:"flex flex-col gap-6",children:a.horizons.map(n=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-baseline gap-2",children:[e.jsx("div",{className:A,children:n.title}),e.jsx(P,{items:n.items,graduatedByItem:N})]}),e.jsxs("div",{className:"flex flex-wrap items-start gap-2.5",children:[n.items.map(i=>e.jsx("div",{className:"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none",children:e.jsx(te,{item:i,graduated:N(i),highlighted:i.name===g,onPromote:()=>f({horizonTitle:n.title,item:i}),onPromoteCandidate:m=>f({horizonTitle:n.title,item:i,candidateName:m}),onAddCandidate:m=>k(n.title,i.name,m),onViewGraduated:()=>j({to:"/",search:{subject:i.name}}),onOpenGraduated:m=>j({to:"/plans/$planId",params:{planId:encodeURIComponent(m)}})})},i.name)),e.jsx("div",{className:"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none",children:e.jsx(Y,{onAdd:(i,m)=>R(n.title,i,m)})})]})]},n.title))}),p==="map"&&e.jsx(ce,{roadmap:a,graduatedByItem:N}),p==="timeline"&&e.jsx(xe,{roadmap:a}),e.jsx(K,{horizonTitle:(o==null?void 0:o.horizonTitle)??null,item:(o==null?void 0:o.item)??null,candidateName:o==null?void 0:o.candidateName,onClose:()=>f(null),onPromoted:()=>C().then(s)})]})};export{me as RoadmapPage,fe as useRoadmapItemNames};
|
|
4
|
+
//# sourceMappingURL=index-V3ou3lzT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-V3ou3lzT.js","sources":["../../../src/app/features/roadmap/promote-roadmap-item-modal.tsx","../../../src/app/features/roadmap/roadmap-page.tsx"],"sourcesContent":["import { buildRoadmapPromotePrompt } from '@/app/features/plans/prompts';\nimport { useSubjectVocabulary } from '@/app/hooks/use-subject-vocabulary';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { oneLineErrorSummary } from '@/app/utils/error-summary';\nimport type { RoadmapItem } from '@/types/index';\nimport { Button, Modal, Select, useToast } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\n\nconst NO_SUBJECT = '__no-subject__';\n\ninterface PromoteRoadmapItemModalProps {\n horizonTitle: string | null;\n item: RoadmapItem | null;\n candidateName?: string | null;\n onClose: () => void;\n onPromoted: () => void;\n}\n\nexport const PromoteRoadmapItemModal = ({\n horizonTitle,\n item,\n candidateName,\n onClose,\n onPromoted,\n}: PromoteRoadmapItemModalProps) => {\n const promoteRoadmapItem = useAppStore((s) => s.promoteRoadmapItem);\n const launchIdeaExtend = useAppStore((s) => s.launchIdeaExtend);\n const { subjects, available: subjectsAvailable } = useSubjectVocabulary();\n const [subject, setSubject] = useState(NO_SUBJECT);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const { toast } = useToast();\n\n useEffect(() => {\n if (item) {\n // A candidate defaults to its parent item's name — the \"big bet becomes a\n // Subject\" rule — but the picker still lets the user override it.\n setSubject(candidateName ? item.name : NO_SUBJECT);\n setLoading(false);\n setError(null);\n }\n }, [item, candidateName]);\n\n const handlePromote = async () => {\n if (!item || !horizonTitle) return;\n setLoading(true);\n setError(null);\n try {\n const id = await promoteRoadmapItem(\n horizonTitle,\n item,\n subject === NO_SUBJECT ? undefined : subject,\n candidateName ?? undefined,\n );\n const idea = useAppStore.getState().ideaEntries.find((e) => e.id === id);\n if (idea) {\n try {\n await launchIdeaExtend(id, buildRoadmapPromotePrompt(idea, horizonTitle));\n } catch (err) {\n // The idea was created either way — a failed refine launch just means\n // no background research ran, not that the promotion itself failed.\n toast({\n title: 'Idea created, but the refine agent failed to launch',\n description: oneLineErrorSummary((err as Error).message),\n variant: 'error',\n });\n }\n }\n onPromoted();\n onClose();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <Modal\n open={item !== null}\n onClose={onClose}\n title={candidateName ?? item?.name ?? ''}\n size=\"small\"\n >\n <div className=\"flex flex-col gap-4\">\n {!candidateName && <p className=\"m-0 opacity-80\">{item?.description}</p>}\n <div>\n <Select\n size=\"small\"\n value={subject}\n onChange={setSubject}\n disabled={loading || !subjectsAvailable}\n options={[\n { value: NO_SUBJECT, label: 'No subject' },\n ...(!subjectsAvailable && subject !== NO_SUBJECT\n ? [{ value: subject, label: subject }]\n : []),\n ...subjects.map((s) => ({ value: s, label: s })),\n ]}\n />\n </div>\n {error && <p className=\"m-0 text-watercolor-rose-dark text-sm\">{error}</p>}\n <div className=\"flex justify-end gap-2\">\n <Button type=\"button\" variant=\"ghost\" onClick={onClose} disabled={loading}>\n Cancel\n </Button>\n <Button type=\"button\" variant=\"primary\" onClick={handlePromote} disabled={loading}>\n {loading ? 'Promoting…' : 'Promote to idea'}\n </Button>\n </div>\n </div>\n </Modal>\n );\n};\n","import { Markdown } from '@/app/components/markdown';\nimport { PageTitle } from '@/app/components/page-title';\nimport { PrBadge } from '@/app/features/plans/components/pr-badge';\nimport { STATUS_LABEL, STATUS_STAMP } from '@/app/features/plans/constants';\nimport { addRoadmapCandidate, addRoadmapItem, fetchRoadmap } from '@/app/services/content/docs-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport type {\n PlanEntry,\n ResolvedRoadmap,\n ResolvedRoadmapItem,\n RoadmapEvent,\n RoadmapEventKind,\n RoadmapLink,\n} from '@/types/index';\nimport { Button, Card, Input, Stamp, Tooltip } from '@dendelion/paper-ui';\nimport { useNavigate, useSearch } from '@tanstack/react-router';\nimport { useEffect, useRef, useState } from 'react';\nimport { PromoteRoadmapItemModal } from './promote-roadmap-item-modal';\n\nconst graduationCounts = (graduated: PlanEntry[]) => ({\n shipped: graduated.filter((p) => p.status === 'done').length,\n queued: graduated.filter((p) => p.status !== 'done' && p.status !== 'dropped').length,\n});\n\nconst HORIZON_HEADER_CLASSES =\n 'font-handwritten text-md font-semibold opacity-70 leading-none pt-2 px-1 pb-0';\n\nconst HORIZON_PULSE_CLASSES = 'text-2xs font-normal px-1 py-0 opacity-50';\n\nconst ChevronRightIcon = ({ size = 14 }: { size?: number }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\nconst CandidateRow = ({\n name,\n onPromote,\n}: {\n name: string;\n onPromote: () => void;\n}) => (\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex-1\">{name}</span>\n <Button type=\"button\" variant=\"ghost\" size=\"small\" onClick={onPromote}>\n Promote to idea\n </Button>\n </div>\n </Card>\n);\n\nconst AddCandidateForm = ({ onAdd }: { onAdd: (name: string) => Promise<void> }) => {\n const [name, setName] = useState('');\n const [saving, setSaving] = useState(false);\n\n const handleAdd = async () => {\n if (!name.trim()) return;\n setSaving(true);\n try {\n await onAdd(name.trim());\n setName('');\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <div className=\"flex gap-2\">\n <Input\n size=\"small\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleAdd();\n }}\n placeholder=\"Add option…\"\n disabled={saving}\n className=\"flex-1\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"small\"\n onClick={handleAdd}\n disabled={saving || !name.trim()}\n >\n Add\n </Button>\n </div>\n );\n};\n\nconst AddItemForm = ({\n onAdd,\n}: {\n onAdd: (name: string, description: string) => Promise<void>;\n}) => {\n const [open, setOpen] = useState(false);\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [saving, setSaving] = useState(false);\n\n const handleAdd = async () => {\n if (!name.trim()) return;\n setSaving(true);\n try {\n await onAdd(name.trim(), description.trim());\n setName('');\n setDescription('');\n setOpen(false);\n } finally {\n setSaving(false);\n }\n };\n\n if (!open) {\n return (\n <Button type=\"button\" variant=\"ghost\" size=\"small\" onClick={() => setOpen(true)}>\n + Add item\n </Button>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <Input\n size=\"small\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"Item name…\"\n disabled={saving}\n autoFocus\n />\n <Input\n size=\"small\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleAdd();\n }}\n placeholder=\"Description…\"\n disabled={saving}\n />\n <div className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"small\"\n onClick={() => setOpen(false)}\n disabled={saving}\n >\n Cancel\n </Button>\n <Button type=\"button\" size=\"small\" onClick={handleAdd} disabled={saving || !name.trim()}>\n Add\n </Button>\n </div>\n </div>\n );\n};\n\nconst plural = (count: number, noun: string) => `${count} ${noun}${count === 1 ? '' : 's'}`;\n\nconst RoadmapLinkTrail = ({ link }: { link: RoadmapLink }) => (\n <div className=\"flex items-center gap-1\">\n <Tooltip content={`${plural(link.taskRuns, 'task run')}${link.released ? ' · released' : ''}`}>\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP[link.status].fill}\n textColor={STATUS_STAMP[link.status].text}\n >\n {link.id}\n </Stamp>\n </Tooltip>\n {link.pr && <PrBadge pr={link.pr} />}\n </div>\n);\n\nconst GraduatedRow = ({ plan, onOpen }: { plan: PlanEntry; onOpen: () => void }) => (\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"flex items-center gap-3 w-full bg-transparent border-none p-0 cursor-pointer [font:inherit] text-inherit text-left\"\n >\n <span className=\"flex-1 overflow-hidden text-ellipsis whitespace-nowrap\">{plan.title}</span>\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP[plan.status].fill}\n textColor={STATUS_STAMP[plan.status].text}\n >\n {STATUS_LABEL[plan.status]}\n </Stamp>\n </button>\n </Card>\n);\n\nconst RoadmapItemRow = ({\n item,\n graduated,\n highlighted,\n onPromote,\n onPromoteCandidate,\n onAddCandidate,\n onViewGraduated,\n onOpenGraduated,\n}: {\n item: ResolvedRoadmapItem;\n graduated: PlanEntry[];\n highlighted: boolean;\n onPromote: () => void;\n onPromoteCandidate: (candidateName: string) => void;\n onAddCandidate: (name: string) => Promise<void>;\n onViewGraduated: () => void;\n onOpenGraduated: (title: string) => void;\n}) => {\n const [expanded, setExpanded] = useState(highlighted);\n const hasCandidates = item.candidates.length > 0;\n const { shipped, queued } = graduationCounts(graduated);\n\n return (\n <div\n className={`flex flex-col gap-1 ${highlighted ? 'roadmap-item-highlighted outline outline-2 outline-offset-[-2px] outline-[rgba(200,154,90,0.5)]' : ''}`}\n >\n <Card size=\"small\" texture=\"canvas\" className=\"plan-row-card\">\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-2\">\n {/* Raw <button>: icon-only toggle, paper-ui Button doesn't offer this compact chrome. */}\n <button\n type=\"button\"\n aria-expanded={expanded}\n aria-label={expanded ? 'Collapse item' : 'Expand item'}\n onClick={() => setExpanded((v) => !v)}\n className={`inline-flex items-center bg-transparent border-none cursor-pointer opacity-50 p-0 ${expanded ? 'rotate-90' : ''}`}\n >\n <ChevronRightIcon />\n </button>\n <span className=\"font-semibold flex-1\">{item.name}</span>\n <Button type=\"button\" variant=\"ghost\" size=\"small\" onClick={onPromote}>\n Promote to idea\n </Button>\n </div>\n <span className={`text-sm opacity-70 ${expanded ? '' : 'line-clamp-2'}`}>\n {item.description}\n </span>\n <div className=\"flex items-center gap-1\">\n {(queued > 0 || shipped > 0) && (\n <button\n type=\"button\"\n onClick={onViewGraduated}\n className=\"flex items-center gap-1 bg-transparent border-none p-0 cursor-pointer\"\n >\n {queued > 0 && (\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP.planned.fill}\n textColor={STATUS_STAMP.planned.text}\n >\n {queued} in queue\n </Stamp>\n )}\n {shipped > 0 && (\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP.done.fill}\n textColor={STATUS_STAMP.done.text}\n >\n {shipped} shipped\n </Stamp>\n )}\n </button>\n )}\n {hasCandidates && (\n <Stamp size=\"small\" fillColor=\"rgba(0, 0, 0, 0.06)\" textColor=\"rgba(0, 0, 0, 0.55)\">\n {item.candidates.length} candidate{item.candidates.length === 1 ? '' : 's'}\n </Stamp>\n )}\n {item.rollup.total > 0 && (\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP.planned.fill}\n textColor={STATUS_STAMP.planned.text}\n >\n {item.rollup.done}/{item.rollup.total} done\n </Stamp>\n )}\n </div>\n {item.links.length > 0 && (\n <div className=\"flex items-center gap-1 flex-wrap\">\n {item.links.map((link) => (\n <RoadmapLinkTrail key={link.id} link={link} />\n ))}\n </div>\n )}\n </div>\n </Card>\n {expanded && (\n <div className=\"flex flex-col gap-1 pl-6\">\n {item.candidates.map((candidateName) => (\n <CandidateRow\n key={candidateName}\n name={candidateName}\n onPromote={() => onPromoteCandidate(candidateName)}\n />\n ))}\n {graduated.map((plan) => (\n <GraduatedRow key={plan.title} plan={plan} onOpen={() => onOpenGraduated(plan.title)} />\n ))}\n <AddCandidateForm onAdd={onAddCandidate} />\n </div>\n )}\n </div>\n );\n};\n\nconst GoalBanner = ({ goal }: { goal: string }) => {\n const [expanded, setExpanded] = useState(false);\n const [firstParagraph, ...restParagraphs] = goal.split(/\\n{2,}/);\n const hasMore = restParagraphs.length > 0;\n\n return (\n <div className=\"mb-6 pb-4 border-b border-black/[8%]\">\n <span className=\"font-handwritten text-xs font-semibold opacity-[0.55]\">The goal</span>\n <div className=\"font-display-luminari text-lg leading-[1.4] mt-2\">\n <div className={expanded ? undefined : 'line-clamp-1'}>\n <Markdown>{firstParagraph}</Markdown>\n </div>\n {expanded && hasMore && <Markdown>{restParagraphs.join('\\n\\n')}</Markdown>}\n </div>\n {hasMore && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"bg-transparent border-none p-0 mt-2 cursor-pointer text-2xs opacity-[0.65] underline\"\n >\n {expanded ? 'Show less' : 'Show more'}\n </button>\n )}\n </div>\n );\n};\n\ntype RoadmapViewMode = 'tree' | 'map' | 'timeline';\n\nconst VIEW_MODES: { id: RoadmapViewMode; label: string }[] = [\n { id: 'tree', label: 'Outline' },\n { id: 'map', label: 'Board' },\n { id: 'timeline', label: 'Timeline' },\n];\n\nconst RoadmapViewModeSwitch = ({\n mode,\n onChange,\n}: {\n mode: RoadmapViewMode;\n onChange: (mode: RoadmapViewMode) => void;\n}) => (\n <nav aria-label=\"Roadmap view\" className=\"flex gap-1 mb-4\">\n {VIEW_MODES.map((viewMode) => (\n <Button\n key={viewMode.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"small\"\n isActive={mode === viewMode.id}\n aria-pressed={mode === viewMode.id}\n onClick={() => onChange(viewMode.id)}\n >\n {viewMode.label}\n </Button>\n ))}\n </nav>\n);\n\nconst HorizonPulse = ({\n items,\n graduatedByItem,\n}: {\n items: ResolvedRoadmapItem[];\n graduatedByItem: (item: ResolvedRoadmapItem) => PlanEntry[];\n}) => {\n const graduated = items.filter((item) => graduatedByItem(item).length > 0).length;\n const charted = items.length - graduated;\n return (\n <div className={HORIZON_PULSE_CLASSES}>\n {graduated} graduated · {charted} charted\n </div>\n );\n};\n\nconst EVENT_KIND_COLOR: Record<RoadmapEventKind, string> = {\n created: 'bg-watercolor-slate',\n 'task-run': 'bg-watercolor-amber',\n};\n\nconst RoadmapTimelineTrack = ({\n events,\n rangeStart,\n rangeEnd,\n}: {\n events: RoadmapEvent[];\n rangeStart: number;\n rangeEnd: number;\n}) => {\n const span = rangeEnd - rangeStart || 1;\n return (\n <div className=\"relative h-8 mx-1 border-b border-black/10\">\n {events.map((event, i) => (\n <button\n type=\"button\"\n key={`${event.entityId}-${event.kind}-${i}`}\n className={`absolute top-1/2 w-2.5 h-2.5 p-0 border-none rounded-full -translate-x-1/2 -translate-y-1/2 cursor-pointer ${EVENT_KIND_COLOR[event.kind]}`}\n style={{\n left: `${((Date.parse(event.date) - rangeStart) / span) * 100}%`,\n }}\n title={`${event.itemName} — ${event.label} (${event.date})`}\n aria-label={`${event.itemName} — ${event.label} (${event.date})`}\n />\n ))}\n </div>\n );\n};\n\nconst RoadmapMapItem = ({ item }: { item: ResolvedRoadmapItem }) => {\n const percent =\n item.rollup.total > 0 ? Math.round((item.rollup.done / item.rollup.total) * 100) : 0;\n return (\n <div className=\"flex flex-col gap-1 py-2 px-2.5\">\n <div className=\"font-semibold text-sm\">{item.name}</div>\n <div className=\"h-1.5 rounded-full bg-black/[0.08] overflow-hidden\">\n <div\n className={`h-full rounded-full ${item.rollup.total > 0 ? 'bg-watercolor-green' : 'bg-transparent'}`}\n style={{\n width: `${percent}%`,\n }}\n />\n </div>\n <div className=\"text-2xs opacity-50\">\n {item.rollup.total > 0 ? `${item.rollup.done}/${item.rollup.total} done` : 'not started'}\n </div>\n </div>\n );\n};\n\nconst RoadmapMap = ({\n roadmap,\n graduatedByItem,\n}: {\n roadmap: ResolvedRoadmap;\n graduatedByItem: (item: ResolvedRoadmapItem) => PlanEntry[];\n}) => (\n <div className=\"flex flex-col gap-6\">\n {roadmap.horizons.map((horizon) => (\n <div key={horizon.title} className=\"flex flex-col gap-1\">\n <div className=\"flex items-baseline gap-2\">\n <div className={HORIZON_HEADER_CLASSES}>{horizon.title}</div>\n <HorizonPulse items={horizon.items} graduatedByItem={graduatedByItem} />\n </div>\n <div className=\"flex flex-wrap items-start gap-2.5\">\n {horizon.items.map((item) => (\n <div\n key={item.name}\n className=\"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\"\n >\n <RoadmapMapItem item={item} />\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n);\n\nconst RoadmapTimeline = ({ roadmap }: { roadmap: ResolvedRoadmap }) => {\n if (roadmap.events.length === 0) {\n return <p className=\"opacity-50\">No dated events yet.</p>;\n }\n\n const dates = roadmap.events.map((event) => Date.parse(event.date));\n const rangeStart = Math.min(...dates);\n const rangeEnd = Math.max(...dates);\n\n return (\n <div className=\"flex flex-col gap-6\">\n {roadmap.horizons.map((horizon) => {\n const events = roadmap.events.filter((event) => event.horizonTitle === horizon.title);\n return (\n <div key={horizon.title} className=\"flex flex-col gap-1\">\n <div className={HORIZON_HEADER_CLASSES}>{horizon.title}</div>\n {events.length === 0 ? (\n <div className=\"text-2xs font-normal px-1 py-0 opacity-40\">No events yet</div>\n ) : (\n <RoadmapTimelineTrack events={events} rangeStart={rangeStart} rangeEnd={rangeEnd} />\n )}\n </div>\n );\n })}\n <div className=\"flex justify-between text-2xs opacity-50\">\n <span>{new Date(rangeStart).toLocaleDateString()}</span>\n <span>{new Date(rangeEnd).toLocaleDateString()}</span>\n </div>\n </div>\n );\n};\n\nexport const RoadmapPage = () => {\n const [roadmap, setRoadmap] = useState<ResolvedRoadmap | null>(null);\n const [loading, setLoading] = useState(true);\n const [loadFailed, setLoadFailed] = useState(false);\n const [promoting, setPromoting] = useState<{\n horizonTitle: string;\n item: ResolvedRoadmapItem;\n candidateName?: string;\n } | null>(null);\n const [viewMode, setViewMode] = useState<RoadmapViewMode>('tree');\n const plans = useAppStore((s) => s.plans);\n const navigate = useNavigate();\n const { item: highlightedItem } = useSearch({ from: '/roadmap' });\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n fetchRoadmap()\n .then(setRoadmap)\n .catch(() => setLoadFailed(true))\n .finally(() => setLoading(false));\n }, []);\n\n useEffect(() => {\n if (!highlightedItem || loading) return;\n const row = containerRef.current?.querySelector('.roadmap-item-highlighted');\n row?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, [highlightedItem, loading]);\n\n if (loading) {\n return (\n <div>\n <PageTitle>Roadmap</PageTitle>\n <p className=\"opacity-50\">Loading…</p>\n </div>\n );\n }\n\n if (loadFailed) {\n return (\n <div>\n <PageTitle>Roadmap</PageTitle>\n <p className=\"opacity-50\">\n Couldn't load the roadmap — the server may need a restart to pick up new routes.\n </p>\n </div>\n );\n }\n\n if (!roadmap || roadmap.horizons.length === 0) {\n return (\n <div>\n <PageTitle>Roadmap</PageTitle>\n <p className=\"opacity-50\">No `ROADMAP.md` found at the project root.</p>\n </div>\n );\n }\n\n const graduatedByItem = (item: ResolvedRoadmapItem) =>\n plans?.entries.filter((p) => p.subject === item.name) ?? [];\n\n const handleAddItem = async (horizonTitle: string, name: string, description: string) => {\n await addRoadmapItem(horizonTitle, name, description);\n setRoadmap(await fetchRoadmap());\n };\n\n const handleAddCandidate = async (horizonTitle: string, itemName: string, name: string) => {\n await addRoadmapCandidate(horizonTitle, itemName, name);\n setRoadmap(await fetchRoadmap());\n };\n\n return (\n <div ref={containerRef}>\n <GoalBanner goal={roadmap.goal} />\n <RoadmapViewModeSwitch mode={viewMode} onChange={setViewMode} />\n {viewMode === 'tree' && (\n <div className=\"flex flex-col gap-6\">\n {roadmap.horizons.map((horizon) => (\n <div key={horizon.title} className=\"flex flex-col gap-1\">\n <div className=\"flex items-baseline gap-2\">\n <div className={HORIZON_HEADER_CLASSES}>{horizon.title}</div>\n <HorizonPulse items={horizon.items} graduatedByItem={graduatedByItem} />\n </div>\n <div className=\"flex flex-wrap items-start gap-2.5\">\n {horizon.items.map((item) => (\n <div\n key={item.name}\n className=\"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\"\n >\n <RoadmapItemRow\n item={item}\n graduated={graduatedByItem(item)}\n highlighted={item.name === highlightedItem}\n onPromote={() => setPromoting({ horizonTitle: horizon.title, item })}\n onPromoteCandidate={(candidateName) =>\n setPromoting({ horizonTitle: horizon.title, item, candidateName })\n }\n onAddCandidate={(name) => handleAddCandidate(horizon.title, item.name, name)}\n onViewGraduated={() => navigate({ to: '/', search: { subject: item.name } })}\n onOpenGraduated={(title) =>\n navigate({\n to: '/plans/$planId',\n params: { planId: encodeURIComponent(title) },\n })\n }\n />\n </div>\n ))}\n <div className=\"flex-[1_1_240px] max-w-[320px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\">\n <AddItemForm\n onAdd={(name, description) => handleAddItem(horizon.title, name, description)}\n />\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n {viewMode === 'map' && <RoadmapMap roadmap={roadmap} graduatedByItem={graduatedByItem} />}\n {viewMode === 'timeline' && <RoadmapTimeline roadmap={roadmap} />}\n <PromoteRoadmapItemModal\n horizonTitle={promoting?.horizonTitle ?? null}\n item={promoting?.item ?? null}\n candidateName={promoting?.candidateName}\n onClose={() => setPromoting(null)}\n onPromoted={() => fetchRoadmap().then(setRoadmap)}\n />\n </div>\n );\n};\n"],"names":["NO_SUBJECT","PromoteRoadmapItemModal","horizonTitle","item","candidateName","onClose","onPromoted","promoteRoadmapItem","useAppStore","s","launchIdeaExtend","subjects","subjectsAvailable","useSubjectVocabulary","subject","setSubject","useState","loading","setLoading","error","setError","toast","useToast","useEffect","handlePromote","id","idea","e","buildRoadmapPromotePrompt","err","oneLineErrorSummary","jsx","Modal","jsxs","Select","Button","graduationCounts","graduated","p","HORIZON_HEADER_CLASSES","HORIZON_PULSE_CLASSES","ChevronRightIcon","size","CandidateRow","name","onPromote","Card","AddCandidateForm","onAdd","setName","saving","setSaving","handleAdd","Input","AddItemForm","open","setOpen","description","setDescription","plural","count","noun","RoadmapLinkTrail","link","Tooltip","Stamp","STATUS_STAMP","PrBadge","GraduatedRow","plan","onOpen","STATUS_LABEL","RoadmapItemRow","highlighted","onPromoteCandidate","onAddCandidate","onViewGraduated","onOpenGraduated","expanded","setExpanded","hasCandidates","shipped","queued","v","GoalBanner","goal","firstParagraph","restParagraphs","hasMore","Markdown","VIEW_MODES","RoadmapViewModeSwitch","mode","onChange","viewMode","HorizonPulse","items","graduatedByItem","charted","EVENT_KIND_COLOR","RoadmapTimelineTrack","events","rangeStart","rangeEnd","span","event","i","RoadmapMapItem","percent","RoadmapMap","roadmap","horizon","RoadmapTimeline","dates","RoadmapPage","setRoadmap","loadFailed","setLoadFailed","promoting","setPromoting","setViewMode","plans","navigate","useNavigate","highlightedItem","useSearch","containerRef","useRef","fetchRoadmap","row","_a","PageTitle","handleAddItem","addRoadmapItem","handleAddCandidate","itemName","addRoadmapCandidate","title"],"mappings":"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"}
|