@njeybe/nativ 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +15 -0
  3. package/dist/runner/agent-supervisor.d.ts +5 -3
  4. package/dist/runner/agent-supervisor.js +17 -12
  5. package/dist/server/studio-server.js +1 -130
  6. package/dist/server/studio-ui/document.d.ts +3 -0
  7. package/dist/server/studio-ui/document.js +22 -0
  8. package/dist/server/studio-ui/pages/benchmarks/index.d.ts +2 -0
  9. package/dist/server/studio-ui/pages/benchmarks/index.js +11 -0
  10. package/dist/server/studio-ui/pages/benchmarks/script.d.ts +1 -0
  11. package/dist/server/studio-ui/pages/benchmarks/script.js +104 -0
  12. package/dist/server/studio-ui/pages/benchmarks/styles.d.ts +1 -0
  13. package/dist/server/studio-ui/pages/benchmarks/styles.js +30 -0
  14. package/dist/server/studio-ui/pages/database/browser.d.ts +1 -0
  15. package/dist/server/studio-ui/pages/database/browser.js +326 -0
  16. package/dist/server/studio-ui/pages/database/connection.d.ts +1 -0
  17. package/dist/server/studio-ui/pages/database/connection.js +158 -0
  18. package/dist/server/studio-ui/pages/database/data-shell.d.ts +1 -0
  19. package/dist/server/studio-ui/pages/database/data-shell.js +165 -0
  20. package/dist/server/studio-ui/pages/database/drift.d.ts +1 -0
  21. package/dist/server/studio-ui/pages/database/drift.js +301 -0
  22. package/dist/server/studio-ui/pages/database/events.d.ts +1 -0
  23. package/dist/server/studio-ui/pages/database/events.js +231 -0
  24. package/dist/server/studio-ui/pages/database/explorer.d.ts +1 -0
  25. package/dist/server/studio-ui/pages/database/explorer.js +183 -0
  26. package/dist/server/studio-ui/pages/database/index.d.ts +2 -0
  27. package/dist/server/studio-ui/pages/database/index.js +28 -0
  28. package/dist/server/studio-ui/pages/database/markup.d.ts +1 -0
  29. package/dist/server/studio-ui/pages/database/markup.js +32 -0
  30. package/dist/server/studio-ui/pages/database/state.d.ts +2 -0
  31. package/dist/server/studio-ui/pages/database/state.js +57 -0
  32. package/dist/server/studio-ui/pages/database/styles.d.ts +1 -0
  33. package/dist/server/studio-ui/pages/database/styles.js +118 -0
  34. package/dist/server/studio-ui/pages/flow/index.d.ts +2 -0
  35. package/dist/server/studio-ui/pages/flow/index.js +11 -0
  36. package/dist/server/studio-ui/pages/flow/layout.d.ts +1 -0
  37. package/dist/server/studio-ui/pages/flow/layout.js +105 -0
  38. package/dist/server/studio-ui/pages/flow/plan-view.d.ts +1 -0
  39. package/dist/server/studio-ui/pages/flow/plan-view.js +67 -0
  40. package/dist/server/studio-ui/pages/flow/script.d.ts +1 -0
  41. package/dist/server/studio-ui/pages/flow/script.js +61 -0
  42. package/dist/server/studio-ui/pages/flow/styles.d.ts +1 -0
  43. package/dist/server/studio-ui/pages/flow/styles.js +59 -0
  44. package/dist/server/studio-ui/pages/flow/timeline-model.d.ts +1 -0
  45. package/dist/server/studio-ui/pages/flow/timeline-model.js +95 -0
  46. package/dist/server/studio-ui/pages/flow/timeline-view.d.ts +1 -0
  47. package/dist/server/studio-ui/pages/flow/timeline-view.js +92 -0
  48. package/dist/server/studio-ui/pages/home/index.d.ts +2 -0
  49. package/dist/server/studio-ui/pages/home/index.js +11 -0
  50. package/dist/server/studio-ui/pages/home/script.d.ts +1 -0
  51. package/dist/server/studio-ui/pages/home/script.js +160 -0
  52. package/dist/server/studio-ui/pages/home/styles.d.ts +1 -0
  53. package/dist/server/studio-ui/pages/home/styles.js +48 -0
  54. package/dist/server/studio-ui/pages/index.d.ts +2 -0
  55. package/dist/server/studio-ui/pages/index.js +17 -0
  56. package/dist/server/studio-ui/pages/tasks/index.d.ts +2 -0
  57. package/dist/server/studio-ui/pages/tasks/index.js +11 -0
  58. package/dist/server/studio-ui/pages/tasks/script.d.ts +1 -0
  59. package/dist/server/studio-ui/pages/tasks/script.js +96 -0
  60. package/dist/server/studio-ui/pages/tasks/styles.d.ts +1 -0
  61. package/dist/server/studio-ui/pages/tasks/styles.js +37 -0
  62. package/dist/server/studio-ui/pages/team/index.d.ts +2 -0
  63. package/dist/server/studio-ui/pages/team/index.js +11 -0
  64. package/dist/server/studio-ui/pages/team/script.d.ts +1 -0
  65. package/dist/server/studio-ui/pages/team/script.js +112 -0
  66. package/dist/server/studio-ui/pages/team/styles.d.ts +1 -0
  67. package/dist/server/studio-ui/pages/team/styles.js +22 -0
  68. package/dist/server/studio-ui/pages/types.d.ts +13 -0
  69. package/dist/server/studio-ui/pages/types.js +1 -0
  70. package/dist/server/studio-ui/pages/worktrees/index.d.ts +2 -0
  71. package/dist/server/studio-ui/pages/worktrees/index.js +11 -0
  72. package/dist/server/studio-ui/pages/worktrees/script.d.ts +1 -0
  73. package/dist/server/studio-ui/pages/worktrees/script.js +104 -0
  74. package/dist/server/studio-ui/pages/worktrees/styles.d.ts +1 -0
  75. package/dist/server/studio-ui/pages/worktrees/styles.js +15 -0
  76. package/dist/server/studio-ui/shell/actions.d.ts +1 -0
  77. package/dist/server/studio-ui/shell/actions.js +49 -0
  78. package/dist/server/studio-ui/shell/boot.d.ts +2 -0
  79. package/dist/server/studio-ui/shell/boot.js +13 -0
  80. package/dist/server/studio-ui/shell/console.d.ts +1 -0
  81. package/dist/server/studio-ui/shell/console.js +194 -0
  82. package/dist/server/studio-ui/shell/core.d.ts +1 -0
  83. package/dist/server/studio-ui/shell/core.js +50 -0
  84. package/dist/server/studio-ui/shell/detail-view.d.ts +1 -0
  85. package/dist/server/studio-ui/shell/detail-view.js +80 -0
  86. package/dist/server/studio-ui/shell/detail.d.ts +1 -0
  87. package/dist/server/studio-ui/shell/detail.js +83 -0
  88. package/dist/server/studio-ui/shell/dispatch.d.ts +1 -0
  89. package/dist/server/studio-ui/shell/dispatch.js +150 -0
  90. package/dist/server/studio-ui/shell/events-tools.d.ts +1 -0
  91. package/dist/server/studio-ui/shell/events-tools.js +94 -0
  92. package/dist/server/studio-ui/shell/format.d.ts +1 -0
  93. package/dist/server/studio-ui/shell/format.js +151 -0
  94. package/dist/server/studio-ui/shell/icons.d.ts +2 -0
  95. package/dist/server/studio-ui/shell/icons.js +20 -0
  96. package/dist/server/studio-ui/shell/markup-head.d.ts +1 -0
  97. package/dist/server/studio-ui/shell/markup-head.js +43 -0
  98. package/dist/server/studio-ui/shell/markup-panels.d.ts +2 -0
  99. package/dist/server/studio-ui/shell/markup-panels.js +7 -0
  100. package/dist/server/studio-ui/shell/markup-tools.d.ts +1 -0
  101. package/dist/server/studio-ui/shell/markup-tools.js +222 -0
  102. package/dist/server/studio-ui/shell/model.d.ts +1 -0
  103. package/dist/server/studio-ui/shell/model.js +192 -0
  104. package/dist/server/studio-ui/shell/paint.d.ts +1 -0
  105. package/dist/server/studio-ui/shell/paint.js +154 -0
  106. package/dist/server/studio-ui/shell/palette-view.d.ts +1 -0
  107. package/dist/server/studio-ui/shell/palette-view.js +60 -0
  108. package/dist/server/studio-ui/shell/palette.d.ts +1 -0
  109. package/dist/server/studio-ui/shell/palette.js +80 -0
  110. package/dist/server/studio-ui/shell/pipeline-state.d.ts +1 -0
  111. package/dist/server/studio-ui/shell/pipeline-state.js +152 -0
  112. package/dist/server/studio-ui/shell/proposals.d.ts +1 -0
  113. package/dist/server/studio-ui/shell/proposals.js +197 -0
  114. package/dist/server/studio-ui/shell/pure.d.ts +3 -0
  115. package/dist/server/studio-ui/shell/pure.js +9 -0
  116. package/dist/server/studio-ui/shell/router.d.ts +1 -0
  117. package/dist/server/studio-ui/shell/router.js +95 -0
  118. package/dist/server/studio-ui/shell/sidebar-view.d.ts +1 -0
  119. package/dist/server/studio-ui/shell/sidebar-view.js +38 -0
  120. package/dist/server/studio-ui/shell/sync.d.ts +1 -0
  121. package/dist/server/studio-ui/shell/sync.js +120 -0
  122. package/dist/server/studio-ui/shell/toasts-format.d.ts +1 -0
  123. package/dist/server/studio-ui/shell/toasts-format.js +89 -0
  124. package/dist/server/studio-ui/shell/triage.d.ts +1 -0
  125. package/dist/server/studio-ui/shell/triage.js +210 -0
  126. package/dist/server/studio-ui/styles/console.d.ts +1 -0
  127. package/dist/server/studio-ui/styles/console.js +83 -0
  128. package/dist/server/studio-ui/styles/dialogs.d.ts +1 -0
  129. package/dist/server/studio-ui/styles/dialogs.js +118 -0
  130. package/dist/server/studio-ui/styles/responsive.d.ts +1 -0
  131. package/dist/server/studio-ui/styles/responsive.js +28 -0
  132. package/dist/server/studio-ui/styles/shell.d.ts +1 -0
  133. package/dist/server/studio-ui/styles/shell.js +117 -0
  134. package/dist/server/studio-ui/styles/strategist.d.ts +1 -0
  135. package/dist/server/studio-ui/styles/strategist.js +77 -0
  136. package/dist/server/studio-ui/styles/tokens-base.d.ts +1 -0
  137. package/dist/server/studio-ui/styles/tokens-base.js +131 -0
  138. package/dist/server/studio-ui.d.ts +1 -10
  139. package/dist/server/studio-ui.js +63 -5314
  140. package/package.json +2 -2
  141. package/plugin/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1 @@
