@dendelion/paper-camp 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/assets/{index-Bkouj3B7.js → index-8_65UWKz.js} +2 -2
- package/dist/app/assets/{index-Bkouj3B7.js.map → index-8_65UWKz.js.map} +1 -1
- package/dist/app/assets/{index-BS3GyKvd.js → index-Bq3vqVCv.js} +2 -2
- package/dist/app/assets/{index-BS3GyKvd.js.map → index-Bq3vqVCv.js.map} +1 -1
- package/dist/app/assets/{index-CDlMKeg5.js → index-Co_9IFtJ.js} +2 -2
- package/dist/app/assets/{index-CDlMKeg5.js.map → index-Co_9IFtJ.js.map} +1 -1
- package/dist/app/assets/{index-UOItJyt3.js → index-DRVlFKav.js} +2 -2
- package/dist/app/assets/{index-UOItJyt3.js.map → index-DRVlFKav.js.map} +1 -1
- package/dist/app/assets/{index-BzD-BgRl.js → index-DdiMHcBX.js} +2 -2
- package/dist/app/assets/{index-BzD-BgRl.js.map → index-DdiMHcBX.js.map} +1 -1
- package/dist/app/assets/{index-DxnAxo8U.js → index-Ds_iTHi0.js} +2 -2
- package/dist/app/assets/{index-DxnAxo8U.js.map → index-Ds_iTHi0.js.map} +1 -1
- package/dist/app/assets/{index-DgEJTUYT.js → index-e0PzaU7Z.js} +2 -2
- package/dist/app/assets/{index-DgEJTUYT.js.map → index-e0PzaU7Z.js.map} +1 -1
- package/dist/app/assets/{index-jxEIOkqg.js → index-siPA4naJ.js} +2 -2
- package/dist/app/assets/{index-jxEIOkqg.js.map → index-siPA4naJ.js.map} +1 -1
- package/dist/app/assets/{main-DEUaro51.css → main-BAlJ6w7u.css} +1 -1
- package/dist/app/assets/{main-BPr1R-3E.js → main-C0TBUyF9.js} +106 -106
- package/dist/app/assets/main-C0TBUyF9.js.map +1 -0
- package/dist/app/assets/{sidebar-section-CVvP-dC0.js → sidebar-section-D0b_1DUA.js} +2 -2
- package/dist/app/assets/{sidebar-section-CVvP-dC0.js.map → sidebar-section-D0b_1DUA.js.map} +1 -1
- package/dist/app/index.html +2 -2
- package/dist/cli/index.js +1781 -1731
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/app/assets/main-BPr1R-3E.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as d,u as c,G as k,j as e,ag as h,C as w,ah as v,ai as E,aj as I,ak as C,al as D,ac as R,J as $,am as P,P as T}from"./main-
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{r as d,u as c,G as k,j as e,ag as h,C as w,ah as v,ai as E,aj as I,ak as C,al as D,ac as R,J as $,am as P,P as T}from"./main-C0TBUyF9.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-8_65UWKz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-Bkouj3B7.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
|
+
{"version":3,"file":"index-8_65UWKz.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,3 +1,3 @@
|
|
|
1
|
-
import{u as h,E as L,r as i,j as e,F as v,J as S,R as u,y as T,g as y,z as f,P as A}from"./main-
|
|
1
|
+
import{u as h,E as L,r as i,j as e,F as v,J as S,R as u,y as T,g as y,z as f,P as A}from"./main-C0TBUyF9.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}=L({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}},E="outline-[rgba(200,154,90,0.5)]",D={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=>{v(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(S,{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
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"},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"})}),P=({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] ${E}`:"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:T[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(y,{size:"small",fillColor:D[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",K=({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(P,{entry:o,highlighted:o.id===a},o.id))]},n.key))]}),F=()=>{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(K,{groups:a,highlightedId:n})]})};export{F as TasksPage};
|
|
3
|
-
//# sourceMappingURL=index-
|
|
3
|
+
//# sourceMappingURL=index-Bq3vqVCv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-BS3GyKvd.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};\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
|
+
{"version":3,"file":"index-Bq3vqVCv.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};\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,7 +1,7 @@
|
|
|
1
|
-
import{u as i,a as S,j as e,J as V,r as j,f as w,d as u,M as R,b,P as L,i as $,p as v}from"./main-
|
|
1
|
+
import{u as i,a as S,j as e,J as V,r as j,f as w,d as u,M as R,b,P as L,i as $,p as v}from"./main-C0TBUyF9.js";import{S as g}from"./sidebar-section-D0b_1DUA.js";const N=(s,r)=>s.toLowerCase().includes(r.toLowerCase()),k=(s,r,a=120)=>{const o=s.toLowerCase().indexOf(r.toLowerCase());if(o===-1)return s.slice(0,a);const c=Math.max(0,o-40),l=Math.min(s.length,o+r.length+40);let h=s.slice(c,l);return c>0&&(h=`…${h}`),l<s.length&&(h=`${h}…`),h},T=({query:s})=>{const r=i(n=>n.repoDocs),a=i(n=>n.setActiveDocTitle),o=i(n=>n.setDocSearchQuery),c=S(),l=[];for(const n of r)(N(n.name,s)||N(n.content,s))&&l.push({title:n.name,snippet:k(n.content,s)});const h=n=>{c({to:"/docs/$section",params:{section:"repo-docs"}}),a(n),o("")};return l.length===0?e.jsxs("p",{className:"opacity-50",children:['No results found for "',s,'".']}):e.jsxs("div",{children:[e.jsxs("p",{className:"mb-3 text-sm opacity-50",children:[l.length," result",l.length===1?"":"s",' for "',s,'"']}),e.jsx("div",{className:"flex flex-col gap-1",children:l.map(n=>e.jsx(V,{variant:"ghost",size:"small",fullWidth:!0,onClick:()=>h(n.title),className:"justify-start px-3 py-2",children:e.jsxs("span",{className:"text-left",children:[e.jsx("span",{className:"mb-[0.2rem] block font-display-luminari text-[0.95rem] font-semibold",children:n.title}),e.jsx("span",{className:"text-sm opacity-60",children:n.snippet})]})},n.title))})]})},C=s=>{const[r,a]=j.useState(null);return j.useEffect(()=>{if(a(null),!s)return;let o=!1;return w(s).then(c=>{o||a(c)}).catch(()=>{}),()=>{o=!0}},[s]),r},Q=()=>{const s=i(o=>o.activeReleaseVersion),r=C(s);if(!s)return null;if(!r)return e.jsxs("div",{children:[e.jsx("h2",{className:`${u} m-0 mb-4`,children:s}),e.jsxs("p",{className:"opacity-50",children:["No release notes found for ",s,"."]})]});if(r.length===0)return e.jsxs("div",{children:[e.jsx("h2",{className:`${u} m-0 mb-4`,children:s}),e.jsx("p",{className:"opacity-50",children:"No ideas could be resolved for this release."})]});const a=r.map(o=>`### ${o.label}
|
|
2
2
|
|
|
3
3
|
${o.ideas.map(c=>`- ${c.title} (${c.id})`).join(`
|
|
4
4
|
`)}`).join(`
|
|
5
5
|
|
|
6
6
|
`);return e.jsxs("div",{children:[e.jsx("h2",{className:`${u} m-0 mb-4`,children:s}),e.jsx("div",{className:"text-base leading-[1.7] text-ink-900",children:e.jsx(R,{children:a})})]})},A=()=>{const s=i(c=>c.repoDocs),r=i(c=>c.activeDocTitle),a=s.find(c=>c.name===r);if(!a)return null;const o=a.name.endsWith(".md");return e.jsxs("div",{children:[e.jsx("h2",{className:`${u} m-0 mb-4`,children:a.name}),o?e.jsx("div",{className:"text-base leading-[1.7] text-ink-900",children:e.jsx(R,{children:a.content})}):e.jsx("pre",{className:"overflow-x-auto whitespace-pre-wrap rounded-md bg-black/[4%] p-4 font-mono text-xs leading-normal",children:a.content})]})},F=()=>{const s=i(c=>c.docSearchQuery),r=i(c=>c.activeDocTitle),a=i(c=>c.activeReleaseVersion),o=b();return s.trim()?e.jsx("div",{children:e.jsx(T,{query:s})}):o==="repo-docs"&&r?e.jsx("div",{children:e.jsx(A,{})}):o==="release-notes"&&a?e.jsx("div",{children:e.jsx(Q,{})}):e.jsxs("div",{children:[e.jsx(L,{children:"Docs"}),e.jsx("p",{className:"opacity-50",children:"Pick a doc from the sidebar — start with a repo doc if you're new here."})]})},E=()=>{const s=i(t=>t.repoDocs),r=i(t=>t.repoDocsLoading),a=i(t=>t.loadRepoDocs),o=i(t=>t.activeDocTitle),c=i(t=>t.setActiveDocTitle),l=i(t=>t.docSearchQuery),h=i(t=>t.setDocSearchQuery),n=i(t=>t.releaseVersions),p=i(t=>t.releaseVersionsLoading),m=i(t=>t.loadReleaseVersions),f=i(t=>t.activeReleaseVersion),d=i(t=>t.setActiveReleaseVersion),y=b(),D=S();return j.useEffect(()=>{a()},[a]),j.useEffect(()=>{m()},[m]),{repoDocs:s,repoDocsLoading:r,activeDocTitle:o,docSearchQuery:l,setDocSearchQuery:h,releaseVersions:n,releaseVersionsLoading:p,activeReleaseVersion:f,activeDocSection:y,selectRepoDoc:t=>{D({to:"/docs/$section",params:{section:"repo-docs"}}),c(t)},selectReleaseVersion:t=>{D({to:"/docs/$section",params:{section:"release-notes"}}),d(t)}}},M=s=>s.charAt(0).toUpperCase()+s.slice(1).toLowerCase(),x=({children:s})=>e.jsx("span",{className:"block px-3 py-1 text-sm italic opacity-[0.35]",children:s}),H=()=>{const{repoDocs:s,repoDocsLoading:r,activeDocTitle:a,docSearchQuery:o,setDocSearchQuery:c,releaseVersions:l,releaseVersionsLoading:h,activeReleaseVersion:n,activeDocSection:p,selectRepoDoc:m,selectReleaseVersion:f}=E();return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex h-[64px] items-center",children:e.jsx($,{size:"small","aria-label":"Search docs",placeholder:"Search docs…",value:o,onChange:d=>c(d.target.value)})}),e.jsx(g,{label:"Repo Docs",children:r&&s.length===0?e.jsx(x,{children:"Loading…"}):s.length>0?s.map(d=>e.jsx(v,{size:"small",className:"pc-row text-xs",active:p==="repo-docs"&&a===d.name,onClick:()=>m(d.name),children:M(d.name)},d.name)):e.jsx(x,{children:"No repo docs found"})}),e.jsx(g,{label:"Releases",children:h&&l.length===0?e.jsx(x,{children:"Loading…"}):l.length>0?l.map(d=>e.jsx(v,{size:"small",className:"pc-row text-xs",active:p==="release-notes"&&n===d,onClick:()=>f(d),children:d},d)):e.jsx(x,{children:"No releases yet"})})]})};export{F as DocsPage,H as DocsSidebar};
|
|
7
|
-
//# sourceMappingURL=index-
|
|
7
|
+
//# sourceMappingURL=index-Co_9IFtJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-CDlMKeg5.js","sources":["../../../src/app/features/docs/components/docs-search.tsx","../../../src/app/features/docs/hooks/use-release-notes.ts","../../../src/app/features/docs/components/release-notes-detail.tsx","../../../src/app/features/docs/components/repo-doc-detail.tsx","../../../src/app/features/docs/docs-page.tsx","../../../src/app/features/docs/hooks/use-docs-sidebar.ts","../../../src/app/features/docs/components/docs-sidebar.tsx"],"sourcesContent":["import { useAppStore } from '@/app/stores/app-store';\nimport { Button } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\n\ninterface SearchMatch {\n title: string;\n snippet: string;\n}\n\ninterface DocsSearchProps {\n query: string;\n}\n\nconst searchIn = (text: string, query: string): boolean =>\n text.toLowerCase().includes(query.toLowerCase());\n\nconst snippet = (text: string, query: string, maxLen = 120): string => {\n const idx = text.toLowerCase().indexOf(query.toLowerCase());\n if (idx === -1) return text.slice(0, maxLen);\n const start = Math.max(0, idx - 40);\n const end = Math.min(text.length, idx + query.length + 40);\n let s = text.slice(start, end);\n if (start > 0) s = `…${s}`;\n if (end < text.length) s = `${s}…`;\n return s;\n};\n\nexport const DocsSearch = ({ query }: DocsSearchProps) => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const navigate = useNavigate();\n\n const results: SearchMatch[] = [];\n\n for (const f of repoDocs) {\n if (searchIn(f.name, query) || searchIn(f.content, query)) {\n results.push({\n title: f.name,\n snippet: snippet(f.content, query),\n });\n }\n }\n\n const handleSelect = (title: string) => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(title);\n setDocSearchQuery('');\n };\n\n if (results.length === 0) {\n return <p className=\"opacity-50\">No results found for \"{query}\".</p>;\n }\n\n return (\n <div>\n <p className=\"mb-3 text-sm opacity-50\">\n {results.length} result{results.length === 1 ? '' : 's'} for \"{query}\"\n </p>\n <div className=\"flex flex-col gap-1\">\n {results.map((m) => (\n <Button\n key={m.title}\n variant=\"ghost\"\n size=\"small\"\n fullWidth\n onClick={() => handleSelect(m.title)}\n className=\"justify-start px-3 py-2\"\n >\n <span className=\"text-left\">\n <span className=\"mb-[0.2rem] block font-display-luminari text-[0.95rem] font-semibold\">\n {m.title}\n </span>\n <span className=\"text-sm opacity-60\">{m.snippet}</span>\n </span>\n </Button>\n ))}\n </div>\n </div>\n );\n};\n","import { fetchReleaseNotes } from '@/app/services/release-notes-api';\nimport type { ReleaseNoteSection } from '@/types/index';\nimport { useEffect, useState } from 'react';\n\nexport const useReleaseNotes = (version: string | null): ReleaseNoteSection[] | null => {\n const [sections, setSections] = useState<ReleaseNoteSection[] | null>(null);\n\n useEffect(() => {\n setSections(null);\n if (!version) return;\n let cancelled = false;\n fetchReleaseNotes(version)\n .then((result) => {\n if (!cancelled) setSections(result);\n })\n .catch(() => {});\n return () => {\n cancelled = true;\n };\n }, [version]);\n\n return sections;\n};\n","import { detailHeadingClassName } from '@/app/components/detail-heading-style';\nimport { Markdown } from '@/app/components/markdown';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { useReleaseNotes } from '../hooks/use-release-notes';\n\nexport const ReleaseNotesDetail = () => {\n const version = useAppStore((s) => s.activeReleaseVersion);\n const sections = useReleaseNotes(version);\n if (!version) return null;\n\n if (!sections) {\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <p className=\"opacity-50\">No release notes found for {version}.</p>\n </div>\n );\n }\n\n if (sections.length === 0) {\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <p className=\"opacity-50\">No ideas could be resolved for this release.</p>\n </div>\n );\n }\n\n const markdown = sections\n .map(\n (section) =>\n `### ${section.label}\\n\\n${section.ideas.map((idea) => `- ${idea.title} (${idea.id})`).join('\\n')}`,\n )\n .join('\\n\\n');\n\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <div className=\"text-base leading-[1.7] text-ink-900\">\n <Markdown>{markdown}</Markdown>\n </div>\n </div>\n );\n};\n","import { detailHeadingClassName } from '@/app/components/detail-heading-style';\nimport { Markdown } from '@/app/components/markdown';\nimport { useAppStore } from '@/app/stores/app-store';\n\nexport const RepoDocDetail = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n\n const file = repoDocs.find((f) => f.name === activeDocTitle);\n if (!file) return null;\n\n const isMarkdown = file.name.endsWith('.md');\n\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{file.name}</h2>\n\n {isMarkdown ? (\n <div className=\"text-base leading-[1.7] text-ink-900\">\n <Markdown>{file.content}</Markdown>\n </div>\n ) : (\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-black/[4%] p-4 font-mono text-xs leading-normal\">\n {file.content}\n </pre>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { DocsSearch } from './components/docs-search';\nimport { ReleaseNotesDetail } from './components/release-notes-detail';\nimport { RepoDocDetail } from './components/repo-doc-detail';\n\nexport const DocsPage = () => {\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const activeReleaseVersion = useAppStore((s) => s.activeReleaseVersion);\n const activeDocSection = useResolvedDocSection();\n\n if (docSearchQuery.trim()) {\n return (\n <div>\n <DocsSearch query={docSearchQuery} />\n </div>\n );\n }\n\n if (activeDocSection === 'repo-docs' && activeDocTitle) {\n return (\n <div>\n <RepoDocDetail />\n </div>\n );\n }\n\n if (activeDocSection === 'release-notes' && activeReleaseVersion) {\n return (\n <div>\n <ReleaseNotesDetail />\n </div>\n );\n }\n\n return (\n <div>\n <PageTitle>Docs</PageTitle>\n <p className=\"opacity-50\">\n Pick a doc from the sidebar — start with a repo doc if you're new here.\n </p>\n </div>\n );\n};\n","import { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { useNavigate } from '@tanstack/react-router';\nimport { useEffect } from 'react';\n\nexport const useDocsSidebar = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const repoDocsLoading = useAppStore((s) => s.repoDocsLoading);\n const loadRepoDocs = useAppStore((s) => s.loadRepoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const releaseVersions = useAppStore((s) => s.releaseVersions);\n const releaseVersionsLoading = useAppStore((s) => s.releaseVersionsLoading);\n const loadReleaseVersions = useAppStore((s) => s.loadReleaseVersions);\n const activeReleaseVersion = useAppStore((s) => s.activeReleaseVersion);\n const setActiveReleaseVersion = useAppStore((s) => s.setActiveReleaseVersion);\n const activeDocSection = useResolvedDocSection();\n const navigate = useNavigate();\n\n useEffect(() => {\n loadRepoDocs();\n }, [loadRepoDocs]);\n\n useEffect(() => {\n loadReleaseVersions();\n }, [loadReleaseVersions]);\n\n const selectRepoDoc = (name: string) => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(name);\n };\n\n const selectReleaseVersion = (version: string) => {\n navigate({ to: '/docs/$section', params: { section: 'release-notes' } });\n setActiveReleaseVersion(version);\n };\n\n return {\n repoDocs,\n repoDocsLoading,\n activeDocTitle,\n docSearchQuery,\n setDocSearchQuery,\n releaseVersions,\n releaseVersionsLoading,\n activeReleaseVersion,\n activeDocSection,\n selectRepoDoc,\n selectReleaseVersion,\n };\n};\n","import { Input, ListItem } from '@dendelion/paper-ui';\nimport { SidebarSection } from '../../plans/components/sidebar-section';\nimport { useDocsSidebar } from '../hooks';\n\nconst simplecaseLabel = (name: string) =>\n name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();\n\nconst EmptyState = ({ children }: { children: React.ReactNode }) => (\n <span className=\"block px-3 py-1 text-sm italic opacity-[0.35]\">{children}</span>\n);\n\nexport const DocsSidebar = () => {\n const {\n repoDocs,\n repoDocsLoading,\n activeDocTitle,\n docSearchQuery,\n setDocSearchQuery,\n releaseVersions,\n releaseVersionsLoading,\n activeReleaseVersion,\n activeDocSection,\n selectRepoDoc,\n selectReleaseVersion,\n } = useDocsSidebar();\n\n return (\n <>\n <div className=\"flex h-[64px] items-center\">\n <Input\n size=\"small\"\n aria-label=\"Search docs\"\n placeholder=\"Search docs…\"\n value={docSearchQuery}\n onChange={(e) => setDocSearchQuery(e.target.value)}\n />\n </div>\n\n <SidebarSection label=\"Repo Docs\">\n {repoDocsLoading && repoDocs.length === 0 ? (\n <EmptyState>Loading…</EmptyState>\n ) : repoDocs.length > 0 ? (\n repoDocs.map((f) => (\n <ListItem\n key={f.name}\n size=\"small\"\n className=\"pc-row text-xs\"\n active={activeDocSection === 'repo-docs' && activeDocTitle === f.name}\n onClick={() => selectRepoDoc(f.name)}\n >\n {simplecaseLabel(f.name)}\n </ListItem>\n ))\n ) : (\n <EmptyState>No repo docs found</EmptyState>\n )}\n </SidebarSection>\n\n <SidebarSection label=\"Releases\">\n {releaseVersionsLoading && releaseVersions.length === 0 ? (\n <EmptyState>Loading…</EmptyState>\n ) : releaseVersions.length > 0 ? (\n releaseVersions.map((version) => (\n <ListItem\n key={version}\n size=\"small\"\n className=\"pc-row text-xs\"\n active={activeDocSection === 'release-notes' && activeReleaseVersion === version}\n onClick={() => selectReleaseVersion(version)}\n >\n {version}\n </ListItem>\n ))\n ) : (\n <EmptyState>No releases yet</EmptyState>\n )}\n </SidebarSection>\n </>\n );\n};\n"],"names":["searchIn","text","query","snippet","maxLen","idx","start","end","s","DocsSearch","repoDocs","useAppStore","setActiveDocTitle","setDocSearchQuery","navigate","useNavigate","results","f","handleSelect","title","jsxs","m","jsx","Button","useReleaseNotes","version","sections","setSections","useState","useEffect","cancelled","fetchReleaseNotes","result","ReleaseNotesDetail","detailHeadingClassName","markdown","section","idea","Markdown","RepoDocDetail","activeDocTitle","file","isMarkdown","DocsPage","docSearchQuery","activeReleaseVersion","activeDocSection","useResolvedDocSection","PageTitle","useDocsSidebar","repoDocsLoading","loadRepoDocs","releaseVersions","releaseVersionsLoading","loadReleaseVersions","setActiveReleaseVersion","name","simplecaseLabel","EmptyState","children","DocsSidebar","selectRepoDoc","selectReleaseVersion","Fragment","Input","e","SidebarSection","ListItem"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-Co_9IFtJ.js","sources":["../../../src/app/features/docs/components/docs-search.tsx","../../../src/app/features/docs/hooks/use-release-notes.ts","../../../src/app/features/docs/components/release-notes-detail.tsx","../../../src/app/features/docs/components/repo-doc-detail.tsx","../../../src/app/features/docs/docs-page.tsx","../../../src/app/features/docs/hooks/use-docs-sidebar.ts","../../../src/app/features/docs/components/docs-sidebar.tsx"],"sourcesContent":["import { useAppStore } from '@/app/stores/app-store';\nimport { Button } from '@dendelion/paper-ui';\nimport { useNavigate } from '@tanstack/react-router';\n\ninterface SearchMatch {\n title: string;\n snippet: string;\n}\n\ninterface DocsSearchProps {\n query: string;\n}\n\nconst searchIn = (text: string, query: string): boolean =>\n text.toLowerCase().includes(query.toLowerCase());\n\nconst snippet = (text: string, query: string, maxLen = 120): string => {\n const idx = text.toLowerCase().indexOf(query.toLowerCase());\n if (idx === -1) return text.slice(0, maxLen);\n const start = Math.max(0, idx - 40);\n const end = Math.min(text.length, idx + query.length + 40);\n let s = text.slice(start, end);\n if (start > 0) s = `…${s}`;\n if (end < text.length) s = `${s}…`;\n return s;\n};\n\nexport const DocsSearch = ({ query }: DocsSearchProps) => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const navigate = useNavigate();\n\n const results: SearchMatch[] = [];\n\n for (const f of repoDocs) {\n if (searchIn(f.name, query) || searchIn(f.content, query)) {\n results.push({\n title: f.name,\n snippet: snippet(f.content, query),\n });\n }\n }\n\n const handleSelect = (title: string) => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(title);\n setDocSearchQuery('');\n };\n\n if (results.length === 0) {\n return <p className=\"opacity-50\">No results found for \"{query}\".</p>;\n }\n\n return (\n <div>\n <p className=\"mb-3 text-sm opacity-50\">\n {results.length} result{results.length === 1 ? '' : 's'} for \"{query}\"\n </p>\n <div className=\"flex flex-col gap-1\">\n {results.map((m) => (\n <Button\n key={m.title}\n variant=\"ghost\"\n size=\"small\"\n fullWidth\n onClick={() => handleSelect(m.title)}\n className=\"justify-start px-3 py-2\"\n >\n <span className=\"text-left\">\n <span className=\"mb-[0.2rem] block font-display-luminari text-[0.95rem] font-semibold\">\n {m.title}\n </span>\n <span className=\"text-sm opacity-60\">{m.snippet}</span>\n </span>\n </Button>\n ))}\n </div>\n </div>\n );\n};\n","import { fetchReleaseNotes } from '@/app/services/release-notes-api';\nimport type { ReleaseNoteSection } from '@/types/index';\nimport { useEffect, useState } from 'react';\n\nexport const useReleaseNotes = (version: string | null): ReleaseNoteSection[] | null => {\n const [sections, setSections] = useState<ReleaseNoteSection[] | null>(null);\n\n useEffect(() => {\n setSections(null);\n if (!version) return;\n let cancelled = false;\n fetchReleaseNotes(version)\n .then((result) => {\n if (!cancelled) setSections(result);\n })\n .catch(() => {});\n return () => {\n cancelled = true;\n };\n }, [version]);\n\n return sections;\n};\n","import { detailHeadingClassName } from '@/app/components/detail-heading-style';\nimport { Markdown } from '@/app/components/markdown';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { useReleaseNotes } from '../hooks/use-release-notes';\n\nexport const ReleaseNotesDetail = () => {\n const version = useAppStore((s) => s.activeReleaseVersion);\n const sections = useReleaseNotes(version);\n if (!version) return null;\n\n if (!sections) {\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <p className=\"opacity-50\">No release notes found for {version}.</p>\n </div>\n );\n }\n\n if (sections.length === 0) {\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <p className=\"opacity-50\">No ideas could be resolved for this release.</p>\n </div>\n );\n }\n\n const markdown = sections\n .map(\n (section) =>\n `### ${section.label}\\n\\n${section.ideas.map((idea) => `- ${idea.title} (${idea.id})`).join('\\n')}`,\n )\n .join('\\n\\n');\n\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{version}</h2>\n <div className=\"text-base leading-[1.7] text-ink-900\">\n <Markdown>{markdown}</Markdown>\n </div>\n </div>\n );\n};\n","import { detailHeadingClassName } from '@/app/components/detail-heading-style';\nimport { Markdown } from '@/app/components/markdown';\nimport { useAppStore } from '@/app/stores/app-store';\n\nexport const RepoDocDetail = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n\n const file = repoDocs.find((f) => f.name === activeDocTitle);\n if (!file) return null;\n\n const isMarkdown = file.name.endsWith('.md');\n\n return (\n <div>\n <h2 className={`${detailHeadingClassName} m-0 mb-4`}>{file.name}</h2>\n\n {isMarkdown ? (\n <div className=\"text-base leading-[1.7] text-ink-900\">\n <Markdown>{file.content}</Markdown>\n </div>\n ) : (\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-black/[4%] p-4 font-mono text-xs leading-normal\">\n {file.content}\n </pre>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { DocsSearch } from './components/docs-search';\nimport { ReleaseNotesDetail } from './components/release-notes-detail';\nimport { RepoDocDetail } from './components/repo-doc-detail';\n\nexport const DocsPage = () => {\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const activeReleaseVersion = useAppStore((s) => s.activeReleaseVersion);\n const activeDocSection = useResolvedDocSection();\n\n if (docSearchQuery.trim()) {\n return (\n <div>\n <DocsSearch query={docSearchQuery} />\n </div>\n );\n }\n\n if (activeDocSection === 'repo-docs' && activeDocTitle) {\n return (\n <div>\n <RepoDocDetail />\n </div>\n );\n }\n\n if (activeDocSection === 'release-notes' && activeReleaseVersion) {\n return (\n <div>\n <ReleaseNotesDetail />\n </div>\n );\n }\n\n return (\n <div>\n <PageTitle>Docs</PageTitle>\n <p className=\"opacity-50\">\n Pick a doc from the sidebar — start with a repo doc if you're new here.\n </p>\n </div>\n );\n};\n","import { useResolvedDocSection } from '@/app/hooks';\nimport { useAppStore } from '@/app/stores/app-store';\nimport { useNavigate } from '@tanstack/react-router';\nimport { useEffect } from 'react';\n\nexport const useDocsSidebar = () => {\n const repoDocs = useAppStore((s) => s.repoDocs);\n const repoDocsLoading = useAppStore((s) => s.repoDocsLoading);\n const loadRepoDocs = useAppStore((s) => s.loadRepoDocs);\n const activeDocTitle = useAppStore((s) => s.activeDocTitle);\n const setActiveDocTitle = useAppStore((s) => s.setActiveDocTitle);\n const docSearchQuery = useAppStore((s) => s.docSearchQuery);\n const setDocSearchQuery = useAppStore((s) => s.setDocSearchQuery);\n const releaseVersions = useAppStore((s) => s.releaseVersions);\n const releaseVersionsLoading = useAppStore((s) => s.releaseVersionsLoading);\n const loadReleaseVersions = useAppStore((s) => s.loadReleaseVersions);\n const activeReleaseVersion = useAppStore((s) => s.activeReleaseVersion);\n const setActiveReleaseVersion = useAppStore((s) => s.setActiveReleaseVersion);\n const activeDocSection = useResolvedDocSection();\n const navigate = useNavigate();\n\n useEffect(() => {\n loadRepoDocs();\n }, [loadRepoDocs]);\n\n useEffect(() => {\n loadReleaseVersions();\n }, [loadReleaseVersions]);\n\n const selectRepoDoc = (name: string) => {\n navigate({ to: '/docs/$section', params: { section: 'repo-docs' } });\n setActiveDocTitle(name);\n };\n\n const selectReleaseVersion = (version: string) => {\n navigate({ to: '/docs/$section', params: { section: 'release-notes' } });\n setActiveReleaseVersion(version);\n };\n\n return {\n repoDocs,\n repoDocsLoading,\n activeDocTitle,\n docSearchQuery,\n setDocSearchQuery,\n releaseVersions,\n releaseVersionsLoading,\n activeReleaseVersion,\n activeDocSection,\n selectRepoDoc,\n selectReleaseVersion,\n };\n};\n","import { Input, ListItem } from '@dendelion/paper-ui';\nimport { SidebarSection } from '../../plans/components/sidebar-section';\nimport { useDocsSidebar } from '../hooks';\n\nconst simplecaseLabel = (name: string) =>\n name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();\n\nconst EmptyState = ({ children }: { children: React.ReactNode }) => (\n <span className=\"block px-3 py-1 text-sm italic opacity-[0.35]\">{children}</span>\n);\n\nexport const DocsSidebar = () => {\n const {\n repoDocs,\n repoDocsLoading,\n activeDocTitle,\n docSearchQuery,\n setDocSearchQuery,\n releaseVersions,\n releaseVersionsLoading,\n activeReleaseVersion,\n activeDocSection,\n selectRepoDoc,\n selectReleaseVersion,\n } = useDocsSidebar();\n\n return (\n <>\n <div className=\"flex h-[64px] items-center\">\n <Input\n size=\"small\"\n aria-label=\"Search docs\"\n placeholder=\"Search docs…\"\n value={docSearchQuery}\n onChange={(e) => setDocSearchQuery(e.target.value)}\n />\n </div>\n\n <SidebarSection label=\"Repo Docs\">\n {repoDocsLoading && repoDocs.length === 0 ? (\n <EmptyState>Loading…</EmptyState>\n ) : repoDocs.length > 0 ? (\n repoDocs.map((f) => (\n <ListItem\n key={f.name}\n size=\"small\"\n className=\"pc-row text-xs\"\n active={activeDocSection === 'repo-docs' && activeDocTitle === f.name}\n onClick={() => selectRepoDoc(f.name)}\n >\n {simplecaseLabel(f.name)}\n </ListItem>\n ))\n ) : (\n <EmptyState>No repo docs found</EmptyState>\n )}\n </SidebarSection>\n\n <SidebarSection label=\"Releases\">\n {releaseVersionsLoading && releaseVersions.length === 0 ? (\n <EmptyState>Loading…</EmptyState>\n ) : releaseVersions.length > 0 ? (\n releaseVersions.map((version) => (\n <ListItem\n key={version}\n size=\"small\"\n className=\"pc-row text-xs\"\n active={activeDocSection === 'release-notes' && activeReleaseVersion === version}\n onClick={() => selectReleaseVersion(version)}\n >\n {version}\n </ListItem>\n ))\n ) : (\n <EmptyState>No releases yet</EmptyState>\n )}\n </SidebarSection>\n </>\n );\n};\n"],"names":["searchIn","text","query","snippet","maxLen","idx","start","end","s","DocsSearch","repoDocs","useAppStore","setActiveDocTitle","setDocSearchQuery","navigate","useNavigate","results","f","handleSelect","title","jsxs","m","jsx","Button","useReleaseNotes","version","sections","setSections","useState","useEffect","cancelled","fetchReleaseNotes","result","ReleaseNotesDetail","detailHeadingClassName","markdown","section","idea","Markdown","RepoDocDetail","activeDocTitle","file","isMarkdown","DocsPage","docSearchQuery","activeReleaseVersion","activeDocSection","useResolvedDocSection","PageTitle","useDocsSidebar","repoDocsLoading","loadRepoDocs","releaseVersions","releaseVersionsLoading","loadReleaseVersions","setActiveReleaseVersion","name","simplecaseLabel","EmptyState","children","DocsSidebar","selectRepoDoc","selectReleaseVersion","Fragment","Input","e","SidebarSection","ListItem"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as m,r as o,X as u,U as h,j as e,R as j,g as d,Y as g,Z as v,_ as p,a0 as i,z as c,a1 as r,P as N}from"./main-
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{c as m,r as o,X as u,U as h,j as e,R as j,g as d,Y as g,Z as v,_ as p,a0 as i,z as c,a1 as r,P as N}from"./main-C0TBUyF9.js";const f=async()=>{const s=await fetch(m("/api/stats"));if(!s.ok)throw new Error(`Failed to fetch stats: ${s.status}`);return s.json()},y=()=>{const[s,a]=o.useState(null),[t,x]=o.useState(!1);return o.useEffect(()=>{f().then(a).catch(()=>x(!0))},[]),{stats:s,loadFailed:t}},T=["open",...u],S={...h,open:"Open"},b={allowed:"success",warning:"warning",rejected:"error"},l=({title:s,children:a})=>e.jsx("div",{className:"flex-[1_1_260px] max-w-[360px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none",children:e.jsx(j,{size:"small",texture:"kraft",className:"h-full",children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("span",{className:"font-handwritten text-xs font-semibold opacity-[0.55]",children:s}),a]})})}),n=({label:s,value:a})=>e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"opacity-70",children:s}),e.jsx("span",{className:"font-semibold",children:a})]}),C=({capacity:s})=>{const[a,t]=o.useState(()=>Date.now());return o.useEffect(()=>{const x=setInterval(()=>t(Date.now()),3e4);return()=>clearInterval(x)},[]),e.jsx(l,{title:"Claude capacity",children:s===null?e.jsx("p",{className:"opacity-50 m-0",children:"No agent run has reported capacity yet."}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(d,{size:"small",variant:b[g(s.snapshot.status)],children:s.snapshot.status}),s.snapshot.overage&&e.jsx(d,{size:"small",variant:"warning",children:"overage"})]}),s.snapshot.rateLimitType&&e.jsx(n,{label:"Window",value:s.snapshot.rateLimitType}),s.snapshot.resetsAt!==void 0&&e.jsx(n,{label:"Resets",value:new Date(v(s.snapshot.resetsAt)).toLocaleTimeString()}),e.jsxs("span",{className:"text-2xs opacity-50",children:["as of last agent run,"," ",p(Math.max(0,a-Date.parse(s.capturedAt)))," ago"]})]})})},L=({sourceLines:s,testLines:a})=>e.jsxs(l,{title:"Codebase size",children:[e.jsx(n,{label:"Source lines",value:s}),e.jsx(n,{label:"Test lines",value:a})]}),w=s=>`${Math.round(s*100)}%`,A=({comments:s})=>e.jsxs(l,{title:"Comment ratio",children:[e.jsx(i,{value:s.ratio*100,color:c.accentSlate}),e.jsx(n,{label:"Ratio",value:w(s.ratio)}),e.jsx(n,{label:"Comment lines",value:s.commentLines}),e.jsx(n,{label:"Source lines",value:s.sourceLines})]}),M=({entitiesByStatus:s})=>e.jsxs(l,{title:"Entities by status",children:[T.filter(a=>s[a]).map(a=>e.jsx(n,{label:S[a],value:s[a]},a)),Object.keys(s).length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No entities yet."})]}),E=({medianPhaseDurationMs:s})=>e.jsx(l,{title:"Median phase duration",children:s===null?e.jsx("p",{className:"opacity-50 m-0",children:"No phase runs recorded yet."}):e.jsx(n,{label:"Median",value:p(s)})}),k=({mostExpensiveIdeas:s})=>e.jsxs(l,{title:"Most expensive ideas",children:[s.length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No usage recorded yet."}),s.map(a=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsx(n,{label:a.planId,value:`${r(a.inputTokens)} in · ${r(a.outputTokens)} out`}),e.jsx("span",{className:"text-2xs opacity-50 truncate",children:a.planTitle})]},a.planId))]}),_=({openQuestions:s,decisions:a})=>e.jsxs(l,{title:"Notes",children:[e.jsx(n,{label:"Open questions",value:s}),e.jsx(n,{label:"Decisions",value:a})]}),P=({tasksPerWeek:s})=>{const a=Math.max(1,...s.map(t=>t.count));return e.jsxs(l,{title:"Tasks per week",children:[s.length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No tasks run yet."}),s.map(t=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(n,{label:t.week,value:t.count}),e.jsx(i,{value:t.count,max:a,color:c.accentAmber,height:4})]},t.week))]})},D=({testCoveragePct:s})=>e.jsx(l,{title:"Test coverage",children:s===null?e.jsx("p",{className:"opacity-50 m-0",children:"Run `pnpm test` to generate a coverage report."}):e.jsxs(e.Fragment,{children:[e.jsx(i,{value:s,color:c.accentGreen}),e.jsx(n,{label:"Line coverage",value:`${Math.round(s)}%`})]})}),R=({usagePerWeek:s})=>{const a=Math.max(1,...s.map(t=>t.agentMinutes));return e.jsxs(l,{title:"Usage per week",children:[s.length===0&&e.jsx("p",{className:"opacity-50 m-0",children:"No usage recorded yet."}),s.map(t=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(n,{label:t.week,value:`${t.agentMinutes}m`}),e.jsx(i,{value:t.agentMinutes,max:a,color:c.accentAmber,height:4}),e.jsxs("span",{className:"text-2xs opacity-50",children:[r(t.inputTokens)," in · ",r(t.outputTokens)," out"]})]},t.week))]})},I=({stats:s})=>e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-wrap items-start gap-2.5",children:[e.jsx(A,{comments:s.comments}),e.jsx(D,{testCoveragePct:s.testCoveragePct}),e.jsx(L,{sourceLines:s.comments.sourceLines,testLines:s.testLines}),e.jsx(M,{entitiesByStatus:s.entitiesByStatus}),e.jsx(_,{openQuestions:s.openQuestions,decisions:s.decisions}),e.jsx(P,{tasksPerWeek:s.tasksPerWeek}),e.jsx(R,{usagePerWeek:s.usagePerWeek}),e.jsx(E,{medianPhaseDurationMs:s.medianPhaseDurationMs}),e.jsx(k,{mostExpensiveIdeas:s.mostExpensiveIdeas}),e.jsx(C,{capacity:s.capacity})]}),e.jsxs("p",{className:"opacity-[0.4] text-2xs mt-6",children:["Generated ",new Date(s.generatedAt).toLocaleString()]})]}),F=()=>{const{stats:s,loadFailed:a}=y();return e.jsxs("div",{children:[e.jsx(N,{children:"Stats"}),e.jsx("p",{className:"opacity-50 mb-6",children:"A read-only look at project health — every number here is informational, none of them gate anything."}),a&&e.jsx("p",{className:"opacity-50",children:"Couldn't load stats."}),!a&&!s&&e.jsx("p",{className:"opacity-50",children:"Loading…"}),s&&e.jsx(I,{stats:s})]})};export{F as StatsPage};
|
|
2
|
+
//# sourceMappingURL=index-DRVlFKav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-UOItJyt3.js","sources":["../../../src/app/services/content/stats-api.ts","../../../src/app/features/stats/hooks/use-stats-page.ts","../../../src/app/features/stats/constants.ts","../../../src/app/features/stats/views/stat-card.tsx","../../../src/app/features/stats/views/claude-capacity-card.tsx","../../../src/app/features/stats/views/codebase-size-card.tsx","../../../src/app/features/stats/helpers/format-stats.ts","../../../src/app/features/stats/views/comment-ratio-card.tsx","../../../src/app/features/stats/views/entities-by-status-card.tsx","../../../src/app/features/stats/views/median-phase-duration-card.tsx","../../../src/app/features/stats/views/most-expensive-ideas-card.tsx","../../../src/app/features/stats/views/notes-card.tsx","../../../src/app/features/stats/views/tasks-per-week-card.tsx","../../../src/app/features/stats/views/test-coverage-card.tsx","../../../src/app/features/stats/views/usage-per-week-card.tsx","../../../src/app/features/stats/views/stats-grid.tsx","../../../src/app/features/stats/stats-page.tsx"],"sourcesContent":["import type { ProjectStats } from '@/types/index';\nimport { apiUrl } from '../api-base';\n\nexport const fetchStats = async () => {\n const res = await fetch(apiUrl('/api/stats'));\n if (!res.ok) throw new Error(`Failed to fetch stats: ${res.status}`);\n return res.json() as Promise<ProjectStats>;\n};\n","import { fetchStats } from '@/app/services/content';\nimport type { ProjectStats } from '@/types/index';\nimport { useEffect, useState } from 'react';\n\nexport const useStatsPage = () => {\n const [stats, setStats] = useState<ProjectStats | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n\n useEffect(() => {\n fetchStats()\n .then(setStats)\n .catch(() => setLoadFailed(true));\n }, []);\n\n return { stats, loadFailed };\n};\n","import { STATUS_LABEL } from '@/app/features/plans/constants';\nimport { type EntityStatus, PLAN_STATUSES } from '@/types/index';\n\nexport const ENTITY_STATUS_ORDER: EntityStatus[] = ['open', ...PLAN_STATUSES];\n\nexport const ENTITY_STATUS_LABEL: Record<EntityStatus, string> = { ...STATUS_LABEL, open: 'Open' };\n\nexport const CAPACITY_STAMP = {\n allowed: 'success',\n warning: 'warning',\n rejected: 'error',\n} as const;\n","import { Card } from '@dendelion/paper-ui';\nimport type { ReactNode } from 'react';\n\nexport interface StatCardProps {\n title: string;\n children: ReactNode;\n}\n\nexport const StatCard = ({ title, children }: StatCardProps) => (\n <div className=\"flex-[1_1_260px] max-w-[360px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\">\n <Card size=\"small\" texture=\"kraft\" className=\"h-full\">\n <div className=\"flex flex-col gap-3\">\n <span className=\"font-handwritten text-xs font-semibold opacity-[0.55]\">{title}</span>\n {children}\n </div>\n </Card>\n </div>\n);\n\nexport interface StatRowProps {\n label: string;\n value: ReactNode;\n}\n\nexport const StatRow = ({ label, value }: StatRowProps) => (\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"opacity-70\">{label}</span>\n <span className=\"font-semibold\">{value}</span>\n </div>\n);\n","import { formatDuration } from '@/core/phase-run';\nimport { capacityLevel, resetsAtMs } from '@/core/rate-limit';\nimport type { ProjectStats } from '@/types/index';\nimport { Stamp } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\nimport { CAPACITY_STAMP } from '../constants';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface ClaudeCapacityCardProps {\n capacity: ProjectStats['capacity'];\n}\n\nexport const ClaudeCapacityCard = ({ capacity }: ClaudeCapacityCardProps) => {\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n const id = setInterval(() => setNow(Date.now()), 30_000);\n return () => clearInterval(id);\n }, []);\n return (\n <StatCard title=\"Claude capacity\">\n {capacity === null ? (\n <p className=\"opacity-50 m-0\">No agent run has reported capacity yet.</p>\n ) : (\n <>\n <div className=\"flex items-center gap-2\">\n <Stamp size=\"small\" variant={CAPACITY_STAMP[capacityLevel(capacity.snapshot.status)]}>\n {capacity.snapshot.status}\n </Stamp>\n {capacity.snapshot.overage && (\n <Stamp size=\"small\" variant=\"warning\">\n overage\n </Stamp>\n )}\n </div>\n {capacity.snapshot.rateLimitType && (\n <StatRow label=\"Window\" value={capacity.snapshot.rateLimitType} />\n )}\n {capacity.snapshot.resetsAt !== undefined && (\n <StatRow\n label=\"Resets\"\n value={new Date(resetsAtMs(capacity.snapshot.resetsAt)).toLocaleTimeString()}\n />\n )}\n <span className=\"text-2xs opacity-50\">\n as of last agent run,{' '}\n {formatDuration(Math.max(0, now - Date.parse(capacity.capturedAt)))} ago\n </span>\n </>\n )}\n </StatCard>\n );\n};\n","import { StatCard, StatRow } from './stat-card';\n\nexport interface CodebaseSizeCardProps {\n sourceLines: number;\n testLines: number;\n}\n\nexport const CodebaseSizeCard = ({ sourceLines, testLines }: CodebaseSizeCardProps) => (\n <StatCard title=\"Codebase size\">\n <StatRow label=\"Source lines\" value={sourceLines} />\n <StatRow label=\"Test lines\" value={testLines} />\n </StatCard>\n);\n","export const pct = (value: number) => `${Math.round(value * 100)}%`;\n","import { color } from '@/app/styles/tokens';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { pct } from '../helpers';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface CommentRatioCardProps {\n comments: ProjectStats['comments'];\n}\n\nexport const CommentRatioCard = ({ comments }: CommentRatioCardProps) => (\n <StatCard title=\"Comment ratio\">\n <Progress value={comments.ratio * 100} color={color.accentSlate} />\n <StatRow label=\"Ratio\" value={pct(comments.ratio)} />\n <StatRow label=\"Comment lines\" value={comments.commentLines} />\n <StatRow label=\"Source lines\" value={comments.sourceLines} />\n </StatCard>\n);\n","import type { ProjectStats } from '@/types/index';\nimport { ENTITY_STATUS_LABEL, ENTITY_STATUS_ORDER } from '../constants';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface EntitiesByStatusCardProps {\n entitiesByStatus: ProjectStats['entitiesByStatus'];\n}\n\nexport const EntitiesByStatusCard = ({ entitiesByStatus }: EntitiesByStatusCardProps) => (\n <StatCard title=\"Entities by status\">\n {ENTITY_STATUS_ORDER.filter((status) => entitiesByStatus[status]).map((status) => (\n <StatRow key={status} label={ENTITY_STATUS_LABEL[status]} value={entitiesByStatus[status]} />\n ))}\n {Object.keys(entitiesByStatus).length === 0 && (\n <p className=\"opacity-50 m-0\">No entities yet.</p>\n )}\n </StatCard>\n);\n","import { formatDuration } from '@/core/phase-run';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface MedianPhaseDurationCardProps {\n medianPhaseDurationMs: number | null;\n}\n\nexport const MedianPhaseDurationCard = ({\n medianPhaseDurationMs,\n}: MedianPhaseDurationCardProps) => (\n <StatCard title=\"Median phase duration\">\n {medianPhaseDurationMs === null ? (\n <p className=\"opacity-50 m-0\">No phase runs recorded yet.</p>\n ) : (\n <StatRow label=\"Median\" value={formatDuration(medianPhaseDurationMs)} />\n )}\n </StatCard>\n);\n","import { formatTokens } from '@/core/phase-run';\nimport type { ProjectStats } from '@/types/index';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface MostExpensiveIdeasCardProps {\n mostExpensiveIdeas: ProjectStats['mostExpensiveIdeas'];\n}\n\nexport const MostExpensiveIdeasCard = ({ mostExpensiveIdeas }: MostExpensiveIdeasCardProps) => (\n <StatCard title=\"Most expensive ideas\">\n {mostExpensiveIdeas.length === 0 && <p className=\"opacity-50 m-0\">No usage recorded yet.</p>}\n {mostExpensiveIdeas.map((idea) => (\n <div key={idea.planId} className=\"flex flex-col gap-0.5\">\n <StatRow\n label={idea.planId}\n value={`${formatTokens(idea.inputTokens)} in · ${formatTokens(idea.outputTokens)} out`}\n />\n <span className=\"text-2xs opacity-50 truncate\">{idea.planTitle}</span>\n </div>\n ))}\n </StatCard>\n);\n","import { StatCard, StatRow } from './stat-card';\n\nexport interface NotesCardProps {\n openQuestions: number;\n decisions: number;\n}\n\nexport const NotesCard = ({ openQuestions, decisions }: NotesCardProps) => (\n <StatCard title=\"Notes\">\n <StatRow label=\"Open questions\" value={openQuestions} />\n <StatRow label=\"Decisions\" value={decisions} />\n </StatCard>\n);\n","import { color } from '@/app/styles/tokens';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface TasksPerWeekCardProps {\n tasksPerWeek: ProjectStats['tasksPerWeek'];\n}\n\nexport const TasksPerWeekCard = ({ tasksPerWeek }: TasksPerWeekCardProps) => {\n const max = Math.max(1, ...tasksPerWeek.map((w) => w.count));\n return (\n <StatCard title=\"Tasks per week\">\n {tasksPerWeek.length === 0 && <p className=\"opacity-50 m-0\">No tasks run yet.</p>}\n {tasksPerWeek.map((week) => (\n <div key={week.week} className=\"flex flex-col gap-1\">\n <StatRow label={week.week} value={week.count} />\n <Progress value={week.count} max={max} color={color.accentAmber} height={4} />\n </div>\n ))}\n </StatCard>\n );\n};\n","import { color } from '@/app/styles/tokens';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface TestCoverageCardProps {\n testCoveragePct: number | null;\n}\n\nexport const TestCoverageCard = ({ testCoveragePct }: TestCoverageCardProps) => (\n <StatCard title=\"Test coverage\">\n {testCoveragePct === null ? (\n <p className=\"opacity-50 m-0\">Run `pnpm test` to generate a coverage report.</p>\n ) : (\n <>\n <Progress value={testCoveragePct} color={color.accentGreen} />\n <StatRow label=\"Line coverage\" value={`${Math.round(testCoveragePct)}%`} />\n </>\n )}\n </StatCard>\n);\n","import { color } from '@/app/styles/tokens';\nimport { formatTokens } from '@/core/phase-run';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface UsagePerWeekCardProps {\n usagePerWeek: ProjectStats['usagePerWeek'];\n}\n\nexport const UsagePerWeekCard = ({ usagePerWeek }: UsagePerWeekCardProps) => {\n const max = Math.max(1, ...usagePerWeek.map((w) => w.agentMinutes));\n return (\n <StatCard title=\"Usage per week\">\n {usagePerWeek.length === 0 && <p className=\"opacity-50 m-0\">No usage recorded yet.</p>}\n {usagePerWeek.map((week) => (\n <div key={week.week} className=\"flex flex-col gap-1\">\n <StatRow label={week.week} value={`${week.agentMinutes}m`} />\n <Progress value={week.agentMinutes} max={max} color={color.accentAmber} height={4} />\n <span className=\"text-2xs opacity-50\">\n {formatTokens(week.inputTokens)} in · {formatTokens(week.outputTokens)} out\n </span>\n </div>\n ))}\n </StatCard>\n );\n};\n","import type { ProjectStats } from '@/types/index';\nimport { ClaudeCapacityCard } from './claude-capacity-card';\nimport { CodebaseSizeCard } from './codebase-size-card';\nimport { CommentRatioCard } from './comment-ratio-card';\nimport { EntitiesByStatusCard } from './entities-by-status-card';\nimport { MedianPhaseDurationCard } from './median-phase-duration-card';\nimport { MostExpensiveIdeasCard } from './most-expensive-ideas-card';\nimport { NotesCard } from './notes-card';\nimport { TasksPerWeekCard } from './tasks-per-week-card';\nimport { TestCoverageCard } from './test-coverage-card';\nimport { UsagePerWeekCard } from './usage-per-week-card';\n\nexport interface StatsGridProps {\n stats: ProjectStats;\n}\n\nexport const StatsGrid = ({ stats }: StatsGridProps) => (\n <>\n <div className=\"flex flex-wrap items-start gap-2.5\">\n <CommentRatioCard comments={stats.comments} />\n <TestCoverageCard testCoveragePct={stats.testCoveragePct} />\n <CodebaseSizeCard sourceLines={stats.comments.sourceLines} testLines={stats.testLines} />\n <EntitiesByStatusCard entitiesByStatus={stats.entitiesByStatus} />\n <NotesCard openQuestions={stats.openQuestions} decisions={stats.decisions} />\n <TasksPerWeekCard tasksPerWeek={stats.tasksPerWeek} />\n <UsagePerWeekCard usagePerWeek={stats.usagePerWeek} />\n <MedianPhaseDurationCard medianPhaseDurationMs={stats.medianPhaseDurationMs} />\n <MostExpensiveIdeasCard mostExpensiveIdeas={stats.mostExpensiveIdeas} />\n <ClaudeCapacityCard capacity={stats.capacity} />\n </div>\n <p className=\"opacity-[0.4] text-2xs mt-6\">\n Generated {new Date(stats.generatedAt).toLocaleString()}\n </p>\n </>\n);\n","import { PageTitle } from '@/app/components/page-title';\nimport { useStatsPage } from './hooks';\nimport { StatsGrid } from './views';\n\nexport const StatsPage = () => {\n const { stats, loadFailed } = useStatsPage();\n\n return (\n <div>\n <PageTitle>Stats</PageTitle>\n <p className=\"opacity-50 mb-6\">\n A read-only look at project health — every number here is informational, none of them gate\n anything.\n </p>\n {loadFailed && <p className=\"opacity-50\">Couldn't load stats.</p>}\n {!loadFailed && !stats && <p className=\"opacity-50\">Loading…</p>}\n {stats && <StatsGrid stats={stats} />}\n </div>\n );\n};\n"],"names":["fetchStats","res","apiUrl","useStatsPage","stats","setStats","useState","loadFailed","setLoadFailed","useEffect","ENTITY_STATUS_ORDER","PLAN_STATUSES","ENTITY_STATUS_LABEL","STATUS_LABEL","CAPACITY_STAMP","StatCard","title","children","jsx","Card","jsxs","StatRow","label","value","ClaudeCapacityCard","capacity","now","setNow","id","Fragment","Stamp","capacityLevel","resetsAtMs","formatDuration","CodebaseSizeCard","sourceLines","testLines","pct","CommentRatioCard","comments","Progress","color","EntitiesByStatusCard","entitiesByStatus","status","MedianPhaseDurationCard","medianPhaseDurationMs","MostExpensiveIdeasCard","mostExpensiveIdeas","idea","formatTokens","NotesCard","openQuestions","decisions","TasksPerWeekCard","tasksPerWeek","max","w","week","TestCoverageCard","testCoveragePct","UsagePerWeekCard","usagePerWeek","StatsGrid","StatsPage","PageTitle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DRVlFKav.js","sources":["../../../src/app/services/content/stats-api.ts","../../../src/app/features/stats/hooks/use-stats-page.ts","../../../src/app/features/stats/constants.ts","../../../src/app/features/stats/views/stat-card.tsx","../../../src/app/features/stats/views/claude-capacity-card.tsx","../../../src/app/features/stats/views/codebase-size-card.tsx","../../../src/app/features/stats/helpers/format-stats.ts","../../../src/app/features/stats/views/comment-ratio-card.tsx","../../../src/app/features/stats/views/entities-by-status-card.tsx","../../../src/app/features/stats/views/median-phase-duration-card.tsx","../../../src/app/features/stats/views/most-expensive-ideas-card.tsx","../../../src/app/features/stats/views/notes-card.tsx","../../../src/app/features/stats/views/tasks-per-week-card.tsx","../../../src/app/features/stats/views/test-coverage-card.tsx","../../../src/app/features/stats/views/usage-per-week-card.tsx","../../../src/app/features/stats/views/stats-grid.tsx","../../../src/app/features/stats/stats-page.tsx"],"sourcesContent":["import type { ProjectStats } from '@/types/index';\nimport { apiUrl } from '../api-base';\n\nexport const fetchStats = async () => {\n const res = await fetch(apiUrl('/api/stats'));\n if (!res.ok) throw new Error(`Failed to fetch stats: ${res.status}`);\n return res.json() as Promise<ProjectStats>;\n};\n","import { fetchStats } from '@/app/services/content';\nimport type { ProjectStats } from '@/types/index';\nimport { useEffect, useState } from 'react';\n\nexport const useStatsPage = () => {\n const [stats, setStats] = useState<ProjectStats | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n\n useEffect(() => {\n fetchStats()\n .then(setStats)\n .catch(() => setLoadFailed(true));\n }, []);\n\n return { stats, loadFailed };\n};\n","import { STATUS_LABEL } from '@/app/features/plans/constants';\nimport { type EntityStatus, PLAN_STATUSES } from '@/types/index';\n\nexport const ENTITY_STATUS_ORDER: EntityStatus[] = ['open', ...PLAN_STATUSES];\n\nexport const ENTITY_STATUS_LABEL: Record<EntityStatus, string> = { ...STATUS_LABEL, open: 'Open' };\n\nexport const CAPACITY_STAMP = {\n allowed: 'success',\n warning: 'warning',\n rejected: 'error',\n} as const;\n","import { Card } from '@dendelion/paper-ui';\nimport type { ReactNode } from 'react';\n\nexport interface StatCardProps {\n title: string;\n children: ReactNode;\n}\n\nexport const StatCard = ({ title, children }: StatCardProps) => (\n <div className=\"flex-[1_1_260px] max-w-[360px] max-[480px]:flex-[1_1_100%] max-[480px]:max-w-none\">\n <Card size=\"small\" texture=\"kraft\" className=\"h-full\">\n <div className=\"flex flex-col gap-3\">\n <span className=\"font-handwritten text-xs font-semibold opacity-[0.55]\">{title}</span>\n {children}\n </div>\n </Card>\n </div>\n);\n\nexport interface StatRowProps {\n label: string;\n value: ReactNode;\n}\n\nexport const StatRow = ({ label, value }: StatRowProps) => (\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"opacity-70\">{label}</span>\n <span className=\"font-semibold\">{value}</span>\n </div>\n);\n","import { formatDuration } from '@/core/phase-run';\nimport { capacityLevel, resetsAtMs } from '@/core/rate-limit';\nimport type { ProjectStats } from '@/types/index';\nimport { Stamp } from '@dendelion/paper-ui';\nimport { useEffect, useState } from 'react';\nimport { CAPACITY_STAMP } from '../constants';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface ClaudeCapacityCardProps {\n capacity: ProjectStats['capacity'];\n}\n\nexport const ClaudeCapacityCard = ({ capacity }: ClaudeCapacityCardProps) => {\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n const id = setInterval(() => setNow(Date.now()), 30_000);\n return () => clearInterval(id);\n }, []);\n return (\n <StatCard title=\"Claude capacity\">\n {capacity === null ? (\n <p className=\"opacity-50 m-0\">No agent run has reported capacity yet.</p>\n ) : (\n <>\n <div className=\"flex items-center gap-2\">\n <Stamp size=\"small\" variant={CAPACITY_STAMP[capacityLevel(capacity.snapshot.status)]}>\n {capacity.snapshot.status}\n </Stamp>\n {capacity.snapshot.overage && (\n <Stamp size=\"small\" variant=\"warning\">\n overage\n </Stamp>\n )}\n </div>\n {capacity.snapshot.rateLimitType && (\n <StatRow label=\"Window\" value={capacity.snapshot.rateLimitType} />\n )}\n {capacity.snapshot.resetsAt !== undefined && (\n <StatRow\n label=\"Resets\"\n value={new Date(resetsAtMs(capacity.snapshot.resetsAt)).toLocaleTimeString()}\n />\n )}\n <span className=\"text-2xs opacity-50\">\n as of last agent run,{' '}\n {formatDuration(Math.max(0, now - Date.parse(capacity.capturedAt)))} ago\n </span>\n </>\n )}\n </StatCard>\n );\n};\n","import { StatCard, StatRow } from './stat-card';\n\nexport interface CodebaseSizeCardProps {\n sourceLines: number;\n testLines: number;\n}\n\nexport const CodebaseSizeCard = ({ sourceLines, testLines }: CodebaseSizeCardProps) => (\n <StatCard title=\"Codebase size\">\n <StatRow label=\"Source lines\" value={sourceLines} />\n <StatRow label=\"Test lines\" value={testLines} />\n </StatCard>\n);\n","export const pct = (value: number) => `${Math.round(value * 100)}%`;\n","import { color } from '@/app/styles/tokens';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { pct } from '../helpers';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface CommentRatioCardProps {\n comments: ProjectStats['comments'];\n}\n\nexport const CommentRatioCard = ({ comments }: CommentRatioCardProps) => (\n <StatCard title=\"Comment ratio\">\n <Progress value={comments.ratio * 100} color={color.accentSlate} />\n <StatRow label=\"Ratio\" value={pct(comments.ratio)} />\n <StatRow label=\"Comment lines\" value={comments.commentLines} />\n <StatRow label=\"Source lines\" value={comments.sourceLines} />\n </StatCard>\n);\n","import type { ProjectStats } from '@/types/index';\nimport { ENTITY_STATUS_LABEL, ENTITY_STATUS_ORDER } from '../constants';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface EntitiesByStatusCardProps {\n entitiesByStatus: ProjectStats['entitiesByStatus'];\n}\n\nexport const EntitiesByStatusCard = ({ entitiesByStatus }: EntitiesByStatusCardProps) => (\n <StatCard title=\"Entities by status\">\n {ENTITY_STATUS_ORDER.filter((status) => entitiesByStatus[status]).map((status) => (\n <StatRow key={status} label={ENTITY_STATUS_LABEL[status]} value={entitiesByStatus[status]} />\n ))}\n {Object.keys(entitiesByStatus).length === 0 && (\n <p className=\"opacity-50 m-0\">No entities yet.</p>\n )}\n </StatCard>\n);\n","import { formatDuration } from '@/core/phase-run';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface MedianPhaseDurationCardProps {\n medianPhaseDurationMs: number | null;\n}\n\nexport const MedianPhaseDurationCard = ({\n medianPhaseDurationMs,\n}: MedianPhaseDurationCardProps) => (\n <StatCard title=\"Median phase duration\">\n {medianPhaseDurationMs === null ? (\n <p className=\"opacity-50 m-0\">No phase runs recorded yet.</p>\n ) : (\n <StatRow label=\"Median\" value={formatDuration(medianPhaseDurationMs)} />\n )}\n </StatCard>\n);\n","import { formatTokens } from '@/core/phase-run';\nimport type { ProjectStats } from '@/types/index';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface MostExpensiveIdeasCardProps {\n mostExpensiveIdeas: ProjectStats['mostExpensiveIdeas'];\n}\n\nexport const MostExpensiveIdeasCard = ({ mostExpensiveIdeas }: MostExpensiveIdeasCardProps) => (\n <StatCard title=\"Most expensive ideas\">\n {mostExpensiveIdeas.length === 0 && <p className=\"opacity-50 m-0\">No usage recorded yet.</p>}\n {mostExpensiveIdeas.map((idea) => (\n <div key={idea.planId} className=\"flex flex-col gap-0.5\">\n <StatRow\n label={idea.planId}\n value={`${formatTokens(idea.inputTokens)} in · ${formatTokens(idea.outputTokens)} out`}\n />\n <span className=\"text-2xs opacity-50 truncate\">{idea.planTitle}</span>\n </div>\n ))}\n </StatCard>\n);\n","import { StatCard, StatRow } from './stat-card';\n\nexport interface NotesCardProps {\n openQuestions: number;\n decisions: number;\n}\n\nexport const NotesCard = ({ openQuestions, decisions }: NotesCardProps) => (\n <StatCard title=\"Notes\">\n <StatRow label=\"Open questions\" value={openQuestions} />\n <StatRow label=\"Decisions\" value={decisions} />\n </StatCard>\n);\n","import { color } from '@/app/styles/tokens';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface TasksPerWeekCardProps {\n tasksPerWeek: ProjectStats['tasksPerWeek'];\n}\n\nexport const TasksPerWeekCard = ({ tasksPerWeek }: TasksPerWeekCardProps) => {\n const max = Math.max(1, ...tasksPerWeek.map((w) => w.count));\n return (\n <StatCard title=\"Tasks per week\">\n {tasksPerWeek.length === 0 && <p className=\"opacity-50 m-0\">No tasks run yet.</p>}\n {tasksPerWeek.map((week) => (\n <div key={week.week} className=\"flex flex-col gap-1\">\n <StatRow label={week.week} value={week.count} />\n <Progress value={week.count} max={max} color={color.accentAmber} height={4} />\n </div>\n ))}\n </StatCard>\n );\n};\n","import { color } from '@/app/styles/tokens';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface TestCoverageCardProps {\n testCoveragePct: number | null;\n}\n\nexport const TestCoverageCard = ({ testCoveragePct }: TestCoverageCardProps) => (\n <StatCard title=\"Test coverage\">\n {testCoveragePct === null ? (\n <p className=\"opacity-50 m-0\">Run `pnpm test` to generate a coverage report.</p>\n ) : (\n <>\n <Progress value={testCoveragePct} color={color.accentGreen} />\n <StatRow label=\"Line coverage\" value={`${Math.round(testCoveragePct)}%`} />\n </>\n )}\n </StatCard>\n);\n","import { color } from '@/app/styles/tokens';\nimport { formatTokens } from '@/core/phase-run';\nimport type { ProjectStats } from '@/types/index';\nimport { Progress } from '@dendelion/paper-ui';\nimport { StatCard, StatRow } from './stat-card';\n\nexport interface UsagePerWeekCardProps {\n usagePerWeek: ProjectStats['usagePerWeek'];\n}\n\nexport const UsagePerWeekCard = ({ usagePerWeek }: UsagePerWeekCardProps) => {\n const max = Math.max(1, ...usagePerWeek.map((w) => w.agentMinutes));\n return (\n <StatCard title=\"Usage per week\">\n {usagePerWeek.length === 0 && <p className=\"opacity-50 m-0\">No usage recorded yet.</p>}\n {usagePerWeek.map((week) => (\n <div key={week.week} className=\"flex flex-col gap-1\">\n <StatRow label={week.week} value={`${week.agentMinutes}m`} />\n <Progress value={week.agentMinutes} max={max} color={color.accentAmber} height={4} />\n <span className=\"text-2xs opacity-50\">\n {formatTokens(week.inputTokens)} in · {formatTokens(week.outputTokens)} out\n </span>\n </div>\n ))}\n </StatCard>\n );\n};\n","import type { ProjectStats } from '@/types/index';\nimport { ClaudeCapacityCard } from './claude-capacity-card';\nimport { CodebaseSizeCard } from './codebase-size-card';\nimport { CommentRatioCard } from './comment-ratio-card';\nimport { EntitiesByStatusCard } from './entities-by-status-card';\nimport { MedianPhaseDurationCard } from './median-phase-duration-card';\nimport { MostExpensiveIdeasCard } from './most-expensive-ideas-card';\nimport { NotesCard } from './notes-card';\nimport { TasksPerWeekCard } from './tasks-per-week-card';\nimport { TestCoverageCard } from './test-coverage-card';\nimport { UsagePerWeekCard } from './usage-per-week-card';\n\nexport interface StatsGridProps {\n stats: ProjectStats;\n}\n\nexport const StatsGrid = ({ stats }: StatsGridProps) => (\n <>\n <div className=\"flex flex-wrap items-start gap-2.5\">\n <CommentRatioCard comments={stats.comments} />\n <TestCoverageCard testCoveragePct={stats.testCoveragePct} />\n <CodebaseSizeCard sourceLines={stats.comments.sourceLines} testLines={stats.testLines} />\n <EntitiesByStatusCard entitiesByStatus={stats.entitiesByStatus} />\n <NotesCard openQuestions={stats.openQuestions} decisions={stats.decisions} />\n <TasksPerWeekCard tasksPerWeek={stats.tasksPerWeek} />\n <UsagePerWeekCard usagePerWeek={stats.usagePerWeek} />\n <MedianPhaseDurationCard medianPhaseDurationMs={stats.medianPhaseDurationMs} />\n <MostExpensiveIdeasCard mostExpensiveIdeas={stats.mostExpensiveIdeas} />\n <ClaudeCapacityCard capacity={stats.capacity} />\n </div>\n <p className=\"opacity-[0.4] text-2xs mt-6\">\n Generated {new Date(stats.generatedAt).toLocaleString()}\n </p>\n </>\n);\n","import { PageTitle } from '@/app/components/page-title';\nimport { useStatsPage } from './hooks';\nimport { StatsGrid } from './views';\n\nexport const StatsPage = () => {\n const { stats, loadFailed } = useStatsPage();\n\n return (\n <div>\n <PageTitle>Stats</PageTitle>\n <p className=\"opacity-50 mb-6\">\n A read-only look at project health — every number here is informational, none of them gate\n anything.\n </p>\n {loadFailed && <p className=\"opacity-50\">Couldn't load stats.</p>}\n {!loadFailed && !stats && <p className=\"opacity-50\">Loading…</p>}\n {stats && <StatsGrid stats={stats} />}\n </div>\n );\n};\n"],"names":["fetchStats","res","apiUrl","useStatsPage","stats","setStats","useState","loadFailed","setLoadFailed","useEffect","ENTITY_STATUS_ORDER","PLAN_STATUSES","ENTITY_STATUS_LABEL","STATUS_LABEL","CAPACITY_STAMP","StatCard","title","children","jsx","Card","jsxs","StatRow","label","value","ClaudeCapacityCard","capacity","now","setNow","id","Fragment","Stamp","capacityLevel","resetsAtMs","formatDuration","CodebaseSizeCard","sourceLines","testLines","pct","CommentRatioCard","comments","Progress","color","EntitiesByStatusCard","entitiesByStatus","status","MedianPhaseDurationCard","medianPhaseDurationMs","MostExpensiveIdeasCard","mostExpensiveIdeas","idea","formatTokens","NotesCard","openQuestions","decisions","TasksPerWeekCard","tasksPerWeek","max","w","week","TestCoverageCard","testCoveragePct","UsagePerWeekCard","usagePerWeek","StatsGrid","StatsPage","PageTitle"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{u as h,a2 as y,r as x,a3 as D,j as s,J as C,$ as k,g as v,a4 as L,C as $,a5 as N,a6 as S,S as A,a7 as E,a8 as T,a9 as R,aa as I,ab as _,p as G,ac as M,ad as U,w as O,P as z,ae as B,af as H}from"./main-
|
|
1
|
+
import{u as h,a2 as y,r as x,a3 as D,j as s,J as C,$ as k,g as v,a4 as L,C as $,a5 as N,a6 as S,S as A,a7 as E,a8 as T,a9 as R,aa as I,ab as _,p as G,ac as M,ad as U,w as O,P as z,ae as B,af as H}from"./main-C0TBUyF9.js";const W=({state:e,filesEmpty:a})=>{const t=h(f=>f.status),{checks:i}=y(),{qualityStatus:l,testStatus:o,consistencyStatus:c}=x.useMemo(()=>D(t,i),[t,i]),n=[l==="fail"&&"Quality",o==="fail"&&"Tests",c==="fail"&&"Consistency"].filter(f=>f!==!1),m=s.jsx(C,{size:"small",disabled:a||!e.commitTitle.trim()||e.committing||e.commitInFlight,onClick:e.handleCommit,children:e.committing||e.commitInFlight?"Committing…":e.stagedCount>0?`Commit ${e.stagedCount} staged`:"Commit"});return n.length===0?m:s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(k,{content:`${n.join(", ")} failing`,children:s.jsx(v,{size:"small",variant:"warning",children:"!"})}),m]})},K="__git__",q=()=>!0,J=e=>L(e,{formKey:K,matchesSuggestionTask:q});function b(e){const a=e.lastIndexOf("/");return a===-1?{dir:"",base:e}:{dir:e.slice(0,a+1),base:e.slice(a+1)}}function Q(e){const a=new Map;for(const t of e){const{dir:i}=b(t.path),l=i||"./",o=a.get(l);o?o.push(t):a.set(l,[t])}return[...a].map(([t,i])=>({dir:t,entries:i}))}const V=()=>{const e=h(d=>d.diffFiles),a=h(d=>d.activeDiffPath),t=h(d=>d.loadDiffFiles),i=h(d=>d.expandDiffPath),[l,o]=x.useState(new Set),[c,n]=x.useState(!1),{toast:m}=$(),f=x.useMemo(()=>Q(e??[]),[e]),r=(e??[]).length>0&&(e??[]).every(d=>d.staged);return{files:e,activePath:a,expandDiffPath:i,groups:f,pending:l,bulkPending:c,allStaged:r,toggleAll:async()=>{const d=(e??[]).filter(j=>j.staged===r);n(!0);try{const p=(await Promise.allSettled(d.map(u=>r?S(u.path):N(u.path)))).filter(u=>u.status==="rejected").length;p>0&&m({title:r?"Unstage failed":"Stage failed",description:`${p} of ${d.length} file(s) could not be updated.`,variant:"error"})}finally{await t(),n(!1)}},toggleStaged:async(d,j)=>{o(p=>new Set(p).add(d.path));try{await(j?N(d.path):S(d.path)),await t()}catch(p){m({title:j?"Stage failed":"Unstage failed",description:A(p.message),variant:"error"})}finally{o(p=>{const u=new Set(p);return u.delete(d.path),u})}}}},X=()=>{const e=h(n=>n.diffFiles),a=h(n=>n.diffLoadFailed),t=h(n=>n.loadDiffFiles),i=h(n=>n.setActiveDiffPath),l=x.useRef(null),o=x.useMemo(()=>(e==null?void 0:e.map(n=>({path:n.path,staged:n.staged})))??[],[e]),c=J(o);return x.useEffect(()=>{t()},[t]),x.useEffect(()=>{let n;const m=E(r=>{r.message==="changed"&&(n&&clearTimeout(n),n=setTimeout(t,250))}),f=()=>t();return window.addEventListener("focus",f),()=>{n&&clearTimeout(n),window.removeEventListener("focus",f),m()}},[t]),x.useEffect(()=>{const n=l.current;if(!n)return;const m=Array.from(n.querySelectorAll("[data-diff-path]"));if(m.length===0)return;const f=new Set,r=new IntersectionObserver(g=>{for(const d of g)d.isIntersecting?f.add(d.target):f.delete(d.target);const w=m.find(d=>f.has(d));w&&i(w.dataset.diffPath??null)},{rootMargin:"0px 0px -70% 0px"});for(const g of m)r.observe(g);return()=>r.disconnect()},[e,i]),{files:e,loadFailed:a,loadDiffFiles:t,sectionsRef:l,commitForm:c}},F=({additions:e,deletions:a})=>s.jsxs("span",{className:"inline-flex shrink-0 gap-1.5 font-mono text-3xs",children:[s.jsxs("span",{className:"text-watercolor-green-dark",children:["+",e]}),s.jsxs("span",{className:"text-watercolor-rose-dark",children:["-",a]})]}),Y=({path:e,prefix:a,className:t=""})=>{const{dir:i,base:l}=b(e),o=`${a??""}${i}`;return s.jsxs("span",{className:`flex min-w-0 items-center font-mono ${t}`,children:[o&&s.jsx("span",{className:"min-w-0 overflow-hidden text-ellipsis whitespace-nowrap opacity-50",children:o}),s.jsx("span",{className:"shrink-0 whitespace-nowrap",children:l})]})},Z={success:"text-watercolor-green-dark",info:"text-watercolor-blue-dark",error:"text-watercolor-rose-dark",warning:"text-watercolor-amber-dark",neutral:"text-watercolor-slate-dark"},ee={A:{letter:"A",label:"Added",variant:"success"},M:{letter:"M",label:"Modified",variant:"info"},D:{letter:"D",label:"Deleted",variant:"error"},R:{letter:"R",label:"Renamed",variant:"info"},C:{letter:"C",label:"Copied",variant:"info"},U:{letter:"U",label:"Conflicted",variant:"warning"}},se=e=>{if(e==="??")return{letter:"??",label:"Untracked",variant:"neutral"};const[a,t]=e,i=a!==" "&&a!=="?"?a:t;return ee[i]??{letter:i??"?",label:i??"Unknown",variant:"neutral"}},P=({status:e,compact:a=!1})=>{const t=se(e);return a?s.jsx("span",{title:t.label,className:`w-4 shrink-0 text-center font-mono text-3xs font-semibold leading-none ${Z[t.variant]??""}`,children:t.letter}):s.jsx("span",{title:t.label,children:s.jsx(v,{size:"small",variant:t.variant,children:t.letter})})},te=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;function ae(e){if(!e)return[];const a=(e.endsWith(`
|
|
2
2
|
`)?e.slice(0,-1):e).split(`
|
|
3
3
|
`),t=[];let i=null,l=0,o=0;for(const c of a){const n=c.match(te);if(n){i={header:c,lines:[]},t.push(i),l=Number(n[1]),o=Number(n[2]);continue}i&&(c.startsWith("\\")||(c.startsWith("+")?(i.lines.push({type:"add",text:c.slice(1),oldLine:null,newLine:o}),o++):c.startsWith("-")?(i.lines.push({type:"remove",text:c.slice(1),oldLine:l,newLine:null}),l++):(i.lines.push({type:"context",text:c.startsWith(" ")?c.slice(1):c,oldLine:l,newLine:o}),l++,o++)))}return t}function ie(e){return e?[{header:"",lines:(e.endsWith(`
|
|
4
4
|
`)?e.slice(0,-1):e).split(`
|
|
5
5
|
`).map((t,i)=>({type:"add",text:t,oldLine:null,newLine:i+1}))}]:[]}const ne=60,le={add:"bg-watercolor-green/[18%] text-watercolor-green-dark",remove:"bg-watercolor-rose/[18%] text-watercolor-rose-dark",context:""},oe={add:"+",remove:"-",context:" "},ce=({entry:e})=>s.jsxs("div",{className:"flex w-full min-w-0 items-center gap-3",children:[s.jsx(P,{status:e.status}),s.jsx(Y,{path:e.path,prefix:e.renameSource?`${e.renameSource} → `:void 0,className:"min-w-0 flex-1 text-xs"}),s.jsxs("span",{className:"flex shrink-0 items-center gap-2",children:[e.staged&&s.jsx(v,{size:"small",children:"staged"}),!e.binary&&s.jsx(F,{additions:e.additions,deletions:e.deletions})]})]}),re=({entry:e})=>{if(e.binary)return s.jsx("p",{className:"m-0 opacity-60",children:"Binary file not shown."});if(e.contentKind==="too-large")return s.jsx("p",{className:"m-0 opacity-60",children:"File too large to preview."});const a=e.contentKind==="raw"?ie(e.patch):ae(e.patch);return a.length===0?e.renameSource?s.jsx("p",{className:"m-0 opacity-60",children:e.additions===0&&e.deletions===0?"Renamed, no content changes.":"Renamed with unrelated content — diff omitted."}):s.jsx("p",{className:"m-0 opacity-60",children:"No changes to preview."}):s.jsx("div",{className:"flex min-w-0 max-w-full flex-col gap-3",children:a.map((t,i)=>s.jsxs("div",{className:"min-w-0 max-w-full",children:[t.header&&s.jsx("div",{className:"mb-1 font-mono text-2xs opacity-50",children:t.header}),s.jsx("pre",{className:"m-0 min-w-0 max-w-full overflow-x-auto font-mono text-2xs leading-normal",children:t.lines.map((l,o)=>s.jsxs("span",{className:`flex ${le[l.type]}`,children:[s.jsxs("span",{className:"inline-flex shrink-0 select-none gap-2 pr-2 opacity-40","aria-hidden":"true",children:[s.jsx("span",{className:"w-8 text-right",children:l.oldLine??""}),s.jsx("span",{className:"w-8 text-right",children:l.newLine??""})]}),s.jsxs("span",{children:[oe[l.type],l.text]})]},`${l.type}-${o}`))})]},`${t.header}-${i}`))})},de=({entry:e})=>{const a=h(c=>c.manuallyExpandedDiffPaths.has(e.path)),t=h(c=>c.manuallyCollapsedDiffPaths.has(e.path)),i=h(c=>c.setDiffCollapsed),l=t||!a&&e.additions+e.deletions>ne,o=e.renameSource?`${e.renameSource} → ${e.path}`:e.path;return s.jsxs("div",{"data-diff-path":e.path,className:"min-w-0 max-w-full scroll-mt-4",children:[s.jsx("h2",{className:"sr-only",children:o}),s.jsx(T,{title:s.jsx(ce,{entry:e}),expanded:!l,onToggle:()=>i(e.path,!l),children:s.jsx(re,{entry:e})})]})},fe="font-handwritten text-xs font-semibold leading-none opacity-[0.45]",me=(e,a)=>{a(e),requestAnimationFrame(()=>{var t;(t=document.querySelector(`[data-diff-path="${CSS.escape(e)}"]`))==null||t.scrollIntoView({behavior:"smooth",block:"start"})})},he=e=>{const[a,t]=e;return a!==" "&&a!=="?"&&t!==" "&&t!=="?"},pe=()=>{const{files:e,activePath:a,expandDiffPath:t,groups:i,pending:l,bulkPending:o,allStaged:c,toggleAll:n,toggleStaged:m}=V();return!e||e.length===0?null:s.jsxs("nav",{"aria-label":"Changed files",className:"flex flex-col",children:[s.jsxs("div",{className:`${fe} flex h-[32px] items-end justify-between pb-1`,children:[s.jsx("span",{children:"Changed files"}),s.jsx(k,{content:c?"Unstage every file":"Stage every file",children:s.jsx(R,{variant:"ghost",size:"tiny",className:"-mb-1",disabled:o,label:c?"Unstage all files":"Stage all files",onClick:n,icon:s.jsx(I,{})})})]}),s.jsx("ul",{className:"m-0 flex list-none flex-col p-0",children:i.map(f=>s.jsxs("li",{children:[s.jsx("div",{className:"flex h-[32px] items-end overflow-hidden text-ellipsis whitespace-nowrap pb-1 font-mono text-3xs leading-none opacity-50",title:f.dir,children:f.dir}),s.jsx("ul",{className:"m-0 flex list-none flex-col p-0",children:f.entries.map(r=>s.jsxs("li",{className:"pc-git-file-row flex h-[32px] min-w-0 items-end gap-1.5 pb-1",children:[s.jsx(_,{checked:r.staged,indeterminate:he(r.status),disabled:l.has(r.path),onChange:g=>m(r,g.target.checked),"aria-label":r.staged?`Unstage ${r.path}`:`Stage ${r.path}`}),s.jsx(P,{status:r.status,compact:!0}),s.jsx(G,{size:"small",active:r.path===a,onClick:()=>me(r.path,t),className:"min-w-0 flex-1 items-end py-0 text-3xs",action:!r.binary&&s.jsx(F,{additions:r.additions,deletions:r.deletions}),children:s.jsx("span",{className:"block min-w-0 overflow-hidden text-ellipsis whitespace-nowrap font-mono text-3xs",title:r.path,children:b(r.path).base})})]},r.path))})]},f.dir))})]})},ue=()=>{const{files:e,loadFailed:a,loadDiffFiles:t,sectionsRef:i,commitForm:l}=X(),o="min-h-page",c=s.jsxs("div",{className:"mb-4 flex flex-wrap items-center justify-between gap-x-4 gap-y-2",children:[s.jsx(z,{className:"!mb-0",children:"Git"}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(B,{}),s.jsx(H,{})]})]});return a?s.jsxs("div",{children:[c,s.jsxs("div",{className:`${o} flex flex-col items-start gap-3`,children:[s.jsx("p",{className:"opacity-50 m-0",children:"Couldn't load the working-tree diff."}),s.jsx(C,{variant:"secondary",size:"small",onClick:t,children:"Retry"})]})]}):e?s.jsxs("div",{children:[c,e.length===0?s.jsx("div",{className:o,children:s.jsx("p",{className:"opacity-50",children:"No changed files."})}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"sticky top-0 z-10 mb-4 flex items-center gap-2 py-2",children:[s.jsx("div",{className:"flex-1",children:s.jsx(U,{state:l,filesEmpty:!1})}),s.jsx(W,{state:l,filesEmpty:!1})]}),s.jsx("div",{ref:i,className:`flex min-w-0 flex-col gap-6 ${o}`,children:e.map((n,m)=>s.jsxs(x.Fragment,{children:[m>0&&s.jsx(O,{}),s.jsx(de,{entry:n})]},n.path))})]})]}):s.jsxs("div",{children:[c,s.jsx("div",{className:o,children:s.jsx(M,{label:"Loading the working-tree diff…"})})]})};export{pe as GitFileList,ue as GitPage};
|
|
6
|
-
//# sourceMappingURL=index-
|
|
6
|
+
//# sourceMappingURL=index-DdiMHcBX.js.map
|