@njeybe/nativ 2.1.0 → 2.3.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 (144) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +17 -0
  3. package/dist/server/studio-ui/document.d.ts +3 -0
  4. package/dist/server/studio-ui/document.js +22 -0
  5. package/dist/server/studio-ui/pages/benchmarks/index.d.ts +2 -0
  6. package/dist/server/studio-ui/pages/benchmarks/index.js +11 -0
  7. package/dist/server/studio-ui/pages/benchmarks/script.d.ts +1 -0
  8. package/dist/server/studio-ui/pages/benchmarks/script.js +104 -0
  9. package/dist/server/studio-ui/pages/benchmarks/styles.d.ts +1 -0
  10. package/dist/server/studio-ui/pages/benchmarks/styles.js +30 -0
  11. package/dist/server/studio-ui/pages/database/browser.d.ts +1 -0
  12. package/dist/server/studio-ui/pages/database/browser.js +326 -0
  13. package/dist/server/studio-ui/pages/database/connection.d.ts +1 -0
  14. package/dist/server/studio-ui/pages/database/connection.js +158 -0
  15. package/dist/server/studio-ui/pages/database/data-shell.d.ts +1 -0
  16. package/dist/server/studio-ui/pages/database/data-shell.js +165 -0
  17. package/dist/server/studio-ui/pages/database/drift.d.ts +1 -0
  18. package/dist/server/studio-ui/pages/database/drift.js +301 -0
  19. package/dist/server/studio-ui/pages/database/events.d.ts +1 -0
  20. package/dist/server/studio-ui/pages/database/events.js +231 -0
  21. package/dist/server/studio-ui/pages/database/explorer.d.ts +1 -0
  22. package/dist/server/studio-ui/pages/database/explorer.js +183 -0
  23. package/dist/server/studio-ui/pages/database/index.d.ts +2 -0
  24. package/dist/server/studio-ui/pages/database/index.js +28 -0
  25. package/dist/server/studio-ui/pages/database/markup.d.ts +1 -0
  26. package/dist/server/studio-ui/pages/database/markup.js +32 -0
  27. package/dist/server/studio-ui/pages/database/state.d.ts +2 -0
  28. package/dist/server/studio-ui/pages/database/state.js +57 -0
  29. package/dist/server/studio-ui/pages/database/styles.d.ts +1 -0
  30. package/dist/server/studio-ui/pages/database/styles.js +118 -0
  31. package/dist/server/studio-ui/pages/flow/index.d.ts +2 -0
  32. package/dist/server/studio-ui/pages/flow/index.js +11 -0
  33. package/dist/server/studio-ui/pages/flow/layout.d.ts +1 -0
  34. package/dist/server/studio-ui/pages/flow/layout.js +105 -0
  35. package/dist/server/studio-ui/pages/flow/plan-view.d.ts +1 -0
  36. package/dist/server/studio-ui/pages/flow/plan-view.js +67 -0
  37. package/dist/server/studio-ui/pages/flow/script.d.ts +1 -0
  38. package/dist/server/studio-ui/pages/flow/script.js +61 -0
  39. package/dist/server/studio-ui/pages/flow/styles.d.ts +1 -0
  40. package/dist/server/studio-ui/pages/flow/styles.js +59 -0
  41. package/dist/server/studio-ui/pages/flow/timeline-model.d.ts +1 -0
  42. package/dist/server/studio-ui/pages/flow/timeline-model.js +95 -0
  43. package/dist/server/studio-ui/pages/flow/timeline-view.d.ts +1 -0
  44. package/dist/server/studio-ui/pages/flow/timeline-view.js +92 -0
  45. package/dist/server/studio-ui/pages/home/index.d.ts +2 -0
  46. package/dist/server/studio-ui/pages/home/index.js +11 -0
  47. package/dist/server/studio-ui/pages/home/script.d.ts +1 -0
  48. package/dist/server/studio-ui/pages/home/script.js +160 -0
  49. package/dist/server/studio-ui/pages/home/styles.d.ts +1 -0
  50. package/dist/server/studio-ui/pages/home/styles.js +48 -0
  51. package/dist/server/studio-ui/pages/index.d.ts +2 -0
  52. package/dist/server/studio-ui/pages/index.js +17 -0
  53. package/dist/server/studio-ui/pages/tasks/index.d.ts +2 -0
  54. package/dist/server/studio-ui/pages/tasks/index.js +11 -0
  55. package/dist/server/studio-ui/pages/tasks/script.d.ts +1 -0
  56. package/dist/server/studio-ui/pages/tasks/script.js +96 -0
  57. package/dist/server/studio-ui/pages/tasks/styles.d.ts +1 -0
  58. package/dist/server/studio-ui/pages/tasks/styles.js +37 -0
  59. package/dist/server/studio-ui/pages/team/index.d.ts +2 -0
  60. package/dist/server/studio-ui/pages/team/index.js +11 -0
  61. package/dist/server/studio-ui/pages/team/script.d.ts +1 -0
  62. package/dist/server/studio-ui/pages/team/script.js +112 -0
  63. package/dist/server/studio-ui/pages/team/styles.d.ts +1 -0
  64. package/dist/server/studio-ui/pages/team/styles.js +22 -0
  65. package/dist/server/studio-ui/pages/types.d.ts +13 -0
  66. package/dist/server/studio-ui/pages/types.js +1 -0
  67. package/dist/server/studio-ui/pages/worktrees/index.d.ts +2 -0
  68. package/dist/server/studio-ui/pages/worktrees/index.js +11 -0
  69. package/dist/server/studio-ui/pages/worktrees/script.d.ts +1 -0
  70. package/dist/server/studio-ui/pages/worktrees/script.js +104 -0
  71. package/dist/server/studio-ui/pages/worktrees/styles.d.ts +1 -0
  72. package/dist/server/studio-ui/pages/worktrees/styles.js +15 -0
  73. package/dist/server/studio-ui/shell/actions.d.ts +1 -0
  74. package/dist/server/studio-ui/shell/actions.js +49 -0
  75. package/dist/server/studio-ui/shell/boot.d.ts +2 -0
  76. package/dist/server/studio-ui/shell/boot.js +13 -0
  77. package/dist/server/studio-ui/shell/console.d.ts +1 -0
  78. package/dist/server/studio-ui/shell/console.js +194 -0
  79. package/dist/server/studio-ui/shell/core.d.ts +1 -0
  80. package/dist/server/studio-ui/shell/core.js +50 -0
  81. package/dist/server/studio-ui/shell/detail-view.d.ts +1 -0
  82. package/dist/server/studio-ui/shell/detail-view.js +80 -0
  83. package/dist/server/studio-ui/shell/detail.d.ts +1 -0
  84. package/dist/server/studio-ui/shell/detail.js +83 -0
  85. package/dist/server/studio-ui/shell/dispatch.d.ts +1 -0
  86. package/dist/server/studio-ui/shell/dispatch.js +156 -0
  87. package/dist/server/studio-ui/shell/events-tools.d.ts +1 -0
  88. package/dist/server/studio-ui/shell/events-tools.js +94 -0
  89. package/dist/server/studio-ui/shell/format.d.ts +1 -0
  90. package/dist/server/studio-ui/shell/format.js +151 -0
  91. package/dist/server/studio-ui/shell/icons.d.ts +2 -0
  92. package/dist/server/studio-ui/shell/icons.js +20 -0
  93. package/dist/server/studio-ui/shell/markup-head.d.ts +1 -0
  94. package/dist/server/studio-ui/shell/markup-head.js +43 -0
  95. package/dist/server/studio-ui/shell/markup-panels.d.ts +2 -0
  96. package/dist/server/studio-ui/shell/markup-panels.js +7 -0
  97. package/dist/server/studio-ui/shell/markup-tools.d.ts +1 -0
  98. package/dist/server/studio-ui/shell/markup-tools.js +222 -0
  99. package/dist/server/studio-ui/shell/model.d.ts +1 -0
  100. package/dist/server/studio-ui/shell/model.js +192 -0
  101. package/dist/server/studio-ui/shell/paint.d.ts +1 -0
  102. package/dist/server/studio-ui/shell/paint.js +154 -0
  103. package/dist/server/studio-ui/shell/palette-view.d.ts +1 -0
  104. package/dist/server/studio-ui/shell/palette-view.js +60 -0
  105. package/dist/server/studio-ui/shell/palette.d.ts +1 -0
  106. package/dist/server/studio-ui/shell/palette.js +80 -0
  107. package/dist/server/studio-ui/shell/pipeline-state.d.ts +1 -0
  108. package/dist/server/studio-ui/shell/pipeline-state.js +152 -0
  109. package/dist/server/studio-ui/shell/proposals.d.ts +1 -0
  110. package/dist/server/studio-ui/shell/proposals.js +197 -0
  111. package/dist/server/studio-ui/shell/pure.d.ts +3 -0
  112. package/dist/server/studio-ui/shell/pure.js +9 -0
  113. package/dist/server/studio-ui/shell/router.d.ts +1 -0
  114. package/dist/server/studio-ui/shell/router.js +95 -0
  115. package/dist/server/studio-ui/shell/sidebar-view.d.ts +1 -0
  116. package/dist/server/studio-ui/shell/sidebar-view.js +38 -0
  117. package/dist/server/studio-ui/shell/sync.d.ts +1 -0
  118. package/dist/server/studio-ui/shell/sync.js +120 -0
  119. package/dist/server/studio-ui/shell/toasts-format.d.ts +1 -0
  120. package/dist/server/studio-ui/shell/toasts-format.js +89 -0
  121. package/dist/server/studio-ui/shell/triage.d.ts +1 -0
  122. package/dist/server/studio-ui/shell/triage.js +210 -0
  123. package/dist/server/studio-ui/styles/console.d.ts +1 -0
  124. package/dist/server/studio-ui/styles/console.js +83 -0
  125. package/dist/server/studio-ui/styles/dialogs.d.ts +1 -0
  126. package/dist/server/studio-ui/styles/dialogs.js +118 -0
  127. package/dist/server/studio-ui/styles/responsive.d.ts +1 -0
  128. package/dist/server/studio-ui/styles/responsive.js +28 -0
  129. package/dist/server/studio-ui/styles/shell.d.ts +1 -0
  130. package/dist/server/studio-ui/styles/shell.js +117 -0
  131. package/dist/server/studio-ui/styles/strategist.d.ts +1 -0
  132. package/dist/server/studio-ui/styles/strategist.js +77 -0
  133. package/dist/server/studio-ui/styles/tokens-base.d.ts +1 -0
  134. package/dist/server/studio-ui/styles/tokens-base.js +131 -0
  135. package/dist/server/studio-ui.d.ts +1 -10
  136. package/dist/server/studio-ui.js +63 -5314
  137. package/package.json +2 -2
  138. package/plugin/.claude-plugin/plugin.json +1 -1
  139. package/plugin/agents/architect.md +9 -5
  140. package/templates/GEMINI.md +12 -8
  141. package/templates/claude-agents/architect.md +9 -5
  142. package/templates/dot-ai/subagents/flutter-developer.md +10 -0
  143. package/templates/dot-ai/subagents/frontend.md +10 -0
  144. package/templates/dot-ai/ui_specs.md +36 -6