1
+ export declare const flowScript: string;
@@ -0,0 +1,61 @@
1
+ import { flowLayoutScript } from './layout.js';
2
+ import { flowPlanScript } from './plan-view.js';
3
+ import { flowTimeModelScript } from './timeline-model.js';
4
+ import { flowTimelineScript } from './timeline-view.js';
5
+ // Flow page: registration, header and click handling. Views live in the sibling files.
6
+ const flowMainScript = String.raw ` var ui = Studio.ui, fmt = Studio.fmt;
7
+ var view = { mode: 'plan', implied: false };
8
+
9
+ function flowHead(m) {
10
+ var seg = function (mode, label) {
11
+ return '<button type="button" data-act="flow-mode" data-mode="' + mode + '" aria-pressed="' + (view.mode === mode) + '">' + label + '</button>';
12
+ };
13
+ return '<div class="panel-head"><div class="panel-title"><h1>Flow</h1><p class="lead">' +
14
+ (m ? ui.esc(m.name) : 'How the work in one milestone connects and how it actually ran.') + '</p></div>' +
15
+ '<div class="panel-actions">' + ui.milestoneSelect() + '<div class="seg" role="group" aria-label="Flow view">' +
16
+ seg('plan', 'Plan') + seg('timeline', 'What happened') + '</div></div></div>';
17
+ }
18
+
19
+ function flowShowFocus() {
20
+ var id = Studio.state.focusTaskId;
21
+ if (!id || typeof document === 'undefined') return;
22
+ setTimeout(function () {
23
+ var el = document.querySelector('#panel-flow [data-id="' + String(id).replace(/["\\]/g, '') + '"]');
24
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: 'nearest', inline: 'center' });
25
+ }, 0);
26
+ }
27
+
28
+ Studio.registerPage('flow', {
29
+ deps: ['tasks', 'telemetry', 'runs'],
30
+ onShow: flowShowFocus,
31
+ render: function (ctx) {
32
+ var wanted = ctx.state.milestoneId || ctx.model.activeMilestoneId;
33
+ var m = ctx.model.milestones.filter(function (x) { return x.id === wanted; })[0];
34
+ if (!m) return flowHead(null) + ui.empty('No milestones yet', 'Add tasks to the plan to see them here.');
35
+ if (ctx.state.focusTaskId) view.mode = 'plan';
36
+ var body = !m.tasks.length ? ui.empty('This milestone has no tasks yet.', '')
37
+ : view.mode === 'plan' ? flowPlan(m, ctx.state.focusTaskId, view) : flowTimeline(m, ctx);
38
+ return flowHead(m) + body;
39
+ },
40
+ actions: {
41
+ 'flow-mode': function (el) {
42
+ view.mode = el.getAttribute('data-mode') === 'timeline' ? 'timeline' : 'plan';
43
+ Studio.state.focusTaskId = null;
44
+ Studio.repaint('flow');
45
+ },
46
+ 'flow-focus': function (el) {
47
+ var id = el.getAttribute('data-id');
48
+ if (Studio.state.focusTaskId === id) { Studio.closeDetail(); return; }
49
+ Studio.state.focusTaskId = id;
50
+ Studio.openTask(id);
51
+ Studio.repaint('flow');
52
+ },
53
+ 'flow-clear': function () { Studio.closeDetail(); },
54
+ 'flow-implied': function (el) {
55
+ view.implied = !!el.checked;
56
+ Studio.repaint('flow');
57
+ }
58
+ }
59
+ });
60
+ `;
61
+ export const flowScript = [flowLayoutScript, flowPlanScript, flowTimeModelScript, flowTimelineScript, flowMainScript].join('');
@@ -0,0 +1 @@
1
+ export declare const flowCss: string;
@@ -0,0 +1,59 @@
1
+ // Flow page styles, all scoped under #panel-flow.
2
+ export const flowCss = String.raw `#panel-flow .seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--surface); }
3
+ #panel-flow .seg button { border: 0; background: none; padding: 0 12px; height: 30px; font-size: 13px; color: var(--fg-2); cursor: pointer; }
4
+ #panel-flow .seg button + button { border-left: 1px solid var(--line); }
5
+ #panel-flow .seg button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
6
+ #panel-flow .fl-panel { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; min-width: 0; overflow: hidden; }
7
+ #panel-flow .fl-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px; padding: 12px 16px; }
8
+ #panel-flow .fl-bar .right { margin-left: auto; display: flex; gap: 14px; align-items: center; flex-wrap: wrap; font-size: 12.5px; color: var(--fg-3); }
9
+ #panel-flow .flow-wrap { overflow: auto; max-height: 72vh; position: relative; border-top: 1px solid var(--line); }
10
+ #panel-flow .canvas { position: relative; }
11
+ #panel-flow .canvas svg.wires { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
12
+ #panel-flow .wire { fill: none; stroke: var(--line-2); stroke-width: 1.5; transition: opacity .15s, stroke .15s; }
13
+ #panel-flow .wire.implied { stroke-dasharray: 4 4; }
14
+ #panel-flow .wire.hot { stroke: var(--accent); stroke-width: 2; }
15
+ #panel-flow .canvas.focus .wire:not(.hot) { opacity: .15; }
16
+ #panel-flow .lane-band { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line); }
17
+ #panel-flow .lane-band:nth-child(even) { background: color-mix(in srgb, var(--surface-2) 45%, transparent); }
18
+ #panel-flow .lane-label { position: sticky; left: 0; width: 128px; padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; background: var(--surface); border-right: 1px solid var(--line); height: 100%; z-index: 2; }
19
+ #panel-flow .lane-label .faint { font-size: 11.5px; }
20
+ #panel-flow .round-head { position: absolute; top: 0; height: 30px; display: flex; align-items: center; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-3); }
21
+ #panel-flow .node { position: absolute; display: flex; flex-direction: column; justify-content: center; gap: 3px; padding: 7px 10px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; text-align: left; cursor: pointer; transition: opacity .15s, border-color .15s, box-shadow .15s; z-index: 1; }
22
+ #panel-flow .node:hover { border-color: var(--line-2); box-shadow: var(--shadow); }
23
+ #panel-flow .node.in_progress { border-color: color-mix(in srgb, var(--run) 50%, var(--line)); background: color-mix(in srgb, var(--run-soft) 55%, var(--surface)); }
24
+ #panel-flow .node.blocked { border-color: color-mix(in srgb, var(--bad) 50%, var(--line)); background: color-mix(in srgb, var(--bad-soft) 55%, var(--surface)); }
25
+ #panel-flow .node .l1 { display: flex; align-items: center; gap: 6px; }
26
+ #panel-flow .node .l1 .d { margin-left: auto; font: 11px var(--mono); color: var(--fg-3); }
27
+ #panel-flow .node .l1 .mono { font-size: 11px; }
28
+ #panel-flow .node .title { font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
29
+ #panel-flow .canvas.focus .node:not(.hot) { opacity: .3; }
30
+ #panel-flow .node.sel { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
31
+ #panel-flow .legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--fg-2); align-items: center; }
32
+ #panel-flow .legend span { display: inline-flex; align-items: center; gap: 6px; }
33
+ #panel-flow .check { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-2); }
34
+ #panel-flow .stats { display: flex; flex-wrap: wrap; }
35
+ #panel-flow .stats > div { padding: 12px 16px; display: flex; flex-direction: column; gap: 1px; min-width: 140px; }
36
+ #panel-flow .stats > div + div { border-left: 1px solid var(--line); }
37
+ #panel-flow .stats .k { font-size: 12px; color: var(--fg-3); }
38
+ #panel-flow .stats .v { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
39
+ #panel-flow .stats .s { font-size: 12px; color: var(--fg-2); }
40
+ #panel-flow svg.tl { display: block; }
41
+ #panel-flow svg.tl text { font-family: var(--font); font-size: 11px; fill: var(--fg-3); }
42
+ #panel-flow svg.tl .lane-t { fill: var(--fg); font-weight: 500; font-size: 12px; }
43
+ #panel-flow svg.tl .grid { stroke: var(--line); stroke-width: 1; }
44
+ #panel-flow svg.tl .band { fill: var(--surface-2); opacity: .5; }
45
+ #panel-flow svg.tl .gap { fill: var(--surface-2); }
46
+ #panel-flow svg.tl .gap-line { stroke: var(--line-2); stroke-dasharray: 3 3; }
47
+ #panel-flow svg.tl .bar-w { fill: color-mix(in srgb, var(--ok) 30%, var(--surface)); stroke: var(--ok); stroke-width: 1; cursor: pointer; }
48
+ #panel-flow svg.tl .bar-c { fill: var(--ok); pointer-events: none; }
49
+ #panel-flow svg.tl .bar-w.in_progress { fill: url(#fl-stripes); stroke: var(--run); }
50
+ #panel-flow svg.tl .bar-w.blocked { fill: color-mix(in srgb, var(--bad) 22%, var(--surface)); stroke: var(--bad); }
51
+ #panel-flow svg.tl .bar-w:hover { stroke-width: 2; }
52
+ #panel-flow svg.tl .bar-l { fill: var(--fg); font-size: 11px; pointer-events: none; font-family: var(--mono); }
53
+ #panel-flow svg.tl .now-line { stroke: var(--run); stroke-width: 1.5; }
54
+ #panel-flow svg.tl .stripe-a { fill: var(--run-soft); }
55
+ #panel-flow svg.tl .stripe-b { fill: color-mix(in srgb, var(--run) 30%, var(--surface)); }
56
+ #panel-flow .fl-untimed { padding: 12px 16px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--fg-3); }
57
+ #panel-flow .fl-untimed button { border: 0; background: none; padding: 0; color: var(--fg-2); cursor: pointer; }
58
+ #panel-flow .fl-untimed button:hover { text-decoration: underline; }
59
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowTimeModelScript: string;
@@ -0,0 +1,95 @@
1
+ // Timeline numbers: bar times, squeezed idle gaps, agent rows and summary figures. Pure.
2
+ export const flowTimeModelScript = String.raw ` var TL = { GAP: 15 * 60000, MIN_BAR: 15000, LW: 130, GW: 40, ROWH: 30, TOPY: 30 };
3
+
4
+ function flowEnd(t, now) {
5
+ if (t.status === 'in_progress') return Math.max(now, t.startT + 1000);
6
+ if (t.endT && t.endT > t.startT) return t.endT;
7
+ return t.startT + (t.durationMs || TL.MIN_BAR);
8
+ }
9
+
10
+ /** Merges sorted [start, end] pairs; touching within slack counts as one stretch. */
11
+ function flowMerge(iv, slack) {
12
+ var out = [];
13
+ iv.forEach(function (p) {
14
+ var last = out[out.length - 1];
15
+ if (last && p[0] - last[1] <= slack) last[1] = Math.max(last[1], p[1]);
16
+ else out.push([p[0], p[1]]);
17
+ });
18
+ return out;
19
+ }
20
+
21
+ function flowSummary(timed, now) {
22
+ var iv = timed.map(function (t) { return [t.startT, flowEnd(t, now)]; }).sort(function (a, b) { return a[0] - b[0]; });
23
+ var busy = flowMerge(iv, 0);
24
+ var busyMs = busy.reduce(function (s, p) { return s + p[1] - p[0]; }, 0);
25
+ var last = Math.max.apply(null, iv.map(function (p) { return p[1]; }));
26
+ var span = last - iv[0][0];
27
+ var longest = 0, at = null;
28
+ for (var i = 1; i < busy.length; i++) {
29
+ var g = busy[i][0] - busy[i - 1][1];
30
+ if (g > longest) { longest = g; at = busy[i - 1][1]; }
31
+ }
32
+ var peak = 0;
33
+ iv.forEach(function (p) {
34
+ var c = iv.filter(function (o) { return o[0] <= p[0] && o[1] > p[0]; }).length;
35
+ peak = Math.max(peak, c);
36
+ });
37
+ return { iv: iv, start: iv[0][0], span: span, busyMs: busyMs, busyPct: Math.round(busyMs / (span || 1) * 100),
38
+ peak: peak, longest: longest, longestAt: at };
39
+ }
40
+
41
+ /** Time segments left to right: real stretches plus fixed-width bands for idle gaps over 15 minutes. */
42
+ function flowSegments(iv, width) {
43
+ var merged = flowMerge(iv, TL.GAP);
44
+ var plotW = width - TL.LW - 24 - (merged.length - 1) * TL.GW;
45
+ var active = merged.reduce(function (s, p) { return s + p[1] - p[0]; }, 0) || 1;
46
+ var k = Math.max(plotW, 120) / active, segs = [], x = TL.LW;
47
+ merged.forEach(function (p, i) {
48
+ if (i) {
49
+ segs.push({ gap: true, t0: merged[i - 1][1], t1: p[0], x0: x, x1: x + TL.GW });
50
+ x += TL.GW;
51
+ }
52
+ segs.push({ t0: p[0], t1: p[1], x0: x, x1: x + (p[1] - p[0]) * k });
53
+ x += (p[1] - p[0]) * k;
54
+ });
55
+ var X = function (t) {
56
+ var s = segs.filter(function (q) { return t >= q.t0 && t <= q.t1; })[0] || (t < segs[0].t0 ? segs[0] : segs[segs.length - 1]);
57
+ var scale = s.gap ? TL.GW / (s.t1 - s.t0) : k;
58
+ return s.x0 + Math.max(0, Math.min(t, s.t1) - s.t0) * scale;
59
+ };
60
+ return { segs: segs, k: k, X: X, right: x };
61
+ }
62
+
63
+ /** Overlapping tasks of one agent go on extra rows. */
64
+ function flowRows(list, now) {
65
+ var rows = [], place = {};
66
+ list.slice().sort(function (a, b) { return a.startT - b.startT; }).forEach(function (t) {
67
+ var r = 0;
68
+ while (r < rows.length && rows[r] > t.startT) r++;
69
+ rows[r] = flowEnd(t, now);
70
+ place[t.id] = r;
71
+ });
72
+ return { count: Math.max(1, rows.length), place: place };
73
+ }
74
+
75
+ function flowTimeModel(m, now, outer) {
76
+ var timed = m.tasks.filter(function (t) { return t.startT; });
77
+ if (!timed.length) return null;
78
+ var sum = flowSummary(timed, now);
79
+ var width = Math.max(760, (outer || 1000) - 2);
80
+ var sg = flowSegments(sum.iv, width);
81
+ var lanes = [], y = TL.TOPY;
82
+ m.tasks.forEach(function (t) {
83
+ var a = flowAgentKey(t);
84
+ if (lanes.some(function (l) { return l.key === a; })) return;
85
+ var list = timed.filter(function (o) { return flowAgentKey(o) === a; });
86
+ if (!list.length) return;
87
+ var rows = flowRows(list, now);
88
+ lanes.push({ key: a, y: y, h: rows.count * TL.ROWH + 12, place: rows.place, tasks: list });
89
+ y += rows.count * TL.ROWH + 12;
90
+ });
91
+ return { timed: timed, untimed: m.tasks.filter(function (t) { return !t.startT; }), sum: sum, sg: sg,
92
+ lanes: lanes, width: width, height: y + 8, now: now };
93
+ }
94
+
95
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowTimelineScript: string;
@@ -0,0 +1,92 @@
1
+ // What happened view: SVG bars from real times, squeezed pauses, summary figures.
2
+ export const flowTimelineScript = String.raw ` function flowStat(k, v, s) {
3
+ return '<div><span class="k">' + k + '</span><span class="v">' + v + '</span><span class="s">' + s + '</span></div>';
4
+ }
5
+
6
+ function flowStats(s) {
7
+ return '<div class="stats">' +
8
+ flowStat('Start to finish', ui.esc(fmt.dur(s.span)), ui.esc(fmt.day(s.start) + ', ' + fmt.clock(s.start))) +
9
+ flowStat('Agents busy', s.busyPct + '%', ui.esc(fmt.dur(s.busyMs)) + ' of work time') +
10
+ flowStat('Most at once', s.peak, s.peak > 1 ? 'tasks ran side by side' : 'tasks ran one at a time') +
11
+ flowStat('Longest pause', s.longest ? ui.esc(fmt.dur(s.longest)) : 'none', s.longestAt ? 'from ' + ui.esc(fmt.clock(s.longestAt)) : 'no idle stretch') +
12
+ '</div>';
13
+ }
14
+
15
+ function flowTicks(tm) {
16
+ var sg = tm.sg, top = TL.TOPY - 6, out = '';
17
+ var nice = [1, 2, 5, 10, 15, 30, 60, 120, 240].map(function (v) { return v * 60000; });
18
+ var step = nice.filter(function (v) { return v * sg.k >= 80; })[0] || nice[nice.length - 1];
19
+ sg.segs.filter(function (s) { return !s.gap; }).forEach(function (s) {
20
+ out += '<line class="grid" x1="' + s.x0 + '" x2="' + s.x0 + '" y1="' + top + '" y2="' + tm.height + '"/><text x="' + (s.x0 + 3) + '" y="14">' + ui.esc(fmt.clock(s.t0)) + '</text>';
21
+ for (var t = Math.ceil(s.t0 / step) * step; t < s.t1; t += step) {
22
+ var tx = sg.X(t);
23
+ if (tx - s.x0 > 44) out += '<line class="grid" x1="' + tx + '" x2="' + tx + '" y1="' + top + '" y2="' + tm.height + '"/><text x="' + (tx + 3) + '" y="14">' + ui.esc(fmt.clock(t)) + '</text>';
24
+ }
25
+ });
26
+ return out;
27
+ }
28
+
29
+ function flowGaps(tm) {
30
+ var top = TL.TOPY - 6;
31
+ return tm.sg.segs.filter(function (s) { return s.gap; }).map(function (s) {
32
+ var mid = s.x0 + TL.GW / 2;
33
+ return '<g class="gap-g" data-gap="' + (s.t1 - s.t0) + '"><rect class="gap" x="' + s.x0 + '" y="' + top + '" width="' + TL.GW + '" height="' + (tm.height - top) +
34
+ '"/><line class="gap-line" x1="' + mid + '" x2="' + mid + '" y1="' + top + '" y2="' + tm.height + '"/><text x="' + mid + '" y="26" text-anchor="middle">+' +
35
+ ui.esc(fmt.dur(s.t1 - s.t0).split(' ')[0]) + '</text></g>';
36
+ }).join('');
37
+ }
38
+
39
+ function flowLaneBands(tm) {
40
+ return tm.lanes.map(function (l, i) {
41
+ return (i % 2 ? '<rect class="band" x="0" y="' + l.y + '" width="' + tm.width + '" height="' + l.h + '"/>' : '') +
42
+ '<line class="grid" x1="0" x2="' + tm.width + '" y1="' + l.y + '" y2="' + l.y + '"/><text class="lane-t" x="12" y="' + (l.y + 20) + '">' +
43
+ ui.esc(Studio.agentInfo(l.key === 'unassigned' ? null : l.key).label) + '</text>';
44
+ }).join('');
45
+ }
46
+
47
+ function flowBar(t, lane, tm) {
48
+ var X = tm.sg.X;
49
+ var x1 = X(t.startT), x2 = Math.max(X(flowEnd(t, tm.now)), x1 + 4), w = x2 - x1;
50
+ var row = lane.place[t.id], by = lane.y + 6 + row * TL.ROWH;
51
+ var cw = t.checkMs && t.status === 'completed' ? Math.min(w, t.checkMs * tm.sg.k) : 0;
52
+ var lx = w > 64 ? x1 + 6 : x2 + 4;
53
+ var took = (t.status === 'in_progress' ? 'running' : 'took ' + fmt.dur(t.durationMs)) + (t.checkMs ? ', check ' + fmt.dur(t.checkMs) : '');
54
+ var tip = ui.esc(t.id + ' · ' + t.title) + '&#10;' + ui.esc(took);
55
+ return '<g><rect class="bar-w ' + ui.esc(t.status) + '" data-task="' + ui.esc(t.id) + '" data-row="' + row + '" x="' + x1 + '" y="' + by +
56
+ '" width="' + w + '" height="21" rx="4"><title>' + tip + '</title></rect>' +
57
+ (cw > 1 ? '<rect class="bar-c" x="' + (x2 - cw) + '" y="' + (by + 17) + '" width="' + cw + '" height="4" rx="1"/>' : '') +
58
+ '<text class="bar-l" x="' + lx + '" y="' + (by + 15) + '">' + ui.esc(t.id) + '</text></g>';
59
+ }
60
+
61
+ function flowLegendTl() {
62
+ return '<div class="legend"><span><svg width="22" height="10"><rect x="0" y="0" width="22" height="10" rx="3" fill="color-mix(in srgb, var(--ok) 30%, var(--surface))" stroke="var(--ok)"/></svg>Work</span>' +
63
+ '<span><svg width="22" height="10"><rect x="0" y="6" width="22" height="4" rx="1" fill="var(--ok)"/></svg>Check at the end</span>' +
64
+ '<span><svg width="16" height="12"><rect x="0" y="0" width="16" height="12" fill="var(--surface-2)"/><line x1="8" x2="8" y1="0" y2="12" stroke="var(--line-2)" stroke-dasharray="2 2"/></svg>Long pause, squeezed</span></div>';
65
+ }
66
+
67
+ function flowUntimed(list) {
68
+ if (!list.length) return '';
69
+ return '<div class="fl-untimed">Not on the timeline: ' + list.map(function (t) {
70
+ return '<button type="button" class="mono" data-task="' + ui.esc(t.id) + '">' + ui.esc(t.id) + '</button> (' + ui.esc(Studio.statusLabel(t.status).toLowerCase()) + ')';
71
+ }).join(', ') + '</div>';
72
+ }
73
+
74
+ function flowTimeline(m, ctx) {
75
+ var tm = flowTimeModel(m, Studio.now(), ctx.width);
76
+ if (!tm) {
77
+ return '<section class="fl-panel" data-mode="timeline">' + ui.empty('No timing recorded for ' + m.id,
78
+ 'These tasks ran before Studio started recording start and finish times. The Plan view still shows how they connect.') + '</section>';
79
+ }
80
+ var nowLine = tm.timed.some(function (t) { return t.status === 'in_progress'; })
81
+ ? '<line class="now-line" x1="' + tm.sg.X(tm.now) + '" x2="' + tm.sg.X(tm.now) + '" y1="' + (TL.TOPY - 6) + '" y2="' + tm.height + '"/>' : '';
82
+ var bars = tm.lanes.map(function (l) {
83
+ return l.tasks.map(function (t) { return flowBar(t, l, tm); }).join('');
84
+ }).join('');
85
+ return '<section class="fl-panel" data-mode="timeline">' + flowStats(tm.sum) +
86
+ '<div class="fl-bar">' + flowLegendTl() + '<span class="right">Click a bar for details</span></div>' +
87
+ '<div class="flow-wrap"><svg class="tl" width="' + tm.width + '" height="' + tm.height + '" viewBox="0 0 ' + tm.width + ' ' + tm.height + '">' +
88
+ '<defs><pattern id="fl-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><rect class="stripe-a" width="8" height="8"/><rect class="stripe-b" width="4" height="8"/></pattern></defs>' +
89
+ flowLaneBands(tm) + flowGaps(tm) + flowTicks(tm) + nowLine + bars + '</svg></div>' + flowUntimed(tm.untimed) + '</section>';
90
+ }
91
+
92
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const homePage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { homeCss } from './styles.js';
3
+ import { homeScript } from './script.js';
4
+ export const homePage = {
5
+ id: 'home',
6
+ group: 'now',
7
+ label: 'Home',
8
+ icon: ICON_PATHS.home,
9
+ css: homeCss,
10
+ script: homeScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const homeScript: string;
@@ -0,0 +1,160 @@
1
+ // Home page client script: one pure render from the shared model.
2
+ export const homeScript = String.raw ` var esc = Studio.ui.esc;
3
+ var RECENT_CAP = 6;
4
+
5
+ function head() {
6
+ return '<div class="page-head"><div><h1>Home</h1><p>What needs you, what is running, and what just finished.</p></div></div>';
7
+ }
8
+
9
+ function panel(title, right, body) {
10
+ return '<section class="panel"><div class="panel-head"><h2>' + esc(title) + '</h2>' + right + '</div>' + body + '</section>';
11
+ }
12
+ function rightText(text) { return '<span class="right">' + esc(text) + '</span>'; }
13
+
14
+ function actBtn(act, id, label, busy) {
15
+ var on = busy === act.slice(5);
16
+ return '<button type="button" class="btn" data-act="' + act + '" data-id="' + esc(id) + '"' + (busy ? ' disabled' : '') + '>' +
17
+ (on ? '<span class="spinner" aria-hidden="true"></span>' : '') + esc(label) + '</button>';
18
+ }
19
+
20
+ function needReason(item) {
21
+ var t = item.task;
22
+ if (t.status === 'blocked') return t.reason || 'Paused after 3 attempts.';
23
+ var e = item.escalation || {};
24
+ return e.reason || e.summary || e.details || 'Waiting for your decision.';
25
+ }
26
+
27
+ function needItem(item) {
28
+ var t = item.task;
29
+ var busy = Studio.busyAction ? Studio.busyAction(t.id) : null;
30
+ var stuck = t.status === 'blocked';
31
+ var out = '<button type="button" class="btn primary" data-task="' + esc(t.id) + '">' + (stuck ? 'See what failed' : 'Open task') + '</button>';
32
+ if (stuck) out += actBtn('home-start', t.id, 'Try again', busy);
33
+ if (item.hasProposal) out += actBtn('home-proposal', t.id, 'Review proposal', busy);
34
+ else if (item.escalation) out += actBtn('home-ask', t.id, 'Ask the architect', busy);
35
+ return '<div class="need" data-need="' + esc(t.id) + '">' + Studio.ui.statusIcon('blocked') + '<div class="body">' +
36
+ '<div><span class="mono faint">' + esc(t.id) + '</span> · ' + Studio.ui.agentChip(t.agent) + '</div>' +
37
+ '<b style="font-weight:500">' + esc(t.title) + '</b><span class="why">' + esc(needReason(item)) + '</span>' +
38
+ '<div class="row">' + out + '</div></div></div>';
39
+ }
40
+
41
+ function needsPanel(model) {
42
+ var n = model.needsYou;
43
+ var body = n.length ? n.map(needItem).join('') :
44
+ '<div class="calm">' + Studio.ui.statusIcon('completed') + 'Nothing needs you. No stuck tasks, questions or proposals.</div>';
45
+ var right = n.length ? n.length + (n.length === 1 ? ' item' : ' items') : 'All clear';
46
+ return panel('Needs you', rightText(right), '<div class="panel-body">' + body + '</div>');
47
+ }
48
+
49
+ function lastFinished(model) {
50
+ var done = model.tasks.filter(function (t) { return t.status === 'completed'; });
51
+ var timed = done.filter(function (t) { return t.endT; }).sort(function (a, b) { return b.endT - a.endT; });
52
+ return timed[0] || done[done.length - 1] || null;
53
+ }
54
+
55
+ function idleText(model) {
56
+ var last = lastFinished(model);
57
+ if (!last) return 'No agent is working. Last finished nothing yet.';
58
+ var when = last.endT ? ' ' + esc(Studio.fmt.ago(last.endT)) : '';
59
+ return 'No agent is working. Last finished <span class="mono">' + esc(last.id) + '</span>' + when + '.';
60
+ }
61
+
62
+ function nowItem(t) {
63
+ var timer = t.startT ? '<span class="timer" data-since="' + t.startT + '">' + Studio.fmt.dur(Studio.now() - t.startT) + '</span>' : '';
64
+ return '<button type="button" class="now" data-task="' + esc(t.id) + '"><div class="l1">' + Studio.ui.statusIcon('in_progress') +
65
+ Studio.ui.agentChip(t.agent) + timer + '</div><div class="title">' + esc(t.title) + '</div>' +
66
+ (t.latestLog ? '<div class="log">' + esc(t.latestLog) + '</div>' : '') + '</button>';
67
+ }
68
+
69
+ function nowPanel(model) {
70
+ var r = model.running;
71
+ var body = r.length ? r.map(nowItem).join('') : '<div class="calm">' + idleText(model) + '</div>';
72
+ return panel('Right now', rightText(r.length ? r.length + ' working' : 'Idle'), '<div class="panel-body">' + body + '</div>');
73
+ }
74
+
75
+ function figure(k, value, total, sub) {
76
+ return '<div><span class="k">' + esc(k) + '</span><span class="v">' + esc(value) +
77
+ (total == null ? '' : '<small> / ' + esc(total) + '</small>') + '</span><span class="s">' + esc(sub) + '</span></div>';
78
+ }
79
+
80
+ function spendNote(s) {
81
+ var parts = [];
82
+ if (s.cacheHitRate != null) parts.push((s.cacheHitRate * 100).toFixed(1) + '% cached');
83
+ if (s.cacheSavingsUsd != null) parts.push('saved about ' + Studio.fmt.money(s.cacheSavingsUsd));
84
+ return parts.length ? parts.join(', ') : 'no spend recorded';
85
+ }
86
+
87
+ function stripPanel(model) {
88
+ var s = model.summary;
89
+ var left = s.tasksLeft ? s.tasksLeft + ' left' : 'all done';
90
+ var have = s.contractsPresent;
91
+ var plan = have == null ? 'not checked' : (have >= s.contractsTotal ? 'all present and intact' : (s.contractsTotal - have) + ' missing');
92
+ return '<section class="panel strip">' +
93
+ figure('Milestones', s.milestonesDone, s.milestonesTotal, 'finished') +
94
+ figure('Tasks', s.tasksDone, s.tasksTotal, left) +
95
+ figure('Spend', Studio.fmt.money(s.spendUsd), null, spendNote(s)) +
96
+ figure('Plan files', have == null ? '–' : have, s.contractsTotal, plan) + '</section>';
97
+ }
98
+
99
+ function recentTasks(model) {
100
+ return model.tasks.filter(function (t) { return t.status === 'completed' && t.endT; })
101
+ .sort(function (a, b) { return b.endT - a.endT; }).slice(0, RECENT_CAP);
102
+ }
103
+
104
+ function finishedRow(t) {
105
+ return '<button type="button" class="row-btn" data-task="' + esc(t.id) + '">' + Studio.ui.statusIcon(t.status) +
106
+ '<span class="mono faint hide-sm">' + esc(t.id) + '</span><span class="t">' + esc(t.title) + '</span>' +
107
+ '<span class="hide-sm">' + Studio.ui.agentChip(t.agent) + '</span>' +
108
+ '<span class="faint num row-right">' + Studio.ui.when(t.endT) + '</span></button>';
109
+ }
110
+
111
+ function finishedPanel(model) {
112
+ var list = recentTasks(model);
113
+ var body = list.length ? '<div class="list">' + list.map(finishedRow).join('') + '</div>' :
114
+ Studio.ui.empty('Nothing has finished yet.', '');
115
+ var btn = '<button type="button" class="btn ghost right" data-go="tasks">Open Tasks</button>';
116
+ return panel('Just finished', btn, body);
117
+ }
118
+
119
+ function msRow(m) {
120
+ var pct = m.total ? Math.round((m.done / m.total) * 100) : 0;
121
+ return '<button type="button" class="ms-row" data-ms="' + esc(m.id) + '"><span class="mono faint">' + esc(m.id) + '</span>' +
122
+ '<span class="t" title="' + esc(m.name) + '">' + esc(m.short) + '</span>' +
123
+ '<span class="bar"><i style="width:' + pct + '%"></i></span>' +
124
+ '<span class="num faint" style="font-size:12px;text-align:right">' + m.done + '/' + m.total + '</span></button>';
125
+ }
126
+
127
+ function milestonesPanel(model) {
128
+ var list = model.milestones.slice().reverse();
129
+ var body = list.length ? '<div class="list">' + list.map(msRow).join('') + '</div>' :
130
+ Studio.ui.empty('No milestones yet.', 'The plan has no milestones.');
131
+ return panel('Milestones', rightText('newest first'), body);
132
+ }
133
+
134
+ function skeleton() {
135
+ var bar = function (w) { return '<div class="skel" style="width:' + w + '"></div>'; };
136
+ var block = '<div class="skel-block">' + bar('40%') + bar('100%') + bar('80%') + '</div>';
137
+ return head() + '<div class="home-top">' + block + block + '</div>' +
138
+ '<div class="skel-strip">' + block + block + block + block + '</div>' +
139
+ '<div class="home-top">' + block + block + '</div>';
140
+ }
141
+
142
+ function render(ctx) {
143
+ var model = ctx.model;
144
+ return head() +
145
+ '<div class="home-top">' + needsPanel(model) + nowPanel(model) + '</div>' +
146
+ stripPanel(model) +
147
+ '<div class="home-top">' + finishedPanel(model) + milestonesPanel(model) + '</div>';
148
+ }
149
+
150
+ function forward(name) {
151
+ return function (el) { Studio.taskAction(name, el.getAttribute('data-id')); };
152
+ }
153
+
154
+ Studio.registerPage('home', {
155
+ deps: ['status', 'tasks', 'telemetry', 'runs', 'escalations'],
156
+ render: render,
157
+ skeleton: skeleton,
158
+ actions: { 'home-start': forward('start'), 'home-proposal': forward('proposal'), 'home-ask': forward('ask') }
159
+ });
160
+ `;
@@ -0,0 +1 @@
1
+ export declare const homeCss: string;
@@ -0,0 +1,48 @@
1
+ // Home page styles, all scoped under #panel-home.
2
+ export const homeCss = String.raw `#panel-home .page-head { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
3
+ #panel-home .page-head h1 { font-size: 22px; font-weight: 600; margin: 0; letter-spacing: -.01em; text-wrap: balance; }
4
+ #panel-home .page-head p { margin: 4px 0 0; color: var(--fg-2); }
5
+ #panel-home .panel { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; min-width: 0; }
6
+ #panel-home .panel-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
7
+ #panel-home .panel-head h2 { font-size: 14px; font-weight: 600; margin: 0; }
8
+ #panel-home .panel-head .right { margin-left: auto; font-size: 12.5px; color: var(--fg-3); }
9
+ #panel-home .panel-body { padding: 14px 16px; }
10
+ #panel-home .home-top { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 16px; }
11
+ #panel-home .need { display: flex; gap: 12px; padding: 12px; border-radius: 10px; background: var(--bad-soft); }
12
+ #panel-home .need + .need { margin-top: 8px; }
13
+ #panel-home .need .body { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
14
+ #panel-home .need .why { font-size: 13px; color: var(--fg-2); overflow-wrap: anywhere; }
15
+ #panel-home .need .row { display: flex; gap: 8px; flex-wrap: wrap; }
16
+ #panel-home .calm { display: flex; align-items: center; gap: 10px; color: var(--fg-2); padding: 6px 0; }
17
+ #panel-home .calm .st { color: var(--ok); }
18
+ #panel-home .now { display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); cursor: pointer; }
19
+ #panel-home .now:hover { background: var(--surface-2); }
20
+ #panel-home .now + .now { margin-top: 8px; }
21
+ #panel-home .now .l1 { display: flex; align-items: center; gap: 8px; }
22
+ #panel-home .now .timer { margin-left: auto; font: 12px var(--mono); color: var(--run); }
23
+ #panel-home .now .log { font: 11.5px var(--mono); color: var(--fg-2); background: var(--surface-2); padding: 6px 8px; border-radius: 6px; overflow-wrap: anywhere; }
24
+ #panel-home .strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
25
+ #panel-home .strip > div { padding: 14px 16px; display: flex; flex-direction: column; gap: 2px; }
26
+ #panel-home .strip > div + div { border-left: 1px solid var(--line); }
27
+ #panel-home .strip .k { font-size: 12px; color: var(--fg-3); }
28
+ #panel-home .strip .v { font-size: 22px; font-weight: 600; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
29
+ #panel-home .strip .v small { font-size: 13px; font-weight: 400; color: var(--fg-3); }
30
+ #panel-home .strip .s { font-size: 12px; color: var(--fg-2); }
31
+ #panel-home .list .row-btn:first-child, #panel-home .list .ms-row:first-child { border-top: 0; }
32
+ #panel-home .ms-row { display: grid; grid-template-columns: 34px minmax(0, 1fr) 120px 64px; gap: 12px; align-items: center; padding: 9px 16px; border: 0; border-top: 1px solid var(--line); background: none; text-align: left; width: 100%; cursor: pointer; }
33
+ #panel-home .ms-row:hover { background: var(--surface-2); }
34
+ #panel-home .ms-row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
35
+ #panel-home .bar { height: 5px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
36
+ #panel-home .bar i { display: block; height: 100%; background: var(--ok); border-radius: 3px; }
37
+ #panel-home .row-right { text-align: right; font-size: 12px; }
38
+ #panel-home .skel-strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
39
+ @media (max-width: 900px) {
40
+ #panel-home .home-top { grid-template-columns: minmax(0, 1fr); }
41
+ #panel-home .strip, #panel-home .skel-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
42
+ #panel-home .strip > div:nth-child(3) { border-left: 0; }
43
+ }
44
+ @media (max-width: 760px) {
45
+ #panel-home .ms-row { grid-template-columns: 34px minmax(0, 1fr) 64px; }
46
+ #panel-home .ms-row .bar { display: none; }
47
+ }
48
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from './types.js';
2
+ export declare const pages: PageModule[];
@@ -0,0 +1,17 @@
1
+ import { homePage } from './home/index.js';
2
+ import { tasksPage } from './tasks/index.js';
3
+ import { flowPage } from './flow/index.js';
4
+ import { teamPage } from './team/index.js';
5
+ import { worktreesPage } from './worktrees/index.js';
6
+ import { benchmarksPage } from './benchmarks/index.js';
7
+ import { databasePage } from './database/index.js';
8
+ // Order here is the order of the sidebar and the palette.
9
+ export const pages = [
10
+ homePage,
11
+ tasksPage,
12
+ flowPage,
13
+ teamPage,
14
+ worktreesPage,
15
+ benchmarksPage,
16
+ databasePage,
17
+ ];
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const tasksPage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { tasksScript } from './script.js';
3
+ import { tasksCss } from './styles.js';
4
+ export const tasksPage = {
5
+ id: 'tasks',
6
+ group: 'now',
7
+ label: 'Tasks',
8
+ icon: ICON_PATHS.tasks,
9
+ css: tasksCss,
10
+ script: tasksScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const tasksScript: string;