@edspencer/paddock 0.66.2 → 0.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/package.json +1 -1
  2. package/packages/server/dist/routes/chats.js +14 -2
  3. package/packages/server/dist/session-hub.d.ts +10 -0
  4. package/packages/server/dist/session-hub.js +16 -2
  5. package/packages/server/dist/ws-protocol.d.ts +15 -0
  6. package/packages/server/dist/ws.js +1 -0
  7. package/packages/server/package.json +1 -1
  8. package/packages/web/dist/assets/InstanceConfigPage-sDbCoFxC.js +2 -0
  9. package/packages/web/dist/assets/{MarkdownRenderer-DDVO0zhl.js → MarkdownRenderer-MaSe5aAI.js} +11 -11
  10. package/packages/web/dist/assets/ProjectMenu-DmWpr9od.js +2 -0
  11. package/packages/web/dist/assets/ProjectRedirect-BJeDERUd.js +2 -0
  12. package/packages/web/dist/assets/ProjectView-Bbecs1u7.js +13 -0
  13. package/packages/web/dist/assets/ProjectsGrid-BE4cAtu5.js +2 -0
  14. package/packages/web/dist/assets/Surfaces-DtSsbnLh.js +2 -0
  15. package/packages/web/dist/assets/{arc-DXqVnx_r.js → arc-NLb6J_GZ.js} +1 -1
  16. package/packages/web/dist/assets/{architectureDiagram-3BPJPVTR-FgkfD1Bk.js → architectureDiagram-3BPJPVTR-BbiSIctn.js} +1 -1
  17. package/packages/web/dist/assets/{blockDiagram-GPEHLZMM-WwkIIJsI.js → blockDiagram-GPEHLZMM-Br1sRzRE.js} +1 -1
  18. package/packages/web/dist/assets/{c4Diagram-AAUBKEIU-BIzkTibx.js → c4Diagram-AAUBKEIU-DUvJurko.js} +1 -1
  19. package/packages/web/dist/assets/channel-bezC0w5b.js +2 -0
  20. package/packages/web/dist/assets/{chunk-2J33WTMH-C4TBH3R6.js → chunk-2J33WTMH-Cm_E_LAD.js} +1 -1
  21. package/packages/web/dist/assets/{chunk-4BX2VUAB-DTKJZlRM.js → chunk-4BX2VUAB-CK6R2MCN.js} +1 -1
  22. package/packages/web/dist/assets/{chunk-55IACEB6-BC9_Qc8h.js → chunk-55IACEB6-D7HXvxGy.js} +1 -1
  23. package/packages/web/dist/assets/{chunk-727SXJPM-D3gQ4rP3.js → chunk-727SXJPM-BLwevzlY.js} +1 -1
  24. package/packages/web/dist/assets/{chunk-AQP2D5EJ-My2AXBvz.js → chunk-AQP2D5EJ-CpLWD5so.js} +1 -1
  25. package/packages/web/dist/assets/{chunk-FMBD7UC4-D09zE6Ow.js → chunk-FMBD7UC4-BNlt1t19.js} +1 -1
  26. package/packages/web/dist/assets/{chunk-ND2GUHAM-CUSgeCLl.js → chunk-ND2GUHAM-CIt5gAq8.js} +1 -1
  27. package/packages/web/dist/assets/{chunk-QZHKN3VN-B6jVPYgy.js → chunk-QZHKN3VN-B1uwIQ9c.js} +1 -1
  28. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-DMgrTmQU.js +2 -0
  29. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-DMgrTmQU.js +2 -0
  30. package/packages/web/dist/assets/{core-DPY0seI-.js → core-HXUN76Po.js} +1 -1
  31. package/packages/web/dist/assets/{cose-bilkent-S5V4N54A-BasCiARv.js → cose-bilkent-S5V4N54A-DJ4_5jxB.js} +1 -1
  32. package/packages/web/dist/assets/{dagre-BM42HDAG-Cimn11Qe.js → dagre-BM42HDAG-Bc9-haAu.js} +1 -1
  33. package/packages/web/dist/assets/{diagram-2AECGRRQ-fe9cDHyn.js → diagram-2AECGRRQ-nLOtPDzu.js} +1 -1
  34. package/packages/web/dist/assets/{diagram-5GNKFQAL-T0tQlfbl.js → diagram-5GNKFQAL-DThV84R3.js} +1 -1
  35. package/packages/web/dist/assets/{diagram-KO2AKTUF-dLORrSiu.js → diagram-KO2AKTUF-C5hcR1vA.js} +1 -1
  36. package/packages/web/dist/assets/{diagram-LMA3HP47-iUMeaR-J.js → diagram-LMA3HP47-BEFzrSHy.js} +1 -1
  37. package/packages/web/dist/assets/{diagram-OG6HWLK6-oEH6olHK.js → diagram-OG6HWLK6-BY5lPsX7.js} +1 -1
  38. package/packages/web/dist/assets/{erDiagram-TEJ5UH35-VtQqF9SY.js → erDiagram-TEJ5UH35-y_kB8ETg.js} +1 -1
  39. package/packages/web/dist/assets/{flowDiagram-I6XJVG4X-wvF2EkJa.js → flowDiagram-I6XJVG4X-Cuhha5cF.js} +1 -1
  40. package/packages/web/dist/assets/{ganttDiagram-6RSMTGT7-ng4qJ0E1.js → ganttDiagram-6RSMTGT7-DFJpciK5.js} +1 -1
  41. package/packages/web/dist/assets/{gitGraphDiagram-PVQCEYII-BV4aWy8k.js → gitGraphDiagram-PVQCEYII-FoA0yuh-.js} +1 -1
  42. package/packages/web/dist/assets/index-BxLurJvz.css +1 -0
  43. package/packages/web/dist/assets/index-C_PXsGTD.js +71 -0
  44. package/packages/web/dist/assets/{infoDiagram-5YYISTIA-BpGTxAjU.js → infoDiagram-5YYISTIA-CKYqD8-c.js} +1 -1
  45. package/packages/web/dist/assets/{ishikawaDiagram-YF4QCWOH-Cj36LyoS.js → ishikawaDiagram-YF4QCWOH-CCIxHIBw.js} +1 -1
  46. package/packages/web/dist/assets/{journeyDiagram-JHISSGLW-0vNlg3um.js → journeyDiagram-JHISSGLW-lGYoSyk0.js} +1 -1
  47. package/packages/web/dist/assets/{kanban-definition-UN3LZRKU-wijkEDaR.js → kanban-definition-UN3LZRKU-B3D7CIwy.js} +1 -1
  48. package/packages/web/dist/assets/{linear-LqRymMD_.js → linear-BiplJBex.js} +1 -1
  49. package/packages/web/dist/assets/{mermaid.core-DPrhSs95.js → mermaid.core-BJ7aEinZ.js} +4 -4
  50. package/packages/web/dist/assets/{mindmap-definition-RKZ34NQL-en-uGqgC.js → mindmap-definition-RKZ34NQL-BiXcOtHo.js} +1 -1
  51. package/packages/web/dist/assets/{pieDiagram-4H26LBE5-DKBZ2Vnx.js → pieDiagram-4H26LBE5-tnkj45YF.js} +1 -1
  52. package/packages/web/dist/assets/{quadrantDiagram-W4KKPZXB--g-WtxyL.js → quadrantDiagram-W4KKPZXB-LqReq3Gl.js} +1 -1
  53. package/packages/web/dist/assets/{requirementDiagram-4Y6WPE33-Dl4eYxDI.js → requirementDiagram-4Y6WPE33-C1uk9Jty.js} +1 -1
  54. package/packages/web/dist/assets/{sankeyDiagram-5OEKKPKP-7lt4gqSw.js → sankeyDiagram-5OEKKPKP-eQQ3OKrV.js} +1 -1
  55. package/packages/web/dist/assets/{sequenceDiagram-3UESZ5HK-COgq-dXg.js → sequenceDiagram-3UESZ5HK-C6-a0lH4.js} +1 -1
  56. package/packages/web/dist/assets/{stateDiagram-AJRCARHV-CL6XC79S.js → stateDiagram-AJRCARHV-CsTOkKqz.js} +1 -1
  57. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-Bsc-VlVI.js +2 -0
  58. package/packages/web/dist/assets/{timeline-definition-PNZ67QCA-C5egKeGT.js → timeline-definition-PNZ67QCA-DUBXH1x-.js} +1 -1
  59. package/packages/web/dist/assets/{vennDiagram-CIIHVFJN-C_UhtC8u.js → vennDiagram-CIIHVFJN-CcQrNnou.js} +1 -1
  60. package/packages/web/dist/assets/{wardley-L42UT6IY-Bj9chUqz.js → wardley-L42UT6IY-Br823uUV.js} +1 -1
  61. package/packages/web/dist/assets/{wardleyDiagram-YWT4CUSO-D7VnZoRp.js → wardleyDiagram-YWT4CUSO-DRBECQc_.js} +1 -1
  62. package/packages/web/dist/assets/{xychartDiagram-2RQKCTM6-VrWLDoi4.js → xychartDiagram-2RQKCTM6--HtSFP24.js} +1 -1
  63. package/packages/web/dist/fonts/alegreya-latin-italic.woff2 +0 -0
  64. package/packages/web/dist/fonts/alegreya-latin.woff2 +0 -0
  65. package/packages/web/dist/fonts/alegreya-sc-latin-500.woff2 +0 -0
  66. package/packages/web/dist/fonts/alegreya-sc-latin-700.woff2 +0 -0
  67. package/packages/web/dist/fonts/courier-prime-latin-400.woff2 +0 -0
  68. package/packages/web/dist/fonts/courier-prime-latin-700.woff2 +0 -0
  69. package/packages/web/dist/fonts/courier-prime-latin-italic.woff2 +0 -0
  70. package/packages/web/dist/fonts/iosevka-terminal-400.woff2 +0 -0
  71. package/packages/web/dist/fonts/iosevka-terminal-700.woff2 +0 -0
  72. package/packages/web/dist/fonts/martian-mono-latin.woff2 +0 -0
  73. package/packages/web/dist/fonts/saira-latin.woff2 +0 -0
  74. package/packages/web/dist/index.html +61 -12
  75. package/packages/web/dist/sw.js +1 -1
  76. package/packages/web/dist/assets/InstanceConfigPage-DKzAHOhx.js +0 -2
  77. package/packages/web/dist/assets/ProjectRedirect-WczXTV0_.js +0 -2
  78. package/packages/web/dist/assets/ProjectView-Dd_myWpV.js +0 -13
  79. package/packages/web/dist/assets/ProjectsGrid-BeJj0q6Y.js +0 -2
  80. package/packages/web/dist/assets/channel-DUsOl5_p.js +0 -2
  81. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-CIdDKEqH.js +0 -2
  82. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-CIdDKEqH.js +0 -2
  83. package/packages/web/dist/assets/format-CvRqOKZU.js +0 -2
  84. package/packages/web/dist/assets/index-DGNSeqL-.js +0 -71
  85. package/packages/web/dist/assets/index-DMRbU1Jl.css +0 -1
  86. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-BDlRyphY.js +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edspencer/paddock",
