@dendelion/paper-camp 0.23.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/app/assets/{index-Bq3vqVCv.js → index-BHPCJror.js} +2 -2
  2. package/dist/app/assets/{index-Bq3vqVCv.js.map → index-BHPCJror.js.map} +1 -1
  3. package/dist/app/assets/{index-DdiMHcBX.js → index-DG2ri3IF.js} +2 -2
  4. package/dist/app/assets/{index-DdiMHcBX.js.map → index-DG2ri3IF.js.map} +1 -1
  5. package/dist/app/assets/{index-siPA4naJ.js → index-D_2SBGCS.js} +2 -2
  6. package/dist/app/assets/{index-siPA4naJ.js.map → index-D_2SBGCS.js.map} +1 -1
  7. package/dist/app/assets/{index-DRVlFKav.js → index-DePKcR5b.js} +2 -2
  8. package/dist/app/assets/{index-DRVlFKav.js.map → index-DePKcR5b.js.map} +1 -1
  9. package/dist/app/assets/{index-e0PzaU7Z.js → index-Do4T60UL.js} +2 -2
  10. package/dist/app/assets/{index-e0PzaU7Z.js.map → index-Do4T60UL.js.map} +1 -1
  11. package/dist/app/assets/{index-8_65UWKz.js → index-DxehIB14.js} +2 -2
  12. package/dist/app/assets/{index-8_65UWKz.js.map → index-DxehIB14.js.map} +1 -1
  13. package/dist/app/assets/{index-Co_9IFtJ.js → index-SD_x9Z5F.js} +2 -2
  14. package/dist/app/assets/{index-Co_9IFtJ.js.map → index-SD_x9Z5F.js.map} +1 -1
  15. package/dist/app/assets/{index-Ds_iTHi0.js → index-mmzETz6G.js} +2 -2
  16. package/dist/app/assets/{index-Ds_iTHi0.js.map → index-mmzETz6G.js.map} +1 -1
  17. package/dist/app/assets/{main-C0TBUyF9.js → main-DiplEwD3.js} +120 -120
  18. package/dist/app/assets/main-DiplEwD3.js.map +1 -0
  19. package/dist/app/assets/main-DoJre3S-.css +1 -0
  20. package/dist/app/assets/{sidebar-section-D0b_1DUA.js → sidebar-section-RSOrdZKe.js} +2 -2
  21. package/dist/app/assets/{sidebar-section-D0b_1DUA.js.map → sidebar-section-RSOrdZKe.js.map} +1 -1
  22. package/dist/app/index.html +2 -2
  23. package/dist/cli/index.js +234 -236
  24. package/dist/cli/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/app/assets/main-BAlJ6w7u.css +0 -1
  27. package/dist/app/assets/main-C0TBUyF9.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index-e0PzaU7Z.js","sources":["../../../src/app/features/issues/helpers.ts","../../../src/core/issues.ts","../../../src/app/features/issues/hooks/use-issues-page.ts","../../../src/app/features/issues/issue-row.tsx","../../../src/app/features/issues/issues-page.tsx"],"sourcesContent":["import { isClosedEntity } from '@/core/status';\nimport type { EntityStatus, Issue } from '@/types/index';\n\nexport const promoteLabel = (\n issue: Issue,\n entities: { id: string; status?: EntityStatus; archived?: boolean }[],\n): string => {\n const parent = issue.entityId ? entities.find((p) => p.id === issue.entityId) : undefined;\n if (!parent) return 'Promote to idea';\n return isClosedEntity(parent) ? 'Promote to fix' : `Add to ${parent.id}'s fixes`;\n};\n\nexport const formatIssueAge = (occurredAt: string | undefined): string => {\n if (!occurredAt) return '';\n const ageDays = Math.floor((Date.now() - Date.parse(occurredAt)) / 86_400_000);\n if (!Number.isFinite(ageDays)) return '';\n if (ageDays <= 0) return 'today';\n return ageDays === 1 ? '1 day ago' : `${ageDays} days ago`;\n};\n\n// Missing timestamps (a live PR review decision) sort last, not first.\nconst NO_TIMESTAMP = '9999-12-31T23:59:59.999Z';\nexport const sortIssuesByAge = <T extends { occurredAt?: string }>(issues: T[]): T[] =>\n [...issues].sort((a, b) =>\n (a.occurredAt ?? NO_TIMESTAMP).localeCompare(b.occurredAt ?? NO_TIMESTAMP),\n );\n","import type {\n DeskCheckState,\n EntityStatus,\n GitSyncFailure,\n Issue,\n PhaseItem,\n PrInfo,\n TaskLogEntry,\n ThreadMessage,\n} from '../types/index';\n\nconst issueId = (sourceKind: Issue['sourceKind'], sourceKey: string): string =>\n `${sourceKind}:${sourceKey}`;\n\n/** A failed agent run (`outcome: 'error'` in tasks.log), keyed by the plan it ran\n * against so a repeat failure on the same plan continues one issue. Runs with no\n * `planId` (e.g. a batch task) key off their own task id instead. */\nexport function collectAgentRunIssues(taskLog: TaskLogEntry[]): Issue[] {\n return taskLog\n .filter((entry) => entry.outcome === 'error')\n .map((entry) => {\n const sourceKey = entry.planId ?? entry.id;\n return {\n id: issueId('agent-run', sourceKey),\n sourceKind: 'agent-run',\n sourceKey,\n entityId: entry.planId,\n entityTitle: entry.planTitle,\n title: `${entry.planTitle} — ${entry.taskKind} failed`,\n reason: entry.reason ?? 'The run ended in error with no reason recorded.',\n occurredAt: entry.endedAt,\n thread: [],\n status: 'open',\n };\n });\n}\n\n/** A red project check (Quality/Tests/Consistency — Docs keeps its own browse flow\n * and is excluded, same carve-out as `buildCheckFixes`), keyed by check name. */\nexport function collectCheckIssues(checks: DeskCheckState[]): Issue[] {\n return checks\n .filter((check) => check.status === 'fail')\n .map((check) => ({\n id: issueId('check', check.name),\n sourceKind: 'check',\n sourceKey: check.name,\n title: `\"${check.name}\" check is failing`,\n reason: `The command was \\`${check.cmd}\\`.`,\n output: check.output || undefined,\n occurredAt: check.lastRun ?? undefined,\n thread: [],\n status: 'open',\n }));\n}\n\n/** A PR review that requested changes, keyed by PR number + head sha so a fresh\n * push that gets re-reviewed continues the same issue rather than a new one. */\nexport function collectPrReviewIssues(\n entities: { id: string; title: string; pr?: PrInfo }[],\n): Issue[] {\n return entities\n .filter((entity) => entity.pr?.reviewDecision === 'changes-requested')\n .map((entity) => {\n const pr = entity.pr as PrInfo;\n const sourceKey = `${pr.number}:${pr.headSha ?? 'unknown'}`;\n return {\n id: issueId('pr-review', sourceKey),\n sourceKind: 'pr-review',\n sourceKey,\n entityId: entity.id,\n entityTitle: entity.title,\n title: `${entity.title} — PR review requested changes`,\n reason: `PR #${pr.number} has unresolved change requests.`,\n thread: [],\n status: 'open',\n };\n });\n}\n\n/** A git rebase/sync failure on the currently checked-out branch, keyed by branch +\n * the ref it failed to reconcile against so a repeat failure of the same rebase\n * continues one issue. */\nexport function collectSyncIssues(\n failure: GitSyncFailure | undefined,\n branch: string,\n entity?: { id: string; title: string },\n): Issue[] {\n if (!failure) return [];\n const sourceKey = `${branch}:${failure.conflictRef ?? failure.stage}`;\n return [\n {\n id: issueId('sync', sourceKey),\n sourceKind: 'sync',\n sourceKey,\n entityId: entity?.id,\n entityTitle: entity?.title,\n title: `${branch} failed to sync`,\n reason: failure.message,\n output: failure.conflictedFiles?.length\n ? `Conflicted files: ${failure.conflictedFiles.join(', ')}`\n : undefined,\n thread: [],\n status: 'open',\n },\n ];\n}\n\n/** Reconciles freshly-collected issues (every one currently detected as failing, so\n * always 'open') against the existing set, keyed by `id`. A repeat failure of the\n * same thing replaces that issue's title/reason/output in place, preserving its\n * position and its thread. An existing issue absent from `fresh` has stopped\n * failing, so it closes — unless it was promoted to a fix entity that hasn't\n * shipped yet (`fixEntityStatus` reports its status), which keeps it open until\n * that entity reaches 'done'. Closure is derived here, never a mark-read button. */\nexport function reconcileIssues(\n existing: Issue[],\n fresh: Issue[],\n fixEntityStatus: (fixId: string) => EntityStatus | undefined = () => undefined,\n): Issue[] {\n const freshById = new Map(fresh.map((issue) => [issue.id, issue]));\n const merged = existing.map((issue): Issue => {\n if (issue.promotedFixId && fixEntityStatus(issue.promotedFixId) === 'done') {\n freshById.delete(issue.id);\n return { ...issue, status: 'closed' };\n }\n const next = freshById.get(issue.id);\n if (!next) return { ...issue, status: 'closed' };\n freshById.delete(issue.id);\n return { ...next, thread: issue.thread, promotedFixId: issue.promotedFixId };\n });\n return [...merged, ...freshById.values()];\n}\n\n/** A spawned fix/idea entity from Promote (IDEA-192 phase 6) stamps `issueSource`;\n * an open parent instead gets a `source: 'issue'` item appended to its inline Fixes list. */\ntype PromotionTarget = {\n id?: string;\n status?: EntityStatus;\n issueSource?: string;\n fixes?: PhaseItem[];\n};\n\n/** Matches every issue against the corpus for a prior Promote (IDEA-192 phase 6),\n * re-derived from disk on every read since issues carry no store of their own —\n * the promotion survives a reload because git holds it, not a stored issue. Once\n * matched, an issue whose target has shipped (`status: 'done'`) closes, even if\n * its original source is still detected as failing. */\nexport function applyPromotions(issues: Issue[], entities: PromotionTarget[]): Issue[] {\n return issues\n .map((issue): Issue => {\n const spawned = entities.find((e) => e.issueSource === issue.id);\n if (spawned?.id) return { ...issue, promotedFixId: spawned.id };\n const parent = issue.entityId ? entities.find((e) => e.id === issue.entityId) : undefined;\n const appended = parent?.fixes?.some((f) => f.source === 'issue' && f.text === issue.title);\n return appended && parent?.id ? { ...issue, promotedFixId: parent.id } : issue;\n })\n .filter((issue) => {\n const target = issue.promotedFixId && entities.find((e) => e.id === issue.promotedFixId);\n return !(target && target.status === 'done');\n });\n}\n\n/** \"Fix it here\" (IDEA-192) launches an `issue-fix` task carrying the issue's id;\n * this reconstructs that issue's thread from every tasks.log entry carrying it,\n * oldest first, so a repeat fix attempt reads as a continuation, not a fresh mystery. */\nexport function issueThreadFromTaskLog(issue: Issue, taskLog: TaskLogEntry[]): ThreadMessage[] {\n return taskLog\n .filter((entry) => entry.issueId === issue.id)\n .sort((a, b) => a.endedAt.localeCompare(b.endedAt))\n .map((entry) => ({\n kind: 'log',\n date: entry.endedAt.slice(0, 10),\n from: 'agent',\n text:\n entry.outcome === 'done'\n ? 'Ran a fix agent — finished.'\n : entry.outcome === 'error'\n ? `Ran a fix agent — failed: ${entry.reason ?? 'no reason recorded'}`\n : 'Ran a fix agent — superseded by a newer run.',\n }));\n}\n","import { useOpenEntity } from '@/app/hooks';\nimport { useDeskChecks } from '@/app/hooks/use-desk-checks';\nimport { promoteIssue } from '@/app/services/agent-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport {\n applyPromotions,\n collectAgentRunIssues,\n collectCheckIssues,\n collectPrReviewIssues,\n issueThreadFromTaskLog,\n} from '@/core/issues';\nimport type { Issue, PlanEntry } from '@/types/index';\nimport { useEffect, useMemo, useState } from 'react';\nimport { sortIssuesByAge } from '../helpers';\n\nexport const useIssuesPage = () => {\n const [expandedId, setExpandedId] = useState<string | null>(null);\n const [promotingId, setPromotingId] = useState<string | null>(null);\n const taskLog = useAppStore((s) => s.taskLog);\n const taskLogLoading = useAppStore((s) => s.taskLogLoading);\n const loadTaskLog = useAppStore((s) => s.loadTaskLog);\n const plans = useAppStore((s) => s.plans);\n const ideaEntries = useAppStore((s) => s.ideaEntries);\n const agentStatus = useAppStore((s) => s.agentStatus);\n const launchIssueFix = useAppStore((s) => s.launchIssueFix);\n const loadPlans = useAppStore((s) => s.loadPlans);\n const loadIdeas = useAppStore((s) => s.loadIdeas);\n const { checks } = useDeskChecks();\n const openEntity = useOpenEntity();\n\n useEffect(() => {\n loadTaskLog();\n }, [loadTaskLog]);\n\n const planEntities = useMemo(\n () => (plans?.entries ?? []).filter((p): p is PlanEntry & { id: string } => p.id != null),\n [plans],\n );\n\n const entities = useMemo(\n () =>\n [...planEntities, ...ideaEntries].filter(\n (entity): entity is typeof entity & { id: string } => entity.id != null,\n ),\n [planEntities, ideaEntries],\n );\n\n const issues = useMemo(() => {\n const collected = sortIssuesByAge([\n ...collectAgentRunIssues(taskLog),\n ...collectCheckIssues(checks),\n ...collectPrReviewIssues(entities),\n ]);\n return applyPromotions(collected, entities).map((issue) => ({\n ...issue,\n thread: issueThreadFromTaskLog(issue, taskLog),\n }));\n }, [taskLog, checks, entities]);\n\n const handlePromote = async (issue: Issue) => {\n setPromotingId(issue.id);\n try {\n await promoteIssue(issue);\n await Promise.all([loadPlans(), loadIdeas()]);\n } finally {\n setPromotingId(null);\n }\n };\n\n const fixingIssueId = agentStatus.find(\n (t) =>\n t.taskKind === 'issue-fix' &&\n t.status !== 'done' &&\n t.status !== 'error' &&\n t.status !== 'superseded',\n )?.issueId;\n\n return {\n issues,\n entities,\n taskLogLoading,\n expandedId,\n setExpandedId,\n openEntity,\n fixingIssueId,\n launchIssueFix,\n promotingId,\n handlePromote,\n };\n};\n","import { FeedbackThread, PlanIdStamp } from '@/app/features/plans/components';\nimport type { Issue } from '@/types/index';\nimport { Button, Spinner } from '@dendelion/paper-ui';\nimport { formatIssueAge } from './helpers';\n\ninterface IssueRowProps {\n issue: Issue;\n expanded: boolean;\n onToggle: () => void;\n onOpen?: () => void;\n fixing: boolean;\n fixDisabled: boolean;\n onFix: () => Promise<void>;\n promoteLabel: string;\n promoting: boolean;\n promoteDisabled: boolean;\n onPromote: () => Promise<void>;\n onOpenPromoted?: () => void;\n}\n\nexport const IssueRow = ({\n issue,\n expanded,\n onToggle,\n onOpen,\n fixing,\n fixDisabled,\n onFix,\n promoteLabel,\n promoting,\n promoteDisabled,\n onPromote,\n onOpenPromoted,\n}: IssueRowProps) => {\n const handleFix = async () => {\n try {\n await onFix();\n } catch (err) {\n alert((err as Error).message);\n }\n };\n\n const handlePromote = async () => {\n try {\n await onPromote();\n } catch (err) {\n alert((err as Error).message);\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 {onOpen && (\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${issue.entityTitle}`}\n className=\"bg-transparent border-none p-0 cursor-pointer\"\n >\n <PlanIdStamp id={issue.entityId} />\n </button>\n )}\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 {issue.title}\n </button>\n <span className=\"opacity-50 text-xs whitespace-nowrap\">\n {formatIssueAge(issue.occurredAt)}\n </span>\n </div>\n {expanded && (\n <div className=\"flex flex-col gap-3 pb-4\">\n <p>{issue.reason}</p>\n {issue.output && (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap text-left font-mono text-2xs opacity-70\">\n {issue.output}\n </pre>\n )}\n {issue.thread.length > 0 && (\n <FeedbackThread messages={issue.thread} undo={null} undoing={false} onUndo={() => {}} />\n )}\n <div className=\"flex items-center gap-3\">\n {issue.promotedFixId ? (\n <Button size=\"small\" onClick={onOpenPromoted}>\n View {issue.promotedFixId}\n </Button>\n ) : (\n <>\n <Button size=\"small\" onClick={handleFix} disabled={fixDisabled}>\n {fixing ? 'Fixing…' : 'Fix it here'}\n </Button>\n {fixing && <Spinner size=\"small\" label=\"Agent fixing…\" />}\n <Button size=\"small\" onClick={handlePromote} disabled={promoteDisabled}>\n {promoting ? 'Promoting…' : promoteLabel}\n </Button>\n {promoting && <Spinner size=\"small\" label=\"Promoting…\" />}\n </>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { promoteLabel } from './helpers';\nimport { useIssuesPage } from './hooks';\nimport { IssueRow } from './issue-row';\n\nexport const IssuesPage = () => {\n const {\n issues,\n entities,\n taskLogLoading,\n expandedId,\n setExpandedId,\n openEntity,\n fixingIssueId,\n launchIssueFix,\n promotingId,\n handlePromote,\n } = useIssuesPage();\n\n return (\n <div>\n <PageTitle>Issues</PageTitle>\n <p className=\"opacity-50 mb-6\">\n Every failing agent run, red check, and PR review, oldest first.\n </p>\n {taskLogLoading && issues.length === 0 && <p className=\"opacity-50\">Loading…</p>}\n {!taskLogLoading && issues.length === 0 && (\n <p className=\"opacity-50\">Nothing broken right now.</p>\n )}\n {issues.length > 0 && (\n <div className=\"flex flex-col\">\n {issues.map((issue) => {\n const promotedTitle = issue.promotedFixId\n ? (entities.find((p) => p.id === issue.promotedFixId)?.title ?? issue.promotedFixId)\n : undefined;\n return (\n <IssueRow\n key={issue.id}\n issue={issue}\n expanded={expandedId === issue.id}\n onToggle={() => setExpandedId((cur) => (cur === issue.id ? null : issue.id))}\n onOpen={\n issue.entityId && issue.entityTitle\n ? () => openEntity(issue.entityId as string, issue.entityTitle as string)\n : undefined\n }\n fixing={fixingIssueId === issue.id}\n fixDisabled={fixingIssueId !== undefined}\n onFix={() => launchIssueFix(issue.id, issue.title, issue.reason, issue.output)}\n promoteLabel={promoteLabel(issue, entities)}\n promoting={promotingId === issue.id}\n promoteDisabled={promotingId !== null}\n onPromote={() => handlePromote(issue)}\n onOpenPromoted={\n issue.promotedFixId\n ? () => openEntity(issue.promotedFixId as string, promotedTitle as string)\n : undefined\n }\n />\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"names":["promoteLabel","issue","entities","parent","p","isClosedEntity","formatIssueAge","occurredAt","ageDays","NO_TIMESTAMP","sortIssuesByAge","issues","a","b","issueId","sourceKind","sourceKey","collectAgentRunIssues","taskLog","entry","collectCheckIssues","checks","check","collectPrReviewIssues","entity","_a","pr","applyPromotions","spawned","e","f","target","issueThreadFromTaskLog","useIssuesPage","expandedId","setExpandedId","useState","promotingId","setPromotingId","useAppStore","s","taskLogLoading","loadTaskLog","plans","ideaEntries","agentStatus","launchIssueFix","loadPlans","loadIdeas","useDeskChecks","openEntity","useOpenEntity","useEffect","planEntities","useMemo","collected","handlePromote","promoteIssue","fixingIssueId","t","IssueRow","expanded","onToggle","onOpen","fixing","fixDisabled","onFix","promoting","promoteDisabled","onPromote","onOpenPromoted","handleFix","err","jsxs","jsx","PlanIdStamp","FeedbackThread","Button","Fragment","Spinner","IssuesPage","PageTitle","promotedTitle","cur"],"mappings":"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"}
