@dendelion/paper-camp 0.27.0 → 0.28.2
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/README.md +26 -9
- package/dist/chunks/{phase-progress.DoB5EhtE.js → daemon-state.CI9RArUn.js} +1394 -1301
- package/dist/chunks/daemon-state.CI9RArUn.js.map +1 -0
- package/dist/chunks/dev-port.BGjGddjm.js +43 -0
- package/dist/chunks/dev-port.BGjGddjm.js.map +1 -0
- package/dist/cli/index.js +7403 -7012
- package/dist/cli/index.js.map +1 -1
- package/dist/core/daemon-state/daemon-state.d.ts +26 -0
- package/dist/core/daemon-state/daemon-state.d.ts.map +1 -0
- package/dist/core/daemon-state/daemon-state.test.d.ts +2 -0
- package/dist/core/daemon-state/daemon-state.test.d.ts.map +1 -0
- package/dist/core/daemon-state/index.d.ts +2 -0
- package/dist/core/daemon-state/index.d.ts.map +1 -0
- package/dist/core/git-log.d.ts.map +1 -1
- package/dist/core/hosted-client.d.ts +2 -0
- package/dist/core/hosted-client.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +139 -129
- package/dist/core/issues.d.ts.map +1 -1
- package/dist/core/machine-registry/machine-registry.d.ts +3 -0
- package/dist/core/machine-registry/machine-registry.d.ts.map +1 -1
- package/dist/core/parse/parser.d.ts +6 -2
- package/dist/core/parse/parser.d.ts.map +1 -1
- package/dist/core/parse/schemas.d.ts +3 -0
- package/dist/core/parse/schemas.d.ts.map +1 -1
- package/dist/core/phase-progress.d.ts.map +1 -1
- package/dist/core/run-filters.d.ts +26 -0
- package/dist/core/run-filters.d.ts.map +1 -0
- package/dist/core/run-filters.test.d.ts +2 -0
- package/dist/core/run-filters.test.d.ts.map +1 -0
- package/dist/core/run-rows.d.ts +16 -0
- package/dist/core/run-rows.d.ts.map +1 -0
- package/dist/core/run-rows.test.d.ts +2 -0
- package/dist/core/run-rows.test.d.ts.map +1 -0
- package/dist/core/run-stats.d.ts +10 -0
- package/dist/core/run-stats.d.ts.map +1 -0
- package/dist/core/run-stats.test.d.ts +2 -0
- package/dist/core/run-stats.test.d.ts.map +1 -0
- package/dist/toolbar/toolbar.js +12065 -0
- package/dist/toolbar/toolbar.js.map +1 -0
- package/dist/types/index.d.ts +58 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/vite/index.js +51 -44
- package/dist/vite/index.js.map +1 -1
- package/package.json +22 -13
- package/dist/app/assets/index-BEeox2IM.js +0 -3
- package/dist/app/assets/index-BEeox2IM.js.map +0 -1
- package/dist/app/assets/index-BW-jiFHN.js +0 -4
- package/dist/app/assets/index-BW-jiFHN.js.map +0 -1
- package/dist/app/assets/index-BhD5WS8p.js +0 -2
- package/dist/app/assets/index-BhD5WS8p.js.map +0 -1
- package/dist/app/assets/index-C1zPjxRZ.js +0 -2
- package/dist/app/assets/index-C1zPjxRZ.js.map +0 -1
- package/dist/app/assets/index-C2q1JS1i.js +0 -7
- package/dist/app/assets/index-C2q1JS1i.js.map +0 -1
- package/dist/app/assets/index-CHcj_gHq.js +0 -6
- package/dist/app/assets/index-CHcj_gHq.js.map +0 -1
- package/dist/app/assets/index-DN8Ti3Ov.js +0 -2
- package/dist/app/assets/index-DN8Ti3Ov.js.map +0 -1
- package/dist/app/assets/index-DX3JeGXT.js +0 -2
- package/dist/app/assets/index-DX3JeGXT.js.map +0 -1
- package/dist/app/assets/main-BZd2c99C.css +0 -1
- package/dist/app/assets/main-DCzV1FeH.js +0 -425
- package/dist/app/assets/main-DCzV1FeH.js.map +0 -1
- package/dist/app/assets/sidebar-section-CFrPSdM0.js +0 -2
- package/dist/app/assets/sidebar-section-CFrPSdM0.js.map +0 -1
- package/dist/app/fonts/Luminari-Regular.woff +0 -0
- package/dist/app/img/paper-logo.png +0 -0
- package/dist/app/img/paper-logo.svg +0 -7
- package/dist/app/index.html +0 -22
- package/dist/app/manifest.json +0 -13
- package/dist/app/toolbar.js +0 -20351
- package/dist/app/toolbar.js.map +0 -1
- package/dist/chunks/dev-port.BHJn90Pm.js +0 -52
- package/dist/chunks/dev-port.BHJn90Pm.js.map +0 -1
- package/dist/chunks/phase-progress.DoB5EhtE.js.map +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/core.d.ts +0 -2
- package/dist/fonts/Luminari-Regular.woff +0 -0
- package/dist/img/paper-logo.png +0 -0
- package/dist/img/paper-logo.svg +0 -7
- package/dist/manifest.json +0 -13
- package/dist/types.d.ts +0 -2
- package/dist/vite.d.ts +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dendelion/paper-camp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.2",
|
|
4
4
|
"description": "Local-first, AI-native project management with retro aesthetics",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.12.1",
|
|
@@ -26,29 +26,39 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
|
-
"dist",
|
|
29
|
+
"dist/chunks",
|
|
30
|
+
"dist/cli",
|
|
31
|
+
"dist/core",
|
|
32
|
+
"dist/mcp",
|
|
33
|
+
"dist/toolbar",
|
|
34
|
+
"dist/types",
|
|
35
|
+
"dist/vite",
|
|
30
36
|
"templates",
|
|
31
37
|
"README.md",
|
|
32
38
|
"LICENSE"
|
|
33
39
|
],
|
|
34
40
|
"scripts": {
|
|
35
41
|
"build": "tsc && vite build && vite build --config vite.app.config.ts && vite build --config vite.toolbar.config.ts",
|
|
36
|
-
"build:app": "vite build --config vite.app.config.ts",
|
|
42
|
+
"build:app": "node scripts/fetch-assets.mjs && vite build --config vite.app.config.ts",
|
|
37
43
|
"build:toolbar": "vite build --config vite.toolbar.config.ts",
|
|
44
|
+
"prepack": "tsc && vite build && vite build --config vite.toolbar.config.ts",
|
|
38
45
|
"dev": "vite --config vite.app.config.ts --port 3333",
|
|
39
46
|
"dev:lib": "vite",
|
|
40
47
|
"cli": "bun src/cli/index.ts",
|
|
41
48
|
"check-types": "tsc --noEmit --incremental",
|
|
42
49
|
"lint": "biome check . && node scripts/comment-stats.mjs",
|
|
43
50
|
"lint:write": "biome check --write .",
|
|
44
|
-
"test": "vitest run --
|
|
51
|
+
"test": "vitest run --project unit",
|
|
52
|
+
"test:integration": "vitest run --project integration",
|
|
53
|
+
"test:all": "vitest run",
|
|
54
|
+
"test:changed": "vitest run --changed",
|
|
45
55
|
"test:watch": "vitest",
|
|
46
56
|
"knip": "knip",
|
|
47
57
|
"depcruise": "depcruise src --config .dependency-cruiser.cjs",
|
|
48
58
|
"consistency": "knip --include files,dependencies && depcruise src --config .dependency-cruiser.cjs",
|
|
49
59
|
"prepare": "husky",
|
|
50
60
|
"comment-stats": "node scripts/comment-stats.mjs",
|
|
51
|
-
"smoke": "node scripts/pack-smoke-test.mjs"
|
|
61
|
+
"smoke": "node scripts/pack-smoke-test.mjs && node scripts/daemon-lifecycle-smoke-test.mjs"
|
|
52
62
|
},
|
|
53
63
|
"keywords": [
|
|
54
64
|
"project-management",
|
|
@@ -65,8 +75,6 @@
|
|
|
65
75
|
"bun": ">=1.0.0"
|
|
66
76
|
},
|
|
67
77
|
"peerDependencies": {
|
|
68
|
-
"react": "^18.0.0",
|
|
69
|
-
"react-dom": "^18.0.0",
|
|
70
78
|
"vite": "^5.0.0"
|
|
71
79
|
},
|
|
72
80
|
"peerDependenciesMeta": {
|
|
@@ -76,20 +84,17 @@
|
|
|
76
84
|
},
|
|
77
85
|
"dependencies": {
|
|
78
86
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
79
|
-
"@tanstack/react-router": "^1.58.3",
|
|
80
87
|
"commander": "^12.0.0",
|
|
81
88
|
"node-pty": "^1.1.0",
|
|
82
|
-
"react-markdown": "^10.1.0",
|
|
83
89
|
"yaml": "^2.9.0",
|
|
84
|
-
"zod": "^4.4.3"
|
|
85
|
-
"zustand": "^4.5.0"
|
|
90
|
+
"zod": "^4.4.3"
|
|
86
91
|
},
|
|
87
92
|
"devDependencies": {
|
|
88
93
|
"@biomejs/biome": "^1.9.3",
|
|
89
94
|
"@commitlint/cli": "^21.0.2",
|
|
90
95
|
"@commitlint/config-conventional": "^21.0.2",
|
|
91
|
-
"@dendelion/paper-camp": "0.16.0",
|
|
92
96
|
"@dendelion/paper-ui": "^0.18.0",
|
|
97
|
+
"@tanstack/react-router": "^1.58.3",
|
|
93
98
|
"@types/node": "^20.0.0",
|
|
94
99
|
"@types/react": "^18.3.11",
|
|
95
100
|
"@types/react-dom": "^18.3.0",
|
|
@@ -100,11 +105,15 @@
|
|
|
100
105
|
"husky": "^9.1.7",
|
|
101
106
|
"knip": "^6.24.0",
|
|
102
107
|
"postcss": "^8.4.47",
|
|
108
|
+
"react": "^18.0.0",
|
|
109
|
+
"react-dom": "^18.0.0",
|
|
110
|
+
"react-markdown": "^10.1.0",
|
|
103
111
|
"tailwindcss": "^3.4.14",
|
|
104
112
|
"typescript": "^5.6.3",
|
|
105
113
|
"vite": "^5.4.9",
|
|
106
114
|
"vite-plugin-dts": "^4.5.4",
|
|
107
115
|
"vite-tsconfig-paths": "^5.0.1",
|
|
108
|
-
"vitest": "^2.1.3"
|
|
116
|
+
"vitest": "^2.1.3",
|
|
117
|
+
"zustand": "^4.5.0"
|
|
109
118
|
}
|
|
110
119
|
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{u as h,N as v,r as i,j as e,O as L,J as y,R as u,z as S,g as T,K as f,P as A}from"./main-DCzV1FeH.js";const m=s=>String(s).padStart(2,"0"),_=s=>{const a=new Date(s);return`${a.getFullYear()}-${m(a.getMonth()+1)}-${m(a.getDate())}`},b=s=>new Date(s).toLocaleDateString(void 0,{weekday:"long",year:"numeric",month:"long",day:"numeric"}),j=s=>{const a=new Date(s);return`${m(a.getHours())}:${m(a.getMinutes())}:${m(a.getSeconds())}`},R=()=>{const s=h(t=>t.taskLog),a=h(t=>t.taskLogLoading),n=h(t=>t.loadTaskLog),{taskId:o}=v({from:"/tasks"}),l=i.useRef(null);i.useEffect(()=>{n()},[n]),i.useEffect(()=>{var c;if(!o)return;const t=(c=l.current)==null?void 0:c.querySelector(".task-row-highlighted");t==null||t.scrollIntoView({behavior:"smooth",block:"center"})},[o]);const r=[...s].sort((t,c)=>c.endedAt.localeCompare(t.endedAt)),x=[];for(const t of r){const c=_(t.endedAt),d=x.at(-1);(d==null?void 0:d.key)===c?d.entries.push(t):x.push({key:c,entries:[t]})}return{taskLogLoading:a,groups:x,taskId:o,containerRef:l}},D="outline-[rgba(200,154,90,0.5)]",E={done:"rgba(143, 185, 150, 0.25)",superseded:"rgba(212, 163, 115, 0.25)",error:"rgba(201, 139, 139, 0.25)"},I=({id:s})=>{const[a,n]=i.useState(null),[o,l]=i.useState(!1),[r,x]=i.useState(0);i.useEffect(()=>{let c=!1,d;n(null),l(!1);const g=k=>{L(s).then(w=>{c||n(w.lines??[])}).catch(()=>{if(!c){if(k>0){d=setTimeout(()=>g(k-1),700);return}l(!0),n([])}})};return g(3),()=>{c=!0,d&&clearTimeout(d)}},[s,r]);const t=e.jsx(y,{variant:"ghost",size:"small",onClick:()=>x(c=>c+1),children:"Retry"});return a===null?e.jsx("p",{className:"opacity-50 m-0",children:"Loading…"}):o?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
|
-
`)})},C={phase:"Phase run",audit:"Audit","batch-reconcile":"Batch reconcile","batch-draft":"Batch draft","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","pr-review":"PR review","issue-fix":"Issue fix","desk-discovery":"Desk discovery"},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",$=({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"})}),K=({entry:s,highlighted:a})=>{const[n,o]=i.useState(a);i.useEffect(()=>{a&&o(!0)},[a]);const l=()=>o(r=>!r);return e.jsxs("div",{className:a?`task-row-highlighted flex flex-col gap-1 rounded-[10px] outline outline-2 outline-offset-[-2px] ${D}`:"flex flex-col gap-1 rounded-[10px]",children:[e.jsx("div",{role:"button",tabIndex:0,"aria-expanded":n,onClick:l,onKeyDown:r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),l())},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($,{className:"group-aria-expanded:rotate-90"})}),e.jsx("span",{className:"font-semibold whitespace-nowrap overflow-hidden",children:C[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:S[s.agentId]}),e.jsxs("span",{className:"max-lg:hidden font-mono text-xs opacity-[0.55] whitespace-nowrap",children:[j(s.startedAt),"–",j(s.endedAt)]}),e.jsx("div",{className:"flex items-center",children:e.jsx(T,{size:"small",fillColor:E[s.outcome],textColor:s.outcome==="done"?f.accentGreenDark:s.outcome==="superseded"?f.accentAmberDark:f.accentRoseDark,children:s.outcome})})]})})}),n&&e.jsxs(u,{size:"small",texture:"kraft",className:"plan-row-card",children:[s.outcome==="error"&&s.reason&&e.jsx("p",{className:"m-0 mb-2 text-sm text-state-danger",children:s.reason}),e.jsx(I,{id:s.id})]})]})},p="text-sm font-semibold whitespace-nowrap overflow-hidden",P=({groups:s,highlightedId:a})=>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"})]})}),s.map(n=>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:b(n.entries[0].endedAt)})}),n.entries.map(o=>e.jsx(K,{entry:o,highlighted:o.id===a},o.id))]},n.key))]}),z=()=>{const{taskLogLoading:s,groups:a,taskId:n,containerRef:o}=R();return e.jsxs("div",{ref:o,children:[e.jsx(A,{children:"Tasks"}),s&&e.jsx("p",{className:"opacity-50",children:"Loading…"}),!s&&a.length===0&&e.jsx("p",{className:"opacity-50",children:"No tasks have run yet."}),!s&&a.length>0&&e.jsx(P,{groups:a,highlightedId:n})]})};export{z as TasksPage};
|
|
3
|
-
//# sourceMappingURL=index-BEeox2IM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-BEeox2IM.js","sources":["../../../src/app/features/tasks/helpers/format-task-log.ts","../../../src/app/features/tasks/hooks/use-tasks-page.ts","../../../src/app/features/tasks/constants.ts","../../../src/app/features/tasks/views/task-log-lines.tsx","../../../src/app/features/tasks/views/task-row.tsx","../../../src/app/features/tasks/views/task-list.tsx","../../../src/app/features/tasks/tasks-page.tsx"],"sourcesContent":["import type { TaskLogEntry } from '@/types/index';\n\nexport interface TaskGroup {\n key: string;\n entries: TaskLogEntry[];\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0');\n\nexport const dayKey = (iso: string) => {\n const d = new Date(iso);\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n};\n\nexport const formatDayHeader = (iso: string) =>\n new Date(iso).toLocaleDateString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n\nexport const formatTime = (iso: string) => {\n const d = new Date(iso);\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;\n};\n","import { useAppStore } from '@/app/stores/app-store';\nimport { useSearch } from '@tanstack/react-router';\nimport { useEffect, useRef } from 'react';\nimport { type TaskGroup, dayKey } from '../helpers';\n\nexport const useTasksPage = () => {\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: TaskGroup[] = [];\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 { taskLogLoading, groups, taskId, containerRef };\n};\n","import type { TaskLogEntry } from '@/types/index';\n\nexport const HIGHLIGHT_OUTLINE_CLASS = 'outline-[rgba(200,154,90,0.5)]';\n\nexport const TASK_OUTCOME_STAMP_FILL: Record<TaskLogEntry['outcome'], string> = {\n done: 'rgba(143, 185, 150, 0.25)',\n superseded: 'rgba(212, 163, 115, 0.25)',\n error: 'rgba(201, 139, 139, 0.25)',\n};\n","import { fetchTaskLogLines } from '@/app/services/content/docs-api';\nimport { Button } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\n\nexport interface TaskLogLinesProps {\n id: string;\n}\n\nexport const TaskLogLines = ({ id }: TaskLogLinesProps) => {\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 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","import { color } from '@/app/styles/tokens';\nimport { AGENT_LABELS, type TaskKind, type TaskLogEntry } from '@/types/index';\nimport { Card, Stamp } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\nimport { HIGHLIGHT_OUTLINE_CLASS, TASK_OUTCOME_STAMP_FILL } from '../constants';\nimport { formatTime } from '../helpers';\nimport { TaskLogLines } from './task-log-lines';\n\nconst TASK_KIND_LABELS: Record<TaskKind, string> = {\n phase: 'Phase run',\n audit: 'Audit',\n 'batch-reconcile': 'Batch reconcile',\n 'batch-draft': 'Batch draft',\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 'pr-review': 'PR review',\n 'issue-fix': 'Issue fix',\n 'desk-discovery': 'Desk discovery',\n};\n\nexport const 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 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\nexport interface TaskRowProps {\n entry: TaskLogEntry;\n highlighted: boolean;\n}\n\nexport const TaskRow = ({ entry, highlighted }: TaskRowProps) => {\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] ${HIGHLIGHT_OUTLINE_CLASS}`\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=\"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={TASK_OUTCOME_STAMP_FILL[entry.outcome]}\n textColor={\n entry.outcome === 'done'\n ? color.accentGreenDark\n : entry.outcome === 'superseded'\n ? color.accentAmberDark\n : color.accentRoseDark\n }\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 {entry.outcome === 'error' && entry.reason && (\n <p className=\"m-0 mb-2 text-sm text-state-danger\">{entry.reason}</p>\n )}\n <TaskLogLines id={entry.id} />\n </Card>\n )}\n </div>\n );\n};\n","import { Card } from '@dendelion/paper-ui';\nimport { type TaskGroup, formatDayHeader } from '../helpers';\nimport { TASK_ROWS_GRID_CLASS, TaskRow } from './task-row';\n\nconst headerLabelClassName = 'text-sm font-semibold whitespace-nowrap overflow-hidden';\n\nexport interface TaskListProps {\n groups: TaskGroup[];\n highlightedId: string | undefined;\n}\n\nexport const TaskList = ({ groups, highlightedId }: TaskListProps) => (\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 === highlightedId} />\n ))}\n </div>\n ))}\n </div>\n);\n","import { PageTitle } from '@/app/components/page-title';\nimport { useTasksPage } from './hooks';\nimport { TaskList } from './views';\n\nexport const TasksPage = () => {\n const { taskLogLoading, groups, taskId, containerRef } = useTasksPage();\n\n return (\n <div ref={containerRef}>\n <PageTitle>Tasks</PageTitle>\n {taskLogLoading && <p className=\"opacity-50\">Loading…</p>}\n {!taskLogLoading && groups.length === 0 && (\n <p className=\"opacity-50\">No tasks have run yet.</p>\n )}\n {!taskLogLoading && groups.length > 0 && <TaskList groups={groups} highlightedId={taskId} />}\n </div>\n );\n};\n"],"names":["pad","n","dayKey","iso","d","formatDayHeader","formatTime","useTasksPage","taskLog","useAppStore","s","taskLogLoading","loadTaskLog","taskId","useSearch","containerRef","useRef","useEffect","row","_a","sorted","a","b","groups","entry","key","group","HIGHLIGHT_OUTLINE_CLASS","TASK_OUTCOME_STAMP_FILL","TaskLogLines","id","lines","setLines","useState","failed","setFailed","attempt","setAttempt","cancelled","timer","load","remaining","fetchTaskLogLines","data","retry","jsx","Button","jsxs","TASK_KIND_LABELS","TASK_ROWS_GRID_CLASS","ChevronRightIcon","size","className","TaskRow","highlighted","expanded","setExpanded","toggle","v","e","Card","AGENT_LABELS","Stamp","color","headerLabelClassName","TaskList","highlightedId","TasksPage","PageTitle"],"mappings":"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"}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{u as b,Q as L,N as D,r as j,S as M,T as B,U as F,V as U,j as e,W as T,x as H,i as A,J as y,X as G,v as V,C as $,Y as q,Z as J,R as _,M as z,_ as K,g as R,a0 as O,a1 as N,P as w,p as E}from"./main-DCzV1FeH.js";import{a2 as je}from"./main-DCzV1FeH.js";const W=()=>{const s=b(n=>n.roadmap),t=b(n=>n.roadmapError),o=b(n=>n.loadRoadmap),i=b(n=>n.roadmapFilters),a=b(n=>n.plans),m=L(),{item:l}=D({from:"/roadmap"}),u=j.useRef(null),[d,h]=j.useState(null);j.useEffect(()=>{o()},[o]),j.useEffect(()=>{var x;if(!l||!s)return;const n=(x=u.current)==null?void 0:x.querySelector(".roadmap-item-highlighted");n==null||n.scrollIntoView({behavior:"smooth",block:"center"})},[l,s]);const f=n=>(a==null?void 0:a.entries.filter(x=>x.subject===n.name))??[],r=async(n,x,S)=>{await B(n,x,S),await o()},c=(n,x,S)=>{h({horizonTitle:n,item:x,candidateName:S})},p=s?M(s,i):[],g=p.reduce((n,x)=>n+x.items.length,0),v=i.horizons.length>0||i.statuses.length>0;return{roadmap:s,roadmapError:t,loadRoadmap:o,horizons:p,totalVisible:g,hasActiveFilters:v,highlightedItem:l,containerRef:u,promoting:d,setPromoting:h,graduatedByItem:f,handleAddCandidate:r,handlePromote:c,onOpenGraduated:m}},Z=["in-progress","review","planned","idea","done","dropped"],Q=()=>{const s=b(c=>c.roadmap),t=b(c=>c.roadmapFilters),o=b(c=>c.toggleRoadmapHorizon),i=b(c=>c.toggleRoadmapStatus),[a,m]=j.useState(!1),l=(s==null?void 0:s.horizons.map(c=>c.title))??[],u=s?F(s,t):{},d=s?U(s,t):{},h=new Set(t.horizons),f=new Set(t.statuses),r=Z.filter(c=>(d[c]??0)>0||f.has(c));return{roadmap:s,horizonTitles:l,horizonCounts:u,statusCounts:d,activeHorizons:h,activeStatuses:f,visibleStatuses:r,addOpen:a,setAddOpen:m,toggleRoadmapHorizon:o,toggleRoadmapStatus:i}},X=({open:s,horizonTitles:t,onClose:o})=>{const i=b(n=>n.loadRoadmap),[a,m]=j.useState(t[0]??""),[l,u]=j.useState(""),[d,h]=j.useState(""),[f,r]=j.useState(!1),[c,p]=j.useState(null),g=j.useRef(t);g.current=t,j.useEffect(()=>{s&&(m(g.current[0]??""),u(""),h(""),p(null),r(!1))},[s]);const v=async()=>{if(!(!l.trim()||!a)){r(!0),p(null);try{await G(a,l.trim(),d.trim()),await i(),o()}catch(n){p(n.message)}finally{r(!1)}}};return e.jsx(T,{open:s,onClose:o,title:"Add roadmap item",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(H,{size:"small",value:a,onChange:m,disabled:f,options:t.map(n=>({value:n,label:n}))}),e.jsx(A,{size:"small",value:l,onChange:n=>u(n.target.value),placeholder:"Item name…",disabled:f,autoFocus:!0}),e.jsx(A,{size:"small",value:d,onChange:n=>h(n.target.value),onKeyDown:n=>{n.key==="Enter"&&v()},placeholder:"Description…",disabled:f}),c&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:c}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:o,disabled:f,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:v,disabled:f||!l.trim()||!a,children:f?"Adding…":"Add item"})]})]})})},C="__no-subject__",Y=({horizonTitle:s,item:t,candidateName:o,onClose:i,onPromoted:a})=>{const m=b(x=>x.promoteRoadmapItem),l=b(x=>x.launchIdeaExtend),{subjects:u,available:d}=V(),[h,f]=j.useState(C),[r,c]=j.useState(!1),[p,g]=j.useState(null),{toast:v}=$();j.useEffect(()=>{t&&(f(o?t.name:C),c(!1),g(null))},[t,o]);const n=async()=>{if(!(!t||!s)){c(!0),g(null);try{const x=await m(s,t,h===C?void 0:h,o??void 0),S=b.getState().ideaEntries.find(k=>k.id===x);if(S)try{await l(x,q(S,s))}catch(k){v({title:"Idea created, but the refine agent failed to launch",description:J(k.message),variant:"error"})}a(),i()}catch(x){g(x.message)}finally{c(!1)}}};return e.jsx(T,{open:t!==null,onClose:i,title:o??(t==null?void 0:t.name)??"",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[!o&&e.jsx("p",{className:"m-0 opacity-80",children:t==null?void 0:t.description}),e.jsx("div",{children:e.jsx(H,{size:"small",value:h,onChange:f,disabled:r||!d,options:[{value:C,label:"No subject"},...!d&&h!==C?[{value:h,label:h}]:[],...u.map(x=>({value:x,label:x}))]})}),p&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:p}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:i,disabled:r,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:n,disabled:r,children:r?"Promoting…":"Promote to idea"})]})]})})},ee=({onAdd:s})=>{const[t,o]=j.useState(""),[i,a]=j.useState(!1),m=async()=>{if(t.trim()){a(!0);try{await s(t.trim()),o("")}finally{a(!1)}}};return e.jsxs("div",{className:"flex gap-2",children:[e.jsx(A,{size:"small",value:t,onChange:l=>o(l.target.value),onKeyDown:l=>{l.key==="Enter"&&m()},placeholder:"Add option…",disabled:i,className:"flex-1"}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:m,disabled:i||!t.trim(),children:"Add"})]})},se=({name:s,onPromote:t})=>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:s}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:t,children:"Promote to idea"})]})}),te=({goal:s})=>{const[t,o]=j.useState(!1),[i,...a]=s.split(/\n{2,}/),m=a.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:"text-sm leading-relaxed opacity-80 mt-2",children:[e.jsx(z,{children:i}),t&&m&&e.jsx(z,{children:a.join(`
|
|
2
|
-
|
|
3
|
-
`)})]}),m&&e.jsx("button",{type:"button",onClick:()=>o(l=>!l),className:"bg-transparent border-none p-0 mt-2 cursor-pointer text-2xs opacity-[0.65] underline",children:t?"Show less":"Show more"})]})},ae="outline-[rgba(200,154,90,0.5)]",I={fill:"rgba(0, 0, 0, 0.06)",text:"rgba(0, 0, 0, 0.55)"},ne=(s,t)=>{const o=new Set,i=t.map(a=>(a.id&&o.add(a.id),a.idea&&o.add(a.idea),{key:a.title,label:a.title,status:a.status,pr:a.pr,planId:a.id,planTitle:a.title}));for(const a of s)o.has(a.id)||i.push({key:a.id,label:a.id,status:a.status,pr:a.pr});return i},oe=({idea:s,onOpen:t})=>e.jsx(_,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:"flex items-center gap-3",children:[t?e.jsx("button",{type:"button",onClick:t,className:"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap bg-transparent border-none p-0 cursor-pointer [font:inherit] text-inherit text-left",children:s.label}):e.jsx("span",{className:"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap",children:s.label}),s.pr&&e.jsx(K,{pr:s.pr}),e.jsx(R,{size:"small",fillColor:N[s.status].fill,textColor:N[s.status].text,children:O[s.status]})]})}),le=({done:s,total:t})=>{const o=t>0?Math.round(s/t*100):0;return e.jsx("div",{className:"w-24 h-1.5 rounded-full bg-black/[0.08] overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${t>0?"bg-watercolor-green":"bg-transparent"}`,style:{width:`${o}%`}})})},re=s=>({shipped:s.filter(t=>t.status==="done").length,queued:s.filter(t=>t.status!=="done"&&t.status!=="dropped").length}),ie=({size:s=14})=>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",children:e.jsx("polyline",{points:"9 18 15 12 9 6"})}),de=({item:s,graduated:t,highlighted:o,onPromote:i,onPromoteCandidate:a,onAddCandidate:m,onOpenGraduated:l})=>{const[u,d]=j.useState(o),{shipped:h,queued:f}=re(t),r=s.candidates.length,c=ne(s.links,t);return j.useEffect(()=>{o&&d(!0)},[o]),e.jsxs("div",{className:`border-b border-black/10 last:border-b-0 ${o?`roadmap-item-highlighted outline outline-2 outline-offset-[-2px] ${ae}`:""}`,children:[e.jsxs("button",{type:"button","aria-expanded":u,onClick:()=>d(p=>!p),className:"flex w-full items-center gap-3 py-2.5 px-0 bg-transparent border-none cursor-pointer text-left [font:inherit] text-inherit",children:[e.jsx("span",{className:`inline-flex items-center opacity-50 shrink-0 ${u?"rotate-90":""}`,children:e.jsx(ie,{})}),e.jsx("span",{className:"font-semibold flex-1 min-w-0 truncate",children:s.name}),e.jsx(le,{done:s.rollup.done,total:s.rollup.total}),f>0&&e.jsxs(R,{size:"small",fillColor:N.planned.fill,textColor:N.planned.text,children:[f," in queue"]}),h>0&&e.jsxs(R,{size:"small",fillColor:N.done.fill,textColor:N.done.text,children:[h," shipped"]}),r>0&&e.jsxs(R,{size:"small",fillColor:I.fill,textColor:I.text,children:[r," candidate",r===1?"":"s"]})]}),u&&e.jsxs("div",{className:"flex flex-col gap-1 pl-6 pb-4",children:[e.jsx("span",{className:"text-sm opacity-70",children:s.description}),c.map(p=>e.jsx(oe,{idea:p,onOpen:p.planTitle?()=>l(p.planId,p.planTitle):void 0},p.key)),s.candidates.map(p=>e.jsx(se,{name:p,onPromote:()=>a(p)},p)),e.jsx(ee,{onAdd:m}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:i,className:"self-start",children:"Promote to idea"})]})]})},ce="font-handwritten text-md font-semibold opacity-70 leading-none pt-2 px-1 pb-0",pe=({horizon:s,highlightedItem:t,graduatedByItem:o,onPromote:i,onAddCandidate:a,onOpenGraduated:m})=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("div",{className:ce,children:s.title}),e.jsx("div",{className:"flex flex-col",children:s.items.map(l=>e.jsx(de,{item:l,graduated:o(l),highlighted:l.name===t,onPromote:()=>i(l),onPromoteCandidate:u=>i(l,u),onAddCandidate:u=>a(l.name,u),onOpenGraduated:m},l.name))})]}),xe=()=>{const{roadmap:s,roadmapError:t,loadRoadmap:o,horizons:i,totalVisible:a,hasActiveFilters:m,highlightedItem:l,containerRef:u,promoting:d,setPromoting:h,graduatedByItem:f,handleAddCandidate:r,handlePromote:c,onOpenGraduated:p}=W();return t?e.jsxs("div",{children:[e.jsx(w,{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."})]}):s?s.horizons.length===0?e.jsxs("div",{children:[e.jsx(w,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"No `ROADMAP.md` found at the project root."})]}):e.jsxs("div",{ref:u,children:[e.jsx(w,{children:"Roadmap"}),e.jsx(te,{goal:s.goal}),a===0?e.jsx("p",{className:"opacity-50 py-6 px-0 text-center",children:m?"Nothing matches these filters — clear one from the sidebar to see more.":"No roadmap items yet — add one from the sidebar."}):e.jsx("div",{className:"flex flex-col gap-6",children:i.filter(g=>g.items.length>0).map(g=>e.jsx(pe,{horizon:g,highlightedItem:l,graduatedByItem:f,onPromote:(v,n)=>c(g.title,v,n),onAddCandidate:(v,n)=>r(g.title,v,n),onOpenGraduated:p},g.title))}),e.jsx(Y,{horizonTitle:(d==null?void 0:d.horizonTitle)??null,item:(d==null?void 0:d.item)??null,candidateName:d==null?void 0:d.candidateName,onClose:()=>h(null),onPromoted:o})]}):e.jsxs("div",{children:[e.jsx(w,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"Loading…"})]})},P="pc-row-label font-handwritten text-xs font-semibold opacity-[0.45]",ue=()=>{const{roadmap:s,horizonTitles:t,horizonCounts:o,statusCounts:i,activeHorizons:a,activeStatuses:m,visibleStatuses:l,addOpen:u,setAddOpen:d,toggleRoadmapHorizon:h,toggleRoadmapStatus:f}=Q();return s?e.jsxs("div",{className:"flex flex-col",children:[e.jsx("div",{className:"flex h-[64px] items-center",children:e.jsx(y,{type:"button",variant:"primary",size:"small",onClick:()=>d(!0),disabled:t.length===0,children:"+ Add item"})}),e.jsxs("div",{children:[e.jsx("div",{className:P,children:"Horizon"}),e.jsx("div",{className:"flex flex-col",children:t.map(r=>e.jsx(E,{size:"small",active:a.has(r),onClick:()=>h(r),className:"pc-row text-xs",action:e.jsx("span",{className:"text-2xs text-ink-500",children:o[r]??0}),children:r},r))})]}),e.jsxs("div",{children:[e.jsx("div",{className:P,children:"Status"}),e.jsxs("div",{className:"flex flex-col",children:[l.length===0&&e.jsx("span",{className:"opacity-50 text-2xs",children:"No linked ideas"}),l.map(r=>e.jsx(E,{size:"small",active:m.has(r),onClick:()=>f(r),className:"pc-row text-xs",icon:e.jsx("span",{className:"w-[9px] h-[9px] rounded-full shrink-0",style:{background:N[r].text}}),action:e.jsx("span",{className:"text-2xs text-ink-500",children:i[r]??0}),children:O[r]},r))]})]}),e.jsx(X,{open:u,horizonTitles:t,onClose:()=>d(!1)})]}):null};export{xe as RoadmapPage,ue as RoadmapSidebar,je as useRoadmapItemNames};
|
|
4
|
-
//# sourceMappingURL=index-BW-jiFHN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-BW-jiFHN.js","sources":["../../../src/app/features/roadmap/hooks/use-roadmap-page.ts","../../../src/app/features/roadmap/hooks/use-roadmap-sidebar.ts","../../../src/app/features/roadmap/modals/add-roadmap-item-modal.tsx","../../../src/app/features/roadmap/modals/promote-roadmap-item-modal.tsx","../../../src/app/features/roadmap/views/add-candidate-form.tsx","../../../src/app/features/roadmap/views/candidate-row.tsx","../../../src/app/features/roadmap/views/goal-banner.tsx","../../../src/app/features/roadmap/constants.ts","../../../src/app/features/roadmap/views/idea-row.tsx","../../../src/app/features/roadmap/views/progress-bar.tsx","../../../src/app/features/roadmap/views/roadmap-item-row.tsx","../../../src/app/features/roadmap/views/horizon-section.tsx","../../../src/app/features/roadmap/roadmap-page.tsx","../../../src/app/features/roadmap/roadmap-sidebar.tsx"],"sourcesContent":["import { useOpenEntity } from '@/app/hooks';\nimport { addRoadmapCandidate } from '@/app/services/content/docs-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport type { PlanEntry, ResolvedRoadmapItem } from '@/types/index';\nimport { useSearch } from '@tanstack/react-router';\nimport { useEffect, useRef, useState } from 'react';\nimport { filterHorizons } from '../helpers';\n\ninterface Promoting {\n horizonTitle: string;\n item: ResolvedRoadmapItem;\n candidateName?: string;\n}\n\nexport const useRoadmapPage = () => {\n const roadmap = useAppStore((s) => s.roadmap);\n const roadmapError = useAppStore((s) => s.roadmapError);\n const loadRoadmap = useAppStore((s) => s.loadRoadmap);\n const filters = useAppStore((s) => s.roadmapFilters);\n const plans = useAppStore((s) => s.plans);\n const openEntity = useOpenEntity();\n const { item: highlightedItem } = useSearch({ from: '/roadmap' });\n const containerRef = useRef<HTMLDivElement>(null);\n const [promoting, setPromoting] = useState<Promoting | null>(null);\n\n useEffect(() => {\n loadRoadmap();\n }, [loadRoadmap]);\n\n useEffect(() => {\n if (!highlightedItem || !roadmap) return;\n const row = containerRef.current?.querySelector('.roadmap-item-highlighted');\n row?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, [highlightedItem, roadmap]);\n\n const graduatedByItem = (item: ResolvedRoadmapItem): PlanEntry[] =>\n plans?.entries.filter((p) => p.subject === item.name) ?? [];\n\n const handleAddCandidate = async (horizonTitle: string, itemName: string, name: string) => {\n await addRoadmapCandidate(horizonTitle, itemName, name);\n await loadRoadmap();\n };\n\n const handlePromote = (\n horizonTitle: string,\n item: ResolvedRoadmapItem,\n candidateName?: string,\n ) => {\n setPromoting({ horizonTitle, item, candidateName });\n };\n\n const horizons = roadmap ? filterHorizons(roadmap, filters) : [];\n const totalVisible = horizons.reduce((count, horizon) => count + horizon.items.length, 0);\n const hasActiveFilters = filters.horizons.length > 0 || filters.statuses.length > 0;\n\n return {\n roadmap,\n roadmapError,\n loadRoadmap,\n horizons,\n totalVisible,\n hasActiveFilters,\n highlightedItem,\n containerRef,\n promoting,\n setPromoting,\n graduatedByItem,\n handleAddCandidate,\n handlePromote,\n onOpenGraduated: openEntity,\n };\n};\n","import { useAppStore } from '@/app/stores/app-store';\nimport type { PlanStatus } from '@/types/index';\nimport { useState } from 'react';\nimport { horizonItemCounts, statusItemCounts } from '../helpers';\n\nconst STATUS_CHIP_ORDER: PlanStatus[] = [\n 'in-progress',\n 'review',\n 'planned',\n 'idea',\n 'done',\n 'dropped',\n];\n\nexport const useRoadmapSidebar = () => {\n const roadmap = useAppStore((s) => s.roadmap);\n const filters = useAppStore((s) => s.roadmapFilters);\n const toggleRoadmapHorizon = useAppStore((s) => s.toggleRoadmapHorizon);\n const toggleRoadmapStatus = useAppStore((s) => s.toggleRoadmapStatus);\n const [addOpen, setAddOpen] = useState(false);\n\n const horizonTitles = roadmap?.horizons.map((horizon) => horizon.title) ?? [];\n const horizonCounts = roadmap ? horizonItemCounts(roadmap, filters) : {};\n const statusCounts = roadmap ? statusItemCounts(roadmap, filters) : {};\n const activeHorizons = new Set(filters.horizons);\n const activeStatuses = new Set(filters.statuses);\n const visibleStatuses = STATUS_CHIP_ORDER.filter(\n (status) => (statusCounts[status] ?? 0) > 0 || activeStatuses.has(status),\n );\n\n return {\n roadmap,\n horizonTitles,\n horizonCounts,\n statusCounts,\n activeHorizons,\n activeStatuses,\n visibleStatuses,\n addOpen,\n setAddOpen,\n toggleRoadmapHorizon,\n toggleRoadmapStatus,\n };\n};\n","import { addRoadmapItem } from '@/app/services/content/docs-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { Button, Input, Modal, Select } from '@dendelion/paper-ui';\nimport { useEffect, useRef, useState } from 'react';\n\ninterface AddRoadmapItemModalProps {\n open: boolean;\n horizonTitles: string[];\n onClose: () => void;\n}\n\nexport const AddRoadmapItemModal = ({ open, horizonTitles, onClose }: AddRoadmapItemModalProps) => {\n const loadRoadmap = useAppStore((s) => s.loadRoadmap);\n const [horizon, setHorizon] = useState(horizonTitles[0] ?? '');\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const horizonTitlesRef = useRef(horizonTitles);\n horizonTitlesRef.current = horizonTitles;\n\n useEffect(() => {\n if (open) {\n setHorizon(horizonTitlesRef.current[0] ?? '');\n setName('');\n setDescription('');\n setError(null);\n setSaving(false);\n }\n }, [open]);\n\n const handleAdd = async () => {\n if (!name.trim() || !horizon) return;\n setSaving(true);\n setError(null);\n try {\n await addRoadmapItem(horizon, name.trim(), description.trim());\n await loadRoadmap();\n onClose();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <Modal open={open} onClose={onClose} title=\"Add roadmap item\" size=\"small\">\n <div className=\"flex flex-col gap-4\">\n <Select\n size=\"small\"\n value={horizon}\n onChange={setHorizon}\n disabled={saving}\n options={horizonTitles.map((title) => ({ value: title, label: title }))}\n />\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 {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={saving}>\n Cancel\n </Button>\n <Button\n type=\"button\"\n variant=\"primary\"\n onClick={handleAdd}\n disabled={saving || !name.trim() || !horizon}\n >\n {saving ? 'Adding…' : 'Add item'}\n </Button>\n </div>\n </div>\n </Modal>\n );\n};\n","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 { Button, Input } from '@dendelion/paper-ui';\nimport { useState } from 'react';\n\ninterface AddCandidateFormProps {\n onAdd: (name: string) => Promise<void>;\n}\n\nexport const AddCandidateForm = ({ onAdd }: AddCandidateFormProps) => {\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","import { Button, Card } from '@dendelion/paper-ui';\n\ninterface CandidateRowProps {\n name: string;\n onPromote: () => void;\n}\n\nexport const CandidateRow = ({ name, onPromote }: CandidateRowProps) => (\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","import { Markdown } from '@/app/components/markdown';\nimport { useState } from 'react';\n\ninterface GoalBannerProps {\n goal: string;\n}\n\nexport const GoalBanner = ({ goal }: GoalBannerProps) => {\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=\"text-sm leading-relaxed opacity-80 mt-2\">\n <Markdown>{firstParagraph}</Markdown>\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","export const HIGHLIGHT_OUTLINE_CLASS = 'outline-[rgba(200,154,90,0.5)]';\n\nexport const CANDIDATE_STAMP = {\n fill: 'rgba(0, 0, 0, 0.06)',\n text: 'rgba(0, 0, 0, 0.55)',\n};\n","import { PrBadge } from '@/app/features/plans/components/pr-badge';\nimport { STATUS_LABEL, STATUS_STAMP } from '@/app/features/plans/constants';\nimport type { PlanEntry, PlanStatus, PrInfo, RoadmapLink } from '@/types/index';\nimport { Card, Stamp } from '@dendelion/paper-ui';\n\nexport interface MergedIdea {\n key: string;\n label: string;\n status: PlanStatus;\n pr?: PrInfo;\n planId?: string;\n planTitle?: string;\n}\n\nexport const mergeIdeas = (links: RoadmapLink[], graduated: PlanEntry[]): MergedIdea[] => {\n const represented = new Set<string>();\n const merged: MergedIdea[] = graduated.map((plan) => {\n if (plan.id) represented.add(plan.id);\n if (plan.idea) represented.add(plan.idea);\n return {\n key: plan.title,\n label: plan.title,\n status: plan.status,\n pr: plan.pr,\n planId: plan.id,\n planTitle: plan.title,\n };\n });\n for (const link of links) {\n if (represented.has(link.id)) continue;\n merged.push({ key: link.id, label: link.id, status: link.status, pr: link.pr });\n }\n return merged;\n};\n\ninterface IdeaRowProps {\n idea: MergedIdea;\n onOpen?: () => void;\n}\n\nexport const IdeaRow = ({ idea, onOpen }: IdeaRowProps) => (\n <Card size=\"small\" texture=\"kraft\" className=\"plan-row-card\">\n <div className=\"flex items-center gap-3\">\n {onOpen ? (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap bg-transparent border-none p-0 cursor-pointer [font:inherit] text-inherit text-left\"\n >\n {idea.label}\n </button>\n ) : (\n <span className=\"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap\">\n {idea.label}\n </span>\n )}\n {idea.pr && <PrBadge pr={idea.pr} />}\n <Stamp\n size=\"small\"\n fillColor={STATUS_STAMP[idea.status].fill}\n textColor={STATUS_STAMP[idea.status].text}\n >\n {STATUS_LABEL[idea.status]}\n </Stamp>\n </div>\n </Card>\n);\n","interface ProgressBarProps {\n done: number;\n total: number;\n}\n\nexport const ProgressBar = ({ done, total }: ProgressBarProps) => {\n const percent = total > 0 ? Math.round((done / total) * 100) : 0;\n return (\n <div className=\"w-24 h-1.5 rounded-full bg-black/[0.08] overflow-hidden shrink-0\">\n <div\n className={`h-full rounded-full ${total > 0 ? 'bg-watercolor-green' : 'bg-transparent'}`}\n style={{ width: `${percent}%` }}\n />\n </div>\n );\n};\n","import { STATUS_STAMP } from '@/app/features/plans/constants';\nimport type { PlanEntry, ResolvedRoadmapItem } from '@/types/index';\nimport { Button, Stamp } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\nimport { CANDIDATE_STAMP, HIGHLIGHT_OUTLINE_CLASS } from '../constants';\nimport { AddCandidateForm } from './add-candidate-form';\nimport { CandidateRow } from './candidate-row';\nimport { IdeaRow, mergeIdeas } from './idea-row';\nimport { ProgressBar } from './progress-bar';\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 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\ninterface RoadmapItemRowProps {\n item: ResolvedRoadmapItem;\n graduated: PlanEntry[];\n highlighted: boolean;\n onPromote: () => void;\n onPromoteCandidate: (candidateName: string) => void;\n onAddCandidate: (name: string) => Promise<void>;\n onOpenGraduated: (id: string | undefined, title: string) => void;\n}\n\nexport const RoadmapItemRow = ({\n item,\n graduated,\n highlighted,\n onPromote,\n onPromoteCandidate,\n onAddCandidate,\n onOpenGraduated,\n}: RoadmapItemRowProps) => {\n const [expanded, setExpanded] = useState(highlighted);\n const { shipped, queued } = graduationCounts(graduated);\n const candidates = item.candidates.length;\n const ideas = mergeIdeas(item.links, graduated);\n\n useEffect(() => {\n if (highlighted) setExpanded(true);\n }, [highlighted]);\n\n return (\n <div\n className={`border-b border-black/10 last:border-b-0 ${highlighted ? `roadmap-item-highlighted outline outline-2 outline-offset-[-2px] ${HIGHLIGHT_OUTLINE_CLASS}` : ''}`}\n >\n <button\n type=\"button\"\n aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}\n className=\"flex w-full items-center gap-3 py-2.5 px-0 bg-transparent border-none cursor-pointer text-left [font:inherit] text-inherit\"\n >\n <span\n className={`inline-flex items-center opacity-50 shrink-0 ${expanded ? 'rotate-90' : ''}`}\n >\n <ChevronRightIcon />\n </span>\n <span className=\"font-semibold flex-1 min-w-0 truncate\">{item.name}</span>\n <ProgressBar done={item.rollup.done} total={item.rollup.total} />\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 size=\"small\" fillColor={STATUS_STAMP.done.fill} textColor={STATUS_STAMP.done.text}>\n {shipped} shipped\n </Stamp>\n )}\n {candidates > 0 && (\n <Stamp size=\"small\" fillColor={CANDIDATE_STAMP.fill} textColor={CANDIDATE_STAMP.text}>\n {candidates} candidate{candidates === 1 ? '' : 's'}\n </Stamp>\n )}\n </button>\n {expanded && (\n <div className=\"flex flex-col gap-1 pl-6 pb-4\">\n <span className=\"text-sm opacity-70\">{item.description}</span>\n {ideas.map((idea) => (\n <IdeaRow\n key={idea.key}\n idea={idea}\n onOpen={\n idea.planTitle\n ? () => onOpenGraduated(idea.planId, idea.planTitle as string)\n : undefined\n }\n />\n ))}\n {item.candidates.map((candidateName) => (\n <CandidateRow\n key={candidateName}\n name={candidateName}\n onPromote={() => onPromoteCandidate(candidateName)}\n />\n ))}\n <AddCandidateForm onAdd={onAddCandidate} />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"small\"\n onClick={onPromote}\n className=\"self-start\"\n >\n Promote to idea\n </Button>\n </div>\n )}\n </div>\n );\n};\n","import type { PlanEntry, ResolvedRoadmapHorizon, ResolvedRoadmapItem } from '@/types/index';\nimport { RoadmapItemRow } from './roadmap-item-row';\n\nconst HORIZON_HEADER_CLASSES =\n 'font-handwritten text-md font-semibold opacity-70 leading-none pt-2 px-1 pb-0';\n\ninterface HorizonSectionProps {\n horizon: ResolvedRoadmapHorizon;\n highlightedItem: string | undefined;\n graduatedByItem: (item: ResolvedRoadmapItem) => PlanEntry[];\n onPromote: (item: ResolvedRoadmapItem, candidateName?: string) => void;\n onAddCandidate: (itemName: string, name: string) => Promise<void>;\n onOpenGraduated: (id: string | undefined, title: string) => void;\n}\n\nexport const HorizonSection = ({\n horizon,\n highlightedItem,\n graduatedByItem,\n onPromote,\n onAddCandidate,\n onOpenGraduated,\n}: HorizonSectionProps) => (\n <div className=\"flex flex-col gap-1\">\n <div className={HORIZON_HEADER_CLASSES}>{horizon.title}</div>\n <div className=\"flex flex-col\">\n {horizon.items.map((item) => (\n <RoadmapItemRow\n key={item.name}\n item={item}\n graduated={graduatedByItem(item)}\n highlighted={item.name === highlightedItem}\n onPromote={() => onPromote(item)}\n onPromoteCandidate={(candidateName) => onPromote(item, candidateName)}\n onAddCandidate={(name) => onAddCandidate(item.name, name)}\n onOpenGraduated={onOpenGraduated}\n />\n ))}\n </div>\n </div>\n);\n","import { PageTitle } from '@/app/components/page-title';\nimport { useRoadmapPage } from './hooks';\nimport { PromoteRoadmapItemModal } from './modals';\nimport { GoalBanner, HorizonSection } from './views';\n\nexport const RoadmapPage = () => {\n const {\n roadmap,\n roadmapError,\n loadRoadmap,\n horizons,\n totalVisible,\n hasActiveFilters,\n highlightedItem,\n containerRef,\n promoting,\n setPromoting,\n graduatedByItem,\n handleAddCandidate,\n handlePromote,\n onOpenGraduated,\n } = useRoadmapPage();\n\n if (roadmapError) {\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) {\n return (\n <div>\n <PageTitle>Roadmap</PageTitle>\n <p className=\"opacity-50\">Loading…</p>\n </div>\n );\n }\n\n if (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 return (\n <div ref={containerRef}>\n <PageTitle>Roadmap</PageTitle>\n <GoalBanner goal={roadmap.goal} />\n {totalVisible === 0 ? (\n <p className=\"opacity-50 py-6 px-0 text-center\">\n {hasActiveFilters\n ? 'Nothing matches these filters — clear one from the sidebar to see more.'\n : 'No roadmap items yet — add one from the sidebar.'}\n </p>\n ) : (\n <div className=\"flex flex-col gap-6\">\n {horizons\n .filter((horizon) => horizon.items.length > 0)\n .map((horizon) => (\n <HorizonSection\n key={horizon.title}\n horizon={horizon}\n highlightedItem={highlightedItem}\n graduatedByItem={graduatedByItem}\n onPromote={(item, candidateName) =>\n handlePromote(horizon.title, item, candidateName)\n }\n onAddCandidate={(itemName, name) =>\n handleAddCandidate(horizon.title, itemName, name)\n }\n onOpenGraduated={onOpenGraduated}\n />\n ))}\n </div>\n )}\n <PromoteRoadmapItemModal\n horizonTitle={promoting?.horizonTitle ?? null}\n item={promoting?.item ?? null}\n candidateName={promoting?.candidateName}\n onClose={() => setPromoting(null)}\n onPromoted={loadRoadmap}\n />\n </div>\n );\n};\n","import { STATUS_LABEL, STATUS_STAMP } from '@/app/features/plans/constants';\nimport { Button, ListItem } from '@dendelion/paper-ui';\nimport { useRoadmapSidebar } from './hooks';\nimport { AddRoadmapItemModal } from './modals';\n\n// Matches SidebarSection — handwritten, no caps, one grid cell.\nconst sectionLabelClass = 'pc-row-label font-handwritten text-xs font-semibold opacity-[0.45]';\n\nexport const RoadmapSidebar = () => {\n const {\n roadmap,\n horizonTitles,\n horizonCounts,\n statusCounts,\n activeHorizons,\n activeStatuses,\n visibleStatuses,\n addOpen,\n setAddOpen,\n toggleRoadmapHorizon,\n toggleRoadmapStatus,\n } = useRoadmapSidebar();\n\n if (!roadmap) return null;\n\n return (\n <div className=\"flex flex-col\">\n <div className=\"flex h-[64px] items-center\">\n <Button\n type=\"button\"\n variant=\"primary\"\n size=\"small\"\n onClick={() => setAddOpen(true)}\n disabled={horizonTitles.length === 0}\n >\n + Add item\n </Button>\n </div>\n\n <div>\n <div className={sectionLabelClass}>Horizon</div>\n <div className=\"flex flex-col\">\n {horizonTitles.map((title) => (\n <ListItem\n key={title}\n size=\"small\"\n active={activeHorizons.has(title)}\n onClick={() => toggleRoadmapHorizon(title)}\n className=\"pc-row text-xs\"\n action={<span className=\"text-2xs text-ink-500\">{horizonCounts[title] ?? 0}</span>}\n >\n {title}\n </ListItem>\n ))}\n </div>\n </div>\n\n <div>\n <div className={sectionLabelClass}>Status</div>\n <div className=\"flex flex-col\">\n {visibleStatuses.length === 0 && (\n <span className=\"opacity-50 text-2xs\">No linked ideas</span>\n )}\n {visibleStatuses.map((status) => (\n <ListItem\n key={status}\n size=\"small\"\n active={activeStatuses.has(status)}\n onClick={() => toggleRoadmapStatus(status)}\n className=\"pc-row text-xs\"\n icon={\n <span\n className=\"w-[9px] h-[9px] rounded-full shrink-0\"\n style={{ background: STATUS_STAMP[status].text }}\n />\n }\n action={<span className=\"text-2xs text-ink-500\">{statusCounts[status] ?? 0}</span>}\n >\n {STATUS_LABEL[status]}\n </ListItem>\n ))}\n </div>\n </div>\n\n <AddRoadmapItemModal\n open={addOpen}\n horizonTitles={horizonTitles}\n onClose={() => setAddOpen(false)}\n />\n </div>\n );\n};\n"],"names":["useRoadmapPage","roadmap","useAppStore","s","roadmapError","loadRoadmap","filters","plans","openEntity","useOpenEntity","highlightedItem","useSearch","containerRef","useRef","promoting","setPromoting","useState","useEffect","row","_a","graduatedByItem","item","p","handleAddCandidate","horizonTitle","itemName","name","addRoadmapCandidate","handlePromote","candidateName","horizons","filterHorizons","totalVisible","count","horizon","hasActiveFilters","STATUS_CHIP_ORDER","useRoadmapSidebar","toggleRoadmapHorizon","toggleRoadmapStatus","addOpen","setAddOpen","horizonTitles","horizonCounts","horizonItemCounts","statusCounts","statusItemCounts","activeHorizons","activeStatuses","visibleStatuses","status","AddRoadmapItemModal","open","onClose","setHorizon","setName","description","setDescription","saving","setSaving","error","setError","horizonTitlesRef","handleAdd","addRoadmapItem","err","jsx","Modal","jsxs","Select","title","Input","e","Button","NO_SUBJECT","PromoteRoadmapItemModal","onPromoted","promoteRoadmapItem","launchIdeaExtend","subjects","subjectsAvailable","useSubjectVocabulary","subject","setSubject","loading","setLoading","toast","useToast","id","idea","buildRoadmapPromotePrompt","oneLineErrorSummary","AddCandidateForm","onAdd","CandidateRow","onPromote","Card","GoalBanner","goal","expanded","setExpanded","firstParagraph","restParagraphs","hasMore","Markdown","v","HIGHLIGHT_OUTLINE_CLASS","CANDIDATE_STAMP","mergeIdeas","links","graduated","represented","merged","plan","link","IdeaRow","onOpen","PrBadge","Stamp","STATUS_STAMP","STATUS_LABEL","ProgressBar","done","total","percent","graduationCounts","ChevronRightIcon","size","RoadmapItemRow","highlighted","onPromoteCandidate","onAddCandidate","onOpenGraduated","shipped","queued","candidates","ideas","HORIZON_HEADER_CLASSES","HorizonSection","RoadmapPage","PageTitle","sectionLabelClass","RoadmapSidebar","ListItem"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as d,u as c,Q as k,j as e,al as h,C as w,am as v,an as E,ao as I,ap as C,aq as D,G as R,J as $,ar as P,P as T}from"./main-DCzV1FeH.js";function S(t){return Math.floor((Date.now()-Date.parse(t))/864e5)}function K(t){return t.kind==="question"?t.ageDays:S(t.date)}const j=t=>Number.isFinite(t)?t<=0?"today":t===1?"1 day ago":`${t} days ago`:"age unknown",L=()=>{const[t,i]=d.useState(null),n=c(s=>s.notifications),p=c(s=>s.notificationsError!==null),x=c(s=>s.plans),u=c(s=>s.loadPlans),o=c(s=>s.loadNotifications),a=c(s=>s.markRead),l=k();return d.useEffect(()=>{o()},[o]),d.useEffect(()=>{if(n)for(const s of n)s.kind!=="question"&&!s.read&&a(s.id)},[n,a]),{notifications:n,loadFailed:p,plans:x,expandedKey:t,setExpandedKey:i,openEntity:l,reload:async()=>{await Promise.all([u(),o()])}}},A={completed:"Completed",reply:"Reply"},F=({notification:t,ageDays:i,onOpen:n})=>e.jsxs("div",{className:"flex items-center gap-3 py-2.5 border-b border-black/10 last:border-b-0",children:[e.jsx("button",{type:"button",onClick:n,"aria-label":`Open ${t.entityTitle}`,className:"bg-transparent border-none p-0 cursor-pointer",children:e.jsx(h,{id:t.entityId})}),e.jsx("span",{className:"text-xs uppercase tracking-wide opacity-40 whitespace-nowrap",children:A[t.kind]}),e.jsx("span",{className:"flex-1 min-w-0 truncate",children:t.text}),e.jsx("span",{className:"opacity-50 text-xs whitespace-nowrap",children:j(i)})]}),O=({question:t,plan:i,expanded:n,onToggle:p,onOpen:x,reload:u})=>{const{toast:o}=w(),[a,l]=d.useState(""),{sending:r,send:s,undo:b,undoing:y,undoEdit:g}=v(i,{reload:u,notify:o}),m=`question-reply:${t.entityId}`;d.useEffect(()=>{const f=E(m);f&&l(f)},[m]),d.useEffect(()=>{a&&I(m,a)},[m,a]);const N=async()=>{a.trim()&&await s(a.trim())&&(l(""),P(m))};return e.jsxs("div",{className:"border-b border-black/10 last:border-b-0",children:[e.jsxs("div",{className:"flex items-center gap-3 py-2.5",children:[e.jsx("button",{type:"button",onClick:x,"aria-label":`Open ${t.entityTitle}`,className:"bg-transparent border-none p-0 cursor-pointer",children:e.jsx(h,{id:t.entityId})}),e.jsx("button",{type:"button",onClick:p,"aria-expanded":n,className:"flex-1 min-w-0 truncate text-left bg-transparent border-none p-0 cursor-pointer",children:t.text}),e.jsx("span",{className:"opacity-50 text-xs whitespace-nowrap",children:j(t.ageDays)})]}),n&&e.jsxs("div",{className:"flex flex-col gap-3 pb-4",children:[e.jsx(C,{messages:i.thread??[],undo:b,undoing:y,onUndo:g}),e.jsx(D,{value:a,onChange:f=>l(f.target.value),"aria-label":`Reply to ${t.entityTitle}`,placeholder:"Reply to resolve and resume…",rows:2,disabled:r}),e.jsxs("div",{className:"flex justify-end items-center gap-3",children:[e.jsx("span",{className:r?"visible":"invisible",children:e.jsx(R,{size:"small",label:"Agent replying…"})}),e.jsx($,{size:"small",onClick:N,disabled:r||!a.trim(),children:"Reply"})]})]})]})},z=()=>{const{notifications:t,loadFailed:i,plans:n,expandedKey:p,setExpandedKey:x,openEntity:u,reload:o}=L();return e.jsxs("div",{children:[e.jsx(T,{children:"Inbox"}),e.jsx("p",{className:"opacity-50 mb-6",children:"Every parked question, completed run, and agent reply, oldest first."}),i&&e.jsx("p",{className:"opacity-50",children:"Couldn't load notifications."}),!i&&!t&&e.jsx("p",{className:"opacity-50",children:"Loading…"}),t&&t.length===0&&e.jsx("p",{className:"opacity-50",children:"Nothing here — no parked questions or recent activity."}),t&&t.length>0&&e.jsx("div",{className:"flex flex-col",children:t.map((a,l)=>{const r=`${a.entityId}-${a.kind}-${l}`,s=()=>u(a.entityId,a.entityTitle);if(a.kind!=="question")return e.jsx(F,{notification:a,ageDays:K(a),onOpen:s},r);const b=n==null?void 0:n.entries.find(y=>y.id===a.entityId);return b?e.jsx(O,{question:a,plan:b,expanded:p===r,onToggle:()=>x(y=>y===r?null:r),onOpen:s,reload:o},r):e.jsxs("div",{className:"flex items-center gap-3 py-2.5 border-b border-black/10 last:border-b-0",children:[e.jsx("button",{type:"button",onClick:s,"aria-label":`Open ${a.entityTitle}`,className:"bg-transparent border-none p-0 cursor-pointer",children:e.jsx(h,{id:a.entityId})}),e.jsx("span",{className:"flex-1 min-w-0 truncate",children:a.text}),e.jsx("span",{className:"opacity-50 text-xs whitespace-nowrap",children:j(a.ageDays)})]},r)})})]})};export{z as InboxPage};
|
|
2
|
-
//# sourceMappingURL=index-BhD5WS8p.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-BhD5WS8p.js","sources":["../../../src/core/notifications.ts","../../../src/app/features/inbox/helpers.ts","../../../src/app/features/inbox/hooks/use-inbox-page.ts","../../../src/app/features/inbox/notification-row.tsx","../../../src/app/features/inbox/question-row.tsx","../../../src/app/features/inbox/inbox-page.tsx"],"sourcesContent":["import type { Notification, ParkedQuestion, StoredNotification } from '../types/index';\n\nfunction ageInDays(date: string): number {\n return Math.floor((Date.now() - Date.parse(date)) / 86_400_000);\n}\n\nexport function notificationAgeDays(notification: Notification): number {\n return notification.kind === 'question' ? notification.ageDays : ageInDays(notification.date);\n}\n\n/** Every parked question alongside completed/reply notifications, oldest-first — the\n * unified feed (IDEA-153) replacing the questions-only Inbox. */\nexport function mergeNotifications(\n parkedQuestions: ParkedQuestion[],\n stored: StoredNotification[],\n): Notification[] {\n const entries: Notification[] = [\n ...parkedQuestions.map((q): Notification => ({ ...q, kind: 'question' })),\n ...stored,\n ];\n return entries.sort((a, b) => notificationAgeDays(b) - notificationAgeDays(a));\n}\n","export const formatAge = (ageDays: number): string => {\n if (!Number.isFinite(ageDays)) return 'age unknown';\n if (ageDays <= 0) return 'today';\n return ageDays === 1 ? '1 day ago' : `${ageDays} days ago`;\n};\n","import { useOpenEntity } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { useEffect, useState } from 'react';\n\nexport const useInboxPage = () => {\n const [expandedKey, setExpandedKey] = useState<string | null>(null);\n const notifications = useAppStore((s) => s.notifications);\n const loadFailed = useAppStore((s) => s.notificationsError !== null);\n const plans = useAppStore((s) => s.plans);\n const loadPlans = useAppStore((s) => s.loadPlans);\n const loadNotifications = useAppStore((s) => s.loadNotifications);\n const markRead = useAppStore((s) => s.markRead);\n const openEntity = useOpenEntity();\n\n useEffect(() => {\n loadNotifications();\n }, [loadNotifications]);\n\n useEffect(() => {\n if (!notifications) return;\n for (const n of notifications) {\n if (n.kind !== 'question' && !n.read) markRead(n.id);\n }\n }, [notifications, markRead]);\n\n const reload = async () => {\n await Promise.all([loadPlans(), loadNotifications()]);\n };\n\n return {\n notifications,\n loadFailed,\n plans,\n expandedKey,\n setExpandedKey,\n openEntity,\n reload,\n };\n};\n","import { PlanIdStamp } from '@/app/features/plans/components';\nimport type { StoredNotification } from '@/types/index';\nimport { formatAge } from './helpers';\n\nconst KIND_LABELS: Record<StoredNotification['kind'], string> = {\n completed: 'Completed',\n reply: 'Reply',\n};\n\ninterface NotificationRowProps {\n notification: StoredNotification;\n ageDays: number;\n onOpen: () => void;\n}\n\nexport const NotificationRow = ({ notification, ageDays, onOpen }: NotificationRowProps) => (\n <div className=\"flex items-center gap-3 py-2.5 border-b border-black/10 last:border-b-0\">\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${notification.entityTitle}`}\n className=\"bg-transparent border-none p-0 cursor-pointer\"\n >\n <PlanIdStamp id={notification.entityId} />\n </button>\n <span className=\"text-xs uppercase tracking-wide opacity-40 whitespace-nowrap\">\n {KIND_LABELS[notification.kind]}\n </span>\n <span className=\"flex-1 min-w-0 truncate\">{notification.text}</span>\n <span className=\"opacity-50 text-xs whitespace-nowrap\">{formatAge(ageDays)}</span>\n </div>\n);\n","import { FeedbackThread, PlanIdStamp } from '@/app/features/plans/components';\nimport { useSendFeedbackMessage } from '@/app/features/plans/hooks';\nimport { readLocalDraft, removeLocalDraft, writeLocalDraft } from '@/app/utils/local-draft-store';\nimport type { ParkedQuestion, PlanEntry } from '@/types/index';\nimport { Button, Spinner, Textarea, useToast } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\nimport { formatAge } from './helpers';\n\ninterface QuestionRowProps {\n question: ParkedQuestion;\n plan: PlanEntry;\n expanded: boolean;\n onToggle: () => void;\n onOpen: () => void;\n reload: () => Promise<void>;\n}\n\nexport const QuestionRow = ({\n question,\n plan,\n expanded,\n onToggle,\n onOpen,\n reload,\n}: QuestionRowProps) => {\n const { toast } = useToast();\n const [input, setInput] = useState('');\n const { sending, send, undo, undoing, undoEdit } = useSendFeedbackMessage(plan, {\n reload,\n notify: toast,\n });\n\n const draftKey = `question-reply:${question.entityId}`;\n\n useEffect(() => {\n const draft = readLocalDraft<string>(draftKey);\n if (draft) setInput(draft);\n }, [draftKey]);\n\n useEffect(() => {\n if (!input) return;\n writeLocalDraft(draftKey, input);\n }, [draftKey, input]);\n\n const handleSend = async () => {\n if (!input.trim()) return;\n if (await send(input.trim())) {\n setInput('');\n removeLocalDraft(draftKey);\n }\n };\n\n return (\n <div className=\"border-b border-black/10 last:border-b-0\">\n <div className=\"flex items-center gap-3 py-2.5\">\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${question.entityTitle}`}\n className=\"bg-transparent border-none p-0 cursor-pointer\"\n >\n <PlanIdStamp id={question.entityId} />\n </button>\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n className=\"flex-1 min-w-0 truncate text-left bg-transparent border-none p-0 cursor-pointer\"\n >\n {question.text}\n </button>\n <span className=\"opacity-50 text-xs whitespace-nowrap\">{formatAge(question.ageDays)}</span>\n </div>\n {expanded && (\n <div className=\"flex flex-col gap-3 pb-4\">\n <FeedbackThread\n messages={plan.thread ?? []}\n undo={undo}\n undoing={undoing}\n onUndo={undoEdit}\n />\n <Textarea\n value={input}\n onChange={(e) => setInput(e.target.value)}\n aria-label={`Reply to ${question.entityTitle}`}\n placeholder=\"Reply to resolve and resume…\"\n rows={2}\n disabled={sending}\n />\n <div className=\"flex justify-end items-center gap-3\">\n <span className={sending ? 'visible' : 'invisible'}>\n <Spinner size=\"small\" label=\"Agent replying…\" />\n </span>\n <Button size=\"small\" onClick={handleSend} disabled={sending || !input.trim()}>\n Reply\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { PlanIdStamp } from '@/app/features/plans/components';\nimport { notificationAgeDays } from '@/core/notifications';\nimport { formatAge } from './helpers';\nimport { useInboxPage } from './hooks';\nimport { NotificationRow } from './notification-row';\nimport { QuestionRow } from './question-row';\n\nexport const InboxPage = () => {\n const { notifications, loadFailed, plans, expandedKey, setExpandedKey, openEntity, reload } =\n useInboxPage();\n\n return (\n <div>\n <PageTitle>Inbox</PageTitle>\n <p className=\"opacity-50 mb-6\">\n Every parked question, completed run, and agent reply, oldest first.\n </p>\n {loadFailed && <p className=\"opacity-50\">Couldn't load notifications.</p>}\n {!loadFailed && !notifications && <p className=\"opacity-50\">Loading…</p>}\n {notifications && notifications.length === 0 && (\n <p className=\"opacity-50\">Nothing here — no parked questions or recent activity.</p>\n )}\n {notifications && notifications.length > 0 && (\n <div className=\"flex flex-col\">\n {notifications.map((n, i) => {\n const key = `${n.entityId}-${n.kind}-${i}`;\n const onOpen = () => openEntity(n.entityId, n.entityTitle);\n\n if (n.kind !== 'question') {\n return (\n <NotificationRow\n key={key}\n notification={n}\n ageDays={notificationAgeDays(n)}\n onOpen={onOpen}\n />\n );\n }\n\n const plan = plans?.entries.find((p) => p.id === n.entityId);\n if (!plan) {\n return (\n <div\n key={key}\n className=\"flex items-center gap-3 py-2.5 border-b border-black/10 last:border-b-0\"\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${n.entityTitle}`}\n className=\"bg-transparent border-none p-0 cursor-pointer\"\n >\n <PlanIdStamp id={n.entityId} />\n </button>\n <span className=\"flex-1 min-w-0 truncate\">{n.text}</span>\n <span className=\"opacity-50 text-xs whitespace-nowrap\">\n {formatAge(n.ageDays)}\n </span>\n </div>\n );\n }\n return (\n <QuestionRow\n key={key}\n question={n}\n plan={plan}\n expanded={expandedKey === key}\n onToggle={() => setExpandedKey((cur) => (cur === key ? null : key))}\n onOpen={onOpen}\n reload={reload}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"names":["ageInDays","date","notificationAgeDays","notification","formatAge","ageDays","useInboxPage","expandedKey","setExpandedKey","useState","notifications","useAppStore","loadFailed","plans","loadPlans","loadNotifications","markRead","openEntity","useOpenEntity","useEffect","n","KIND_LABELS","NotificationRow","onOpen","jsxs","jsx","PlanIdStamp","QuestionRow","question","plan","expanded","onToggle","reload","toast","useToast","input","setInput","sending","send","undo","undoing","undoEdit","useSendFeedbackMessage","draftKey","draft","readLocalDraft","writeLocalDraft","handleSend","removeLocalDraft","FeedbackThread","Textarea","e","Spinner","Button","InboxPage","PageTitle","i","key","p","cur"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{c as se,r as h,C as U,j as e,e as T,g as q,R as E,J as C,w as A,h as de,k as _,s as L,l as me,m as he,D as R,n as pe,o as V,q as ue,u as O,t as te,$ as xe,v as ge,a as ae,A as G,x as Y,y as je,z as fe,i as D,B as ne,E as le,F as ve,G as be,H as J,I as ye,K as ke,L as ie,P as Se,p as F}from"./main-DCzV1FeH.js";import{S as $}from"./sidebar-section-CFrPSdM0.js";const Ne=async()=>{try{const s=await fetch(se("/api/merge-policy"));return s.ok?await s.json():null}catch{return null}},Ce=async()=>{try{const s=await fetch(se("/api/merge-policy/apply"),{method:"POST"});return s.ok?await s.json():null}catch{return null}},W=["phase","planDraft","ideaExtend","commitSuggest","feedback","codeReview","deskDiscovery"],we={phase:"Phase run",planDraft:"Plan draft",ideaExtend:"Idea extend",commitSuggest:"Commit suggest",feedback:"Scout chat",codeReview:"Code review",deskDiscovery:"Desk discovery"},Ae={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"}},B={upToDate:{fill:"rgba(143, 185, 150, 0.25)",text:"#5E8A66"},outdated:{fill:"rgba(212, 163, 115, 0.25)",text:"#A67B4F"}},De="rgba(143, 185, 150, 0.25)",oe={allowSquashMerge:!0,allowMergeCommit:!1,allowRebaseMerge:!1,squashMergeCommitTitle:"PR_TITLE",squashMergeCommitMessage:"PR_BODY"},H=[{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 K(s){return typeof s=="boolean"?s?"Yes":"No":s}function Ie(s){return H.every(t=>s[t.key]===oe[t.key])}const Re=({label:s,current:t,recommended:n,isLast:i})=>{const l=t===n;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:l?"opacity-[0.65] font-normal":"font-semibold",children:K(t)}),!l&&e.jsxs("span",{className:"opacity-[0.45]",children:["→ ",K(n)]})]}),!i&&e.jsx(A,{})]})},ze=()=>{const[s,t]=h.useState(void 0),[n,i]=h.useState(!1),{toast:l}=U();h.useEffect(()=>{Ne().then(t)},[]);const o=async()=>{i(!0);const c=await Ce();i(!1),(c==null?void 0:c.status)==="ok"?(t(c),l({title:"Merge policy applied",variant:"success"})):l({title:"Failed to apply merge policy",description:(c==null?void 0:c.status)==="unavailable"?c.reason:void 0,variant:"error"})},r=(s==null?void 0:s.status)==="ok"&&Ie(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(T,{variant:"warning",children:"Failed to load merge policy."}),(s==null?void 0:s.status)==="unavailable"&&e.jsx(T,{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(q,{size:"small",fillColor:r?B.upToDate.fill:B.outdated.fill,textColor:r?B.upToDate.text:B.outdated.text,children:r?"Matches recommended policy":"Differs from recommended policy"})]}),e.jsx(E,{size:"small",texture:"kraft",children:H.map((c,m)=>e.jsx(Re,{label:c.label,current:s.policy[c.key],recommended:oe[c.key],isLast:m===H.length-1},c.key))}),e.jsx("div",{className:"mt-4",children:e.jsx(C,{size:"small",onClick:o,disabled:n||r,children:n?"Applying…":"Apply recommended policy"})})]})]})},X={repo:""},Pe=s=>s.name.trim()!==""&&s.cmd.trim()!=="",Ee=s=>s.name.trim()!==""&&s.cmd.trim()!=="",Q=({id:s,...t})=>t;function Z(s,t){const n=new Set,i=t.map(o=>{const r=o.cmd?s.find(j=>!n.has(j.id)&&j.cmd===o.cmd):void 0;if(!r)return{...o,id:crypto.randomUUID()};n.add(r.id);const{id:c,...m}=r;return JSON.stringify(m)===JSON.stringify(o)?r:{...o,id:c}}),l=s.filter(o=>!n.has(o.id)&&(o.name.trim()===""||o.cmd.trim()===""));return[...i,...l]}const Le=()=>{const[s,t]=h.useState(void 0),[n,i]=h.useState([]),[l,o]=h.useState([]),[r,c]=h.useState(X),{toast:m}=U(),j=de(),v=h.useCallback(a=>{var x;t(a),i(d=>{var u;return Z(d,((u=a==null?void 0:a.desk)==null?void 0:u.services)??[])}),o(d=>{var u;return Z(d,((u=a==null?void 0:a.desk)==null?void 0:u.checks)??[])}),c(((x=a==null?void 0:a.desk)==null?void 0:x.ci)??X)},[]);h.useEffect(()=>{_().then(v)},[v]);const f=h.useCallback(async()=>{const a=await _();v(a)},[v]),k=async(a,x,d)=>{const u=a.filter(Pe).map(Q),I=x.filter(Ee).map(Q),z=d.repo.trim()?d:void 0,P={services:u.length?u:void 0,checks:I.length?I:void 0,ci:z},{ok:M,error:ce}=await L({desk:P});if(M){await j.reloadCurrent();const re=await _();v(re),m({title:"Saved",variant:"success"})}else m({title:"Failed to save",description:ce,variant:"error"})},w=()=>i(a=>[...a,{id:crypto.randomUUID(),name:"",cmd:""}]),b=(a,x)=>{const d=n.map(u=>u.id===a?{...x,id:a}:u);i(d),k(d,l,r)},g=a=>{const x=n.filter(d=>d.id!==a);i(x),k(x,l,r)},y=()=>o(a=>[...a,{id:crypto.randomUUID(),name:"",cmd:""}]),S=(a,x)=>{const d=l.map(u=>u.id===a?{...x,id:a}:u);o(d),k(n,d,r)},p=a=>{const x=l.filter(d=>d.id!==a);o(x),k(n,x,r)},N=a=>{c(a),k(n,l,a)};return{config:s,services:n,checks:l,ci:r,proposal:j.proposal,diff:j.diff,discovering:j.discovering,addService:w,updateService:b,removeService:g,addCheck:y,updateCheck:S,removeCheck:p,updateCi:N,startDiscovery:j.startDiscovery,cancelProposal:j.cancelProposal,applyProposal:async a=>{await j.applyProposal(a),await f()}}},Te=()=>{const s=h.useRef(null),[t,n]=h.useState(void 0),{iconDataUri:i,loading:l}=me(),[o,r]=h.useState(null),c=o??i,[m,j]=h.useState(!1),[v,f]=h.useState(""),[k,w]=h.useState(""),{toast:b}=U();return h.useEffect(()=>{_().then(a=>{n(a),(a==null?void 0:a.port)!==void 0&&f(String(a.port)),(a==null?void 0:a.projectName)!==void 0&&w(a.projectName)})},[]),{fileRef:s,config:t,identityLoading:l,iconDataUri:c,uploading:m,portInput:v,setPortInput:f,nameInput:k,setNameInput:w,handleSaveAgentConfig:async(a,x)=>{const d=t==null?void 0:t.defaultAgents,u={phase:(d==null?void 0:d.phase)??R.phase,planDraft:(d==null?void 0:d.planDraft)??R.planDraft,ideaExtend:(d==null?void 0:d.ideaExtend)??R.ideaExtend,commitSuggest:(d==null?void 0:d.commitSuggest)??R.commitSuggest,feedback:(d==null?void 0:d.feedback)??R.feedback,codeReview:(d==null?void 0:d.codeReview)??R.codeReview,deskDiscovery:(d==null?void 0:d.deskDiscovery)??R.deskDiscovery,[a]:x};if(a==="codeReview"&&pe(x,u.phase)){b({title:"Failed to save",description:"Code review must use a different model than Phase run.",variant:"error"});return}const{ok:I,error:z}=await L({defaultAgents:u});I?(n(P=>P&&{...P,defaultAgents:u}),b({title:"Saved",variant:"success"})):b({title:"Failed to save",description:z,variant:"error"})},handleSavePort:async()=>{const a=Number(v);if(!t||!Number.isInteger(a)||a<=0||a===t.port)return;const{ok:x,error:d}=await L({port:a});x?(n(u=>u&&{...u,port:a}),b({title:"Saved",description:"Restart `paper-camp dev` to apply the new port.",variant:"success"})):b({title:"Failed to save",description:d,variant:"error"})},handleToggleIntegration:async()=>{var I,z,P;if(!t)return;const a=!(((z=(I=t.integration)==null?void 0:I.toolbar)==null?void 0:z.enabled)??!0),x={...t.integration,toolbar:{...(P=t.integration)==null?void 0:P.toolbar,enabled:a}},{ok:d,error:u}=await L({integration:x});d?(n(M=>M&&{...M,integration:x}),b({title:"Saved",variant:"success"})):b({title:"Failed to save",description:u,variant:"error"})},handleSaveName:async()=>{const a=k.trim();if(!t||!a||a===t.projectName)return;const{ok:x,error:d}=await L({projectName:a});x?(n(u=>u&&{...u,projectName:a}),w(a),b({title:"Saved",variant:"success"})):b({title:"Failed to save",description:d,variant:"error"})},handleFile:async a=>{var u;const x=(u=a.target.files)==null?void 0:u[0];if(!x)return;j(!0);const d=new FileReader;d.onloadend=async()=>{const I=d.result,z=await he(I);j(!1),z?(r(I),b({title:"Saved",variant:"success"})):b({title:"Failed to save",variant:"error"})},d.readAsDataURL(x)}}},Me=()=>{const[s,t]=h.useState(null),[n,i]=h.useState(!1),[l,o]=h.useState(null),[r,c]=h.useState(null),[m,j]=h.useState(!1),{toast:v}=U(),f=h.useCallback(p=>{if(p===null){i(!0);return}i(!1),t(p)},[]);h.useEffect(()=>{V().then(f),_().then(p=>j((p==null?void 0:p.setupDismissed)??!1))},[f]);const k=async p=>{o(p),f(await V()),o(null)},w=async p=>{c(p);const N=await ue(p);N?t(a=>(a==null?void 0:a.map(x=>x.id===p?N:x))??a):v({title:"Failed to connect",variant:"error"}),c(null)},b=async()=>{const p=!m,{ok:N}=await L({setupDismissed:p});N?(j(p),v({title:"Saved",variant:"success"})):v({title:"Failed to save",variant:"error"})},g=(s==null?void 0:s.every(p=>p.status==="ok"))??!0,y=(s==null?void 0:s.filter(p=>p.kind==="external"))??[],S=(s==null?void 0:s.filter(p=>p.kind==="local"))??[];return{connections:s,loadFailed:n,reloadingId:l,connectingId:r,setupDismissed:m,handleRecheck:k,handleConnect:w,handleDismissToggle:b,allOk:g,externalConnections:y,localConnections:S}},Fe=2e3,_e=s=>{const t=O(m=>m.loginRelay),n=O(m=>m.startLoginRelay),i=O(m=>m.loadLoginRelayStatus),l=O(m=>m.cancelLoginRelay),{toast:o}=U(),r=h.useRef(null),c=t==null?void 0:t.phase;return h.useEffect(()=>{if(c!=="starting"&&c!=="awaiting-authorization")return;const m=window.setInterval(()=>i(),Fe);return()=>window.clearInterval(m)},[c,i]),h.useEffect(()=>{const m=t==null?void 0:t.authorizeUrl;c!=="awaiting-authorization"||!m||r.current===m||(r.current=m,window.open(m,"_blank","noopener,noreferrer"))},[c,t==null?void 0:t.authorizeUrl]),h.useEffect(()=>{c==="success"?(o({title:"Signed in",variant:"success"}),s()):c==="error"&&o({title:"Sign-in failed",description:t==null?void 0:t.error,variant:"error"})},[c]),{phase:c,authorizeUrl:t==null?void 0:t.authorizeUrl,startLoginRelay:n,cancelLoginRelay:l}},Ue=({connection:s,onConnect:t,connecting:n})=>{const{connect:i}=s;return i?i.kind==="command"&&i.runnable?e.jsx(C,{size:"small",onClick:()=>t(s.id),disabled:n,children:n?"Running…":`Run \`${i.command}\``}):i.kind==="command"?e.jsx("div",{className:"mt-2",children:e.jsx(te,{code:i.command})}):i.kind==="link"?e.jsx(C,{size:"small",onClick:()=>window.open(i.url,"_blank","noopener,noreferrer"),children:i.label}):e.jsx("p",{className:"opacity-[0.65] text-sm mt-2 mx-0 mb-0",children:i.message}):null},Oe=["claude auth login","claude setup-token"],Be=()=>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:"}),Oe.map(s=>e.jsx(te,{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."]})]}),qe=({onSignedIn:s})=>{const{phase:t,authorizeUrl:n,startLoginRelay:i,cancelLoginRelay:l}=_e(s);return t==="starting"?e.jsx(C,{size:"small",disabled:!0,children:"Starting…"}):t==="awaiting-authorization"?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(C,{size:"small",onClick:()=>n&&window.open(n,"_blank","noopener,noreferrer"),children:"Reopen sign-in tab"}),e.jsx(C,{size:"small",onClick:()=>l(),children:"Cancel"})]}):e.jsxs("div",{children:[e.jsx(C,{size:"small",onClick:()=>i(),children:"Sign in"}),t==="error"&&e.jsx(Be,{})]})},Ge="agent:claude-code",ee=({connection:s,isLast:t,onRecheck:n,rechecking:i,onConnect:l,connecting:o})=>{const r=Ae[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(xe,{content:"Installed, but not signed in",children:e.jsx(q,{size:"small",variant:"warning",children:"Not signed in"})}),e.jsx(q,{size:"small",fillColor:r.fill,textColor:r.text,children:r.label}),e.jsx(C,{size:"small",onClick:()=>n(s.id),disabled:i,children:i?"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===Ge&&s.authenticated===!1?e.jsx(qe,{onSignedIn:()=>n(s.id)}):e.jsx(Ue,{connection:s,onConnect:l,connecting:o})})]}),!t&&e.jsx(A,{})]})},Ye=()=>{const{connections:s,loadFailed:t,reloadingId:n,connectingId:i,setupDismissed:l,handleRecheck:o,handleConnect:r,handleDismissToggle:c,allOk:m,externalConnections:j,localConnections:v}=Me();return e.jsxs("div",{children:[e.jsx("div",{className:"mb-6",children:e.jsx("h2",{className:"m-0",children:"Setup"})}),s===null&&!t&&e.jsx("p",{children:"Loading…"}),t&&e.jsx("div",{className:"mb-4",children:e.jsx(T,{variant:"warning",children:"Failed to load connections. Try refreshing."})}),s&&e.jsxs(e.Fragment,{children:[!m&&e.jsx("div",{className:"mb-4",children:e.jsx(T,{variant:"warning",children:"Some connections are incomplete — features that depend on them stay disabled until fixed. Run the connect command below, then recheck."})}),j.length>0&&e.jsxs("div",{className:"mb-4",children:[e.jsx("p",{className:"opacity-[0.45] text-sm mt-0 mb-2",children:"External services — reached on your behalf with their own remote credential."}),e.jsx(E,{size:"small",texture:"kraft",children:j.map((f,k)=>e.jsx(ee,{connection:f,isLast:k===j.length-1,onRecheck:o,rechecking:n===f.id,onConnect:r,connecting:i===f.id},f.id))})]}),v.length>0&&e.jsxs("div",{className:"mb-4",children:[e.jsx("p",{className:"opacity-[0.45] text-sm mt-0 mb-2",children:"Local adapters — driven on this machine with their own local session."}),e.jsx(E,{size:"small",texture:"kraft",children:v.map((f,k)=>e.jsx(ee,{connection:f,isLast:k===v.length-1,onRecheck:o,rechecking:n===f.id,onConnect:r,connecting:i===f.id},f.id))})]}),e.jsx("div",{className:"mt-4",children:e.jsx(C,{size:"small",onClick:c,children:l?"Show Setup on open again":"Don't show Setup on open"})})]})]})},$e=()=>{const{subjects:s,loading:t,available:n}=ge(),i=ae();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&&!n&&e.jsx("p",{className:"opacity-60 m-0",children:"Couldn't load subjects — check the server config."}),!t&&n&&e.jsxs(E,{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((l,o)=>e.jsxs("div",{children:[e.jsx("div",{className:"pb-2 pt-2",children:l}),o<s.length-1&&e.jsx(A,{})]},l)),s.length>0&&e.jsx(A,{}),e.jsx("div",{className:"pt-3",children:e.jsx(C,{size:"small",onClick:()=>i({to:"/roadmap"}),children:"Go to Roadmap"})})]})]})},He=140,Ve=160,Je=110,We=({taskKey:s,agentConfig:t,isLast:n,onSave:i,authorConfig:l})=>{const o=G[t.agent]??G["claude-code"],r=l&&l.agent===t.agent?l.model??"":void 0,c=Array.isArray(o.model)?o.model.filter(g=>g!==r):o.model,m=o.effort,[j,v]=h.useState(t.model??"");h.useEffect(()=>{v(t.model??"")},[t.model]);const f=g=>{var N;const y=g,S=G[y],p={agent:y};t.model&&(S.model===null||(N=S.model)!=null&&N.includes(t.model))&&(p.model=t.model),t.effort&&Array.isArray(S.effort)&&(p.effort=t.effort),i(s,p)},k=g=>{i(s,{...t,model:g||void 0})},w=()=>{i(s,{...t,model:j||void 0})},b=g=>{i(s,{...t,effort:g||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:we[s]}),e.jsx(Y,{size:"small",width:He,value:t.agent,onChange:f,options:je.map(g=>({value:g,label:fe[g]}))}),Array.isArray(c)?e.jsx(Y,{size:"small",width:Ve,value:t.model??"",onChange:k,options:[...r===""?[]:[{value:"",label:"Default"}],...c.map(g=>({value:g,label:g}))]}):c===null?e.jsx(D,{size:"small",className:"w-[160px]",value:j,placeholder:"Default model",onChange:g=>v(g.target.value),onBlur:w}):null,e.jsx("div",{className:Array.isArray(m)?"visible":"invisible",children:e.jsx(Y,{size:"small",width:Je,value:t.effort??"",onChange:b,options:[{value:"",label:"Default"},...(Array.isArray(m)?m:[]).map(g=>({value:g,label:g}))]})})]}),!n&&e.jsx(A,{})]})},Ke=()=>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"})]}),Xe=({check:s,onSave:t,onRemove:n,isLast:i})=>{const[l,o]=h.useState(s);h.useEffect(()=>o(s),[s]);const r=()=>{l.name===s.name&&l.cmd===s.cmd||t(l)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-end gap-3 pb-2 pt-2",children:[e.jsx(D,{size:"small",label:"Name",value:l.name,onChange:c=>o({...l,name:c.target.value}),onBlur:r}),e.jsx(D,{size:"small",label:"Command",value:l.cmd,onChange:c=>o({...l,cmd:c.target.value}),onBlur:r}),e.jsx(ne,{icon:e.jsx(le,{size:16}),variant:"danger",size:"small",onClick:n,label:`Remove ${s.name||"check"}`})]}),!i&&e.jsx(A,{})]})},Qe=({ci:s,onSave:t})=>{const[n,i]=h.useState(s);h.useEffect(()=>i(s),[s]);const l=()=>{n.repo===s.repo&&(n.branch??"")===(s.branch??"")&&(n.releasePlease??!1)===(s.releasePlease??!1)||t(n)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-end gap-3 pb-3",children:[e.jsx(D,{size:"small",label:"Repo (owner/name)",value:n.repo,onChange:o=>i({...n,repo:o.target.value}),onBlur:l}),e.jsx(D,{size:"small",label:"Branch",value:n.branch??"",onChange:o=>i({...n,branch:o.target.value||void 0}),onBlur:l})]}),e.jsx(A,{}),e.jsxs("div",{className:"flex items-center justify-between pt-3",children:[e.jsx("span",{children:"Release Please"}),e.jsx(ve,{checked:n.releasePlease??!1,onChange:o=>{const r={...n,releasePlease:o.target.checked};i(r),t(r)}})]})]})},Ze=({service:s,onSave:t,onRemove:n,isLast:i})=>{const[l,o]=h.useState(s);h.useEffect(()=>o(s),[s]);const r=()=>{l.name===s.name&&l.cmd===s.cmd&&(l.port??void 0)===(s.port??void 0)&&(l.healthcheck??void 0)===(s.healthcheck??void 0)||t(l)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-end gap-3 pb-2 pt-2",children:[e.jsx(D,{size:"small",label:"Name",value:l.name,onChange:c=>o({...l,name:c.target.value}),onBlur:r}),e.jsx(D,{size:"small",label:"Command",value:l.cmd,onChange:c=>o({...l,cmd:c.target.value}),onBlur:r}),e.jsx(D,{size:"small",type:"number",label:"Port",className:"w-[100px]",value:l.port??"",onChange:c=>o({...l,port:c.target.value?Number(c.target.value):void 0}),onBlur:r}),e.jsx(D,{size:"small",label:"Healthcheck URL",value:l.healthcheck??"",onChange:c=>o({...l,healthcheck:c.target.value||void 0}),onBlur:r}),e.jsx(ne,{icon:e.jsx(le,{size:16}),variant:"danger",size:"small",onClick:n,label:`Remove ${s.name||"service"}`})]}),!i&&e.jsx(A,{})]})},es=()=>{const{config:s,services:t,checks:n,ci:i,proposal:l,diff:o,discovering:r,addService:c,updateService:m,removeService:j,addCheck:v,updateCheck:f,removeCheck:k,updateCi:w,startDiscovery:b,cancelProposal:g,applyProposal:y}=Le();return e.jsxs("div",{children:[e.jsxs("div",{className:"mb-6 flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"m-0",children:"Desk"}),e.jsx("p",{className:"opacity-50 mt-1",children:"Services, checks, and CI/release sources for this project's Stack panel."})]}),s&&e.jsx(C,{size:"small",onClick:b,disabled:r,children:r?e.jsxs(e.Fragment,{children:[e.jsx(be,{size:"small",label:"Discovering"})," Discovering…"]}):s.desk?"Re-scan with discovery":"Discover from project"})]}),s===void 0&&e.jsx("p",{children:"Loading…"}),s===null&&e.jsxs(T,{variant:"warning",children:["No papercamp/config.json found — run ",e.jsx("code",{children:"paper-camp init"})," in this directory first."]}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-6",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h3",{className:"m-0",children:"Services"}),e.jsx(C,{size:"small",icon:e.jsx(J,{size:16}),onClick:c,children:"Add service"})]}),e.jsxs(E,{size:"small",texture:"kraft",children:[t.length===0&&e.jsx("p",{className:"opacity-[0.45] m-0 pb-2",children:"No services yet."}),t.map((S,p)=>e.jsx(Ze,{service:S,onSave:N=>m(S.id,N),onRemove:()=>j(S.id),isLast:p===t.length-1},S.id))]})]}),e.jsxs("div",{className:"mb-6",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h3",{className:"m-0",children:"Checks"}),e.jsx(C,{size:"small",icon:e.jsx(J,{size:16}),onClick:v,children:"Add check"})]}),e.jsxs(E,{size:"small",texture:"kraft",children:[n.length===0&&e.jsx("p",{className:"opacity-[0.45] m-0 pb-2",children:"No checks yet."}),n.map((S,p)=>e.jsx(Xe,{check:S,onSave:N=>f(S.id,N),onRemove:()=>k(S.id),isLast:p===n.length-1},S.id))]})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"m-0 mb-3",children:"CI"}),e.jsx(E,{size:"small",texture:"kraft",children:e.jsx(Qe,{ci:i,onSave:w})})]}),l&&o&&e.jsx(ye,{current:s.desk,proposal:l,diff:o,onApply:y,onCancel:g})]})]})},ss=()=>{var b,g;const{fileRef:s,config:t,identityLoading:n,iconDataUri:i,uploading:l,portInput:o,setPortInput:r,nameInput:c,setNameInput:m,handleSaveAgentConfig:j,handleSavePort:v,handleToggleIntegration:f,handleSaveName:k,handleFile:w}=Te();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(T,{variant:"warning",children:["No papercamp/config.json found — run ",e.jsx("code",{children:"paper-camp init"})," in this directory first."]}),t&&e.jsxs(E,{size:"small",texture:"kraft",children:[e.jsxs("div",{className:"flex items-end gap-3 pb-3",children:[e.jsx(D,{value:c,onChange:y=>m(y.target.value),onBlur:k,label:"Project Name"}),e.jsxs(q,{size:"small",fillColor:De,textColor:ke.accentGreenDark,children:["v",t.version]})]}),e.jsx(A,{}),e.jsxs("div",{className:"flex items-center gap-3 pb-3 pt-3",children:[i&&e.jsx("img",{src:i,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:w,className:"hidden"}),e.jsx(C,{size:"small",onClick:()=>{var y;return(y=s.current)==null?void 0:y.click()},disabled:l,children:l?"Uploading…":"Choose File"}),n&&e.jsx("p",{className:"text-sm opacity-50 mt-1 mx-0 mb-0",children:"Loading…"}),!n&&!i&&!l&&e.jsx("p",{className:"text-sm opacity-[0.45] mt-1 mx-0 mb-0",children:"No icon set."})]})]}),e.jsx(A,{}),e.jsx("div",{className:"flex items-end gap-3 pb-3 pt-3",children:e.jsx(D,{type:"number",value:o,onChange:y=>r(y.target.value),onBlur:v,label:"Port",helperText:"Default for `paper-camp dev`. Restart the server to apply a change."})}),e.jsx(A,{}),e.jsxs("div",{className:"flex items-center justify-between gap-3 pb-3 pt-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"m-0",children:"In-app dev toolbar"}),e.jsx("p",{className:"opacity-[0.45] text-sm mt-1 mx-0 mb-0",children:"Inject the paper-camp toolbar into this project's dev server via the Vite plugin."})]}),e.jsx(C,{size:"small",onClick:f,children:((g=(b=t.integration)==null?void 0:b.toolbar)==null?void 0:g.enabled)??!0?"Disable":"Enable"})]}),e.jsx(A,{}),e.jsx(Ke,{}),W.map((y,S)=>{var p,N;return e.jsx(We,{taskKey:y,agentConfig:((p=t.defaultAgents)==null?void 0:p[y])??R[y],isLast:S===W.length-1,onSave:j,authorConfig:y==="codeReview"?((N=t.defaultAgents)==null?void 0:N.phase)??R.phase:void 0},y)})]}),e.jsxs("p",{className:"opacity-[0.45] text-sm mt-4",children:[e.jsx("strong",{children:"Initialized:"})," ",t?new Date(t.initializedAt).toLocaleString():"—"]})]})},ns=()=>{const s=ie();return e.jsxs("div",{children:[e.jsx(Se,{children:"Settings"}),s==="subjects"?e.jsx($e,{}):s==="setup"?e.jsx(Ye,{}):s==="merge-policy"?e.jsx(ze,{}):s==="desk"?e.jsx(es,{}):e.jsx(ss,{})]})},ls=()=>{const s=ie(),t=ae();return e.jsxs(e.Fragment,{children:[e.jsxs($,{label:"General",children:[e.jsx(F,{size:"small",className:"pc-row text-xs",active:s===null,onClick:()=>t({to:"/settings"}),children:"Project Info"}),e.jsx(F,{size:"small",className:"pc-row text-xs",active:s==="setup",onClick:()=>t({to:"/settings/$section",params:{section:"setup"}}),children:"Setup"}),e.jsx(F,{size:"small",className:"pc-row text-xs",active:s==="merge-policy",onClick:()=>t({to:"/settings/$section",params:{section:"merge-policy"}}),children:"Merge Policy"})]}),e.jsx($,{label:"Ideas",children:e.jsx(F,{size:"small",className:"pc-row text-xs",active:s==="subjects",onClick:()=>t({to:"/settings/$section",params:{section:"subjects"}}),children:"Subjects"})}),e.jsx($,{label:"Stack",children:e.jsx(F,{size:"small",className:"pc-row text-xs",active:s==="desk",onClick:()=>t({to:"/settings/$section",params:{section:"desk"}}),children:"Desk"})})]})};export{ns as SettingsPage,ls as SettingsSidebar};
|
|
2
|
-
//# sourceMappingURL=index-C1zPjxRZ.js.map
|