3
- "version": "0.66.2",
3
+ "version": "0.67.0",
4
4
  "description": "Project-first launchpad for Claude Code: server-hosted, persistent, resumable sessions organized by project.",
5
5
  "keywords": [
6
6
  "claude",
@@ -150,7 +150,7 @@ export function registerChatWorkspaceRoutes(app, ctx) {
150
150
  schema: {
151
151
  tags: ["Chats"],
152
152
  summary: "List the chats in this workspace's subtree that are running or unread",
153
- description: "Returns `{ running, unread }` — the chats in this workspace AND its descendants that currently have a live turn (`running`) or hold an unread reply (`unread`). Each row is a chat DTO plus the `projectSlug`/`projectName` it belongs to, so a fleet-wide list stays attributable. On the root mount (`/api/root/chats/attention`) the subtree is the whole instance, because the root workspace's key (`\"\"`) prefixes every workspace key; on a project mount it is that project alone. A chat is never in both lists: a live turn hasn't landed a reply yet, so running wins.",
153
+ description: "Returns `{ running, unread }` — the chats in this workspace AND its descendants that currently have a live turn (`running`) or hold an unread reply (`unread`). Each row is a chat DTO plus the `projectSlug`/`projectName` it belongs to, so a fleet-wide list stays attributable. On the root mount (`/api/root/chats/attention`) the subtree is the whole instance, because the root workspace's key (`\"\"`) prefixes every workspace key; on a project mount it is that project alone. A chat is never in both lists: a live turn hasn't landed a reply yet, so running wins. `running` rows additionally carry resolved `contextTokens`/`contextLimit` so a caller can draw a context gauge for live work; `unread` rows do not, because resolving usage streams a transcript per chat and only the running set is bounded by how many turns can be in flight at once.",
154
154
  params: {
155
155
  type: "object",
156
156
  properties: {
@@ -197,7 +197,19 @@ export function registerChatWorkspaceRoutes(app, ctx) {
197
197
  const provenanceOf = (s) => runProvenance.get(s.sessionId);
198
198
  const triggerOf = makeTriggerResolver(p);
199
199
  const parentOf = makeParentResolver(runProvenance, messageProvenance, p.slug, (id) => parentDetach.isDetached(keeper, id));
200
- const chats = await buildProjectChats(p.dir, sessions, undefined, archivedOf, lastTurnAt, lastSeenOf, provenanceOf, triggerOf, starredOf, unreadOf, parentOf);
200
+ // Usage is deliberately NOT resolved for the whole subtree: it streams
201
+ // a transcript per chat, and this route refetches on every turn
202
+ // boundary across the fleet. A RUNNING chat is the one exception —
203
+ // every context gauge in the app (Home's running rows, the fleet
204
+ // readout) reads `contextTokens`/`contextLimit` off these rows, and
205
+ // with no resolver they were always null, so the gauges rendered
206
+ // blank. The running set is bounded by how many turns can be in
207
+ // flight at once (a handful), not by how many chats the instance
208
+ // holds, so the cost stays proportional to live work and not to
209
+ // history.
210
+ const usageOf = chatUsageResolver(p.dir, p.model ?? DEFAULT_MODEL);
211
+ const usageIfRunning = async (s) => isRunning(s.sessionId) ? usageOf(s) : null;
212
+ const chats = await buildProjectChats(p.dir, sessions, usageIfRunning, archivedOf, lastTurnAt, lastSeenOf, provenanceOf, triggerOf, starredOf, unreadOf, parentOf);
201
213
  for (const c of chats) {
202
214
  const row = { ...c, projectSlug: p.slug, projectName: p.name };
203
215
  if (isRunning(c.sessionId)) {
@@ -72,6 +72,14 @@ interface Turn {
72
72
  baseSeq: number;
73
73
  nextSeq: number;
74
74
  running: boolean;
75
+ /**
76
+ * Epoch-ms the turn began. The hub is the only place that knows this: a job
77
+ * record is written when a turn ENDS, and the transcript's own timestamps are
78
+ * the model's, not the run's. Surfaced on {@link ActiveInfo} so a client that
79
+ * loads mid-turn — or reconnects — can still say how long the fleet has been
80
+ * working, rather than restarting every clock at zero on every page load.
81
+ */
82
+ startedAt: number;
75
83
  evictTimer: ReturnType<typeof setTimeout> | null;
76
84
  }
77
85
  /** How long a completed turn's buffer lingers so an end-of-turn reconnect still gets the tail. */
@@ -97,6 +105,8 @@ export interface ActiveInfo {
97
105
  /** The running turn's cancellable job id, if known yet (null early in a turn). */
98
106
  jobId: string | null;
99
107
  running: boolean;
108
+ /** Epoch-ms this turn started. See {@link Turn.startedAt}. */
109
+ startedAt: number;
100
110
  }
101
111
  /** Why a turn is being cancelled, and which socket asked for it. */
102
112
  export interface CancelIntent {
@@ -107,6 +107,7 @@ export class SessionHub {
107
107
  baseSeq: 0,
108
108
  nextSeq: 0,
109
109
  running: true,
110
+ startedAt: Date.now(),
110
111
  evictTimer: null,
111
112
  };
112
113
  const handle = new TurnHandle(this, turn);
@@ -203,14 +204,26 @@ export class SessionHub {
203
204
  const turn = this.bySession.get(sessionId);
204
205
  if (!turn || !turn.running)
205
206
  return null;
206
- return { sessionId, projectSlug: turn.projectSlug, jobId: turn.jobId, running: true };
207
+ return {
208
+ sessionId,
209
+ projectSlug: turn.projectSlug,
210
+ jobId: turn.jobId,
211
+ running: true,
212
+ startedAt: turn.startedAt,
213
+ };
207
214
  }
208
215
  /** Snapshot of every session with a running turn (for a newly-connected client). */
209
216
  runningSessions() {
210
217
  const out = [];
211
218
  for (const [sessionId, turn] of this.bySession) {
212
219
  if (turn.running) {
213
- out.push({ sessionId, projectSlug: turn.projectSlug, jobId: turn.jobId, running: true });
220
+ out.push({
221
+ sessionId,
222
+ projectSlug: turn.projectSlug,
223
+ jobId: turn.jobId,
224
+ running: true,
225
+ startedAt: turn.startedAt,
226
+ });
214
227
  }
215
228
  }
216
229
  return out;
@@ -288,6 +301,7 @@ export class SessionHub {
288
301
  projectSlug: turn.projectSlug,
289
302
  jobId: turn.jobId,
290
303
  running: turn.running,
304
+ startedAt: turn.startedAt,
291
305
  });
292
306
  }
293
307
  recipients(turn) {
@@ -393,6 +393,21 @@ export interface ChatActiveMessage {
393
393
  /** The running turn's cancellable job id, when known. */
394
394
  jobId: string | null;
395
395
  running: boolean;
396
+ /**
397
+ * Epoch-ms the running turn began, from the session hub. The hub is the only
398
+ * thing that knows: a job record is written when a turn ENDS, and the
399
+ * transcript's timestamps are the model's rather than the run's. Because the
400
+ * server replays its whole running snapshot to every socket on connect, a
401
+ * client that reloads mid-turn still learns the true start — which is what
402
+ * lets a fleet readout say "running 12:04" instead of restarting at zero on
403
+ * every page load.
404
+ *
405
+ * Sent on the STOP frame too (`running: false`), where it describes the turn
406
+ * that just ended and a client has no use for it — the hub emits one frame
407
+ * shape for both transitions rather than branching. Optional on the wire so
408
+ * a client built against an older server still parses the frame.
409
+ */
410
+ startedAt?: number;
396
411
  };
397
412
  }
398
413
  /**
@@ -215,6 +215,7 @@ export function makeChatHandler(deps) {
215
215
  sessionId: info.sessionId,
216
216
  jobId: info.jobId,
217
217
  running: info.running,
218
+ startedAt: info.startedAt,
218
219
  },
219
220
  });
220
221
  hub.onActive = (info) => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paddock/server",
3
- "version": "0.66.2",
3
+ "version": "0.67.0",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -0,0 +1,2 @@
1
+ import{j as e,l as f,r as c,q as Z,s as ee,t as te,v as ve,w as ie,x as le,y as ye,z as we,D as oe,E as Ne,F as ke,b as F,A as ce,G as Ce,X as Se,H as Ae,I as Ee,J as Re,K as $e,O as Me}from"./index-C_PXsGTD.js";import{B as R,S as ne}from"./Surfaces-DtSsbnLh.js";const K=["w-full rounded-lg border border-edge-strong bg-surface-sunken px-3 py-2 text-sm text-fg","outline-none motion-fast transition-[border-color,box-shadow]","focus:border-accent focus:ring-2 focus:ring-accent/25","disabled:cursor-not-allowed disabled:opacity-60","aria-[invalid=true]:border-danger aria-[invalid=true]:focus:ring-danger/25"].join(" ");function B({className:t,...n}){return e.jsx("input",{className:f(K,t),...n})}function Oe({className:t,...n}){return e.jsx("textarea",{className:f(K,"resize-y",t),...n})}function Ie({className:t,...n}){return e.jsx("select",{className:f(K,t),...n})}function Pe({checked:t,onChange:n,label:s,disabled:l,id:r,className:a}){const x=c.useId(),m=r??x;return e.jsxs("label",{htmlFor:m,className:f("relative inline-block shrink-0 cursor-pointer",a),children:[e.jsx("input",{id:m,type:"checkbox","aria-label":s,className:"peer sr-only",checked:t,disabled:l,onChange:g=>n(g.target.checked)}),e.jsx("span",{className:f("block h-[18px] w-8 rounded-full bg-edge-strong","motion-fast transition-colors","after:absolute after:left-0.5 after:top-0.5 after:h-3.5 after:w-3.5 after:rounded-full","after:bg-surface-raised after:shadow-2xs after:transition-transform after:content-['']","peer-checked:bg-accent-solid peer-checked:after:translate-x-3.5","peer-focus-visible:ring-2 peer-focus-visible:ring-accent/50","peer-disabled:opacity-50")})]})}const de={neutral:"bg-surface-active text-fg-muted",accent:"bg-accent-soft text-accent",success:"bg-success-soft text-success",warn:"bg-warn-soft text-warn",danger:"bg-danger-soft text-danger",info:"bg-info-soft text-info",lineage:"bg-lineage-soft text-lineage"},ue={neutral:"bg-fg-subtle",accent:"bg-accent-solid",success:"bg-success-solid",warn:"bg-warn-solid",danger:"bg-danger-solid",info:"bg-info-solid",lineage:"bg-lineage-solid"};function xe({tone:t="neutral",shape:n="tag",size:s="md",dot:l=!1,icon:r,title:a,className:x,children:m}){return e.jsxs("span",{title:a,className:f("inline-flex items-center gap-1 font-medium leading-none",n==="pill"?"rounded-full px-2 py-0.5":"rounded-md px-1.5 py-0.5",s==="sm"?"text-3xs":"text-2xs",de[t],x),children:[l&&e.jsx("span",{className:f("h-1.5 w-1.5 shrink-0 rounded-full",ue[t])}),r,m]})}function L({tone:t="neutral",pulse:n=!1,className:s}){return e.jsx("span",{className:f("inline-block h-2 w-2 shrink-0 rounded-full",ue[t],n&&"animate-pulse",s)})}function Te({tone:t="info",icon:n,className:s,children:l}){const r={neutral:"border-edge",accent:"border-accent-edge",success:"border-success-edge",warn:"border-warn-edge",danger:"border-danger-edge",info:"border-info-edge",lineage:"border-lineage-edge"};return e.jsxs("div",{role:t==="danger"?"alert":void 0,className:f("flex items-start gap-2 rounded-lg border px-3 py-2 text-sm",de[t],r[t],s),children:[n&&e.jsx("span",{className:"mt-0.5 shrink-0",children:n}),e.jsx("div",{className:"min-w-0",children:l})]})}const Fe=Array.from({length:72},(t,n)=>n*5),S=[...le].sort((t,n)=>t.hue-n.hue),z=(t,n)=>Math.min(Math.abs(t-n),360-Math.abs(t-n));function se(t){const n=S.reduce((r,a)=>z(a.hue,t)<z(r.hue,t)?a:r);if(z(n.hue,t)<=8)return n.name;const s=S.find(r=>r.hue>t)??S[0];return`between ${S[(S.indexOf(s)-1+S.length)%S.length].name} and ${s.name}`}function Le({theme:t,dark:n,hue:s,className:l}){const r=c.useRef(null);return c.useLayoutEffect(()=>{const a=r.current;a&&(s===null?we(a):oe(s,a))},[t,n,s]),e.jsx("div",{ref:r,"data-theme":t,className:f("overflow-hidden rounded-lg border border-edge",n&&"dark",l),children:e.jsxs("div",{className:"flex h-full items-center gap-2 bg-surface p-2",children:[e.jsxs("div",{className:"flex-1 rounded-md border border-edge-subtle bg-surface-raised p-1.5",children:[e.jsx("div",{className:"h-1 w-4/5 rounded-full bg-fg"}),e.jsx("div",{className:"mt-1 h-1 w-3/5 rounded-full bg-fg-subtle"})]}),e.jsx("div",{className:"grid h-6 w-6 shrink-0 place-items-center rounded-md bg-accent-solid",children:e.jsx("div",{className:"h-1 w-3 rounded-full bg-accent-fg"})})]})})}function De({hexes:t,hue:n,active:s,onCommit:l}){const r=c.useRef(null),a=c.useRef(null),x=c.useRef(null),m=c.useRef(0),g=c.useRef(null),p=c.useRef(!1),j=c.useRef(null),u=c.useMemo(()=>{if(t.length===0)return;const d=t.map((b,y)=>`${b} ${(y/t.length*100).toFixed(2)}%`);return d.push(`${t[0]} 100%`),`linear-gradient(to right, ${d.join(", ")})`},[t]),h=c.useCallback(d=>{a.current&&(a.current.style.left=`${d/360*100}%`),x.current&&(x.current.textContent=se(d))},[]);c.useEffect(()=>{p.current||h(n)},[n,h]);const N=d=>{var $;const b=($=r.current)==null?void 0:$.getBoundingClientRect();if(!b||b.width===0)return 0;const y=Math.min(1,Math.max(0,(d-b.left)/b.width));return Math.round(y*360)%360},w=c.useCallback(d=>{g.current=d,!m.current&&(m.current=requestAnimationFrame(()=>{m.current=0;const b=g.current;g.current=null,b!==null&&(h(b),oe(b,document.documentElement,{context:j.current??void 0,live:!0}))}))},[h]);c.useEffect(()=>()=>cancelAnimationFrame(m.current),[]);const P=d=>{p.current=!1,j.current=null,cancelAnimationFrame(m.current),m.current=0,g.current=null,l(d)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mt-2 flex items-baseline justify-between gap-2",children:[e.jsx("span",{className:"text-3xs text-fg-subtle",children:"Drag along the strip"}),e.jsx("span",{ref:x,className:"text-3xs font-medium text-fg-muted","aria-hidden":!0})]}),e.jsx("div",{ref:r,role:"slider",tabIndex:0,"aria-label":"Accent colour","aria-valuemin":0,"aria-valuemax":359,"aria-valuenow":n,"aria-valuetext":s?se(n):"the theme’s own colour",onPointerDown:d=>{d.currentTarget.setPointerCapture(d.pointerId),p.current=!0,j.current=Ne(),w(N(d.clientX))},onPointerMove:d=>{p.current&&w(N(d.clientX))},onPointerUp:d=>{p.current&&P(N(d.clientX))},onPointerCancel:()=>{p.current&&P(n)},onKeyDown:d=>{const b=d.key==="PageUp"||d.key==="PageDown"?15:d.shiftKey?10:2;let y=null;d.key==="ArrowLeft"||d.key==="ArrowDown"||d.key==="PageDown"?y=(n-b+360)%360:d.key==="ArrowRight"||d.key==="ArrowUp"||d.key==="PageUp"?y=(n+b)%360:d.key==="Home"?y=0:d.key==="End"&&(y=359),y!==null&&(d.preventDefault(),l(y))},style:{backgroundImage:u,touchAction:"none"},className:f("relative mt-1 h-8 cursor-pointer rounded-lg border border-edge shadow-xs select-none","focus-visible:focus-ring"),children:e.jsx("div",{ref:a,"aria-hidden":!0,style:{left:`${n/360*100}%`},className:f("pointer-events-none absolute top-1/2 h-[calc(100%+6px)] w-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full","border border-fg bg-surface-raised shadow-sm",!s&&"opacity-0")})})]})}function Ve({className:t}){const[n,s]=c.useState(Z),[l,r]=c.useState(null),a=typeof document<"u"&&document.documentElement.classList.contains("dark"),x=c.useRef(n),m=c.useCallback(u=>{const h={...x.current,...u};x.current=h,s(h),r(ee(h))},[]);c.useEffect(()=>{const u=h=>r(h.detail);return window.addEventListener(te,u),r(ee(Z())),()=>window.removeEventListener(te,u)},[]);const g=c.useMemo(()=>ve(Fe),[n.theme,a,l==null?void 0:l.mode]),p=(l==null?void 0:l.hue)??0,j=n.hue!==null;return e.jsxs("div",{className:f("space-y-5",t),children:[e.jsxs("fieldset",{children:[e.jsx("legend",{className:"text-2xs font-semibold uppercase tracking-wide text-fg-muted",children:"Theme"}),e.jsx("div",{className:f("mt-2 grid gap-2","grid-cols-2 sm:grid-cols-4"),children:ie.map(u=>{const h=n.theme===u.id;return e.jsxs("button",{type:"button","aria-pressed":h,title:`${u.label} — ${u.blurb}`,onClick:()=>m({theme:u.id}),className:f("motion-fast rounded-xl border text-left transition-[border-color,box-shadow]","focus-visible:focus-ring","p-1.5",h?"border-accent-edge bg-accent-soft shadow-xs":"border-edge bg-surface-raised hover:border-edge-strong"),children:[e.jsx(Le,{theme:u.id,dark:a,hue:n.hue,className:"h-10"}),e.jsxs("div",{className:"mt-1.5 min-w-0 px-0.5 pb-0.5",children:[e.jsx("span",{className:"block truncate text-xs font-semibold text-fg",children:u.label}),e.jsx("span",{className:"mt-0.5 line-clamp-2 text-3xs leading-snug text-fg-muted",children:u.blurb})]})]},u.id)})})]}),e.jsxs("div",{className:f("grid gap-5 lg:grid-cols-[minmax(0,1fr)_15rem] lg:items-start"),children:[e.jsxs("fieldset",{children:[e.jsx("legend",{className:"text-2xs font-semibold uppercase tracking-wide text-fg-muted",children:"Accent colour"}),e.jsx("p",{className:"mt-1 text-3xs leading-snug text-fg-subtle",children:"Pick any colour. The theme adjusts it so buttons and labels stay readable in both light and dark."}),e.jsx(De,{hexes:g,hue:p,active:j,onCommit:u=>m({hue:u})}),e.jsxs("div",{className:"mt-2 flex flex-wrap gap-1",children:[e.jsx("button",{type:"button",onClick:()=>m({hue:null}),"aria-pressed":!j,className:f("motion-fast rounded-md border px-2 py-1 text-3xs transition-colors","focus-visible:focus-ring",j?"border-edge text-fg-muted hover:border-edge-strong hover:text-fg":"border-accent-edge bg-accent-soft text-accent"),children:"Theme’s own"}),le.map(u=>{const h=j&&Math.abs(p-u.hue)<2.5;return e.jsxs("button",{type:"button","aria-pressed":h,onClick:()=>m({hue:u.hue}),className:f("motion-fast inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-3xs transition-colors","focus-visible:focus-ring",h?"border-edge-strong bg-surface-active text-fg":"border-edge text-fg-muted hover:border-edge-strong hover:text-fg"),children:[e.jsx("span",{"aria-hidden":!0,className:"h-2.5 w-2.5 rounded-full",style:{backgroundColor:g[Math.round(u.hue/5)%g.length]}}),u.name]},u.name)})]})]}),e.jsxs("fieldset",{children:[e.jsx("legend",{className:"text-2xs font-semibold uppercase tracking-wide text-fg-muted",children:"Tint the background"}),e.jsx("p",{className:"mt-1 text-3xs leading-snug text-fg-subtle",children:"Colours the whole page a little, not just the buttons. Useful for telling two Paddock instances apart at a glance."}),e.jsx("div",{className:"mt-2 inline-flex rounded-lg border border-edge p-0.5",children:ye.map(u=>e.jsx("button",{type:"button","aria-pressed":n.tint===u.level,onClick:()=>m({tint:u.level}),className:f("motion-fast rounded-md px-2.5 py-1 text-3xs transition-colors","focus-visible:focus-ring",n.tint===u.level?"bg-surface-active font-medium text-fg":"text-fg-muted hover:text-fg"),children:u.label},u.level))})]})]}),e.jsx(Be,{})]})}function Be(){return e.jsxs("div",{children:[e.jsx("p",{className:"text-2xs font-semibold uppercase tracking-wide text-fg-muted",children:"Preview"}),e.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-2 rounded-xl border border-edge bg-surface p-3",children:[e.jsx(R,{variant:"primary",size:"sm",children:"Send"}),e.jsx(R,{variant:"ghost",size:"sm",children:"Cancel"}),e.jsx(R,{variant:"link",size:"sm",children:"A link"}),e.jsx("span",{className:"inline-flex items-center gap-1 rounded-md border border-accent-edge bg-accent-soft px-1.5 py-0.5 text-2xs font-medium text-accent",children:"accent chip"}),e.jsx(xe,{tone:"accent",shape:"pill",dot:!0,children:"running"}),e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-2xs text-fg-subtle",children:[e.jsx("span",{className:"h-4 w-4 rounded-md border border-edge bg-accent-solid-hover","aria-hidden":!0}),"hovered"]}),e.jsxs("span",{className:"ml-auto inline-flex items-center gap-1.5",children:[e.jsx(L,{tone:"success"}),e.jsx(L,{tone:"warn"}),e.jsx(L,{tone:"danger"}),e.jsx(L,{tone:"info"}),e.jsx("span",{className:"text-2xs text-fg-subtle",children:"status hues (theme’s, not yours)"})]})]})]})}const G="mx-auto w-full max-w-4xl",He="sm:w-64";function Ue(){const[t,n]=c.useState(null),[s,l]=c.useState(!0),[r,a]=c.useState(null),[x,m]=c.useState({}),[g,p]=c.useState(!1),[j,u]=c.useState(null),[h,N]=c.useState(!1),[w,P]=c.useState(""),[d,b]=c.useState(!1),[y,$]=c.useState(null),X=c.useRef(null),me=ke("(min-width: 1024px)");c.useEffect(()=>{let i=!0;return l(!0),F.getInstanceConfig().then(o=>{i&&(n(o),a(null))}).catch(o=>i&&a(o instanceof Error?o.message:String(o))).finally(()=>i&&l(!1)),()=>{i=!1}},[]);const fe=(i,o)=>{N(!1),u(null),m(v=>({...v,[i]:o}))},ge=i=>re(i,x),H=c.useMemo(()=>t?t.groups.flatMap(i=>i.fields):[],[t]),k=c.useMemo(()=>H.filter(i=>i.editable&&!i.envOverridden).filter(i=>Object.prototype.hasOwnProperty.call(x,i.key)).filter(i=>!ae(x[i.key],q(i))).map(i=>i.key),[H,x]),J=c.useMemo(()=>new Set(k),[k]),C=c.useMemo(()=>{if(!t)return[];const i=w.trim().toLowerCase(),o=[];for(const v of t.groups){const O=i!==""&&v.label.toLowerCase().includes(i),I=v.fields.filter(A=>{const T=re(A,x);return d&&ae(T,A.default)?!1:i===""||O?!0:Ze(A).includes(i)});I.length>0&&o.push({...v,fields:I})}return o},[t,w,d,x]),Y=C.reduce((i,o)=>i+o.fields.length,0),he=w.trim()!==""||d,U=!d&&(w.trim()===""||tt.includes(w.trim().toLowerCase())),M=c.useMemo(()=>U?[V,...C]:C,[U,C]),pe=C.some(i=>i.fields.some(o=>o.envOverridden)),Q=et(C,J);c.useEffect(()=>{const i=X.current;if(!i||M.length===0)return;const o=M.map(O=>O.id),v=()=>{const O=i.getBoundingClientRect().top;let I=o[0];for(const A of o){const T=document.getElementById(D(A));if(T)if(T.getBoundingClientRect().top-O<=80)I=A;else break}$(I)};return v(),i.addEventListener("scroll",v,{passive:!0}),()=>i.removeEventListener("scroll",v)},[M]);const W=i=>{var v;const o=document.getElementById(D(i));(v=o==null?void 0:o.scrollIntoView)==null||v.call(o,{behavior:"smooth",block:"start"}),$(i)},be=async()=>{if(k.length===0||!t)return;const i={};for(const o of k)i[o]=x[o];p(!0),u(null);try{await F.updateInstanceConfig(i,t.configVersion);const o=await F.getInstanceConfig();n(o),m({}),N(!0)}catch(o){if(o instanceof Ee&&o.status===409)try{n(await F.getInstanceConfig())}catch{}u(o instanceof Error?o.message:String(o))}finally{p(!1)}},je=()=>{m({}),u(null),N(!1)};return e.jsxs("div",{className:"flex min-h-0 flex-1",children:[t&&e.jsx(ze,{groups:M,active:y,onJump:W,dirtyCount:Q}),e.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col",children:[t&&e.jsx("div",{className:"border-b border-edge px-4 py-2.5 sm:px-6",children:e.jsxs("div",{className:G,children:[e.jsx(_e,{query:w,onQuery:P,modifiedOnly:d,onModifiedOnly:b,visibleCount:Y,totalCount:H.length,filtering:he,autoFocus:me}),e.jsx(Ge,{groups:M,active:y,onJump:W,dirtyCount:Q})]})}),e.jsx("div",{ref:X,className:"min-h-0 flex-1 overflow-y-auto px-4 py-5 sm:px-6",children:e.jsxs("div",{className:G,children:[U&&e.jsx("div",{className:"mb-8 border-b border-edge pb-7",children:e.jsx(ne,{id:D(V.id),title:V.label,description:V.description,variant:"rule",first:!0,children:e.jsx(Ve,{})})}),e.jsx(Ke,{saved:h,configPath:t==null?void 0:t.configPath,restartRequired:(t==null?void 0:t.restartRequired)??!1,fileError:t==null?void 0:t.configFileError}),s&&e.jsx("p",{className:"text-sm text-fg-muted",children:"Loading…"}),r&&e.jsxs("p",{className:"text-sm text-danger",role:"alert",children:["Failed to load settings: ",r]}),t&&pe&&e.jsx(qe,{}),t&&Y===0&&e.jsxs("p",{className:"py-8 text-center text-sm text-fg-muted",children:["No settings match"," ",w.trim()?e.jsxs(e.Fragment,{children:["“",e.jsx("span",{className:"font-medium",children:w.trim()}),"”"]}):"this filter","."]}),C.map(i=>e.jsx(ne,{id:D(i.id),title:i.label,description:i.description,variant:"card",flush:!0,children:e.jsx("div",{className:"divide-y divide-edge-subtle",children:i.fields.map(o=>e.jsx(Xe,{field:o,value:ge(o),dirty:J.has(o.key),onChange:v=>fe(o.key,v)},o.key))})},i.id))]})}),t&&e.jsx("footer",{className:"border-t border-edge px-4 py-3 sm:px-6",children:e.jsxs("div",{className:`${G} flex items-center gap-3`,children:[j&&e.jsxs("span",{className:"flex min-w-0 items-center gap-1.5 text-sm text-danger",role:"alert",children:[e.jsx(ce,{width:14,height:14,className:"shrink-0"}),e.jsx("span",{className:"truncate",children:j})]}),e.jsx("span",{className:"ml-auto shrink-0 text-xs tabular text-fg-subtle",children:k.length>0?`${k.length} unsaved change${k.length===1?"":"s"}`:"No changes"}),e.jsx(R,{variant:"subtle",onClick:je,disabled:k.length===0||g,children:"Reset"}),e.jsx(R,{variant:"primary",onClick:be,disabled:k.length===0,loading:g,loadingLabel:"Saving…",children:"Save changes"})]})})]})]})}function ze({groups:t,active:n,onJump:s,dirtyCount:l}){return e.jsx("nav",{"aria-label":"Config sections",className:"hidden w-52 shrink-0 overflow-y-auto border-r border-edge px-2 py-4 lg:block",children:e.jsx("ul",{className:"space-y-0.5",children:t.map(r=>{const a=r.id===n,x=l[r.id]??0;return e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>s(r.id),"aria-current":a?"true":void 0,className:f("motion-fast flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors",a?"bg-surface-selected font-medium text-fg":"text-fg-muted hover:bg-surface-hover hover:text-fg"),children:[e.jsx("span",{className:"min-w-0 flex-1 truncate",children:r.label}),x>0&&e.jsx("span",{className:"h-1.5 w-1.5 shrink-0 rounded-full bg-accent-solid","aria-label":`${x} unsaved`}),r.fields.length>0&&e.jsx("span",{className:"shrink-0 text-2xs tabular text-fg-subtle",children:r.fields.length})]})},r.id)})})})}function Ge({groups:t,active:n,onJump:s,dirtyCount:l}){return t.length===0?null:e.jsx("nav",{"aria-label":"Config sections",className:"-mx-1 mt-2 flex gap-1 overflow-x-auto px-1 pb-0.5 lg:hidden",children:t.map(r=>{const a=r.id===n,x=l[r.id]??0;return e.jsxs("button",{type:"button",onClick:()=>s(r.id),"aria-current":a?"true":void 0,className:f("motion-fast flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1 text-xs transition-colors",a?"bg-surface-selected font-medium text-fg":"text-fg-muted hover:bg-surface-hover hover:text-fg"),children:[r.label,x>0&&e.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-accent-solid","aria-label":`${x} unsaved`})]},r.id)})})}function _e({query:t,onQuery:n,modifiedOnly:s,onModifiedOnly:l,visibleCount:r,totalCount:a,filtering:x,autoFocus:m}){return e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative min-w-0 flex-1 sm:max-w-sm",children:[e.jsx(Ce,{width:14,height:14,className:"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-fg-subtle"}),e.jsx(B,{type:"search","aria-label":"Search settings",placeholder:"Search settings, keys, env vars…",className:"h-8 py-0 pl-8 pr-7 [&::-webkit-search-cancel-button]:appearance-none",value:t,autoFocus:m,onChange:g=>n(g.target.value),onKeyDown:g=>{g.key==="Escape"&&t!==""&&(g.preventDefault(),n(""))}}),t!==""&&e.jsx("button",{type:"button","aria-label":"Clear search",onClick:()=>n(""),className:"absolute right-2 top-1/2 -translate-y-1/2 text-fg-subtle hover:text-fg",children:e.jsx(Se,{width:13,height:13})})]}),e.jsx("button",{type:"button","aria-pressed":s,onClick:()=>l(!s),className:f("motion-fast shrink-0 rounded-md border px-2.5 py-1 text-xs transition-colors",s?"border-accent-edge bg-accent-soft text-accent":"border-edge text-fg-muted hover:text-fg"),children:"Modified only"}),e.jsx("span",{className:"shrink-0 text-xs tabular text-fg-subtle",children:x?`${r} of ${a}`:`${a} settings`})]})}function Ke({saved:t,configPath:n,restartRequired:s,fileError:l}){const r=l?"danger":t?"success":"warn";return e.jsx(Te,{tone:r,className:"mb-5 leading-snug",icon:r==="success"?e.jsx(Ae,{width:15,height:15}):e.jsx(ce,{width:15,height:15}),children:e.jsx("span",{role:"status",children:l?e.jsxs(e.Fragment,{children:[e.jsxs("strong",{children:["Could not read ",_(n),"."]})," ",l," — the values below are the running instance's; what a restart would load is unknown until the file parses."]}):t?e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Saved to disk."})," These changes take effect only after the server restarts."]}):s?e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Restart pending."})," ",e.jsx("code",{className:"font-mono text-xs",children:_(n)})," holds changes the running instance has not picked up — the fields below marked"," ",e.jsx(E,{tone:"warn",children:"restart"})," differ from what is in force right now."]}):e.jsxs(e.Fragment,{children:["Changes here are written to"," ",e.jsx("code",{className:"font-mono text-xs",children:_(n)})," and take effect only after the server restarts — the running instance keeps its current config until then."]})})})}function qe(){return e.jsxs("p",{className:"mb-5 text-xs leading-snug text-fg-muted",children:["Settings marked ",e.jsx(E,{tone:"neutral",children:"env"})," are overridden by an environment variable, which wins over this file — edit that variable (and restart) to change them."]})}function Xe({field:t,value:n,dirty:s,onChange:l}){const r=!t.editable||t.envOverridden,a=`cfg-${t.key}`,x=t.help?`${a}-help`:void 0,m=t.type==="boolean",g=c.useMemo(()=>We(t,q(t)),[t]),p=m&&!r,j=!t.help&&!t.envOverridden&&!t.pendingRestart,u=j?"items-center":"items-start",h=j?"sm:items-center":"sm:items-start",N=r?e.jsx(Qe,{field:t,value:n}):m?e.jsx(Pe,{id:a,checked:!!n,onChange:l,label:t.label}):e.jsx(Ye,{field:t,value:n,onChange:l,inputId:a,describedBy:x});return e.jsxs("div",{className:"relative px-4 py-3",children:[s&&e.jsx("span",{"aria-hidden":!0,className:"absolute inset-y-2 left-0 w-0.5 rounded-full bg-accent-solid"}),e.jsxs("div",{className:f(g?"":p?`flex gap-4 ${u}`:`sm:flex sm:gap-6 ${h}`),children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[e.jsx("label",{htmlFor:r?void 0:a,className:"text-sm font-medium text-fg",children:t.label}),t.sensitive&&e.jsx(E,{tone:"warn",children:"sensitive"}),t.envOverridden&&e.jsx(E,{tone:"neutral",title:`Overridden by environment variable ${t.envVar} — edit that env var (and restart) to change it.`,children:"env"}),!t.editable&&!t.envOverridden&&e.jsx(E,{tone:"neutral",children:"read-only"}),t.pendingRestart&&e.jsx(E,{tone:"warn",title:"The config file and the running instance disagree on this field — restart to apply.",children:"restart"})]}),t.help&&e.jsx("p",{id:x,className:"mt-0.5 text-xs leading-snug text-fg-muted",children:t.help}),t.envOverridden&&e.jsx("p",{className:"mt-1 truncate font-mono text-2xs text-fg-subtle",children:t.envVar}),t.pendingRestart&&e.jsxs("p",{className:"mt-1 text-2xs leading-snug text-warn",children:["In force now: ",e.jsx("span",{className:"font-mono",children:Je(t.value)})]})]}),e.jsx("div",{className:f(g?"mt-2":p?"flex shrink-0 justify-end":`mt-2 sm:mt-0 sm:shrink-0 ${He}`),children:N})]})]})}function Je(t){if(t==null||t==="")return"(not set)";const s=(Array.isArray(t)?t.join(", "):String(t)).replace(/\s+/g," ").trim();return s.length>60?`${s.slice(0,60)}…`:s}function E({children:t,tone:n,title:s}){return e.jsx(xe,{tone:n,size:"sm",title:s,className:"uppercase tracking-wide",children:t})}function Ye({field:t,value:n,onChange:s,inputId:l,describedBy:r}){if(t.type==="enum")return e.jsx(Ie,{id:l,"aria-describedby":r,value:String(n??""),onChange:a=>s(a.target.value),children:(t.enumValues??[]).map(a=>e.jsx("option",{value:a,children:a},a))});if(t.type==="number")return e.jsx(B,{id:l,type:"number","aria-describedby":r,className:"tabular",value:n==null?"":String(n),placeholder:t.default===null?"default":String(t.default),onChange:a=>s(a.target.value===""?null:Number(a.target.value))});if(t.type==="text"){const a=n==null||n===t.default,x=n==null?String(t.default??""):String(n),m=typeof t.default=="string"&&!a;return e.jsxs("div",{children:[e.jsx(Oe,{id:l,"aria-describedby":r,className:"min-h-[7rem] font-mono text-xs leading-relaxed",rows:6,spellCheck:!1,value:x,onChange:g=>s(g.target.value)}),e.jsxs("div",{className:"mt-1 flex items-center justify-between gap-3 text-xs text-fg-muted",children:[e.jsx("span",{children:a?"Using the built-in default.":n===""?"Empty — nothing will be appended.":`${x.length.toLocaleString()} characters.`}),m&&e.jsx(R,{variant:"link",size:"sm",className:"shrink-0",onClick:()=>s(null),children:"Restore default"})]})]})}if(t.type==="string-list"){const a=Array.isArray(n)?n.join(", "):String(n??"");return e.jsx(B,{id:l,type:"text","aria-describedby":r,value:a,onChange:x=>s(x.target.value.split(",").map(m=>m.trim()).filter(Boolean))})}return e.jsx(B,{id:l,type:"text","aria-describedby":r,value:n==null?"":String(n),placeholder:t.default===null?"":String(t.default),onChange:a=>s(a.target.value)})}function Qe({field:t,value:n}){const s="rounded-lg bg-surface-sunken px-3 py-2 font-mono text-xs text-fg-muted";return t.type==="boolean"?e.jsx("div",{className:s,children:n?"true":"false"}):n==null||n===""?e.jsx("div",{className:s,children:e.jsx("span",{className:"italic text-fg-subtle",children:t.type==="text"&&n===""?"(empty — nothing appended)":"(not set)"})}):t.type==="text"?e.jsx("div",{className:f(s,"max-h-40 overflow-y-auto whitespace-pre-wrap leading-relaxed"),children:String(n)}):e.jsx("div",{className:f(s,"break-all"),children:Array.isArray(n)?n.join(", "):String(n)})}function We(t,n){return t.type==="text"||t.type==="string-list"?!0:(Array.isArray(n)?n.join(", "):n==null?"":String(n)).length>38}function Ze(t){return[t.label,t.key,t.help,t.envVar,...t.enumValues??[]].filter(Boolean).join(" ").toLowerCase()}function et(t,n){const s={};for(const l of t)s[l.id]=l.fields.filter(r=>n.has(r.key)).length;return s}const D=t=>`cfg-section-${t}`,V={id:"appearance",label:"Appearance",description:"Theme and accent colour for this browser. Applies immediately — no save, no restart.",fields:[]},tt=["appearance theme themes colour color accent tint dark light font",...ie.map(t=>t.id)].join(" ");function q(t){return t.pendingValue===void 0?t.value:t.pendingValue}function re(t,n){return Object.prototype.hasOwnProperty.call(n,t.key)?n[t.key]:q(t)}function ae(t,n){return Array.isArray(t)&&Array.isArray(n)?t.length===n.length&&t.every((s,l)=>s===n[l]):t===n}function _(t){if(!t)return"paddock.config.yaml";const n=t.split("/");return n[n.length-1]||t}function rt(){const t=Re(),n=(t==null?void 0:t.openNav)??(()=>{});return e.jsxs("div",{className:"flex h-full min-h-0 flex-col",children:[e.jsxs("header",{className:"pt-safe flex items-center gap-2 border-b border-edge px-3 pb-2.5 sm:px-6 lg:py-4",children:[e.jsx("button",{type:"button",onClick:n,"aria-label":"Open menu",className:"btn-subtle -ml-1 shrink-0 px-2 py-1.5 lg:hidden",children:e.jsx($e,{width:20,height:20})}),e.jsx(Me,{width:18,height:18,className:"shrink-0 text-fg-subtle"}),e.jsx("h1",{className:"text-md font-semibold tracking-tight",children:"Config"})]}),e.jsx(Ue,{})]})}export{rt as InstanceConfigPage};
2
+