1
+ {"version":3,"file":"index-Do4T60UL.js","sources":["../../../src/app/features/issues/helpers.ts","../../../src/core/issues.ts","../../../src/app/features/issues/hooks/use-issues-page.ts","../../../src/app/features/issues/issue-row.tsx","../../../src/app/features/issues/issues-page.tsx"],"sourcesContent":["import { isClosedEntity } from '@/core/status';\nimport type { EntityStatus, Issue } from '@/types/index';\n\nexport const promoteLabel = (\n issue: Issue,\n entities: { id: string; status?: EntityStatus; archived?: boolean }[],\n): string => {\n const parent = issue.entityId ? entities.find((p) => p.id === issue.entityId) : undefined;\n if (!parent) return 'Promote to idea';\n return isClosedEntity(parent) ? 'Promote to fix' : `Add to ${parent.id}'s fixes`;\n};\n\nexport const formatIssueAge = (occurredAt: string | undefined): string => {\n if (!occurredAt) return '';\n const ageDays = Math.floor((Date.now() - Date.parse(occurredAt)) / 86_400_000);\n if (!Number.isFinite(ageDays)) return '';\n if (ageDays <= 0) return 'today';\n return ageDays === 1 ? '1 day ago' : `${ageDays} days ago`;\n};\n\n// Missing timestamps (a live PR review decision) sort last, not first.\nconst NO_TIMESTAMP = '9999-12-31T23:59:59.999Z';\nexport const sortIssuesByAge = <T extends { occurredAt?: string }>(issues: T[]): T[] =>\n [...issues].sort((a, b) =>\n (a.occurredAt ?? NO_TIMESTAMP).localeCompare(b.occurredAt ?? NO_TIMESTAMP),\n );\n","import type {\n DeskCheckState,\n EntityStatus,\n GitSyncFailure,\n Issue,\n PhaseItem,\n PrInfo,\n TaskLogEntry,\n ThreadMessage,\n} from '../types/index';\n\nconst issueId = (sourceKind: Issue['sourceKind'], sourceKey: string): string =>\n `${sourceKind}:${sourceKey}`;\n\n/** A failed agent run (`outcome: 'error'` in tasks.log), keyed by the plan it ran\n * against so a repeat failure on the same plan continues one issue. Runs with no\n * `planId` (e.g. a batch task) key off their own task id instead. */\nexport function collectAgentRunIssues(taskLog: TaskLogEntry[]): Issue[] {\n return taskLog\n .filter((entry) => entry.outcome === 'error')\n .map((entry) => {\n const sourceKey = entry.planId ?? entry.id;\n return {\n id: issueId('agent-run', sourceKey),\n sourceKind: 'agent-run',\n sourceKey,\n entityId: entry.planId,\n entityTitle: entry.planTitle,\n title: `${entry.planTitle} — ${entry.taskKind} failed`,\n reason: entry.reason ?? 'The run ended in error with no reason recorded.',\n occurredAt: entry.endedAt,\n thread: [],\n status: 'open',\n };\n });\n}\n\n/** A red project check (Quality/Tests/Consistency — Docs keeps its own browse flow\n * and is excluded, same carve-out as `buildCheckFixes`), keyed by check name. */\nexport function collectCheckIssues(checks: DeskCheckState[]): Issue[] {\n return checks\n .filter((check) => check.status === 'fail')\n .map((check) => ({\n id: issueId('check', check.name),\n sourceKind: 'check',\n sourceKey: check.name,\n title: `\"${check.name}\" check is failing`,\n reason: `The command was \\`${check.cmd}\\`.`,\n output: check.output || undefined,\n occurredAt: check.lastRun ?? undefined,\n thread: [],\n status: 'open',\n }));\n}\n\n/** A PR review that requested changes, keyed by PR number + head sha so a fresh\n * push that gets re-reviewed continues the same issue rather than a new one. */\nexport function collectPrReviewIssues(\n entities: { id: string; title: string; pr?: PrInfo }[],\n): Issue[] {\n return entities\n .filter((entity) => entity.pr?.reviewDecision === 'changes-requested')\n .map((entity) => {\n const pr = entity.pr as PrInfo;\n const sourceKey = `${pr.number}:${pr.headSha ?? 'unknown'}`;\n return {\n id: issueId('pr-review', sourceKey),\n sourceKind: 'pr-review',\n sourceKey,\n entityId: entity.id,\n entityTitle: entity.title,\n title: `${entity.title} — PR review requested changes`,\n reason: `PR #${pr.number} has unresolved change requests.`,\n thread: [],\n status: 'open',\n };\n });\n}\n\n/** A git rebase/sync failure on the currently checked-out branch, keyed by branch +\n * the ref it failed to reconcile against so a repeat failure of the same rebase\n * continues one issue. */\nexport function collectSyncIssues(\n failure: GitSyncFailure | undefined,\n branch: string,\n entity?: { id: string; title: string },\n): Issue[] {\n if (!failure) return [];\n const sourceKey = `${branch}:${failure.conflictRef ?? failure.stage}`;\n return [\n {\n id: issueId('sync', sourceKey),\n sourceKind: 'sync',\n sourceKey,\n entityId: entity?.id,\n entityTitle: entity?.title,\n title: `${branch} failed to sync`,\n reason: failure.message,\n output: failure.conflictedFiles?.length\n ? `Conflicted files: ${failure.conflictedFiles.join(', ')}`\n : undefined,\n thread: [],\n status: 'open',\n },\n ];\n}\n\n/** Reconciles freshly-collected issues (every one currently detected as failing, so\n * always 'open') against the existing set, keyed by `id`. A repeat failure of the\n * same thing replaces that issue's title/reason/output in place, preserving its\n * position and its thread. An existing issue absent from `fresh` has stopped\n * failing, so it closes — unless it was promoted to a fix entity that hasn't\n * shipped yet (`fixEntityStatus` reports its status), which keeps it open until\n * that entity reaches 'done'. Closure is derived here, never a mark-read button. */\nexport function reconcileIssues(\n existing: Issue[],\n fresh: Issue[],\n fixEntityStatus: (fixId: string) => EntityStatus | undefined = () => undefined,\n): Issue[] {\n const freshById = new Map(fresh.map((issue) => [issue.id, issue]));\n const merged = existing.map((issue): Issue => {\n if (issue.promotedFixId && fixEntityStatus(issue.promotedFixId) === 'done') {\n freshById.delete(issue.id);\n return { ...issue, status: 'closed' };\n }\n const next = freshById.get(issue.id);\n if (!next) return { ...issue, status: 'closed' };\n freshById.delete(issue.id);\n return { ...next, thread: issue.thread, promotedFixId: issue.promotedFixId };\n });\n return [...merged, ...freshById.values()];\n}\n\n/** A spawned fix/idea entity from Promote (IDEA-192 phase 6) stamps `issueSource`;\n * an open parent instead gets a `source: 'issue'` item appended to its inline Fixes list. */\ntype PromotionTarget = {\n id?: string;\n status?: EntityStatus;\n issueSource?: string;\n fixes?: PhaseItem[];\n};\n\n/** Matches every issue against the corpus for a prior Promote (IDEA-192 phase 6),\n * re-derived from disk on every read since issues carry no store of their own —\n * the promotion survives a reload because git holds it, not a stored issue. Once\n * matched, an issue whose target has shipped (`status: 'done'`) closes, even if\n * its original source is still detected as failing. */\nexport function applyPromotions(issues: Issue[], entities: PromotionTarget[]): Issue[] {\n return issues\n .map((issue): Issue => {\n const spawned = entities.find((e) => e.issueSource === issue.id);\n if (spawned?.id) return { ...issue, promotedFixId: spawned.id };\n const parent = issue.entityId ? entities.find((e) => e.id === issue.entityId) : undefined;\n const appended = parent?.fixes?.some((f) => f.source === 'issue' && f.text === issue.title);\n return appended && parent?.id ? { ...issue, promotedFixId: parent.id } : issue;\n })\n .filter((issue) => {\n const target = issue.promotedFixId && entities.find((e) => e.id === issue.promotedFixId);\n return !(target && target.status === 'done');\n });\n}\n\n/** \"Fix it here\" (IDEA-192) launches an `issue-fix` task carrying the issue's id;\n * this reconstructs that issue's thread from every tasks.log entry carrying it,\n * oldest first, so a repeat fix attempt reads as a continuation, not a fresh mystery. */\nexport function issueThreadFromTaskLog(issue: Issue, taskLog: TaskLogEntry[]): ThreadMessage[] {\n return taskLog\n .filter((entry) => entry.issueId === issue.id)\n .sort((a, b) => a.endedAt.localeCompare(b.endedAt))\n .map((entry) => ({\n kind: 'log',\n date: entry.endedAt.slice(0, 10),\n from: 'agent',\n text:\n entry.outcome === 'done'\n ? 'Ran a fix agent — finished.'\n : entry.outcome === 'error'\n ? `Ran a fix agent — failed: ${entry.reason ?? 'no reason recorded'}`\n : 'Ran a fix agent — superseded by a newer run.',\n }));\n}\n","import { useOpenEntity } from '@/app/hooks';\nimport { useDeskChecks } from '@/app/hooks/use-desk-checks';\nimport { promoteIssue } from '@/app/services/agent-api';\nimport { useAppStore } from '@/app/stores/app-store';\nimport {\n applyPromotions,\n collectAgentRunIssues,\n collectCheckIssues,\n collectPrReviewIssues,\n issueThreadFromTaskLog,\n} from '@/core/issues';\nimport type { Issue, PlanEntry } from '@/types/index';\nimport { useEffect, useMemo, useState } from 'react';\nimport { sortIssuesByAge } from '../helpers';\n\nexport const useIssuesPage = () => {\n const [expandedId, setExpandedId] = useState<string | null>(null);\n const [promotingId, setPromotingId] = useState<string | null>(null);\n const taskLog = useAppStore((s) => s.taskLog);\n const taskLogLoading = useAppStore((s) => s.taskLogLoading);\n const loadTaskLog = useAppStore((s) => s.loadTaskLog);\n const plans = useAppStore((s) => s.plans);\n const ideaEntries = useAppStore((s) => s.ideaEntries);\n const agentStatus = useAppStore((s) => s.agentStatus);\n const launchIssueFix = useAppStore((s) => s.launchIssueFix);\n const loadPlans = useAppStore((s) => s.loadPlans);\n const loadIdeas = useAppStore((s) => s.loadIdeas);\n const { checks } = useDeskChecks();\n const openEntity = useOpenEntity();\n\n useEffect(() => {\n loadTaskLog();\n }, [loadTaskLog]);\n\n const planEntities = useMemo(\n () => (plans?.entries ?? []).filter((p): p is PlanEntry & { id: string } => p.id != null),\n [plans],\n );\n\n const entities = useMemo(\n () =>\n [...planEntities, ...ideaEntries].filter(\n (entity): entity is typeof entity & { id: string } => entity.id != null,\n ),\n [planEntities, ideaEntries],\n );\n\n const issues = useMemo(() => {\n const collected = sortIssuesByAge([\n ...collectAgentRunIssues(taskLog),\n ...collectCheckIssues(checks),\n ...collectPrReviewIssues(entities),\n ]);\n return applyPromotions(collected, entities).map((issue) => ({\n ...issue,\n thread: issueThreadFromTaskLog(issue, taskLog),\n }));\n }, [taskLog, checks, entities]);\n\n const handlePromote = async (issue: Issue) => {\n setPromotingId(issue.id);\n try {\n await promoteIssue(issue);\n await Promise.all([loadPlans(), loadIdeas()]);\n } finally {\n setPromotingId(null);\n }\n };\n\n const fixingIssueId = agentStatus.find(\n (t) =>\n t.taskKind === 'issue-fix' &&\n t.status !== 'done' &&\n t.status !== 'error' &&\n t.status !== 'superseded',\n )?.issueId;\n\n return {\n issues,\n entities,\n taskLogLoading,\n expandedId,\n setExpandedId,\n openEntity,\n fixingIssueId,\n launchIssueFix,\n promotingId,\n handlePromote,\n };\n};\n","import { FeedbackThread, PlanIdStamp } from '@/app/features/plans/components';\nimport type { Issue } from '@/types/index';\nimport { Button, Spinner } from '@dendelion/paper-ui';\nimport { formatIssueAge } from './helpers';\n\ninterface IssueRowProps {\n issue: Issue;\n expanded: boolean;\n onToggle: () => void;\n onOpen?: () => void;\n fixing: boolean;\n fixDisabled: boolean;\n onFix: () => Promise<void>;\n promoteLabel: string;\n promoting: boolean;\n promoteDisabled: boolean;\n onPromote: () => Promise<void>;\n onOpenPromoted?: () => void;\n}\n\nexport const IssueRow = ({\n issue,\n expanded,\n onToggle,\n onOpen,\n fixing,\n fixDisabled,\n onFix,\n promoteLabel,\n promoting,\n promoteDisabled,\n onPromote,\n onOpenPromoted,\n}: IssueRowProps) => {\n const handleFix = async () => {\n try {\n await onFix();\n } catch (err) {\n alert((err as Error).message);\n }\n };\n\n const handlePromote = async () => {\n try {\n await onPromote();\n } catch (err) {\n alert((err as Error).message);\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 {onOpen && (\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${issue.entityTitle}`}\n className=\"bg-transparent border-none p-0 cursor-pointer\"\n >\n <PlanIdStamp id={issue.entityId} />\n </button>\n )}\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 {issue.title}\n </button>\n <span className=\"opacity-50 text-xs whitespace-nowrap\">\n {formatIssueAge(issue.occurredAt)}\n </span>\n </div>\n {expanded && (\n <div className=\"flex flex-col gap-3 pb-4\">\n <p>{issue.reason}</p>\n {issue.output && (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap text-left font-mono text-2xs opacity-70\">\n {issue.output}\n </pre>\n )}\n {issue.thread.length > 0 && (\n <FeedbackThread messages={issue.thread} undo={null} undoing={false} onUndo={() => {}} />\n )}\n <div className=\"flex items-center gap-3\">\n {issue.promotedFixId ? (\n <Button size=\"small\" onClick={onOpenPromoted}>\n View {issue.promotedFixId}\n </Button>\n ) : (\n <>\n <Button size=\"small\" onClick={handleFix} disabled={fixDisabled}>\n {fixing ? 'Fixing…' : 'Fix it here'}\n </Button>\n {fixing && <Spinner size=\"small\" label=\"Agent fixing…\" />}\n <Button size=\"small\" onClick={handlePromote} disabled={promoteDisabled}>\n {promoting ? 'Promoting…' : promoteLabel}\n </Button>\n {promoting && <Spinner size=\"small\" label=\"Promoting…\" />}\n </>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { PageTitle } from '@/app/components/page-title';\nimport { promoteLabel } from './helpers';\nimport { useIssuesPage } from './hooks';\nimport { IssueRow } from './issue-row';\n\nexport const IssuesPage = () => {\n const {\n issues,\n entities,\n taskLogLoading,\n expandedId,\n setExpandedId,\n openEntity,\n fixingIssueId,\n launchIssueFix,\n promotingId,\n handlePromote,\n } = useIssuesPage();\n\n return (\n <div>\n <PageTitle>Issues</PageTitle>\n <p className=\"opacity-50 mb-6\">\n Every failing agent run, red check, and PR review, oldest first.\n </p>\n {taskLogLoading && issues.length === 0 && <p className=\"opacity-50\">Loading…</p>}\n {!taskLogLoading && issues.length === 0 && (\n <p className=\"opacity-50\">Nothing broken right now.</p>\n )}\n {issues.length > 0 && (\n <div className=\"flex flex-col\">\n {issues.map((issue) => {\n const promotedTitle = issue.promotedFixId\n ? (entities.find((p) => p.id === issue.promotedFixId)?.title ?? issue.promotedFixId)\n : undefined;\n return (\n <IssueRow\n key={issue.id}\n issue={issue}\n expanded={expandedId === issue.id}\n onToggle={() => setExpandedId((cur) => (cur === issue.id ? null : issue.id))}\n onOpen={\n issue.entityId && issue.entityTitle\n ? () => openEntity(issue.entityId as string, issue.entityTitle as string)\n : undefined\n }\n fixing={fixingIssueId === issue.id}\n fixDisabled={fixingIssueId !== undefined}\n onFix={() => launchIssueFix(issue.id, issue.title, issue.reason, issue.output)}\n promoteLabel={promoteLabel(issue, entities)}\n promoting={promotingId === issue.id}\n promoteDisabled={promotingId !== null}\n onPromote={() => handlePromote(issue)}\n onOpenPromoted={\n issue.promotedFixId\n ? () => openEntity(issue.promotedFixId as string, promotedTitle as string)\n : undefined\n }\n />\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"names":["promoteLabel","issue","entities","parent","p","isClosedEntity","formatIssueAge","occurredAt","ageDays","NO_TIMESTAMP","sortIssuesByAge","issues","a","b","issueId","sourceKind","sourceKey","collectAgentRunIssues","taskLog","entry","collectCheckIssues","checks","check","collectPrReviewIssues","entity","_a","pr","applyPromotions","spawned","e","f","target","issueThreadFromTaskLog","useIssuesPage","expandedId","setExpandedId","useState","promotingId","setPromotingId","useAppStore","s","taskLogLoading","loadTaskLog","plans","ideaEntries","agentStatus","launchIssueFix","loadPlans","loadIdeas","useDeskChecks","openEntity","useOpenEntity","useEffect","planEntities","useMemo","collected","handlePromote","promoteIssue","fixingIssueId","t","IssueRow","expanded","onToggle","onOpen","fixing","fixDisabled","onFix","promoting","promoteDisabled","onPromote","onOpenPromoted","handleFix","err","jsxs","jsx","PlanIdStamp","FeedbackThread","Button","Fragment","Spinner","IssuesPage","PageTitle","promotedTitle","cur"],"mappings":"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"}
@@ -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-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
+ 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-DiplEwD3.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-DxehIB14.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"index-DxehIB14.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,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-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}
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-DiplEwD3.js";import{S as g}from"./sidebar-section-RSOrdZKe.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-Co_9IFtJ.js.map
7
+ //# sourceMappingURL=index-SD_x9Z5F.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"index-SD_x9Z5F.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,4 +1,4 @@
1
- import{u as b,G as L,E as D,r as j,H as M,I as B,K as F,L as G,j as e,N as T,v as H,i as A,J as y,O as U,q,C as V,Q as $,S as J,R as O,M as z,T as K,g as R,U as _,V as N,P as w,p as E}from"./main-C0TBUyF9.js";import{W as je}from"./main-C0TBUyF9.js";const W=()=>{const s=b(n=>n.roadmap),t=b(n=>n.roadmapError),o=b(n=>n.loadRoadmap),i=b(n=>n.roadmapFilters),a=b(n=>n.plans),m=L(),{item:l}=D({from:"/roadmap"}),u=j.useRef(null),[d,h]=j.useState(null);j.useEffect(()=>{o()},[o]),j.useEffect(()=>{var x;if(!l||!s)return;const n=(x=u.current)==null?void 0:x.querySelector(".roadmap-item-highlighted");n==null||n.scrollIntoView({behavior:"smooth",block:"center"})},[l,s]);const f=n=>(a==null?void 0:a.entries.filter(x=>x.subject===n.name))??[],r=async(n,x,S)=>{await B(n,x,S),await o()},c=(n,x,S)=>{h({horizonTitle:n,item:x,candidateName:S})},p=s?M(s,i):[],g=p.reduce((n,x)=>n+x.items.length,0),v=i.horizons.length>0||i.statuses.length>0;return{roadmap:s,roadmapError:t,loadRoadmap:o,horizons:p,totalVisible:g,hasActiveFilters:v,highlightedItem:l,containerRef:u,promoting:d,setPromoting:h,graduatedByItem:f,handleAddCandidate:r,handlePromote:c,onOpenGraduated:m}},Q=["in-progress","review","planned","idea","done","dropped"],Z=()=>{const s=b(c=>c.roadmap),t=b(c=>c.roadmapFilters),o=b(c=>c.toggleRoadmapHorizon),i=b(c=>c.toggleRoadmapStatus),[a,m]=j.useState(!1),l=(s==null?void 0:s.horizons.map(c=>c.title))??[],u=s?F(s,t):{},d=s?G(s,t):{},h=new Set(t.horizons),f=new Set(t.statuses),r=Q.filter(c=>(d[c]??0)>0||f.has(c));return{roadmap:s,horizonTitles:l,horizonCounts:u,statusCounts:d,activeHorizons:h,activeStatuses:f,visibleStatuses:r,addOpen:a,setAddOpen:m,toggleRoadmapHorizon:o,toggleRoadmapStatus:i}},X=({open:s,horizonTitles:t,onClose:o})=>{const i=b(n=>n.loadRoadmap),[a,m]=j.useState(t[0]??""),[l,u]=j.useState(""),[d,h]=j.useState(""),[f,r]=j.useState(!1),[c,p]=j.useState(null),g=j.useRef(t);g.current=t,j.useEffect(()=>{s&&(m(g.current[0]??""),u(""),h(""),p(null),r(!1))},[s]);const v=async()=>{if(!(!l.trim()||!a)){r(!0),p(null);try{await U(a,l.trim(),d.trim()),await i(),o()}catch(n){p(n.message)}finally{r(!1)}}};return e.jsx(T,{open:s,onClose:o,title:"Add roadmap item",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(H,{size:"small",value:a,onChange:m,disabled:f,options:t.map(n=>({value:n,label:n}))}),e.jsx(A,{size:"small",value:l,onChange:n=>u(n.target.value),placeholder:"Item name…",disabled:f,autoFocus:!0}),e.jsx(A,{size:"small",value:d,onChange:n=>h(n.target.value),onKeyDown:n=>{n.key==="Enter"&&v()},placeholder:"Description…",disabled:f}),c&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:c}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:o,disabled:f,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:v,disabled:f||!l.trim()||!a,children:f?"Adding…":"Add item"})]})]})})},C="__no-subject__",Y=({horizonTitle:s,item:t,candidateName:o,onClose:i,onPromoted:a})=>{const m=b(x=>x.promoteRoadmapItem),l=b(x=>x.launchIdeaExtend),{subjects:u,available:d}=q(),[h,f]=j.useState(C),[r,c]=j.useState(!1),[p,g]=j.useState(null),{toast:v}=V();j.useEffect(()=>{t&&(f(o?t.name:C),c(!1),g(null))},[t,o]);const n=async()=>{if(!(!t||!s)){c(!0),g(null);try{const x=await m(s,t,h===C?void 0:h,o??void 0),S=b.getState().ideaEntries.find(k=>k.id===x);if(S)try{await l(x,$(S,s))}catch(k){v({title:"Idea created, but the refine agent failed to launch",description:J(k.message),variant:"error"})}a(),i()}catch(x){g(x.message)}finally{c(!1)}}};return e.jsx(T,{open:t!==null,onClose:i,title:o??(t==null?void 0:t.name)??"",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[!o&&e.jsx("p",{className:"m-0 opacity-80",children:t==null?void 0:t.description}),e.jsx("div",{children:e.jsx(H,{size:"small",value:h,onChange:f,disabled:r||!d,options:[{value:C,label:"No subject"},...!d&&h!==C?[{value:h,label:h}]:[],...u.map(x=>({value:x,label:x}))]})}),p&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:p}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:i,disabled:r,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:n,disabled:r,children:r?"Promoting…":"Promote to idea"})]})]})})},ee=({onAdd:s})=>{const[t,o]=j.useState(""),[i,a]=j.useState(!1),m=async()=>{if(t.trim()){a(!0);try{await s(t.trim()),o("")}finally{a(!1)}}};return e.jsxs("div",{className:"flex gap-2",children:[e.jsx(A,{size:"small",value:t,onChange:l=>o(l.target.value),onKeyDown:l=>{l.key==="Enter"&&m()},placeholder:"Add option…",disabled:i,className:"flex-1"}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:m,disabled:i||!t.trim(),children:"Add"})]})},se=({name:s,onPromote:t})=>e.jsx(O,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex-1",children:s}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:t,children:"Promote to idea"})]})}),te=({goal:s})=>{const[t,o]=j.useState(!1),[i,...a]=s.split(/\n{2,}/),m=a.length>0;return e.jsxs("div",{className:"mb-6 pb-4 border-b border-black/[8%]",children:[e.jsx("span",{className:"font-handwritten text-xs font-semibold opacity-[0.55]",children:"The goal"}),e.jsxs("div",{className:"text-sm leading-relaxed opacity-80 mt-2",children:[e.jsx(z,{children:i}),t&&m&&e.jsx(z,{children:a.join(`
1
+ import{u as b,G as L,E as D,r as j,H as M,I as B,K as F,L as G,j as e,N as T,v as H,i as A,J as y,O as U,q,C as V,Q as $,S as J,R as O,M as z,T as K,g as R,U as _,V as N,P as w,p as E}from"./main-DiplEwD3.js";import{W as je}from"./main-DiplEwD3.js";const W=()=>{const s=b(n=>n.roadmap),t=b(n=>n.roadmapError),o=b(n=>n.loadRoadmap),i=b(n=>n.roadmapFilters),a=b(n=>n.plans),m=L(),{item:l}=D({from:"/roadmap"}),u=j.useRef(null),[d,h]=j.useState(null);j.useEffect(()=>{o()},[o]),j.useEffect(()=>{var x;if(!l||!s)return;const n=(x=u.current)==null?void 0:x.querySelector(".roadmap-item-highlighted");n==null||n.scrollIntoView({behavior:"smooth",block:"center"})},[l,s]);const f=n=>(a==null?void 0:a.entries.filter(x=>x.subject===n.name))??[],r=async(n,x,S)=>{await B(n,x,S),await o()},c=(n,x,S)=>{h({horizonTitle:n,item:x,candidateName:S})},p=s?M(s,i):[],g=p.reduce((n,x)=>n+x.items.length,0),v=i.horizons.length>0||i.statuses.length>0;return{roadmap:s,roadmapError:t,loadRoadmap:o,horizons:p,totalVisible:g,hasActiveFilters:v,highlightedItem:l,containerRef:u,promoting:d,setPromoting:h,graduatedByItem:f,handleAddCandidate:r,handlePromote:c,onOpenGraduated:m}},Q=["in-progress","review","planned","idea","done","dropped"],Z=()=>{const s=b(c=>c.roadmap),t=b(c=>c.roadmapFilters),o=b(c=>c.toggleRoadmapHorizon),i=b(c=>c.toggleRoadmapStatus),[a,m]=j.useState(!1),l=(s==null?void 0:s.horizons.map(c=>c.title))??[],u=s?F(s,t):{},d=s?G(s,t):{},h=new Set(t.horizons),f=new Set(t.statuses),r=Q.filter(c=>(d[c]??0)>0||f.has(c));return{roadmap:s,horizonTitles:l,horizonCounts:u,statusCounts:d,activeHorizons:h,activeStatuses:f,visibleStatuses:r,addOpen:a,setAddOpen:m,toggleRoadmapHorizon:o,toggleRoadmapStatus:i}},X=({open:s,horizonTitles:t,onClose:o})=>{const i=b(n=>n.loadRoadmap),[a,m]=j.useState(t[0]??""),[l,u]=j.useState(""),[d,h]=j.useState(""),[f,r]=j.useState(!1),[c,p]=j.useState(null),g=j.useRef(t);g.current=t,j.useEffect(()=>{s&&(m(g.current[0]??""),u(""),h(""),p(null),r(!1))},[s]);const v=async()=>{if(!(!l.trim()||!a)){r(!0),p(null);try{await U(a,l.trim(),d.trim()),await i(),o()}catch(n){p(n.message)}finally{r(!1)}}};return e.jsx(T,{open:s,onClose:o,title:"Add roadmap item",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(H,{size:"small",value:a,onChange:m,disabled:f,options:t.map(n=>({value:n,label:n}))}),e.jsx(A,{size:"small",value:l,onChange:n=>u(n.target.value),placeholder:"Item name…",disabled:f,autoFocus:!0}),e.jsx(A,{size:"small",value:d,onChange:n=>h(n.target.value),onKeyDown:n=>{n.key==="Enter"&&v()},placeholder:"Description…",disabled:f}),c&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:c}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:o,disabled:f,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:v,disabled:f||!l.trim()||!a,children:f?"Adding…":"Add item"})]})]})})},C="__no-subject__",Y=({horizonTitle:s,item:t,candidateName:o,onClose:i,onPromoted:a})=>{const m=b(x=>x.promoteRoadmapItem),l=b(x=>x.launchIdeaExtend),{subjects:u,available:d}=q(),[h,f]=j.useState(C),[r,c]=j.useState(!1),[p,g]=j.useState(null),{toast:v}=V();j.useEffect(()=>{t&&(f(o?t.name:C),c(!1),g(null))},[t,o]);const n=async()=>{if(!(!t||!s)){c(!0),g(null);try{const x=await m(s,t,h===C?void 0:h,o??void 0),S=b.getState().ideaEntries.find(k=>k.id===x);if(S)try{await l(x,$(S,s))}catch(k){v({title:"Idea created, but the refine agent failed to launch",description:J(k.message),variant:"error"})}a(),i()}catch(x){g(x.message)}finally{c(!1)}}};return e.jsx(T,{open:t!==null,onClose:i,title:o??(t==null?void 0:t.name)??"",size:"small",children:e.jsxs("div",{className:"flex flex-col gap-4",children:[!o&&e.jsx("p",{className:"m-0 opacity-80",children:t==null?void 0:t.description}),e.jsx("div",{children:e.jsx(H,{size:"small",value:h,onChange:f,disabled:r||!d,options:[{value:C,label:"No subject"},...!d&&h!==C?[{value:h,label:h}]:[],...u.map(x=>({value:x,label:x}))]})}),p&&e.jsx("p",{className:"m-0 text-watercolor-rose-dark text-sm",children:p}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"ghost",onClick:i,disabled:r,children:"Cancel"}),e.jsx(y,{type:"button",variant:"primary",onClick:n,disabled:r,children:r?"Promoting…":"Promote to idea"})]})]})})},ee=({onAdd:s})=>{const[t,o]=j.useState(""),[i,a]=j.useState(!1),m=async()=>{if(t.trim()){a(!0);try{await s(t.trim()),o("")}finally{a(!1)}}};return e.jsxs("div",{className:"flex gap-2",children:[e.jsx(A,{size:"small",value:t,onChange:l=>o(l.target.value),onKeyDown:l=>{l.key==="Enter"&&m()},placeholder:"Add option…",disabled:i,className:"flex-1"}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:m,disabled:i||!t.trim(),children:"Add"})]})},se=({name:s,onPromote:t})=>e.jsx(O,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex-1",children:s}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:t,children:"Promote to idea"})]})}),te=({goal:s})=>{const[t,o]=j.useState(!1),[i,...a]=s.split(/\n{2,}/),m=a.length>0;return e.jsxs("div",{className:"mb-6 pb-4 border-b border-black/[8%]",children:[e.jsx("span",{className:"font-handwritten text-xs font-semibold opacity-[0.55]",children:"The goal"}),e.jsxs("div",{className:"text-sm leading-relaxed opacity-80 mt-2",children:[e.jsx(z,{children:i}),t&&m&&e.jsx(z,{children:a.join(`
2
2
 
3
3
  `)})]}),m&&e.jsx("button",{type:"button",onClick:()=>o(l=>!l),className:"bg-transparent border-none p-0 mt-2 cursor-pointer text-2xs opacity-[0.65] underline",children:t?"Show less":"Show more"})]})},ae="outline-[rgba(200,154,90,0.5)]",I={fill:"rgba(0, 0, 0, 0.06)",text:"rgba(0, 0, 0, 0.55)"},ne=(s,t)=>{const o=new Set,i=t.map(a=>(a.id&&o.add(a.id),a.idea&&o.add(a.idea),{key:a.title,label:a.title,status:a.status,pr:a.pr,planId:a.id,planTitle:a.title}));for(const a of s)o.has(a.id)||i.push({key:a.id,label:a.id,status:a.status,pr:a.pr});return i},oe=({idea:s,onOpen:t})=>e.jsx(O,{size:"small",texture:"kraft",className:"plan-row-card",children:e.jsxs("div",{className:"flex items-center gap-3",children:[t?e.jsx("button",{type:"button",onClick:t,className:"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap bg-transparent border-none p-0 cursor-pointer [font:inherit] text-inherit text-left",children:s.label}):e.jsx("span",{className:"flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap",children:s.label}),s.pr&&e.jsx(K,{pr:s.pr}),e.jsx(R,{size:"small",fillColor:N[s.status].fill,textColor:N[s.status].text,children:_[s.status]})]})}),le=({done:s,total:t})=>{const o=t>0?Math.round(s/t*100):0;return e.jsx("div",{className:"w-24 h-1.5 rounded-full bg-black/[0.08] overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${t>0?"bg-watercolor-green":"bg-transparent"}`,style:{width:`${o}%`}})})},re=s=>({shipped:s.filter(t=>t.status==="done").length,queued:s.filter(t=>t.status!=="done"&&t.status!=="dropped").length}),ie=({size:s=14})=>e.jsx("svg",{width:s,height:s,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:e.jsx("polyline",{points:"9 18 15 12 9 6"})}),de=({item:s,graduated:t,highlighted:o,onPromote:i,onPromoteCandidate:a,onAddCandidate:m,onOpenGraduated:l})=>{const[u,d]=j.useState(o),{shipped:h,queued:f}=re(t),r=s.candidates.length,c=ne(s.links,t);return j.useEffect(()=>{o&&d(!0)},[o]),e.jsxs("div",{className:`border-b border-black/10 last:border-b-0 ${o?`roadmap-item-highlighted outline outline-2 outline-offset-[-2px] ${ae}`:""}`,children:[e.jsxs("button",{type:"button","aria-expanded":u,onClick:()=>d(p=>!p),className:"flex w-full items-center gap-3 py-2.5 px-0 bg-transparent border-none cursor-pointer text-left [font:inherit] text-inherit",children:[e.jsx("span",{className:`inline-flex items-center opacity-50 shrink-0 ${u?"rotate-90":""}`,children:e.jsx(ie,{})}),e.jsx("span",{className:"font-semibold flex-1 min-w-0 truncate",children:s.name}),e.jsx(le,{done:s.rollup.done,total:s.rollup.total}),f>0&&e.jsxs(R,{size:"small",fillColor:N.planned.fill,textColor:N.planned.text,children:[f," in queue"]}),h>0&&e.jsxs(R,{size:"small",fillColor:N.done.fill,textColor:N.done.text,children:[h," shipped"]}),r>0&&e.jsxs(R,{size:"small",fillColor:I.fill,textColor:I.text,children:[r," candidate",r===1?"":"s"]})]}),u&&e.jsxs("div",{className:"flex flex-col gap-1 pl-6 pb-4",children:[e.jsx("span",{className:"text-sm opacity-70",children:s.description}),c.map(p=>e.jsx(oe,{idea:p,onOpen:p.planTitle?()=>l(p.planId,p.planTitle):void 0},p.key)),s.candidates.map(p=>e.jsx(se,{name:p,onPromote:()=>a(p)},p)),e.jsx(ee,{onAdd:m}),e.jsx(y,{type:"button",variant:"ghost",size:"small",onClick:i,className:"self-start",children:"Promote to idea"})]})]})},ce="font-handwritten text-md font-semibold opacity-70 leading-none pt-2 px-1 pb-0",pe=({horizon:s,highlightedItem:t,graduatedByItem:o,onPromote:i,onAddCandidate:a,onOpenGraduated:m})=>e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("div",{className:ce,children:s.title}),e.jsx("div",{className:"flex flex-col",children:s.items.map(l=>e.jsx(de,{item:l,graduated:o(l),highlighted:l.name===t,onPromote:()=>i(l),onPromoteCandidate:u=>i(l,u),onAddCandidate:u=>a(l.name,u),onOpenGraduated:m},l.name))})]}),xe=()=>{const{roadmap:s,roadmapError:t,loadRoadmap:o,horizons:i,totalVisible:a,hasActiveFilters:m,highlightedItem:l,containerRef:u,promoting:d,setPromoting:h,graduatedByItem:f,handleAddCandidate:r,handlePromote:c,onOpenGraduated:p}=W();return t?e.jsxs("div",{children:[e.jsx(w,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"Couldn't load the roadmap — the server may need a restart to pick up new routes."})]}):s?s.horizons.length===0?e.jsxs("div",{children:[e.jsx(w,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"No `ROADMAP.md` found at the project root."})]}):e.jsxs("div",{ref:u,children:[e.jsx(w,{children:"Roadmap"}),e.jsx(te,{goal:s.goal}),a===0?e.jsx("p",{className:"opacity-50 py-6 px-0 text-center",children:m?"Nothing matches these filters — clear one from the sidebar to see more.":"No roadmap items yet — add one from the sidebar."}):e.jsx("div",{className:"flex flex-col gap-6",children:i.filter(g=>g.items.length>0).map(g=>e.jsx(pe,{horizon:g,highlightedItem:l,graduatedByItem:f,onPromote:(v,n)=>c(g.title,v,n),onAddCandidate:(v,n)=>r(g.title,v,n),onOpenGraduated:p},g.title))}),e.jsx(Y,{horizonTitle:(d==null?void 0:d.horizonTitle)??null,item:(d==null?void 0:d.item)??null,candidateName:d==null?void 0:d.candidateName,onClose:()=>h(null),onPromoted:o})]}):e.jsxs("div",{children:[e.jsx(w,{children:"Roadmap"}),e.jsx("p",{className:"opacity-50",children:"Loading…"})]})},P="pc-row-label font-handwritten text-xs font-semibold opacity-[0.45]",ue=()=>{const{roadmap:s,horizonTitles:t,horizonCounts:o,statusCounts:i,activeHorizons:a,activeStatuses:m,visibleStatuses:l,addOpen:u,setAddOpen:d,toggleRoadmapHorizon:h,toggleRoadmapStatus:f}=Z();return s?e.jsxs("div",{className:"flex flex-col",children:[e.jsx("div",{className:"flex h-[64px] items-center",children:e.jsx(y,{type:"button",variant:"primary",size:"small",onClick:()=>d(!0),disabled:t.length===0,children:"+ Add item"})}),e.jsxs("div",{children:[e.jsx("div",{className:P,children:"Horizon"}),e.jsx("div",{className:"flex flex-col",children:t.map(r=>e.jsx(E,{size:"small",active:a.has(r),onClick:()=>h(r),className:"pc-row text-xs",action:e.jsx("span",{className:"text-2xs text-ink-500",children:o[r]??0}),children:r},r))})]}),e.jsxs("div",{children:[e.jsx("div",{className:P,children:"Status"}),e.jsxs("div",{className:"flex flex-col",children:[l.length===0&&e.jsx("span",{className:"opacity-50 text-2xs",children:"No linked ideas"}),l.map(r=>e.jsx(E,{size:"small",active:m.has(r),onClick:()=>f(r),className:"pc-row text-xs",icon:e.jsx("span",{className:"w-[9px] h-[9px] rounded-full shrink-0",style:{background:N[r].text}}),action:e.jsx("span",{className:"text-2xs text-ink-500",children:i[r]??0}),children:_[r]},r))]})]}),e.jsx(X,{open:u,horizonTitles:t,onClose:()=>d(!1)})]}):null};export{xe as RoadmapPage,ue as RoadmapSidebar,je as useRoadmapItemNames};
4
- //# sourceMappingURL=index-Ds_iTHi0.js.map
4
+ //# sourceMappingURL=index-mmzETz6G.js.map