@@ -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;
@@ -0,0 +1,96 @@
1
+ // Tasks page: one milestone as four columns, then the other milestones as a history list.
2
+ export const tasksScript = String.raw ` var ui = Studio.ui;
3
+ var CAP = 5;
4
+ var view = { lastMs: null, showAll: false, open: {} };
5
+
6
+ function head(m) {
7
+ var lead = m ? ui.esc(m.name) : 'The work of one milestone, by state.';
8
+ return '<div class="panel-head"><div class="panel-title"><h1>Tasks</h1><p class="lead">' + lead + '</p></div>' +
9
+ '<div class="panel-actions">' + ui.milestoneSelect() + '</div></div>';
10
+ }
11
+
12
+ function byNewest(a, b) { return (b.endT || 0) - (a.endT || 0); }
13
+
14
+ function metaHtml(t, fmt) {
15
+ if (t.status === 'completed') {
16
+ return '<span>' + ui.esc(fmt.ago(t.endT)) + '</span>' + (t.durationMs ? '<span>took ' + ui.esc(fmt.dur(t.durationMs)) + '</span>' : '');
17
+ }
18
+ if (t.status === 'in_progress') {
19
+ var live = t.startT ? '<span data-since="' + t.startT + '">' + fmt.dur(Studio.now() - t.startT) + '</span>' : '';
20
+ return '<span class="run">Running ' + live + '</span><span>attempt ' + (t.attempt || 1) + ' of ' + t.maxAttempts + '</span>';
21
+ }
22
+ if (t.status === 'blocked') {
23
+ return '<span class="bad">Paused after ' + (t.attempt || t.maxAttempts) + ' of ' + t.maxAttempts + ' attempts</span>';
24
+ }
25
+ if (t.waitingOn.length) return '<span>Waiting on ' + t.waitingOn.map(ui.esc).join(', ') + '</span>';
26
+ return '<span>Ready to start</span>';
27
+ }
28
+
29
+ function card(t, fmt) {
30
+ return '<button type="button" class="tcard ' + ui.esc(t.status) + '" data-task="' + ui.esc(t.id) + '">' +
31
+ '<div class="l1">' + ui.statusIcon(t.status) + '<span class="mono faint">' + ui.esc(t.id) + '</span>' + ui.agentChip(t.agent) + '</div>' +
32
+ '<div class="title">' + ui.esc(t.title) + '</div><div class="meta">' + metaHtml(t, fmt) + '</div></button>';
33
+ }
34
+
35
+ function column(label, status, list, shown, empty, extra, fmt) {
36
+ var body = shown.length ? shown.map(function (t) { return card(t, fmt); }).join('') : '<div class="col-empty">' + empty + '</div>';
37
+ return '<div class="col" data-col="' + status + '"><div class="col-head">' + ui.statusIcon(status) + label +
38
+ '<span class="count">' + list.length + '</span></div>' + body + (extra || '') + '</div>';
39
+ }
40
+
41
+ function board(m, fmt) {
42
+ var by = function (s) { return m.tasks.filter(function (t) { return t.status === s; }); };
43
+ var done = by('completed').reverse().sort(byNewest);
44
+ var shown = view.showAll ? done : done.slice(0, CAP);
45
+ var more = done.length > CAP
46
+ ? '<button type="button" class="more" data-act="tasks-more">' + (view.showAll ? 'Show fewer' : 'Show all ' + done.length) + '</button>' : '';
47
+ var up = by('pending'), run = by('in_progress'), stuck = by('blocked');
48
+ return '<div class="board">' +
49
+ column('Up next', 'pending', up, up, 'Nothing queued', '', fmt) +
50
+ column('Running', 'in_progress', run, run, 'No task is running', '', fmt) +
51
+ column('Just finished', 'completed', done, shown, 'Nothing finished yet', more, fmt) +
52
+ column('Stuck', 'blocked', stuck, stuck, 'Nothing is stuck', '', fmt) + '</div>';
53
+ }
54
+
55
+ function histRow(m, fmt) {
56
+ var open = !!view.open[m.id];
57
+ var full = m.total > 0 && m.done === m.total;
58
+ var tasks = open ? '<div class="hist-tasks">' + m.tasks.slice().reverse().sort(byNewest).map(function (t) {
59
+ return ui.taskRow(t, t.durationMs ? fmt.dur(t.durationMs) : '');
60
+ }).join('') + '</div>' : '';
61
+ return '<div><button type="button" class="hist-row" data-act="tasks-hist" data-ms-id="' + ui.esc(m.id) + '" aria-expanded="' + open + '">' +
62
+ '<span class="chev">' + ui.icon('chev', 2) + '</span><span class="mono faint">' + ui.esc(m.id) + '</span>' +
63
+ '<span class="t">' + ui.esc(m.short) + '</span>' +
64
+ '<span class="status-pill ' + (full ? 'completed' : 'in_progress') + '">' + m.done + '/' + m.total + '</span>' +
65
+ '<span class="faint num small">' + (m.lastEndT ? ui.esc(fmt.day(m.lastEndT)) : 'no timing') + '</span>' +
66
+ '<span class="faint num small">' + (m.workMs ? ui.esc(fmt.dur(m.workMs)) + ' work' : '') + '</span>' +
67
+ '<span class="faint num small">' + (m.costUsd != null ? ui.esc(fmt.money(m.costUsd)) : '') + '</span></button>' + tasks + '</div>';
68
+ }
69
+
70
+ function others(model, current, fmt) {
71
+ var list = model.milestones.filter(function (m) { return m.id !== current.id; }).reverse();
72
+ if (!list.length) return '';
73
+ return '<h2 class="section-title">Other milestones</h2><section class="hist">' +
74
+ list.map(function (m) { return histRow(m, fmt); }).join('') + '</section>';
75
+ }
76
+
77
+ Studio.registerPage('tasks', {
78
+ deps: ['tasks', 'runs', 'escalations'],
79
+ render: function (ctx) {
80
+ var model = ctx.model;
81
+ var wanted = ctx.state.milestoneId || model.activeMilestoneId;
82
+ var m = model.milestones.filter(function (x) { return x.id === wanted; })[0];
83
+ if (!m) return head(null) + ui.empty('No milestones yet', 'Add tasks to the plan to see them here.');
84
+ if (view.lastMs !== m.id) { view.lastMs = m.id; view.showAll = false; }
85
+ return head(m) + board(m, ctx.fmt) + others(model, m, ctx.fmt);
86
+ },
87
+ actions: {
88
+ 'tasks-more': function () { view.showAll = !view.showAll; Studio.repaint('tasks'); },
89
+ 'tasks-hist': function (el) {
90
+ var id = el.getAttribute('data-ms-id');
91
+ view.open[id] = !view.open[id];
92
+ Studio.repaint('tasks');
93
+ }
94
+ }
95
+ });
96
+ `;
@@ -0,0 +1 @@
1
+ export declare const tasksCss: string;
@@ -0,0 +1,37 @@
1
+ // Board and history styles, scoped under the Tasks panel.
2
+ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; align-items: start; }
3
+ #panel-tasks .col { background: var(--surface-2); border-radius: 12px; padding: 8px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
4
+ #panel-tasks .col-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 2px; font-weight: 500; font-size: 13px; }
5
+ #panel-tasks .col-head .count { margin-left: auto; background: var(--surface); border-radius: 999px; padding: 0 8px; font-size: 12px; color: var(--fg-2); }
6
+ #panel-tasks .col-empty { font-size: 12.5px; color: var(--fg-3); padding: 14px 8px; text-align: center; border: 1px dashed var(--line-2); border-radius: 8px; }
7
+ #panel-tasks .tcard { display: flex; flex-direction: column; gap: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 10px; text-align: left; width: 100%; cursor: pointer; }
8
+ #panel-tasks .tcard:hover { border-color: var(--line-2); box-shadow: var(--shadow); }
9
+ #panel-tasks .tcard.in_progress { border-color: color-mix(in srgb, var(--run) 45%, var(--line)); }
10
+ #panel-tasks .tcard.blocked { border-color: color-mix(in srgb, var(--bad) 45%, var(--line)); }
11
+ #panel-tasks .tcard .l1 { display: flex; align-items: center; gap: 6px; }
12
+ #panel-tasks .tcard .l1 .agent { margin-left: auto; }
13
+ #panel-tasks .tcard .title { font-size: 13px; font-weight: 500; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
14
+ #panel-tasks .tcard .meta { font-size: 12px; color: var(--fg-3); display: flex; gap: 8px; flex-wrap: wrap; }
15
+ #panel-tasks .tcard .meta .run { color: var(--run); }
16
+ #panel-tasks .tcard .meta .bad { color: var(--bad); }
17
+ #panel-tasks .more { border: 0; background: none; color: var(--accent); font-size: 12.5px; padding: 6px; cursor: pointer; text-align: center; }
18
+ #panel-tasks .more:hover { text-decoration: underline; }
19
+ #panel-tasks .section-title { margin: 22px 0 8px; font-size: 15px; font-weight: 600; }
20
+ #panel-tasks .hist { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
21
+ #panel-tasks .hist-row { display: grid; grid-template-columns: 16px 34px minmax(0, 1fr) auto 70px 90px 60px; 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; }
22
+ #panel-tasks .hist > div:first-child .hist-row { border-top: 0; }
23
+ #panel-tasks .hist-row:hover { background: var(--surface-2); }
24
+ #panel-tasks .hist-row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
25
+ #panel-tasks .hist-row .chev { display: inline-grid; transition: transform .15s; }
26
+ #panel-tasks .hist-row .chev svg { width: 14px; height: 14px; }
27
+ #panel-tasks .hist-row[aria-expanded="true"] .chev { transform: rotate(90deg); }
28
+ #panel-tasks .hist-row .small { font-size: 12px; }
29
+ #panel-tasks .hist-tasks { background: var(--surface-2); }
30
+ #panel-tasks .hist-tasks .row-btn { padding-left: 46px; }
31
+ @media (max-width: 1100px) { #panel-tasks .board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
32
+ @media (max-width: 640px) {
33
+ #panel-tasks .board { grid-template-columns: minmax(0, 1fr); }
34
+ #panel-tasks .hist-row { grid-template-columns: 16px 34px minmax(0, 1fr) auto; }
35
+ #panel-tasks .hist-row .small { display: none; }
36
+ }
37
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const teamPage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { teamScript } from './script.js';
3
+ import { teamCss } from './styles.js';
4
+ export const teamPage = {
5
+ id: 'team',
6
+ group: 'project',
7
+ label: 'Team',
8
+ icon: ICON_PATHS.team,
9
+ css: teamCss,
10
+ script: teamScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const teamScript: string;