@vpxa/aikit 0.1.164 → 0.1.166
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/server/dist/index.js +1 -1
- package/packages/server/dist/{server-BXHUbkn9.js → server-CkKB4ez2.js} +125 -124
- package/packages/server/viewers/task-plan-static.html +1237 -859
- package/packages/tools/dist/index.js +71 -70
- package/scaffold/dist/definitions/bodies.mjs +24 -24
- package/scaffold/dist/definitions/flows.mjs +20 -20
- package/scaffold/dist/definitions/protocols.mjs +18 -20
- package/scaffold/dist/definitions/skills/aikit.mjs +1 -1
- package/scaffold/dist/definitions/skills/present.mjs +1 -1
|
@@ -4,69 +4,120 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>AI Kit - Task Plan Static Viewer</title>
|
|
7
|
-
<style>
|
|
7
|
+
<style id="viewer-style">
|
|
8
8
|
:root {
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
|
|
27
|
-
--
|
|
28
|
-
--tp-
|
|
29
|
-
--tp-
|
|
30
|
-
--tp-
|
|
31
|
-
--tp-
|
|
32
|
-
--tp-
|
|
33
|
-
--tp-
|
|
34
|
-
--tp-
|
|
35
|
-
--tp-
|
|
36
|
-
--tp-
|
|
9
|
+
--font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
10
|
+
--font-mono: "JetBrains Mono", "Geist Mono", "Fira Code", "SF Mono", Consolas, monospace;
|
|
11
|
+
|
|
12
|
+
--space-1: 4px;
|
|
13
|
+
--space-2: 8px;
|
|
14
|
+
--space-3: 12px;
|
|
15
|
+
--space-4: 16px;
|
|
16
|
+
--space-5: 20px;
|
|
17
|
+
--space-6: 24px;
|
|
18
|
+
--space-8: 32px;
|
|
19
|
+
--space-10: 40px;
|
|
20
|
+
--space-12: 48px;
|
|
21
|
+
|
|
22
|
+
--radius-sm: 10px;
|
|
23
|
+
--radius-md: 14px;
|
|
24
|
+
--radius-xl: 22px;
|
|
25
|
+
--radius-pill: 999px;
|
|
26
|
+
|
|
27
|
+
--tp-agent-researcher: #818cf8;
|
|
28
|
+
--tp-agent-implementer: #34d399;
|
|
29
|
+
--tp-agent-frontend: #f472b6;
|
|
30
|
+
--tp-agent-reviewer: #fbbf24;
|
|
31
|
+
--tp-agent-debugger: #f87171;
|
|
32
|
+
--tp-agent-security: #fb923c;
|
|
33
|
+
--tp-agent-explorer: #22d3ee;
|
|
34
|
+
--tp-agent-documenter: #a78bfa;
|
|
35
|
+
--tp-agent-refactor: #2dd4bf;
|
|
36
|
+
--tp-agent-planner: #c084fc;
|
|
37
|
+
--tp-agent-orchestrator: #94a3b8;
|
|
38
|
+
|
|
39
|
+
--tp-status-pending: #94a3b8;
|
|
40
|
+
--tp-status-in-progress: #60a5fa;
|
|
41
|
+
--tp-status-done: #34d399;
|
|
42
|
+
--tp-status-blocked: #f87171;
|
|
43
|
+
|
|
44
|
+
--tp-phase-1: #06b6d4;
|
|
45
|
+
--tp-phase-2: #6366f1;
|
|
46
|
+
--tp-phase-3: #f59e0b;
|
|
47
|
+
--tp-phase-4: #ec4899;
|
|
48
|
+
--tp-phase-5: #14b8a6;
|
|
49
|
+
--tp-phase-6: #8b5cf6;
|
|
50
|
+
|
|
51
|
+
--viewer-bg: #020617;
|
|
52
|
+
--viewer-surface: #0f172a;
|
|
53
|
+
--viewer-surface-2: #111c32;
|
|
54
|
+
--viewer-card: rgba(15, 23, 42, 0.92);
|
|
55
|
+
--viewer-card-strong: rgba(20, 31, 56, 0.98);
|
|
56
|
+
--viewer-toolbar: rgba(8, 15, 31, 0.84);
|
|
57
|
+
--viewer-text: #f8fafc;
|
|
58
|
+
--viewer-text-muted: #94a3b8;
|
|
59
|
+
--viewer-text-dim: #64748b;
|
|
60
|
+
--viewer-border: rgba(148, 163, 184, 0.22);
|
|
61
|
+
--viewer-border-strong: rgba(148, 163, 184, 0.34);
|
|
62
|
+
--viewer-shadow: 0 30px 70px rgba(2, 6, 23, 0.42);
|
|
63
|
+
--viewer-grid: rgba(51, 65, 85, 0.38);
|
|
64
|
+
--viewer-badge-bg: rgba(15, 23, 42, 0.88);
|
|
65
|
+
--viewer-button-bg: rgba(15, 23, 42, 0.78);
|
|
66
|
+
--viewer-button-hover: rgba(30, 41, 59, 0.96);
|
|
67
|
+
--viewer-button-active: rgba(37, 99, 235, 0.14);
|
|
68
|
+
--viewer-empty-bg: rgba(15, 23, 42, 0.72);
|
|
69
|
+
--viewer-toast-bg: rgba(127, 29, 29, 0.92);
|
|
70
|
+
--viewer-toast-border: rgba(248, 113, 113, 0.55);
|
|
71
|
+
--viewer-backdrop: radial-gradient(circle at top left, rgba(6, 182, 212, 0.14), transparent 24%), radial-gradient(circle at bottom right, rgba(139, 92, 246, 0.12), transparent 28%), linear-gradient(180deg, rgba(15, 23, 42, 0.2), rgba(2, 6, 23, 0));
|
|
37
72
|
}
|
|
38
73
|
|
|
39
74
|
[data-theme="light"] {
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--
|
|
44
|
-
--
|
|
45
|
-
--
|
|
46
|
-
--
|
|
47
|
-
--
|
|
48
|
-
--
|
|
49
|
-
--
|
|
50
|
-
--
|
|
51
|
-
--
|
|
52
|
-
--
|
|
75
|
+
--viewer-bg: #ffffff;
|
|
76
|
+
--viewer-surface: #f8fafc;
|
|
77
|
+
--viewer-surface-2: #eef4fb;
|
|
78
|
+
--viewer-card: rgba(255, 255, 255, 0.96);
|
|
79
|
+
--viewer-card-strong: rgba(248, 250, 252, 0.98);
|
|
80
|
+
--viewer-toolbar: rgba(255, 255, 255, 0.9);
|
|
81
|
+
--viewer-text: #0f172a;
|
|
82
|
+
--viewer-text-muted: #475569;
|
|
83
|
+
--viewer-text-dim: #64748b;
|
|
84
|
+
--viewer-border: rgba(148, 163, 184, 0.28);
|
|
85
|
+
--viewer-border-strong: rgba(100, 116, 139, 0.36);
|
|
86
|
+
--viewer-shadow: 0 22px 48px rgba(148, 163, 184, 0.2);
|
|
87
|
+
--viewer-grid: rgba(203, 213, 225, 0.55);
|
|
88
|
+
--viewer-badge-bg: rgba(255, 255, 255, 0.92);
|
|
89
|
+
--viewer-button-bg: rgba(255, 255, 255, 0.78);
|
|
90
|
+
--viewer-button-hover: rgba(241, 245, 249, 0.98);
|
|
91
|
+
--viewer-button-active: rgba(59, 130, 246, 0.1);
|
|
92
|
+
--viewer-empty-bg: rgba(255, 255, 255, 0.82);
|
|
93
|
+
--viewer-toast-bg: rgba(254, 242, 242, 0.96);
|
|
94
|
+
--viewer-toast-border: rgba(248, 113, 113, 0.42);
|
|
95
|
+
--viewer-backdrop: radial-gradient(circle at top left, rgba(99, 102, 241, 0.08), transparent 24%), radial-gradient(circle at bottom right, rgba(20, 184, 166, 0.08), transparent 28%), linear-gradient(180deg, rgba(255, 255, 255, 0.44), rgba(248, 250, 252, 0));
|
|
53
96
|
}
|
|
54
97
|
|
|
55
98
|
@media (prefers-color-scheme: light) {
|
|
56
99
|
:root:not([data-theme="dark"]) {
|
|
57
|
-
--
|
|
58
|
-
--
|
|
59
|
-
--
|
|
60
|
-
--
|
|
61
|
-
--
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
--
|
|
65
|
-
--
|
|
66
|
-
--
|
|
67
|
-
--
|
|
68
|
-
--
|
|
69
|
-
--
|
|
100
|
+
--viewer-bg: #ffffff;
|
|
101
|
+
--viewer-surface: #f8fafc;
|
|
102
|
+
--viewer-surface-2: #eef4fb;
|
|
103
|
+
--viewer-card: rgba(255, 255, 255, 0.96);
|
|
104
|
+
--viewer-card-strong: rgba(248, 250, 252, 0.98);
|
|
105
|
+
--viewer-toolbar: rgba(255, 255, 255, 0.9);
|
|
106
|
+
--viewer-text: #0f172a;
|
|
107
|
+
--viewer-text-muted: #475569;
|
|
108
|
+
--viewer-text-dim: #64748b;
|
|
109
|
+
--viewer-border: rgba(148, 163, 184, 0.28);
|
|
110
|
+
--viewer-border-strong: rgba(100, 116, 139, 0.36);
|
|
111
|
+
--viewer-shadow: 0 22px 48px rgba(148, 163, 184, 0.2);
|
|
112
|
+
--viewer-grid: rgba(203, 213, 225, 0.55);
|
|
113
|
+
--viewer-badge-bg: rgba(255, 255, 255, 0.92);
|
|
114
|
+
--viewer-button-bg: rgba(255, 255, 255, 0.78);
|
|
115
|
+
--viewer-button-hover: rgba(241, 245, 249, 0.98);
|
|
116
|
+
--viewer-button-active: rgba(59, 130, 246, 0.1);
|
|
117
|
+
--viewer-empty-bg: rgba(255, 255, 255, 0.82);
|
|
118
|
+
--viewer-toast-bg: rgba(254, 242, 242, 0.96);
|
|
119
|
+
--viewer-toast-border: rgba(248, 113, 113, 0.42);
|
|
120
|
+
--viewer-backdrop: radial-gradient(circle at top left, rgba(99, 102, 241, 0.08), transparent 24%), radial-gradient(circle at bottom right, rgba(20, 184, 166, 0.08), transparent 28%), linear-gradient(180deg, rgba(255, 255, 255, 0.44), rgba(248, 250, 252, 0));
|
|
70
121
|
}
|
|
71
122
|
}
|
|
72
123
|
|
|
@@ -76,998 +127,1325 @@
|
|
|
76
127
|
|
|
77
128
|
html,
|
|
78
129
|
body {
|
|
79
|
-
height: 100%;
|
|
130
|
+
min-height: 100%;
|
|
80
131
|
margin: 0;
|
|
81
132
|
}
|
|
82
133
|
|
|
83
134
|
body {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
radial-gradient(circle at bottom right, rgba(20, 184, 166, 0.12), transparent 28%),
|
|
89
|
-
var(--dt-bg-primary);
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
font-family: var(--font-sans);
|
|
137
|
+
color: var(--viewer-text);
|
|
138
|
+
background: var(--viewer-backdrop), var(--viewer-bg);
|
|
90
139
|
}
|
|
91
140
|
|
|
92
|
-
|
|
93
|
-
|
|
141
|
+
button {
|
|
142
|
+
appearance: none;
|
|
143
|
+
border: 1px solid var(--viewer-border);
|
|
144
|
+
border-radius: var(--radius-pill);
|
|
145
|
+
background: var(--viewer-button-bg);
|
|
146
|
+
color: var(--viewer-text);
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
font: inherit;
|
|
149
|
+
font-size: 13px;
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
line-height: 1;
|
|
152
|
+
padding: 12px 16px;
|
|
153
|
+
transition: transform 160ms ease, border-color 160ms ease, background-color 160ms ease, color 160ms ease;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
button:hover,
|
|
157
|
+
button:focus-visible {
|
|
158
|
+
outline: none;
|
|
159
|
+
transform: translateY(-1px);
|
|
160
|
+
border-color: var(--viewer-border-strong);
|
|
161
|
+
background: var(--viewer-button-hover);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
button:focus-visible {
|
|
165
|
+
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.2);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.viewer-shell {
|
|
169
|
+
min-height: 100vh;
|
|
94
170
|
display: grid;
|
|
95
171
|
grid-template-rows: auto 1fr;
|
|
96
172
|
}
|
|
97
173
|
|
|
98
|
-
.toolbar {
|
|
174
|
+
.viewer-toolbar {
|
|
99
175
|
position: sticky;
|
|
100
176
|
top: 0;
|
|
101
|
-
z-index:
|
|
177
|
+
z-index: 10;
|
|
102
178
|
display: flex;
|
|
103
|
-
align-items:
|
|
179
|
+
align-items: flex-start;
|
|
104
180
|
justify-content: space-between;
|
|
105
|
-
gap:
|
|
106
|
-
padding:
|
|
107
|
-
border-bottom: 1px solid var(--
|
|
108
|
-
background: var(--
|
|
109
|
-
backdrop-filter: blur(
|
|
181
|
+
gap: var(--space-4);
|
|
182
|
+
padding: var(--space-5) var(--space-6);
|
|
183
|
+
border-bottom: 1px solid var(--viewer-border);
|
|
184
|
+
background: var(--viewer-toolbar);
|
|
185
|
+
backdrop-filter: blur(18px);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.viewer-title {
|
|
189
|
+
min-width: 0;
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
gap: 6px;
|
|
110
193
|
}
|
|
111
194
|
|
|
112
|
-
.
|
|
195
|
+
.viewer-title__eyebrow {
|
|
196
|
+
color: var(--viewer-text-dim);
|
|
197
|
+
font-family: var(--font-mono);
|
|
198
|
+
font-size: 11px;
|
|
199
|
+
letter-spacing: 0.1em;
|
|
200
|
+
text-transform: uppercase;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.viewer-title h1 {
|
|
113
204
|
margin: 0;
|
|
114
|
-
font-size:
|
|
115
|
-
font-weight:
|
|
116
|
-
letter-spacing: 0.
|
|
205
|
+
font-size: clamp(20px, 2.6vw, 30px);
|
|
206
|
+
font-weight: 600;
|
|
207
|
+
letter-spacing: -0.03em;
|
|
208
|
+
line-height: 1.05;
|
|
117
209
|
}
|
|
118
210
|
|
|
119
|
-
.title p {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
color: var(--
|
|
211
|
+
.viewer-title p {
|
|
212
|
+
max-width: 76ch;
|
|
213
|
+
margin: 0;
|
|
214
|
+
color: var(--viewer-text-muted);
|
|
215
|
+
font-size: 14px;
|
|
216
|
+
line-height: 1.55;
|
|
123
217
|
}
|
|
124
218
|
|
|
125
|
-
.actions {
|
|
219
|
+
.viewer-actions {
|
|
126
220
|
display: flex;
|
|
127
221
|
flex-wrap: wrap;
|
|
128
222
|
justify-content: flex-end;
|
|
129
|
-
gap:
|
|
223
|
+
gap: var(--space-2);
|
|
224
|
+
padding-top: 2px;
|
|
130
225
|
}
|
|
131
226
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
227
|
+
.viewer-stage {
|
|
228
|
+
overflow: auto;
|
|
229
|
+
padding: var(--space-6);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.task-plan__shell {
|
|
233
|
+
position: relative;
|
|
234
|
+
max-width: 1280px;
|
|
235
|
+
margin: 0 auto;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.task-plan__surface {
|
|
239
|
+
display: flex;
|
|
240
|
+
flex-direction: column;
|
|
241
|
+
gap: var(--space-6);
|
|
242
|
+
min-height: calc(100vh - 180px);
|
|
243
|
+
padding: clamp(18px, 2vw, 28px);
|
|
244
|
+
border: 1px solid var(--viewer-border);
|
|
245
|
+
border-radius: calc(var(--radius-xl) + 4px);
|
|
246
|
+
background:
|
|
247
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.02), transparent 28%),
|
|
248
|
+
linear-gradient(90deg, transparent 0, transparent calc(100% - 1px), var(--viewer-grid) calc(100% - 1px), var(--viewer-grid) 100%),
|
|
249
|
+
var(--viewer-card-strong);
|
|
250
|
+
box-shadow: var(--viewer-shadow);
|
|
251
|
+
isolation: isolate;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.task-plan__pipeline {
|
|
255
|
+
display: flex;
|
|
256
|
+
flex-direction: column;
|
|
257
|
+
gap: 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.task-plan__phase-block {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
gap: 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.task-plan-phase {
|
|
267
|
+
display: flex;
|
|
268
|
+
flex-direction: column;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.task-plan-phase__content {
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
gap: var(--space-4);
|
|
275
|
+
padding: clamp(18px, 2vw, 24px);
|
|
276
|
+
border: 1px solid var(--viewer-border);
|
|
277
|
+
border-left: 4px solid var(--phase-accent);
|
|
278
|
+
border-radius: var(--radius-xl);
|
|
279
|
+
background:
|
|
280
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.04), transparent 34%),
|
|
281
|
+
linear-gradient(180deg, rgba(2, 6, 23, 0.05), transparent 44%),
|
|
282
|
+
var(--viewer-card);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.task-plan-phase__header {
|
|
286
|
+
display: flex;
|
|
287
|
+
align-items: center;
|
|
288
|
+
gap: 14px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.task-plan-phase__number {
|
|
292
|
+
display: inline-flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
width: 34px;
|
|
296
|
+
height: 34px;
|
|
297
|
+
border: 1px solid color-mix(in srgb, var(--phase-accent), var(--viewer-border) 45%);
|
|
137
298
|
border-radius: 999px;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
299
|
+
background: color-mix(in srgb, var(--phase-accent), transparent 88%);
|
|
300
|
+
color: var(--phase-accent);
|
|
301
|
+
font-family: var(--font-mono);
|
|
302
|
+
font-size: 12px;
|
|
303
|
+
font-weight: 700;
|
|
304
|
+
flex-shrink: 0;
|
|
142
305
|
}
|
|
143
306
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
307
|
+
.task-plan-phase__label {
|
|
308
|
+
flex: 1;
|
|
309
|
+
min-width: 0;
|
|
310
|
+
margin: 0;
|
|
311
|
+
color: var(--viewer-text);
|
|
312
|
+
font-size: clamp(18px, 2vw, 22px);
|
|
313
|
+
font-weight: 600;
|
|
314
|
+
letter-spacing: -0.02em;
|
|
315
|
+
line-height: 1.2;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.task-plan-phase__badge {
|
|
319
|
+
flex-shrink: 0;
|
|
320
|
+
border: 1px solid var(--viewer-border);
|
|
321
|
+
border-radius: var(--radius-pill);
|
|
322
|
+
background: color-mix(in srgb, var(--phase-accent), transparent 92%);
|
|
323
|
+
color: var(--viewer-text-dim);
|
|
324
|
+
font-family: var(--font-mono);
|
|
325
|
+
font-size: 11px;
|
|
326
|
+
letter-spacing: 0.08em;
|
|
327
|
+
padding: 7px 10px;
|
|
328
|
+
text-transform: uppercase;
|
|
329
|
+
white-space: nowrap;
|
|
147
330
|
}
|
|
148
331
|
|
|
149
|
-
.
|
|
150
|
-
|
|
332
|
+
.task-plan-phase__outcome {
|
|
333
|
+
margin: 0;
|
|
334
|
+
padding-bottom: var(--space-4);
|
|
335
|
+
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
|
336
|
+
color: var(--viewer-text-muted);
|
|
337
|
+
font-size: 14px;
|
|
338
|
+
line-height: 1.6;
|
|
151
339
|
}
|
|
152
340
|
|
|
153
|
-
.
|
|
341
|
+
.task-plan-phase__batches {
|
|
342
|
+
display: flex;
|
|
343
|
+
flex-direction: column;
|
|
344
|
+
gap: var(--space-4);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.task-plan-batch {
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-direction: column;
|
|
350
|
+
gap: 10px;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.task-plan-batch + .task-plan-batch {
|
|
354
|
+
margin-top: 2px;
|
|
355
|
+
padding-top: var(--space-4);
|
|
356
|
+
border-top: 1px dashed rgba(148, 163, 184, 0.2);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.task-plan-batch__label {
|
|
360
|
+
display: flex;
|
|
361
|
+
align-items: center;
|
|
362
|
+
gap: 8px;
|
|
363
|
+
color: var(--viewer-text-dim);
|
|
364
|
+
font-family: var(--font-mono);
|
|
365
|
+
font-size: 11px;
|
|
366
|
+
letter-spacing: 0.08em;
|
|
367
|
+
text-transform: uppercase;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.task-plan-batch__icon {
|
|
371
|
+
display: inline-flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
width: 18px;
|
|
375
|
+
height: 18px;
|
|
376
|
+
border-radius: 999px;
|
|
377
|
+
background: rgba(148, 163, 184, 0.12);
|
|
378
|
+
color: var(--viewer-text-muted);
|
|
379
|
+
font-size: 12px;
|
|
380
|
+
line-height: 1;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.task-plan-batch__tasks {
|
|
384
|
+
display: grid;
|
|
385
|
+
grid-template-columns: 1fr;
|
|
386
|
+
gap: 12px;
|
|
387
|
+
width: 100%;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.task-plan-batch__tasks--parallel {
|
|
391
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.task-plan-batch__tasks--sequential-compact {
|
|
395
|
+
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.task-plan-task {
|
|
154
399
|
position: relative;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
400
|
+
display: flex;
|
|
401
|
+
flex-direction: column;
|
|
402
|
+
gap: 10px;
|
|
403
|
+
min-width: 0;
|
|
404
|
+
min-height: 148px;
|
|
405
|
+
padding: 16px 16px 16px 20px;
|
|
406
|
+
border: 1px solid var(--viewer-border);
|
|
407
|
+
border-radius: var(--radius-md);
|
|
408
|
+
background: color-mix(in srgb, var(--viewer-card), rgba(255, 255, 255, 0.02) 3%);
|
|
409
|
+
transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.task-plan-task::before {
|
|
413
|
+
content: "";
|
|
414
|
+
position: absolute;
|
|
415
|
+
top: 0;
|
|
416
|
+
bottom: 0;
|
|
417
|
+
left: 0;
|
|
418
|
+
width: 4px;
|
|
419
|
+
border-radius: 4px 0 0 4px;
|
|
420
|
+
background: var(--card-accent);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.task-plan-task:hover {
|
|
424
|
+
transform: translateY(-1px);
|
|
425
|
+
border-color: color-mix(in srgb, var(--card-accent), var(--viewer-border) 44%);
|
|
426
|
+
box-shadow: 0 16px 28px rgba(2, 6, 23, 0.16);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.task-plan-task__topline {
|
|
430
|
+
display: flex;
|
|
431
|
+
align-items: center;
|
|
432
|
+
justify-content: space-between;
|
|
433
|
+
gap: 12px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.task-plan-task__agent {
|
|
437
|
+
display: flex;
|
|
438
|
+
align-items: center;
|
|
439
|
+
gap: 8px;
|
|
440
|
+
min-width: 0;
|
|
441
|
+
color: var(--card-accent);
|
|
442
|
+
font-family: var(--font-mono);
|
|
443
|
+
font-size: 11px;
|
|
444
|
+
font-weight: 700;
|
|
445
|
+
letter-spacing: 0.06em;
|
|
446
|
+
text-transform: uppercase;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.task-plan-task__agent-icon {
|
|
450
|
+
display: inline-flex;
|
|
451
|
+
align-items: center;
|
|
452
|
+
justify-content: center;
|
|
453
|
+
width: 22px;
|
|
454
|
+
height: 22px;
|
|
455
|
+
border-radius: 999px;
|
|
456
|
+
background: color-mix(in srgb, var(--card-accent), transparent 82%);
|
|
457
|
+
font-size: 12px;
|
|
458
|
+
flex-shrink: 0;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.task-plan-task__agent-label {
|
|
163
462
|
overflow: hidden;
|
|
164
|
-
|
|
463
|
+
text-overflow: ellipsis;
|
|
464
|
+
white-space: nowrap;
|
|
165
465
|
}
|
|
166
466
|
|
|
167
|
-
|
|
168
|
-
display:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
467
|
+
.task-plan-task__status {
|
|
468
|
+
display: inline-flex;
|
|
469
|
+
align-items: center;
|
|
470
|
+
gap: 6px;
|
|
471
|
+
flex-shrink: 0;
|
|
472
|
+
color: var(--status-color);
|
|
473
|
+
font-family: var(--font-mono);
|
|
474
|
+
font-size: 11px;
|
|
475
|
+
letter-spacing: 0.06em;
|
|
476
|
+
text-transform: uppercase;
|
|
477
|
+
white-space: nowrap;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.task-plan-task__status-dot {
|
|
481
|
+
width: 8px;
|
|
482
|
+
height: 8px;
|
|
483
|
+
border-radius: 999px;
|
|
484
|
+
background: currentColor;
|
|
485
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, currentColor, transparent 82%);
|
|
486
|
+
flex-shrink: 0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.task-plan-task__status[data-status="in-progress"] .task-plan-task__status-dot {
|
|
490
|
+
animation: task-plan-pulse 2s ease-in-out infinite;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.task-plan-task__title {
|
|
494
|
+
color: var(--viewer-text);
|
|
495
|
+
font-size: 15px;
|
|
496
|
+
font-weight: 700;
|
|
497
|
+
line-height: 1.4;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.task-plan-task__description {
|
|
501
|
+
margin: 0;
|
|
502
|
+
color: var(--viewer-text-muted);
|
|
503
|
+
font-size: 13px;
|
|
504
|
+
line-height: 1.55;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.task-plan-task__meta {
|
|
508
|
+
display: flex;
|
|
509
|
+
flex-wrap: wrap;
|
|
510
|
+
gap: 8px;
|
|
511
|
+
margin-top: auto;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.task-plan-task__chip {
|
|
515
|
+
display: inline-flex;
|
|
516
|
+
align-items: center;
|
|
517
|
+
gap: 6px;
|
|
518
|
+
max-width: 100%;
|
|
519
|
+
overflow: hidden;
|
|
520
|
+
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
521
|
+
border-radius: var(--radius-pill);
|
|
522
|
+
background: color-mix(in srgb, var(--viewer-surface-2), transparent 16%);
|
|
523
|
+
color: var(--viewer-text-dim);
|
|
524
|
+
font-family: var(--font-mono);
|
|
525
|
+
font-size: 11px;
|
|
526
|
+
line-height: 1.1;
|
|
527
|
+
padding: 6px 10px;
|
|
528
|
+
text-overflow: ellipsis;
|
|
529
|
+
white-space: nowrap;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.task-plan__connector {
|
|
533
|
+
position: relative;
|
|
534
|
+
display: flex;
|
|
535
|
+
align-items: center;
|
|
536
|
+
justify-content: center;
|
|
537
|
+
height: 46px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.task-plan__connector::before {
|
|
541
|
+
content: "";
|
|
542
|
+
position: absolute;
|
|
543
|
+
top: 0;
|
|
544
|
+
bottom: 12px;
|
|
545
|
+
left: 50%;
|
|
546
|
+
width: 2px;
|
|
547
|
+
background: repeating-linear-gradient(180deg, var(--viewer-border-strong) 0, var(--viewer-border-strong) 6px, transparent 6px, transparent 12px);
|
|
548
|
+
transform: translateX(-50%);
|
|
172
549
|
}
|
|
173
550
|
|
|
174
|
-
.
|
|
551
|
+
.task-plan__connector::after {
|
|
552
|
+
content: "";
|
|
175
553
|
position: absolute;
|
|
176
|
-
|
|
177
|
-
|
|
554
|
+
bottom: 0;
|
|
555
|
+
left: 50%;
|
|
556
|
+
width: 0;
|
|
557
|
+
height: 0;
|
|
558
|
+
border-left: 6px solid transparent;
|
|
559
|
+
border-right: 6px solid transparent;
|
|
560
|
+
border-top: 9px solid var(--viewer-border-strong);
|
|
561
|
+
transform: translateX(-50%);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.task-plan__summary {
|
|
565
|
+
display: flex;
|
|
566
|
+
flex-wrap: wrap;
|
|
567
|
+
gap: 18px 26px;
|
|
568
|
+
padding: 18px 20px;
|
|
569
|
+
border: 1px solid var(--viewer-border);
|
|
570
|
+
border-radius: var(--radius-xl);
|
|
571
|
+
background: color-mix(in srgb, var(--viewer-card), rgba(255, 255, 255, 0.02) 3%);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.task-plan-summary__stat {
|
|
575
|
+
display: flex;
|
|
576
|
+
flex-direction: column;
|
|
577
|
+
gap: 4px;
|
|
578
|
+
min-width: 78px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.task-plan-summary__value {
|
|
582
|
+
color: var(--viewer-text);
|
|
583
|
+
font-size: 24px;
|
|
584
|
+
font-weight: 700;
|
|
585
|
+
letter-spacing: -0.03em;
|
|
586
|
+
line-height: 1;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.task-plan-summary__label {
|
|
590
|
+
color: var(--viewer-text-dim);
|
|
591
|
+
font-family: var(--font-mono);
|
|
592
|
+
font-size: 11px;
|
|
593
|
+
letter-spacing: 0.08em;
|
|
594
|
+
text-transform: uppercase;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.task-plan__legend {
|
|
598
|
+
display: flex;
|
|
599
|
+
flex-wrap: wrap;
|
|
600
|
+
align-items: center;
|
|
601
|
+
gap: 12px;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.task-plan__legend-title {
|
|
605
|
+
color: var(--viewer-text-dim);
|
|
606
|
+
font-family: var(--font-mono);
|
|
607
|
+
font-size: 11px;
|
|
608
|
+
letter-spacing: 0.08em;
|
|
609
|
+
text-transform: uppercase;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.task-plan__legend-item {
|
|
613
|
+
display: inline-flex;
|
|
614
|
+
align-items: center;
|
|
615
|
+
gap: 8px;
|
|
616
|
+
padding: 8px 12px;
|
|
617
|
+
border: 1px solid var(--viewer-border);
|
|
618
|
+
border-radius: var(--radius-pill);
|
|
619
|
+
background: rgba(148, 163, 184, 0.08);
|
|
620
|
+
color: var(--viewer-text-muted);
|
|
621
|
+
font-size: 12px;
|
|
622
|
+
line-height: 1;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.task-plan__legend-dot {
|
|
626
|
+
width: 8px;
|
|
627
|
+
height: 8px;
|
|
628
|
+
border-radius: 999px;
|
|
629
|
+
flex-shrink: 0;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.task-plan__legend-icon {
|
|
633
|
+
opacity: 0.95;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.task-plan__empty {
|
|
637
|
+
display: grid;
|
|
178
638
|
place-items: center;
|
|
179
|
-
|
|
639
|
+
gap: 8px;
|
|
640
|
+
min-height: 320px;
|
|
641
|
+
padding: 48px 24px;
|
|
642
|
+
border: 1px dashed rgba(148, 163, 184, 0.3);
|
|
643
|
+
border-radius: var(--radius-xl);
|
|
644
|
+
background: var(--viewer-empty-bg);
|
|
645
|
+
color: var(--viewer-text-muted);
|
|
180
646
|
text-align: center;
|
|
181
|
-
color: var(--dt-text-secondary);
|
|
182
647
|
}
|
|
183
648
|
|
|
184
|
-
.
|
|
185
|
-
|
|
649
|
+
.task-plan__empty strong {
|
|
650
|
+
color: var(--viewer-text);
|
|
651
|
+
font-size: 16px;
|
|
186
652
|
}
|
|
187
653
|
|
|
188
|
-
.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
654
|
+
.task-plan__empty span {
|
|
655
|
+
max-width: 56ch;
|
|
656
|
+
font-size: 14px;
|
|
657
|
+
line-height: 1.6;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.task-plan__export-error {
|
|
661
|
+
position: fixed;
|
|
662
|
+
left: 50%;
|
|
663
|
+
bottom: 24px;
|
|
664
|
+
z-index: 20;
|
|
665
|
+
transform: translateX(-50%);
|
|
666
|
+
max-width: min(92vw, 560px);
|
|
667
|
+
padding: 12px 16px;
|
|
668
|
+
border: 1px solid var(--viewer-toast-border);
|
|
669
|
+
border-radius: var(--radius-pill);
|
|
670
|
+
background: var(--viewer-toast-bg);
|
|
671
|
+
color: var(--viewer-text);
|
|
672
|
+
font-size: 13px;
|
|
673
|
+
line-height: 1.4;
|
|
674
|
+
box-shadow: 0 18px 32px rgba(15, 23, 42, 0.22);
|
|
193
675
|
}
|
|
194
676
|
|
|
195
|
-
.badge {
|
|
677
|
+
.viewer-badge {
|
|
196
678
|
position: fixed;
|
|
197
|
-
right:
|
|
198
|
-
bottom:
|
|
199
|
-
z-index:
|
|
679
|
+
right: 16px;
|
|
680
|
+
bottom: 16px;
|
|
681
|
+
z-index: 12;
|
|
200
682
|
display: inline-flex;
|
|
201
683
|
align-items: center;
|
|
202
|
-
gap:
|
|
203
|
-
padding:
|
|
204
|
-
border: 1px solid var(--
|
|
205
|
-
border-radius:
|
|
206
|
-
background: var(--
|
|
207
|
-
color: var(--
|
|
684
|
+
gap: 10px;
|
|
685
|
+
padding: 10px 14px;
|
|
686
|
+
border: 1px solid var(--viewer-border);
|
|
687
|
+
border-radius: var(--radius-pill);
|
|
688
|
+
background: var(--viewer-badge-bg);
|
|
689
|
+
color: var(--viewer-text-dim);
|
|
208
690
|
backdrop-filter: blur(14px);
|
|
209
|
-
font-
|
|
210
|
-
|
|
691
|
+
font-family: var(--font-mono);
|
|
692
|
+
font-size: 11px;
|
|
693
|
+
letter-spacing: 0.08em;
|
|
211
694
|
text-transform: uppercase;
|
|
212
695
|
}
|
|
213
696
|
|
|
214
|
-
.
|
|
215
|
-
width:
|
|
216
|
-
height:
|
|
697
|
+
.viewer-badge__mark {
|
|
698
|
+
width: 9px;
|
|
699
|
+
height: 9px;
|
|
217
700
|
border-radius: 999px;
|
|
218
|
-
background: var(--tp-
|
|
219
|
-
box-shadow: 0 0 0
|
|
701
|
+
background: var(--tp-phase-2);
|
|
702
|
+
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.18);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
[hidden] {
|
|
706
|
+
display: none !important;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
@keyframes task-plan-pulse {
|
|
710
|
+
0%,
|
|
711
|
+
100% {
|
|
712
|
+
opacity: 1;
|
|
713
|
+
box-shadow: 0 0 0 0 color-mix(in srgb, var(--tp-status-in-progress), transparent 58%);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
50% {
|
|
717
|
+
opacity: 0.55;
|
|
718
|
+
box-shadow: 0 0 0 8px color-mix(in srgb, var(--tp-status-in-progress), transparent 100%);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
@media (max-width: 960px) {
|
|
723
|
+
.viewer-toolbar {
|
|
724
|
+
flex-direction: column;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.viewer-actions {
|
|
728
|
+
justify-content: flex-start;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.viewer-stage {
|
|
732
|
+
padding: var(--space-4);
|
|
733
|
+
}
|
|
220
734
|
}
|
|
221
735
|
|
|
222
736
|
@media (max-width: 720px) {
|
|
223
|
-
|
|
737
|
+
body {
|
|
738
|
+
overflow: auto;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.viewer-toolbar {
|
|
742
|
+
padding: var(--space-4);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.viewer-title h1 {
|
|
746
|
+
font-size: 22px;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.task-plan__surface {
|
|
750
|
+
min-height: 0;
|
|
751
|
+
padding: 16px;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.task-plan-phase__header,
|
|
755
|
+
.task-plan-task__topline {
|
|
224
756
|
align-items: flex-start;
|
|
225
757
|
flex-direction: column;
|
|
226
758
|
}
|
|
227
759
|
|
|
228
|
-
.
|
|
229
|
-
|
|
230
|
-
|
|
760
|
+
.task-plan-phase__badge,
|
|
761
|
+
.task-plan-task__status {
|
|
762
|
+
white-space: normal;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.task-plan__summary {
|
|
766
|
+
gap: 16px;
|
|
231
767
|
}
|
|
232
768
|
|
|
233
|
-
.
|
|
234
|
-
|
|
769
|
+
.viewer-badge {
|
|
770
|
+
bottom: 12px;
|
|
771
|
+
right: 12px;
|
|
772
|
+
padding: 9px 12px;
|
|
235
773
|
}
|
|
774
|
+
}
|
|
236
775
|
|
|
237
|
-
|
|
238
|
-
|
|
776
|
+
@media (prefers-reduced-motion: reduce) {
|
|
777
|
+
*,
|
|
778
|
+
*::before,
|
|
779
|
+
*::after {
|
|
780
|
+
animation: none !important;
|
|
781
|
+
transition: none !important;
|
|
782
|
+
scroll-behavior: auto !important;
|
|
239
783
|
}
|
|
240
784
|
}
|
|
241
785
|
</style>
|
|
242
786
|
</head>
|
|
243
787
|
<body>
|
|
244
|
-
<div class="shell">
|
|
245
|
-
<header class="toolbar">
|
|
246
|
-
<div class="title">
|
|
247
|
-
<
|
|
248
|
-
<
|
|
788
|
+
<div class="viewer-shell">
|
|
789
|
+
<header class="viewer-toolbar">
|
|
790
|
+
<div class="viewer-title">
|
|
791
|
+
<span class="viewer-title__eyebrow">Task Plan Viewer</span>
|
|
792
|
+
<h1 id="viewer-title">Task Plan</h1>
|
|
793
|
+
<p id="viewer-description">Phase-based pipeline for agent work, dependencies, and delivery state.</p>
|
|
249
794
|
</div>
|
|
250
|
-
<div class="actions">
|
|
795
|
+
<div class="viewer-actions" role="toolbar" aria-label="Task plan viewer actions">
|
|
251
796
|
<button id="theme-button" type="button">Theme: Auto</button>
|
|
252
|
-
<button id="export-button" type="button">Export
|
|
797
|
+
<button id="export-png-button" type="button">Export PNG</button>
|
|
798
|
+
<button id="export-svg-button" type="button">Export SVG</button>
|
|
253
799
|
</div>
|
|
254
800
|
</header>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<
|
|
801
|
+
|
|
802
|
+
<main class="viewer-stage">
|
|
803
|
+
<div class="task-plan__shell">
|
|
804
|
+
<div class="task-plan__surface" id="pipeline-surface">
|
|
805
|
+
<div class="task-plan__pipeline" id="pipeline" aria-label="Task plan pipeline"></div>
|
|
806
|
+
|
|
807
|
+
<section class="task-plan__summary" id="summary" aria-label="Task plan summary" hidden></section>
|
|
808
|
+
|
|
809
|
+
<section class="task-plan__legend" id="legend" aria-label="Agent legend" hidden></section>
|
|
810
|
+
|
|
811
|
+
<div class="task-plan__empty" id="empty-state" role="status" aria-live="polite" hidden>
|
|
812
|
+
<strong>No tasks to render</strong>
|
|
813
|
+
<span>Add phases, batches, and tasks to populate the plan.</span>
|
|
814
|
+
</div>
|
|
815
|
+
</div>
|
|
259
816
|
</div>
|
|
260
817
|
</main>
|
|
261
818
|
</div>
|
|
262
|
-
|
|
819
|
+
|
|
820
|
+
<div class="viewer-badge"><span class="viewer-badge__mark"></span>AI Kit static viewer</div>
|
|
821
|
+
<div class="task-plan__export-error" id="export-error" role="status" aria-live="polite" hidden></div>
|
|
822
|
+
|
|
263
823
|
<script type="application/json" id="diagram-data">{}</script>
|
|
264
824
|
<script>
|
|
265
825
|
(() => {
|
|
266
|
-
const svgNs = 'http://www.w3.org/2000/svg';
|
|
267
826
|
const root = document.documentElement;
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
const
|
|
827
|
+
const colorSchemeQuery = window.matchMedia ? window.matchMedia('(prefers-color-scheme: light)') : null;
|
|
828
|
+
|
|
829
|
+
const pipelineEl = document.getElementById('pipeline');
|
|
830
|
+
const summaryEl = document.getElementById('summary');
|
|
831
|
+
const legendEl = document.getElementById('legend');
|
|
832
|
+
const emptyStateEl = document.getElementById('empty-state');
|
|
833
|
+
const exportErrorEl = document.getElementById('export-error');
|
|
834
|
+
const surfaceEl = document.getElementById('pipeline-surface');
|
|
835
|
+
const viewerTitleEl = document.getElementById('viewer-title');
|
|
836
|
+
const viewerDescriptionEl = document.getElementById('viewer-description');
|
|
271
837
|
const themeButton = document.getElementById('theme-button');
|
|
838
|
+
const exportPngButton = document.getElementById('export-png-button');
|
|
839
|
+
const exportSvgButton = document.getElementById('export-svg-button');
|
|
840
|
+
const styleTag = document.getElementById('viewer-style');
|
|
841
|
+
|
|
272
842
|
const themes = ['auto', 'dark', 'light'];
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
'
|
|
287
|
-
'
|
|
288
|
-
'
|
|
289
|
-
'
|
|
290
|
-
'
|
|
291
|
-
'
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
if (attributes) {
|
|
297
|
-
Object.entries(attributes).forEach(([name, value]) => {
|
|
298
|
-
if (value !== undefined && value !== null) {
|
|
299
|
-
element.setAttribute(name, String(value));
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
if (text !== undefined) {
|
|
304
|
-
element.innerHTML = escapeHtml(text);
|
|
305
|
-
}
|
|
306
|
-
return element;
|
|
843
|
+
const phasePalette = ['#06b6d4', '#6366f1', '#f59e0b', '#ec4899', '#14b8a6', '#8b5cf6'];
|
|
844
|
+
const statusLabels = {
|
|
845
|
+
pending: 'Pending',
|
|
846
|
+
'in-progress': 'In Progress',
|
|
847
|
+
done: 'Done',
|
|
848
|
+
blocked: 'Blocked'
|
|
849
|
+
};
|
|
850
|
+
const agentIcons = {
|
|
851
|
+
researcher: '🔬',
|
|
852
|
+
implementer: '🛠',
|
|
853
|
+
frontend: '🎨',
|
|
854
|
+
reviewer: '🧪',
|
|
855
|
+
debugger: '🐞',
|
|
856
|
+
security: '🛡',
|
|
857
|
+
explorer: '🧭',
|
|
858
|
+
documenter: '📝',
|
|
859
|
+
refactor: '♻',
|
|
860
|
+
planner: '🧠',
|
|
861
|
+
orchestrator: '🎛'
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
function getTrimmedString(value) {
|
|
865
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
|
|
307
866
|
}
|
|
308
867
|
|
|
309
|
-
function
|
|
310
|
-
|
|
311
|
-
const value = getComputedStyle(root).getPropertyValue(arguments[index]).trim();
|
|
312
|
-
if (value) {
|
|
313
|
-
return value;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return '';
|
|
868
|
+
function isTaskStatus(value) {
|
|
869
|
+
return value === 'pending' || value === 'in-progress' || value === 'done' || value === 'blocked';
|
|
317
870
|
}
|
|
318
871
|
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return
|
|
872
|
+
function classifyAgent(agent) {
|
|
873
|
+
if (!agent) return 'orchestrator';
|
|
874
|
+
if (agent.startsWith('Researcher-')) return 'researcher';
|
|
875
|
+
if (agent.startsWith('Code-Reviewer-') || agent.startsWith('Architect-Reviewer-')) return 'reviewer';
|
|
876
|
+
switch (agent) {
|
|
877
|
+
case 'Implementer': return 'implementer';
|
|
878
|
+
case 'Frontend': return 'frontend';
|
|
879
|
+
case 'Refactor': return 'refactor';
|
|
880
|
+
case 'Debugger': return 'debugger';
|
|
881
|
+
case 'Security': return 'security';
|
|
882
|
+
case 'Explorer': return 'explorer';
|
|
883
|
+
case 'Documenter': return 'documenter';
|
|
884
|
+
case 'Planner': return 'planner';
|
|
885
|
+
default: return 'orchestrator';
|
|
327
886
|
}
|
|
328
|
-
const red = (number >> 16) & 255;
|
|
329
|
-
const green = (number >> 8) & 255;
|
|
330
|
-
const blue = number & 255;
|
|
331
|
-
return 'rgba(' + red + ', ' + green + ', ' + blue + ', ' + alpha + ')';
|
|
332
887
|
}
|
|
333
888
|
|
|
334
|
-
function
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
current = word;
|
|
346
|
-
} else {
|
|
347
|
-
current = next;
|
|
348
|
-
}
|
|
889
|
+
function getAgentIcon(agent) {
|
|
890
|
+
return agentIcons[classifyAgent(agent)] || agentIcons.orchestrator;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function getAgentColor(agent) {
|
|
894
|
+
return 'var(--tp-agent-' + classifyAgent(agent) + ')';
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function clearElement(element) {
|
|
898
|
+
while (element.firstChild) {
|
|
899
|
+
element.removeChild(element.firstChild);
|
|
349
900
|
}
|
|
350
|
-
|
|
351
|
-
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function createElement(tagName, className, text) {
|
|
904
|
+
const element = document.createElement(tagName);
|
|
905
|
+
if (className) {
|
|
906
|
+
element.className = className;
|
|
352
907
|
}
|
|
353
|
-
if (
|
|
354
|
-
|
|
908
|
+
if (text !== undefined) {
|
|
909
|
+
element.textContent = text;
|
|
355
910
|
}
|
|
356
|
-
|
|
357
|
-
clipped[maxLines - 1] = clipped[maxLines - 1].replace(/[\s.]+$/, '') + '...';
|
|
358
|
-
return clipped;
|
|
911
|
+
return element;
|
|
359
912
|
}
|
|
360
913
|
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
.
|
|
914
|
+
function normalizeTask(task, taskIndex) {
|
|
915
|
+
const id = getTrimmedString(task && task.id) || 'task-' + (taskIndex + 1);
|
|
916
|
+
const files = Array.isArray(task && task.files)
|
|
917
|
+
? task.files.filter((file) => typeof file === 'string' && file.trim().length > 0)
|
|
918
|
+
: [];
|
|
919
|
+
const dependsOn = Array.isArray(task && task.dependsOn)
|
|
920
|
+
? Array.from(new Set(task.dependsOn.filter((dependency) => typeof dependency === 'string' && dependency.trim().length > 0)))
|
|
921
|
+
.filter((dependency) => dependency !== id)
|
|
922
|
+
: [];
|
|
923
|
+
|
|
924
|
+
return {
|
|
925
|
+
id,
|
|
926
|
+
title: getTrimmedString(task && task.title) || 'Task ' + (taskIndex + 1),
|
|
927
|
+
agent: getTrimmedString(task && task.agent),
|
|
928
|
+
files,
|
|
929
|
+
status: isTaskStatus(task && task.status) ? task.status : 'pending',
|
|
930
|
+
description: getTrimmedString(task && task.description),
|
|
931
|
+
dependsOn
|
|
932
|
+
};
|
|
368
933
|
}
|
|
369
934
|
|
|
370
|
-
function
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
935
|
+
function normalizeBatch(batch, batchIndex) {
|
|
936
|
+
const order = typeof (batch && batch.order) === 'number' && Number.isFinite(batch.order)
|
|
937
|
+
? batch.order
|
|
938
|
+
: batchIndex + 1;
|
|
939
|
+
|
|
940
|
+
return {
|
|
941
|
+
id: getTrimmedString(batch && batch.id) || 'batch-' + (batchIndex + 1),
|
|
942
|
+
order,
|
|
943
|
+
parallel: Boolean(batch && batch.parallel),
|
|
944
|
+
label: getTrimmedString(batch && batch.label),
|
|
945
|
+
tasks: Array.isArray(batch && batch.tasks)
|
|
946
|
+
? batch.tasks.map((task, taskIndex) => normalizeTask(task, taskIndex))
|
|
947
|
+
: []
|
|
948
|
+
};
|
|
374
949
|
}
|
|
375
950
|
|
|
376
|
-
function
|
|
377
|
-
|
|
378
|
-
|
|
951
|
+
function normalizePhase(phase, phaseIndex) {
|
|
952
|
+
const batches = Array.isArray(phase && phase.batches)
|
|
953
|
+
? phase.batches.map((batch, batchIndex) => normalizeBatch(batch, batchIndex)).sort((left, right) => left.order - right.order)
|
|
954
|
+
: [];
|
|
955
|
+
|
|
956
|
+
return {
|
|
957
|
+
id: getTrimmedString(phase && phase.id) || 'phase-' + (phaseIndex + 1),
|
|
958
|
+
label: getTrimmedString(phase && phase.label) || 'Phase ' + (phaseIndex + 1),
|
|
959
|
+
outcome: getTrimmedString(phase && phase.outcome),
|
|
960
|
+
batches
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function normalizeData(raw) {
|
|
965
|
+
return {
|
|
966
|
+
title: getTrimmedString(raw && raw.title),
|
|
967
|
+
description: getTrimmedString(raw && raw.description),
|
|
968
|
+
phases: Array.isArray(raw && raw.phases)
|
|
969
|
+
? raw.phases.map((phase, phaseIndex) => normalizePhase(phase, phaseIndex))
|
|
970
|
+
: []
|
|
971
|
+
};
|
|
379
972
|
}
|
|
380
973
|
|
|
381
|
-
function
|
|
974
|
+
function readData() {
|
|
382
975
|
const source = document.getElementById('diagram-data');
|
|
383
|
-
if (!source) {
|
|
384
|
-
return
|
|
976
|
+
if (!source || !source.textContent) {
|
|
977
|
+
return normalizeData(undefined);
|
|
385
978
|
}
|
|
979
|
+
|
|
386
980
|
try {
|
|
387
|
-
return JSON.parse(source.textContent
|
|
981
|
+
return normalizeData(JSON.parse(source.textContent));
|
|
388
982
|
} catch (error) {
|
|
389
|
-
showEmpty('Invalid
|
|
983
|
+
showEmpty('Invalid task plan JSON', error instanceof Error ? error.message : String(error));
|
|
390
984
|
return null;
|
|
391
985
|
}
|
|
392
986
|
}
|
|
393
987
|
|
|
394
|
-
function
|
|
395
|
-
return
|
|
988
|
+
function getTasks(plan) {
|
|
989
|
+
return plan.phases.flatMap((phase) => phase.batches.flatMap((batch) => batch.tasks));
|
|
396
990
|
}
|
|
397
991
|
|
|
398
|
-
function
|
|
399
|
-
|
|
400
|
-
const value = agent.toLowerCase();
|
|
401
|
-
if (value.includes('researcher')) return cssValue('--tp-indigo');
|
|
402
|
-
if (value.includes('implementer')) return cssValue('--dt-emerald');
|
|
403
|
-
if (value.includes('frontend')) return cssValue('--tp-pink');
|
|
404
|
-
if (value.includes('reviewer') || value.includes('architect')) return cssValue('--dt-amber');
|
|
405
|
-
if (value.includes('debugger')) return cssValue('--tp-red', '--dt-rose');
|
|
406
|
-
if (value.includes('security')) return cssValue('--tp-orange');
|
|
407
|
-
if (value.includes('explorer')) return cssValue('--dt-cyan');
|
|
408
|
-
if (value.includes('documenter')) return cssValue('--dt-violet');
|
|
409
|
-
if (value.includes('refactor')) return cssValue('--tp-teal');
|
|
410
|
-
if (value.includes('planner')) return cssValue('--tp-purple');
|
|
411
|
-
if (value.includes('orchestrator')) return cssValue('--dt-slate');
|
|
412
|
-
return cssValue('--dt-slate');
|
|
992
|
+
function getPhaseTaskCount(phase) {
|
|
993
|
+
return phase.batches.reduce((count, batch) => count + batch.tasks.length, 0);
|
|
413
994
|
}
|
|
414
995
|
|
|
415
|
-
function
|
|
416
|
-
const
|
|
417
|
-
if (
|
|
418
|
-
return '?';
|
|
419
|
-
}
|
|
420
|
-
const cleaned = value.replace(/[^A-Za-z0-9\s-]/g, ' ').trim();
|
|
421
|
-
const parts = cleaned.split(/[\s-]+/).filter(Boolean);
|
|
422
|
-
if (parts.length === 1) {
|
|
423
|
-
return parts[0].slice(0, 2).toUpperCase();
|
|
996
|
+
function getPhaseBadge(phase) {
|
|
997
|
+
const taskCount = getPhaseTaskCount(phase);
|
|
998
|
+
if (phase.batches.length <= 1) {
|
|
999
|
+
return taskCount + ' ' + (taskCount === 1 ? 'task' : 'tasks');
|
|
424
1000
|
}
|
|
425
|
-
return
|
|
1001
|
+
return taskCount + ' ' + (taskCount === 1 ? 'task' : 'tasks') + ' · ' + phase.batches.length + ' batches';
|
|
426
1002
|
}
|
|
427
1003
|
|
|
428
|
-
function
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
431
|
-
return
|
|
432
|
-
}
|
|
433
|
-
if (value === 'in-progress') {
|
|
434
|
-
return { key: 'in-progress', color: cssValue('--tp-blue'), label: 'In progress' };
|
|
1004
|
+
function getBatchDescriptor(batch) {
|
|
1005
|
+
const mode = batch.parallel ? 'Parallel' : 'Sequential';
|
|
1006
|
+
if (batch.label) {
|
|
1007
|
+
return batch.label + ' - ' + mode;
|
|
435
1008
|
}
|
|
436
|
-
|
|
437
|
-
return { key: 'blocked', color: cssValue('--tp-red'), label: 'Blocked' };
|
|
438
|
-
}
|
|
439
|
-
return { key: 'pending', color: cssValue('--tp-gray', '--dt-slate'), label: 'Pending' };
|
|
1009
|
+
return 'Batch ' + batch.order + ' - ' + mode;
|
|
440
1010
|
}
|
|
441
1011
|
|
|
442
|
-
function
|
|
443
|
-
return
|
|
444
|
-
id: typeof task.id === 'string' ? task.id : phase.id + '-' + batch.id + '-task-' + index,
|
|
445
|
-
title: typeof task.title === 'string' ? task.title : 'Untitled task',
|
|
446
|
-
agent: typeof task.agent === 'string' ? task.agent : '',
|
|
447
|
-
files: Array.isArray(task.files) ? task.files.filter((file) => typeof file === 'string') : [],
|
|
448
|
-
status: typeof task.status === 'string' ? task.status : 'pending',
|
|
449
|
-
dependsOn: Array.isArray(task.dependsOn) ? task.dependsOn.filter((id) => typeof id === 'string') : [],
|
|
450
|
-
phaseId: phase.id,
|
|
451
|
-
batchId: batch.id,
|
|
452
|
-
};
|
|
1012
|
+
function getBatchIcon(batch) {
|
|
1013
|
+
return batch.parallel ? '⫸' : '→';
|
|
453
1014
|
}
|
|
454
1015
|
|
|
455
|
-
function
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const taskMap = new Map();
|
|
459
|
-
const edges = [];
|
|
460
|
-
const edgeKeys = new Set();
|
|
1016
|
+
function getFileCountLabel(fileCount) {
|
|
1017
|
+
return fileCount + ' ' + (fileCount === 1 ? 'file' : 'files');
|
|
1018
|
+
}
|
|
461
1019
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
outcome: typeof phase.outcome === 'string' ? phase.outcome : '',
|
|
470
|
-
batches: [],
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
const batches = Array.isArray(phase.batches) ? phase.batches : [];
|
|
474
|
-
batches.forEach((batch, batchIndex) => {
|
|
475
|
-
if (!batch || typeof batch !== 'object') {
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
const batchValue = {
|
|
479
|
-
id: typeof batch.id === 'string' ? batch.id : phaseValue.id + '-batch-' + (batchIndex + 1),
|
|
480
|
-
order: typeof batch.order === 'number' ? batch.order : batchIndex + 1,
|
|
481
|
-
parallel: Boolean(batch.parallel),
|
|
482
|
-
label: typeof batch.label === 'string' ? batch.label : 'Batch ' + (batchIndex + 1),
|
|
483
|
-
tasks: [],
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
const tasks = Array.isArray(batch.tasks) ? batch.tasks : [];
|
|
487
|
-
tasks.forEach((task, taskIndex) => {
|
|
488
|
-
if (!task || typeof task !== 'object') {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const normalizedTask = normalizeTask(task, phaseValue, batchValue, taskIndex + 1);
|
|
492
|
-
batchValue.tasks.push(normalizedTask);
|
|
493
|
-
taskMap.set(normalizedTask.id, normalizedTask);
|
|
494
|
-
});
|
|
1020
|
+
function getDependencyLabels(task) {
|
|
1021
|
+
const dependencies = task.dependsOn || [];
|
|
1022
|
+
if (dependencies.length <= 3) {
|
|
1023
|
+
return dependencies.map((dependency) => 'Depends: ' + dependency);
|
|
1024
|
+
}
|
|
1025
|
+
return dependencies.slice(0, 2).map((dependency) => 'Depends: ' + dependency).concat(['+' + (dependencies.length - 2) + ' more']);
|
|
1026
|
+
}
|
|
495
1027
|
|
|
496
|
-
|
|
497
|
-
|
|
1028
|
+
function buildLegendItems(tasks) {
|
|
1029
|
+
const seen = new Set();
|
|
1030
|
+
return tasks
|
|
1031
|
+
.filter((task) => Boolean(task.agent))
|
|
1032
|
+
.filter((task) => {
|
|
1033
|
+
if (seen.has(task.agent)) {
|
|
1034
|
+
return false;
|
|
498
1035
|
}
|
|
499
|
-
|
|
1036
|
+
seen.add(task.agent);
|
|
1037
|
+
return true;
|
|
1038
|
+
})
|
|
1039
|
+
.map((task) => ({
|
|
1040
|
+
label: task.agent,
|
|
1041
|
+
color: getAgentColor(task.agent),
|
|
1042
|
+
icon: getAgentIcon(task.agent)
|
|
1043
|
+
}));
|
|
1044
|
+
}
|
|
500
1045
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1046
|
+
function getSummary(plan) {
|
|
1047
|
+
const tasks = getTasks(plan);
|
|
1048
|
+
const counts = {
|
|
1049
|
+
pending: 0,
|
|
1050
|
+
'in-progress': 0,
|
|
1051
|
+
done: 0,
|
|
1052
|
+
blocked: 0
|
|
1053
|
+
};
|
|
1054
|
+
const agents = new Set();
|
|
505
1055
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
const key = dependencyId + '->' + task.id;
|
|
514
|
-
if (edgeKeys.has(key)) {
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
edgeKeys.add(key);
|
|
518
|
-
edges.push({ source: dependencyId, target: task.id });
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
});
|
|
1056
|
+
tasks.forEach((task) => {
|
|
1057
|
+
counts[task.status] += 1;
|
|
1058
|
+
if (task.agent) {
|
|
1059
|
+
agents.add(task.agent);
|
|
1060
|
+
}
|
|
522
1061
|
});
|
|
523
1062
|
|
|
524
1063
|
return {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
edges,
|
|
1064
|
+
totalTasks: tasks.length,
|
|
1065
|
+
totalPhases: plan.phases.length,
|
|
1066
|
+
totalAgents: agents.size,
|
|
1067
|
+
statusCounts: counts
|
|
530
1068
|
};
|
|
531
1069
|
}
|
|
532
1070
|
|
|
533
|
-
function
|
|
534
|
-
|
|
535
|
-
const agentLines = wrapText(task.agent || 'Unassigned', 24, 1);
|
|
536
|
-
const height = taskHeight + Math.max(0, titleLines.length - 1) * 20;
|
|
537
|
-
return {
|
|
538
|
-
width: taskWidth,
|
|
539
|
-
height,
|
|
540
|
-
titleLines,
|
|
541
|
-
agentLines,
|
|
542
|
-
};
|
|
1071
|
+
function getThemeMode() {
|
|
1072
|
+
return root.getAttribute('data-theme') || 'auto';
|
|
543
1073
|
}
|
|
544
1074
|
|
|
545
|
-
function
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (batch.parallel) {
|
|
551
|
-
let cursorY = contentY;
|
|
552
|
-
let width = taskWidth;
|
|
553
|
-
tasks.forEach(({ task, size }) => {
|
|
554
|
-
boxes.push({ task, x: startX, y: cursorY, width: size.width, height: size.height, size });
|
|
555
|
-
width = Math.max(width, size.width);
|
|
556
|
-
cursorY += size.height + taskGap;
|
|
557
|
-
});
|
|
558
|
-
return {
|
|
559
|
-
boxes,
|
|
560
|
-
width,
|
|
561
|
-
height: Math.max(batchLabelHeight + 8, cursorY - startY - taskGap),
|
|
562
|
-
};
|
|
1075
|
+
function getResolvedTheme() {
|
|
1076
|
+
const mode = getThemeMode();
|
|
1077
|
+
if (mode === 'light' || mode === 'dark') {
|
|
1078
|
+
return mode;
|
|
563
1079
|
}
|
|
564
|
-
|
|
565
|
-
let cursorX = startX;
|
|
566
|
-
let maxHeight = 0;
|
|
567
|
-
tasks.forEach(({ task, size }) => {
|
|
568
|
-
boxes.push({ task, x: cursorX, y: contentY, width: size.width, height: size.height, size });
|
|
569
|
-
cursorX += size.width + sequentialTaskGap;
|
|
570
|
-
maxHeight = Math.max(maxHeight, size.height);
|
|
571
|
-
});
|
|
572
|
-
return {
|
|
573
|
-
boxes,
|
|
574
|
-
width: Math.max(taskWidth, cursorX - startX - sequentialTaskGap),
|
|
575
|
-
height: batchLabelHeight + 8 + maxHeight,
|
|
576
|
-
};
|
|
1080
|
+
return colorSchemeQuery && colorSchemeQuery.matches ? 'light' : 'dark';
|
|
577
1081
|
}
|
|
578
1082
|
|
|
579
|
-
function
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
let maxWidth = 0;
|
|
584
|
-
|
|
585
|
-
model.phases.forEach((phase, phaseIndex) => {
|
|
586
|
-
const phaseX = canvasPadding;
|
|
587
|
-
const phaseY = currentY;
|
|
588
|
-
let batchCursorX = phaseX + phasePaddingX;
|
|
589
|
-
const batchBaseY = phaseY + phaseHeaderHeight + 18;
|
|
590
|
-
let rightEdge = phaseX + 320;
|
|
591
|
-
let maxBatchBottom = batchBaseY;
|
|
592
|
-
const batchLayouts = [];
|
|
593
|
-
|
|
594
|
-
phase.batches.forEach((batch) => {
|
|
595
|
-
const batchLayout = layoutBatch(batch, batchCursorX, batchBaseY);
|
|
596
|
-
batchLayout.boxes.forEach((box) => {
|
|
597
|
-
taskBoxes.set(box.task.id, box);
|
|
598
|
-
});
|
|
599
|
-
batchLayouts.push({
|
|
600
|
-
batch,
|
|
601
|
-
x: batchCursorX,
|
|
602
|
-
y: batchBaseY,
|
|
603
|
-
width: batchLayout.width,
|
|
604
|
-
height: batchLayout.height,
|
|
605
|
-
boxes: batchLayout.boxes,
|
|
606
|
-
});
|
|
607
|
-
rightEdge = Math.max(rightEdge, batchCursorX + batchLayout.width + phasePaddingX);
|
|
608
|
-
maxBatchBottom = Math.max(maxBatchBottom, batchBaseY + batchLayout.height);
|
|
609
|
-
batchCursorX += Math.max(taskWidth, batchLayout.width) + batchGap;
|
|
610
|
-
});
|
|
1083
|
+
function updateThemeButton() {
|
|
1084
|
+
const mode = getThemeMode();
|
|
1085
|
+
themeButton.textContent = 'Theme: ' + mode.charAt(0).toUpperCase() + mode.slice(1);
|
|
1086
|
+
}
|
|
611
1087
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
phaseLayouts.push({
|
|
619
|
-
phase,
|
|
620
|
-
x: phaseX,
|
|
621
|
-
y: phaseY,
|
|
622
|
-
width: phaseWidth,
|
|
623
|
-
height: phaseHeight,
|
|
624
|
-
color: phaseColor(phaseIndex),
|
|
625
|
-
batches: batchLayouts,
|
|
626
|
-
});
|
|
1088
|
+
function cycleTheme() {
|
|
1089
|
+
const current = getThemeMode();
|
|
1090
|
+
const next = themes[(themes.indexOf(current) + 1) % themes.length];
|
|
1091
|
+
root.setAttribute('data-theme', next);
|
|
1092
|
+
updateThemeButton();
|
|
1093
|
+
}
|
|
627
1094
|
|
|
628
|
-
|
|
629
|
-
currentY += phaseHeight + phaseGap;
|
|
630
|
-
});
|
|
1095
|
+
let exportErrorTimer = null;
|
|
631
1096
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
1097
|
+
function showExportError(message) {
|
|
1098
|
+
exportErrorEl.textContent = message;
|
|
1099
|
+
exportErrorEl.hidden = false;
|
|
1100
|
+
if (exportErrorTimer) {
|
|
1101
|
+
window.clearTimeout(exportErrorTimer);
|
|
1102
|
+
}
|
|
1103
|
+
exportErrorTimer = window.setTimeout(() => {
|
|
1104
|
+
exportErrorEl.hidden = true;
|
|
1105
|
+
}, 4500);
|
|
638
1106
|
}
|
|
639
1107
|
|
|
640
|
-
function
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const curve = Math.max(68, Math.abs(deltaX) * 0.38);
|
|
647
|
-
|
|
648
|
-
if (deltaX >= 0) {
|
|
649
|
-
return {
|
|
650
|
-
d: 'M ' + startX + ' ' + startY + ' C ' + (startX + curve) + ' ' + startY + ', ' + (endX - curve) + ' ' + endY + ', ' + endX + ' ' + endY,
|
|
651
|
-
labelX: startX + deltaX / 2,
|
|
652
|
-
labelY: Math.min(startY, endY) - 16,
|
|
653
|
-
};
|
|
1108
|
+
function clearExportError() {
|
|
1109
|
+
exportErrorEl.hidden = true;
|
|
1110
|
+
exportErrorEl.textContent = '';
|
|
1111
|
+
if (exportErrorTimer) {
|
|
1112
|
+
window.clearTimeout(exportErrorTimer);
|
|
1113
|
+
exportErrorTimer = null;
|
|
654
1114
|
}
|
|
1115
|
+
}
|
|
655
1116
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1117
|
+
function showEmpty(title, message) {
|
|
1118
|
+
clearElement(pipelineEl);
|
|
1119
|
+
clearElement(summaryEl);
|
|
1120
|
+
clearElement(legendEl);
|
|
1121
|
+
summaryEl.hidden = true;
|
|
1122
|
+
legendEl.hidden = true;
|
|
1123
|
+
|
|
1124
|
+
clearElement(emptyStateEl);
|
|
1125
|
+
emptyStateEl.appendChild(createElement('strong', '', title));
|
|
1126
|
+
emptyStateEl.appendChild(createElement('span', '', message));
|
|
1127
|
+
emptyStateEl.hidden = false;
|
|
663
1128
|
}
|
|
664
1129
|
|
|
665
|
-
function
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
dx: 0,
|
|
692
|
-
dy: 12,
|
|
693
|
-
stdDeviation: 12,
|
|
694
|
-
'flood-color': cssValue('--dt-shadow'),
|
|
695
|
-
}));
|
|
696
|
-
defs.appendChild(filter);
|
|
697
|
-
|
|
698
|
-
phasePalette.forEach((token, index) => {
|
|
699
|
-
const color = cssValue(token, '--dt-cyan');
|
|
700
|
-
const gradient = node('linearGradient', {
|
|
701
|
-
id: 'phase-grad-' + index,
|
|
702
|
-
x1: '0%',
|
|
703
|
-
y1: '0%',
|
|
704
|
-
x2: '100%',
|
|
705
|
-
y2: '100%',
|
|
706
|
-
});
|
|
707
|
-
gradient.appendChild(node('stop', { offset: '0%', 'stop-color': rgba(color, 0.14) }));
|
|
708
|
-
gradient.appendChild(node('stop', { offset: '100%', 'stop-color': rgba(color, 0.03) }));
|
|
709
|
-
defs.appendChild(gradient);
|
|
1130
|
+
function hideEmpty() {
|
|
1131
|
+
emptyStateEl.hidden = true;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function renderSummary(summary) {
|
|
1135
|
+
clearElement(summaryEl);
|
|
1136
|
+
|
|
1137
|
+
const statItems = [
|
|
1138
|
+
{ label: 'Total Tasks', value: String(summary.totalTasks) },
|
|
1139
|
+
{ label: 'Phases', value: String(summary.totalPhases) },
|
|
1140
|
+
{ label: 'Pending', value: String(summary.statusCounts.pending), color: 'var(--tp-status-pending)' },
|
|
1141
|
+
{ label: 'In Progress', value: String(summary.statusCounts['in-progress']), color: 'var(--tp-status-in-progress)' },
|
|
1142
|
+
{ label: 'Done', value: String(summary.statusCounts.done), color: 'var(--tp-status-done)' },
|
|
1143
|
+
{ label: 'Blocked', value: String(summary.statusCounts.blocked), color: 'var(--tp-status-blocked)' },
|
|
1144
|
+
{ label: 'Agents', value: String(summary.totalAgents) }
|
|
1145
|
+
];
|
|
1146
|
+
|
|
1147
|
+
statItems.forEach((stat) => {
|
|
1148
|
+
const statEl = createElement('div', 'task-plan-summary__stat');
|
|
1149
|
+
const valueEl = createElement('span', 'task-plan-summary__value', stat.value);
|
|
1150
|
+
if (stat.color) {
|
|
1151
|
+
valueEl.style.color = stat.color;
|
|
1152
|
+
}
|
|
1153
|
+
statEl.appendChild(valueEl);
|
|
1154
|
+
statEl.appendChild(createElement('span', 'task-plan-summary__label', stat.label));
|
|
1155
|
+
summaryEl.appendChild(statEl);
|
|
710
1156
|
});
|
|
711
1157
|
|
|
712
|
-
|
|
1158
|
+
summaryEl.hidden = false;
|
|
713
1159
|
}
|
|
714
1160
|
|
|
715
|
-
function
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1161
|
+
function renderLegend(tasks) {
|
|
1162
|
+
clearElement(legendEl);
|
|
1163
|
+
const legendItems = buildLegendItems(tasks);
|
|
1164
|
+
|
|
1165
|
+
if (legendItems.length <= 1) {
|
|
1166
|
+
legendEl.hidden = true;
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
legendEl.appendChild(createElement('span', 'task-plan__legend-title', 'Agents'));
|
|
1171
|
+
|
|
1172
|
+
legendItems.forEach((item) => {
|
|
1173
|
+
const legendItemEl = createElement('span', 'task-plan__legend-item');
|
|
1174
|
+
const dotEl = createElement('span', 'task-plan__legend-dot');
|
|
1175
|
+
dotEl.style.backgroundColor = item.color;
|
|
1176
|
+
|
|
1177
|
+
const textWrapEl = createElement('span', '');
|
|
1178
|
+
textWrapEl.appendChild(createElement('span', 'task-plan__legend-icon', item.icon));
|
|
1179
|
+
textWrapEl.appendChild(document.createTextNode(' ' + item.label));
|
|
1180
|
+
|
|
1181
|
+
legendItemEl.appendChild(dotEl);
|
|
1182
|
+
legendItemEl.appendChild(textWrapEl);
|
|
1183
|
+
legendEl.appendChild(legendItemEl);
|
|
725
1184
|
});
|
|
726
|
-
}
|
|
727
1185
|
|
|
728
|
-
|
|
729
|
-
const group = node('g');
|
|
730
|
-
const gradientId = 'phase-grad-' + (index % phasePalette.length);
|
|
731
|
-
|
|
732
|
-
group.appendChild(node('rect', {
|
|
733
|
-
x: phaseLayout.x,
|
|
734
|
-
y: phaseLayout.y,
|
|
735
|
-
width: phaseLayout.width,
|
|
736
|
-
height: phaseLayout.height,
|
|
737
|
-
rx: 28,
|
|
738
|
-
fill: 'url(#' + gradientId + ')',
|
|
739
|
-
stroke: rgba(phaseLayout.color, 0.42),
|
|
740
|
-
'stroke-width': 1.6,
|
|
741
|
-
'stroke-dasharray': '10 10',
|
|
742
|
-
}));
|
|
743
|
-
|
|
744
|
-
group.appendChild(node('text', {
|
|
745
|
-
x: phaseLayout.x + phasePaddingX,
|
|
746
|
-
y: phaseLayout.y + 30,
|
|
747
|
-
fill: phaseLayout.color,
|
|
748
|
-
'font-size': 10,
|
|
749
|
-
'font-family': monoFont,
|
|
750
|
-
'font-weight': 700,
|
|
751
|
-
'letter-spacing': '0.08em',
|
|
752
|
-
}, 'PHASE'));
|
|
753
|
-
|
|
754
|
-
group.appendChild(node('text', {
|
|
755
|
-
x: phaseLayout.x + phasePaddingX,
|
|
756
|
-
y: phaseLayout.y + 50,
|
|
757
|
-
fill: cssValue('--dt-text-primary'),
|
|
758
|
-
'font-size': 18,
|
|
759
|
-
'font-weight': 700,
|
|
760
|
-
}, phaseLayout.phase.label));
|
|
761
|
-
|
|
762
|
-
const outcomeLines = wrapText(phaseLayout.phase.outcome || '', 38, 2);
|
|
763
|
-
renderTextLines(
|
|
764
|
-
outcomeLines,
|
|
765
|
-
group,
|
|
766
|
-
phaseLayout.x + phasePaddingX,
|
|
767
|
-
phaseLayout.y + 68,
|
|
768
|
-
11.5,
|
|
769
|
-
cssValue('--dt-text-secondary'),
|
|
770
|
-
500,
|
|
771
|
-
15,
|
|
772
|
-
'start',
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
layer.appendChild(group);
|
|
1186
|
+
legendEl.hidden = false;
|
|
776
1187
|
}
|
|
777
1188
|
|
|
778
|
-
function
|
|
779
|
-
const
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
y: batchLayout.y + 14,
|
|
785
|
-
fill: cssValue('--dt-text-secondary'),
|
|
786
|
-
'font-size': 10.5,
|
|
787
|
-
'font-family': monoFont,
|
|
788
|
-
'font-weight': 600,
|
|
789
|
-
'letter-spacing': '0.03em',
|
|
790
|
-
}, label));
|
|
791
|
-
|
|
792
|
-
const orderText = '#' + String(batchLayout.batch.order);
|
|
793
|
-
const badgeWidth = Math.max(30, orderText.length * 7 + 14);
|
|
794
|
-
group.appendChild(node('rect', {
|
|
795
|
-
x: batchLayout.x + Math.max(120, Math.min(batchLayout.width - badgeWidth, label.length * 5.9 + 16)),
|
|
796
|
-
y: batchLayout.y,
|
|
797
|
-
width: badgeWidth,
|
|
798
|
-
height: 18,
|
|
799
|
-
rx: 9,
|
|
800
|
-
fill: rgba(cssValue('--dt-slate'), 0.14),
|
|
801
|
-
stroke: rgba(cssValue('--dt-slate'), 0.26),
|
|
802
|
-
}));
|
|
803
|
-
group.appendChild(node('text', {
|
|
804
|
-
x: batchLayout.x + Math.max(120, Math.min(batchLayout.width - badgeWidth, label.length * 5.9 + 16)) + badgeWidth / 2,
|
|
805
|
-
y: batchLayout.y + 12,
|
|
806
|
-
fill: cssValue('--dt-text-secondary'),
|
|
807
|
-
'font-size': 10,
|
|
808
|
-
'font-family': monoFont,
|
|
809
|
-
'font-weight': 700,
|
|
810
|
-
'text-anchor': 'middle',
|
|
811
|
-
}, orderText));
|
|
812
|
-
|
|
813
|
-
layer.appendChild(group);
|
|
814
|
-
}
|
|
1189
|
+
function createTaskCard(task) {
|
|
1190
|
+
const status = task.status || 'pending';
|
|
1191
|
+
const article = createElement('article', 'task-plan-task');
|
|
1192
|
+
article.dataset.status = status;
|
|
1193
|
+
article.style.setProperty('--card-accent', getAgentColor(task.agent));
|
|
1194
|
+
article.style.setProperty('--status-color', 'var(--tp-status-' + status + ')');
|
|
815
1195
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
attributeName: 'opacity',
|
|
837
|
-
values: '0.9;0.2;0.9',
|
|
838
|
-
dur: '1.6s',
|
|
839
|
-
repeatCount: 'indefinite',
|
|
840
|
-
}));
|
|
841
|
-
group.appendChild(pulse);
|
|
1196
|
+
const topLineEl = createElement('div', 'task-plan-task__topline');
|
|
1197
|
+
|
|
1198
|
+
const agentEl = createElement('span', 'task-plan-task__agent');
|
|
1199
|
+
agentEl.title = task.agent || 'Orchestrator';
|
|
1200
|
+
agentEl.appendChild(createElement('span', 'task-plan-task__agent-icon', getAgentIcon(task.agent)));
|
|
1201
|
+
agentEl.appendChild(createElement('span', 'task-plan-task__agent-label', task.agent || 'Orchestrator'));
|
|
1202
|
+
|
|
1203
|
+
const statusEl = createElement('span', 'task-plan-task__status');
|
|
1204
|
+
statusEl.dataset.status = status;
|
|
1205
|
+
statusEl.appendChild(createElement('span', 'task-plan-task__status-dot'));
|
|
1206
|
+
statusEl.appendChild(createElement('span', '', statusLabels[status] || statusLabels.pending));
|
|
1207
|
+
|
|
1208
|
+
topLineEl.appendChild(agentEl);
|
|
1209
|
+
topLineEl.appendChild(statusEl);
|
|
1210
|
+
article.appendChild(topLineEl);
|
|
1211
|
+
|
|
1212
|
+
article.appendChild(createElement('div', 'task-plan-task__title', task.title));
|
|
1213
|
+
|
|
1214
|
+
if (task.description) {
|
|
1215
|
+
article.appendChild(createElement('p', 'task-plan-task__description', task.description));
|
|
842
1216
|
}
|
|
843
1217
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
fill: meta.color,
|
|
849
|
-
}));
|
|
850
|
-
|
|
851
|
-
if (meta.key === 'done') {
|
|
852
|
-
group.appendChild(node('path', {
|
|
853
|
-
d: 'M ' + (cx - 3) + ' ' + cy + ' L ' + (cx - 1) + ' ' + (cy + 3) + ' L ' + (cx + 4) + ' ' + (cy - 3),
|
|
854
|
-
fill: 'none',
|
|
855
|
-
stroke: '#ffffff',
|
|
856
|
-
'stroke-width': 1.6,
|
|
857
|
-
'stroke-linecap': 'round',
|
|
858
|
-
'stroke-linejoin': 'round',
|
|
859
|
-
}));
|
|
1218
|
+
const metaEl = createElement('div', 'task-plan-task__meta');
|
|
1219
|
+
const fileCount = task.files ? task.files.length : 0;
|
|
1220
|
+
if (fileCount > 0) {
|
|
1221
|
+
metaEl.appendChild(createElement('span', 'task-plan-task__chip', '📁 ' + getFileCountLabel(fileCount)));
|
|
860
1222
|
}
|
|
861
1223
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1224
|
+
getDependencyLabels(task).forEach((label) => {
|
|
1225
|
+
const chipEl = createElement('span', 'task-plan-task__chip', '🔗 ' + label);
|
|
1226
|
+
chipEl.title = label;
|
|
1227
|
+
metaEl.appendChild(chipEl);
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
if (metaEl.childNodes.length > 0) {
|
|
1231
|
+
article.appendChild(metaEl);
|
|
870
1232
|
}
|
|
871
1233
|
|
|
872
|
-
|
|
873
|
-
x: box.x + 30,
|
|
874
|
-
y: box.y + box.height - 14,
|
|
875
|
-
fill: cssValue('--dt-text-secondary'),
|
|
876
|
-
'font-size': 10.5,
|
|
877
|
-
'font-weight': 600,
|
|
878
|
-
}, meta.label));
|
|
1234
|
+
return article;
|
|
879
1235
|
}
|
|
880
1236
|
|
|
881
|
-
function
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1237
|
+
function renderPipeline(plan) {
|
|
1238
|
+
clearElement(pipelineEl);
|
|
1239
|
+
|
|
1240
|
+
plan.phases.forEach((phase, phaseIndex) => {
|
|
1241
|
+
const phaseBlockEl = createElement('div', 'task-plan__phase-block');
|
|
1242
|
+
const phaseSectionEl = createElement('section', 'task-plan-phase');
|
|
1243
|
+
const headingId = 'task-plan-phase-' + phase.id;
|
|
1244
|
+
|
|
1245
|
+
phaseSectionEl.setAttribute('aria-labelledby', headingId);
|
|
1246
|
+
|
|
1247
|
+
const contentEl = createElement('div', 'task-plan-phase__content');
|
|
1248
|
+
contentEl.style.setProperty('--phase-accent', phasePalette[phaseIndex % phasePalette.length]);
|
|
1249
|
+
|
|
1250
|
+
const headerEl = createElement('div', 'task-plan-phase__header');
|
|
1251
|
+
headerEl.appendChild(createElement('span', 'task-plan-phase__number', String(phaseIndex + 1)));
|
|
1252
|
+
|
|
1253
|
+
const labelEl = createElement('h2', 'task-plan-phase__label', phase.label);
|
|
1254
|
+
labelEl.id = headingId;
|
|
1255
|
+
headerEl.appendChild(labelEl);
|
|
1256
|
+
|
|
1257
|
+
headerEl.appendChild(createElement('span', 'task-plan-phase__badge', getPhaseBadge(phase)));
|
|
1258
|
+
contentEl.appendChild(headerEl);
|
|
1259
|
+
|
|
1260
|
+
if (phase.outcome) {
|
|
1261
|
+
contentEl.appendChild(createElement('p', 'task-plan-phase__outcome', phase.outcome));
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const batchesEl = createElement('div', 'task-plan-phase__batches');
|
|
1265
|
+
phase.batches.forEach((batch) => {
|
|
1266
|
+
const batchEl = createElement('section', 'task-plan-batch');
|
|
1267
|
+
batchEl.setAttribute('aria-label', getBatchDescriptor(batch));
|
|
1268
|
+
|
|
1269
|
+
const batchLabelEl = createElement('div', 'task-plan-batch__label');
|
|
1270
|
+
batchLabelEl.appendChild(createElement('span', 'task-plan-batch__icon', getBatchIcon(batch)));
|
|
1271
|
+
batchLabelEl.appendChild(createElement('span', '', getBatchDescriptor(batch)));
|
|
1272
|
+
batchEl.appendChild(batchLabelEl);
|
|
1273
|
+
|
|
1274
|
+
const tasksClass = ['task-plan-batch__tasks'];
|
|
1275
|
+
if (batch.parallel) {
|
|
1276
|
+
tasksClass.push('task-plan-batch__tasks--parallel');
|
|
1277
|
+
} else if (batch.tasks.length <= 2) {
|
|
1278
|
+
tasksClass.push('task-plan-batch__tasks--sequential-compact');
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const tasksEl = createElement('div', tasksClass.join(' '));
|
|
1282
|
+
batch.tasks.forEach((task) => {
|
|
1283
|
+
tasksEl.appendChild(createTaskCard(task));
|
|
1284
|
+
});
|
|
1285
|
+
batchEl.appendChild(tasksEl);
|
|
1286
|
+
batchesEl.appendChild(batchEl);
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
contentEl.appendChild(batchesEl);
|
|
1290
|
+
phaseSectionEl.appendChild(contentEl);
|
|
1291
|
+
phaseBlockEl.appendChild(phaseSectionEl);
|
|
1292
|
+
|
|
1293
|
+
if (phaseIndex < plan.phases.length - 1) {
|
|
1294
|
+
phaseBlockEl.appendChild(createElement('div', 'task-plan__connector'));
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
pipelineEl.appendChild(phaseBlockEl);
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
function updateHeader(plan) {
|
|
1302
|
+
const title = plan.title || 'Task Plan';
|
|
1303
|
+
const description = plan.description || 'Phase-based pipeline for agent work, dependencies, and delivery state.';
|
|
1304
|
+
|
|
1305
|
+
viewerTitleEl.textContent = title;
|
|
1306
|
+
viewerDescriptionEl.textContent = description;
|
|
1307
|
+
document.title = 'AI Kit - ' + title;
|
|
908
1308
|
}
|
|
909
1309
|
|
|
910
|
-
function
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
const
|
|
914
|
-
const
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
rx: 4,
|
|
945
|
-
fill: accent,
|
|
946
|
-
}));
|
|
947
|
-
|
|
948
|
-
group.appendChild(node('circle', {
|
|
949
|
-
cx: box.x + 22,
|
|
950
|
-
cy: box.y + 19,
|
|
951
|
-
r: 11,
|
|
952
|
-
fill: rgba(accent, 0.18),
|
|
953
|
-
stroke: rgba(accent, 0.4),
|
|
954
|
-
}));
|
|
955
|
-
group.appendChild(node('text', {
|
|
956
|
-
x: box.x + 22,
|
|
957
|
-
y: box.y + 23,
|
|
958
|
-
fill: accent,
|
|
959
|
-
'font-size': 10,
|
|
960
|
-
'font-family': monoFont,
|
|
961
|
-
'font-weight': 700,
|
|
962
|
-
'text-anchor': 'middle',
|
|
963
|
-
}, token));
|
|
964
|
-
|
|
965
|
-
renderTextLines(box.size.agentLines, group, titleX, box.y + 21, 10.5, accent, 700, 14, 'start');
|
|
966
|
-
renderTextLines(box.size.titleLines, group, titleX, box.y + 40, 13.5, cssValue('--dt-text-primary'), 700, 17, 'start');
|
|
967
|
-
|
|
968
|
-
renderStatus(group, box, meta);
|
|
969
|
-
renderFileBadge(group, box, task.files);
|
|
970
|
-
|
|
971
|
-
layer.appendChild(group);
|
|
1310
|
+
function buildExportSvgMarkup() {
|
|
1311
|
+
const resolvedTheme = getResolvedTheme();
|
|
1312
|
+
const exportClone = surfaceEl.cloneNode(true);
|
|
1313
|
+
const padding = 24;
|
|
1314
|
+
const contentWidth = Math.ceil(surfaceEl.scrollWidth);
|
|
1315
|
+
const contentHeight = Math.ceil(surfaceEl.scrollHeight);
|
|
1316
|
+
const width = contentWidth + padding * 2;
|
|
1317
|
+
const height = contentHeight + padding * 2;
|
|
1318
|
+
|
|
1319
|
+
exportClone.removeAttribute('id');
|
|
1320
|
+
exportClone.style.width = contentWidth + 'px';
|
|
1321
|
+
exportClone.style.minHeight = '0';
|
|
1322
|
+
exportClone.style.boxShadow = 'none';
|
|
1323
|
+
|
|
1324
|
+
let stylesheet = styleTag && styleTag.textContent ? styleTag.textContent : '';
|
|
1325
|
+
stylesheet = stylesheet.replace(/:root\b/g, '.tp-export-root');
|
|
1326
|
+
stylesheet = stylesheet.replace(/\]\]>/g, ']]>');
|
|
1327
|
+
stylesheet += '\n.tp-export-root{width:' + contentWidth + 'px;padding:' + padding + 'px;background:transparent;font-family:var(--font-sans);color:var(--viewer-text);}\n';
|
|
1328
|
+
stylesheet += '.tp-export-root .task-plan__surface{min-height:0;box-shadow:none;}\n';
|
|
1329
|
+
|
|
1330
|
+
const serializedSurface = exportClone.outerHTML;
|
|
1331
|
+
const svg = [
|
|
1332
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
1333
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '" viewBox="0 0 ' + width + ' ' + height + '">',
|
|
1334
|
+
'<foreignObject width="100%" height="100%">',
|
|
1335
|
+
'<div xmlns="http://www.w3.org/1999/xhtml" class="tp-export-root" data-theme="' + resolvedTheme + '">',
|
|
1336
|
+
'<style><![CDATA[' + stylesheet + ']]></style>',
|
|
1337
|
+
serializedSurface,
|
|
1338
|
+
'</div>',
|
|
1339
|
+
'</foreignObject>',
|
|
1340
|
+
'</svg>'
|
|
1341
|
+
].join('');
|
|
1342
|
+
|
|
1343
|
+
return { markup: svg, width, height };
|
|
972
1344
|
}
|
|
973
1345
|
|
|
974
|
-
function
|
|
975
|
-
const
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
1346
|
+
function downloadBlob(blob, filename) {
|
|
1347
|
+
const url = URL.createObjectURL(blob);
|
|
1348
|
+
const anchor = document.createElement('a');
|
|
1349
|
+
anchor.href = url;
|
|
1350
|
+
anchor.download = filename;
|
|
1351
|
+
document.body.appendChild(anchor);
|
|
1352
|
+
anchor.click();
|
|
1353
|
+
anchor.remove();
|
|
1354
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
async function exportSvg() {
|
|
1358
|
+
clearExportError();
|
|
1359
|
+
try {
|
|
1360
|
+
const exported = buildExportSvgMarkup();
|
|
1361
|
+
const blob = new Blob([exported.markup], { type: 'image/svg+xml;charset=utf-8' });
|
|
1362
|
+
downloadBlob(blob, 'task-plan.svg');
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
showExportError(error instanceof Error ? error.message : 'SVG export failed.');
|
|
979
1365
|
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
async function exportPng() {
|
|
1369
|
+
clearExportError();
|
|
1370
|
+
try {
|
|
1371
|
+
const exported = buildExportSvgMarkup();
|
|
1372
|
+
const blob = new Blob([exported.markup], { type: 'image/svg+xml;charset=utf-8' });
|
|
1373
|
+
const url = URL.createObjectURL(blob);
|
|
1374
|
+
const image = new Image();
|
|
1375
|
+
const scale = Math.max(window.devicePixelRatio || 1, 2);
|
|
1376
|
+
|
|
1377
|
+
await new Promise((resolve, reject) => {
|
|
1378
|
+
image.onload = resolve;
|
|
1379
|
+
image.onerror = () => reject(new Error('PNG export is unavailable in this browser. Use SVG export instead.'));
|
|
1380
|
+
image.src = url;
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
const canvas = document.createElement('canvas');
|
|
1384
|
+
canvas.width = Math.ceil(exported.width * scale);
|
|
1385
|
+
canvas.height = Math.ceil(exported.height * scale);
|
|
1386
|
+
|
|
1387
|
+
const context = canvas.getContext('2d');
|
|
1388
|
+
if (!context) {
|
|
1389
|
+
URL.revokeObjectURL(url);
|
|
1390
|
+
throw new Error('Canvas export is unavailable in this browser.');
|
|
1391
|
+
}
|
|
980
1392
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1393
|
+
context.scale(scale, scale);
|
|
1394
|
+
context.drawImage(image, 0, 0, exported.width, exported.height);
|
|
1395
|
+
|
|
1396
|
+
const pngBlob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
|
|
1397
|
+
URL.revokeObjectURL(url);
|
|
1398
|
+
|
|
1399
|
+
if (!pngBlob) {
|
|
1400
|
+
throw new Error('PNG export failed.');
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
downloadBlob(pngBlob, 'task-plan.png');
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
showExportError(error instanceof Error ? error.message : 'PNG export failed.');
|
|
1406
|
+
}
|
|
991
1407
|
}
|
|
992
1408
|
|
|
993
1409
|
function render() {
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
1410
|
+
const plan = readData();
|
|
1411
|
+
if (!plan) {
|
|
996
1412
|
return;
|
|
997
1413
|
}
|
|
998
1414
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
return count + phase.batches.reduce((batchCount, batch) => batchCount + batch.tasks.length, 0);
|
|
1002
|
-
}, 0);
|
|
1415
|
+
clearExportError();
|
|
1416
|
+
updateHeader(plan);
|
|
1003
1417
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1418
|
+
const tasks = getTasks(plan);
|
|
1419
|
+
if (tasks.length === 0) {
|
|
1420
|
+
showEmpty('No tasks to render', 'Add phases, batches, and tasks to populate the plan.');
|
|
1006
1421
|
return;
|
|
1007
1422
|
}
|
|
1008
1423
|
|
|
1009
1424
|
hideEmpty();
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
svg.style.height = layoutResult.height + 'px';
|
|
1014
|
-
svg.setAttribute('aria-label', model.title + ' with ' + totalTasks + ' tasks');
|
|
1015
|
-
svg.appendChild(renderDefs());
|
|
1016
|
-
svg.appendChild(node('title', { id: 'diagram-title' }, model.title || 'Task execution plan'));
|
|
1017
|
-
svg.appendChild(node('desc', { id: 'diagram-description' }, model.description || 'Static task execution plan rendered with SVG'));
|
|
1018
|
-
|
|
1019
|
-
const phaseLayer = node('g', { 'aria-hidden': 'true' });
|
|
1020
|
-
const batchLayer = node('g', { 'aria-hidden': 'true' });
|
|
1021
|
-
const edgeLayer = node('g', { 'aria-hidden': 'true' });
|
|
1022
|
-
const nodeLayer = node('g');
|
|
1023
|
-
|
|
1024
|
-
layoutResult.phases.forEach((phaseLayout, index) => {
|
|
1025
|
-
renderPhase(phaseLayout, phaseLayer, index);
|
|
1026
|
-
phaseLayout.batches.forEach((batchLayout) => {
|
|
1027
|
-
renderBatch(batchLayout, batchLayer);
|
|
1028
|
-
batchLayout.boxes.forEach((box) => renderTask(box, nodeLayer));
|
|
1029
|
-
});
|
|
1030
|
-
});
|
|
1031
|
-
|
|
1032
|
-
model.edges.forEach((edge) => renderEdge(edge, layoutResult.taskBoxes, edgeLayer));
|
|
1033
|
-
|
|
1034
|
-
svg.appendChild(phaseLayer);
|
|
1035
|
-
svg.appendChild(edgeLayer);
|
|
1036
|
-
svg.appendChild(batchLayer);
|
|
1037
|
-
svg.appendChild(nodeLayer);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
function updateThemeButton() {
|
|
1041
|
-
const value = root.getAttribute('data-theme') || 'auto';
|
|
1042
|
-
themeButton.textContent = 'Theme: ' + value.charAt(0).toUpperCase() + value.slice(1);
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
function cycleTheme() {
|
|
1046
|
-
const current = root.getAttribute('data-theme') || 'auto';
|
|
1047
|
-
const next = themes[(themes.indexOf(current) + 1) % themes.length];
|
|
1048
|
-
root.setAttribute('data-theme', next);
|
|
1049
|
-
updateThemeButton();
|
|
1050
|
-
render();
|
|
1425
|
+
renderPipeline(plan);
|
|
1426
|
+
renderSummary(getSummary(plan));
|
|
1427
|
+
renderLegend(tasks);
|
|
1051
1428
|
}
|
|
1052
1429
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1430
|
+
themeButton.addEventListener('click', cycleTheme);
|
|
1431
|
+
exportSvgButton.addEventListener('click', () => {
|
|
1432
|
+
void exportSvg();
|
|
1433
|
+
});
|
|
1434
|
+
exportPngButton.addEventListener('click', () => {
|
|
1435
|
+
void exportPng();
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
if (colorSchemeQuery && typeof colorSchemeQuery.addEventListener === 'function') {
|
|
1439
|
+
colorSchemeQuery.addEventListener('change', () => {
|
|
1440
|
+
if (getThemeMode() === 'auto') {
|
|
1441
|
+
clearExportError();
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1064
1444
|
}
|
|
1065
1445
|
|
|
1066
|
-
themeButton.addEventListener('click', cycleTheme);
|
|
1067
|
-
exportButton.addEventListener('click', exportSvg);
|
|
1068
1446
|
updateThemeButton();
|
|
1069
1447
|
render();
|
|
1070
1448
|
})();
|
|
1071
1449
|
</script>
|
|
1072
1450
|
</body>
|
|
1073
|
-
</html>
|
|
1451
|
+
</html>
|