@njeybe/nativ 2.7.0 → 2.9.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.
- package/CHANGELOG.md +36 -0
- package/README.md +26 -1
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.js +36 -0
- package/dist/commands/doctor.js +7 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +1 -1
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +1 -0
- package/dist/commands/task/task-add.js +49 -6
- package/dist/commands/task/task-common.d.ts +6 -0
- package/dist/commands/task/task-lifecycle.d.ts +3 -1
- package/dist/commands/task/task-lifecycle.js +40 -2
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/verify.d.ts +7 -0
- package/dist/commands/verify.js +26 -2
- package/dist/core/checks/docs-check.d.ts +18 -0
- package/dist/core/checks/docs-check.js +186 -0
- package/dist/core/code-shape.d.ts +7 -2
- package/dist/core/code-shape.js +46 -16
- package/dist/core/env-reads.d.ts +15 -0
- package/dist/core/env-reads.js +126 -0
- package/dist/core/human-gate.js +6 -3
- package/dist/core/profiles.d.ts +8 -0
- package/dist/core/profiles.js +43 -2
- package/dist/core/root-resolver.d.ts +4 -0
- package/dist/core/root-resolver.js +32 -0
- package/dist/core/setup/gitignore-block.d.ts +17 -0
- package/dist/core/setup/gitignore-block.js +182 -0
- package/dist/core/setup/pm-playbook.d.ts +1 -1
- package/dist/core/setup/pm-playbook.js +1 -1
- package/dist/core/setup/setup-planner.js +36 -3
- package/dist/core/setup/types.d.ts +6 -0
- package/dist/core/verifier/clean-tree.d.ts +14 -0
- package/dist/core/verifier/clean-tree.js +151 -0
- package/dist/core/verifier/executor.d.ts +3 -1
- package/dist/core/verifier/executor.js +54 -31
- package/dist/core/verifier/isolation.d.ts +25 -0
- package/dist/core/verifier/isolation.js +77 -0
- package/dist/core/verifier/portability-lint.d.ts +2 -0
- package/dist/core/verifier/portability-lint.js +64 -0
- package/dist/core/verifier/skip-policy.d.ts +8 -0
- package/dist/core/verifier/skip-policy.js +13 -0
- package/dist/core/verifier/task-verifier.d.ts +6 -8
- package/dist/core/verifier/task-verifier.js +68 -39
- package/dist/core/verifier/types.d.ts +4 -0
- package/dist/core/verifier/verify-env.d.ts +2 -0
- package/dist/core/verifier/verify-env.js +19 -0
- package/dist/index.js +33 -4
- package/dist/mcp/server.js +9 -2
- package/dist/scanner/types.d.ts +4 -0
- package/dist/scanner/types.js +9 -0
- package/dist/server/routes/task-routes.d.ts +1 -0
- package/dist/server/studio-ui/pages/benchmarks/script.js +9 -8
- package/dist/server/studio-ui/pages/benchmarks/styles.js +3 -1
- package/dist/server/studio-ui/pages/database/browser.js +1 -1
- package/dist/server/studio-ui/pages/database/connection.js +5 -4
- package/dist/server/studio-ui/pages/database/data-shell.js +8 -13
- package/dist/server/studio-ui/pages/database/drift.js +1 -1
- package/dist/server/studio-ui/pages/database/events.js +1 -2
- package/dist/server/studio-ui/pages/database/explorer.js +4 -4
- package/dist/server/studio-ui/pages/flow/layout.js +1 -1
- package/dist/server/studio-ui/pages/flow/styles.js +1 -1
- package/dist/server/studio-ui/pages/flow/timeline-view.js +1 -1
- package/dist/server/studio-ui/pages/home/script.js +26 -29
- package/dist/server/studio-ui/pages/home/styles.js +2 -6
- package/dist/server/studio-ui/pages/tasks/script.js +25 -40
- package/dist/server/studio-ui/pages/tasks/styles.js +9 -9
- package/dist/server/studio-ui/pages/team/script.js +27 -8
- package/dist/server/studio-ui/pages/team/styles.js +0 -1
- package/dist/server/studio-ui/shell/console.js +13 -7
- package/dist/server/studio-ui/shell/detail-view.js +3 -3
- package/dist/server/studio-ui/shell/dispatch.js +9 -8
- package/dist/server/studio-ui/shell/events-tools.js +11 -0
- package/dist/server/studio-ui/shell/format.js +31 -2
- package/dist/server/studio-ui/shell/markup-head.js +1 -1
- package/dist/server/studio-ui/shell/markup-tools.js +13 -8
- package/dist/server/studio-ui/shell/model.js +8 -2
- package/dist/server/studio-ui/shell/paint.js +1 -3
- package/dist/server/studio-ui/shell/pipeline-state.js +2 -11
- package/dist/server/studio-ui/shell/pulse.js +8 -7
- package/dist/server/studio-ui/shell/sidebar-view.js +2 -3
- package/dist/server/studio-ui/shell/triage.js +1 -1
- package/dist/server/studio-ui/styles/console.js +3 -0
- package/dist/server/studio-ui/styles/shell.js +4 -6
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/agents/architect.md +15 -8
- package/plugin/agents/verifier.md +16 -11
- package/plugin/skills/nativ/SKILL.md +18 -15
- package/templates/AGENTS.md +18 -15
- package/templates/GEMINI.md +1 -1
- package/templates/claude-agents/architect.md +15 -8
- package/templates/claude-agents/verifier.md +16 -11
- package/templates/dot-ai/subagents/architect.md +2 -2
- package/templates/dot-ai/subagents/qa-tester.md +14 -13
|
@@ -34,9 +34,9 @@ export const benchmarksScript = String.raw ` // ─── View 4: Synthetic ben
|
|
|
34
34
|
if (env.cpuCount) meta.push(env.cpuCount + ' CPUs');
|
|
35
35
|
var score = clampPct(s.score);
|
|
36
36
|
return '<section class="card hero' + (running ? ' is-running' : '') + '" aria-labelledby="hero-h" aria-busy="' + running + '">' +
|
|
37
|
-
'<div><h2 class="
|
|
37
|
+
'<div><h2 class="hero-title" id="hero-h">Average throughput</h2>' +
|
|
38
38
|
'<div class="hero-value">' + num(Math.round(s.averageThroughputOpsPerSec || 0)) + '<span class="badge badge-accent">ops/sec</span></div>' +
|
|
39
|
-
'<p class="hero-meta">' + meta.join('
|
|
39
|
+
'<p class="hero-meta">' + meta.join(', ') + '</p></div>' +
|
|
40
40
|
'<dl class="hero-stats">' +
|
|
41
41
|
'<div><dt>Score</dt><dd class="' + (score < 100 ? 'text-danger' : '') + '">' + score + '%</dd></div>' +
|
|
42
42
|
'<div><dt>Scenarios passed</dt><dd>' + num(s.passedScenarios) + '/' + num(s.totalScenarios) + '</dd></div>' +
|
|
@@ -48,16 +48,17 @@ export const benchmarksScript = String.raw ` // ─── View 4: Synthetic ben
|
|
|
48
48
|
function scenarioCards(r) {
|
|
49
49
|
var byId = {}, known = {};
|
|
50
50
|
(r.scenarios || []).forEach(function (s) { byId[s.id] = s; });
|
|
51
|
-
var cards =
|
|
51
|
+
var cards = [], idle = [];
|
|
52
|
+
SCENARIOS.forEach(function (meta) {
|
|
53
|
+
known[meta.id] = true;
|
|
54
|
+
if (byId[meta.id]) cards.push(scenarioCard(meta.label, byId[meta.id])); else idle.push(meta.label);
|
|
55
|
+
});
|
|
52
56
|
(r.scenarios || []).forEach(function (s) { if (!known[s.id]) cards.push(scenarioCard(s.name || s.id, s)); });
|
|
57
|
+
if (idle.length) cards.push('<p class="muted not-run">Not run: ' + idle.map(esc).join(', ') + '.</p>');
|
|
53
58
|
return cards.join('');
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
function scenarioCard(label, s) {
|
|
57
|
-
if (!s) {
|
|
58
|
-
return '<article class="card scenario is-idle"><header class="scenario-head"><h3>' + esc(label) + '</h3><span class="badge badge-neutral">Not run</span></header>' +
|
|
59
|
-
'<p class="muted">Not part of the last run. Run the full matrix to score it.</p></article>';
|
|
60
|
-
}
|
|
61
62
|
var total = (s.assertionsPassed || 0) + (s.assertionsFailed || 0);
|
|
62
63
|
var passed = s.status === 'passed';
|
|
63
64
|
var score = total ? Math.round((s.assertionsPassed || 0) / total * 100) : (passed ? 100 : 0);
|
|
@@ -76,7 +77,7 @@ export const benchmarksScript = String.raw ` // ─── View 4: Synthetic ben
|
|
|
76
77
|
'<div class="metrics"><div><span class="metric-v">' + score + '%</span><span class="metric-l">Score</span></div>' +
|
|
77
78
|
'<div><span class="metric-v">' + fmtMs(s.durationMs) + '</span><span class="metric-l">Latency</span></div>' +
|
|
78
79
|
'<div><span class="metric-v">' + num(Math.round(s.throughputOpsPerSec || 0)) + '</span><span class="metric-l">ops/sec</span></div></div>' +
|
|
79
|
-
progressBar(score, 'sc-' + s.id, label + ' score', passed ? '
|
|
80
|
+
progressBar(score, 'sc-' + s.id, label + ' score', passed ? '' : 'danger') +
|
|
80
81
|
(details ? '<dl class="details">' + details + '</dl>' : '') + errors + '</article>';
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
export const benchmarksCss = String.raw `/* Benchmarks */
|
|
3
3
|
.hero { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 20px 32px; padding: 22px 0; margin-bottom: 0; background: none; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; box-shadow: none; transition: opacity .2s; }
|
|
4
4
|
.hero.is-running { opacity: .6; }
|
|
5
|
-
.
|
|
5
|
+
.hero-title { margin: 0; font-size: 14px; font-weight: 600; color: var(--muted); }
|
|
6
|
+
.not-run { margin: 0; padding: 12px 0; font-size: 13px; }
|
|
7
|
+
.scenario .progress { height: 4px; }
|
|
6
8
|
.hero-value { display: flex; align-items: center; gap: 10px; margin: 6px 0; font-size: 40px; font-weight: 650; letter-spacing: -0.03em; line-height: 1.1; font-variant-numeric: tabular-nums; }
|
|
7
9
|
.hero-value .badge { font-size: 12px; letter-spacing: 0; }
|
|
8
10
|
.hero-meta { margin: 0; font-size: 13.5px; color: var(--muted); }
|
|
@@ -168,7 +168,7 @@ export const databaseBrowserScript = String.raw ` // ─── Database Studio:
|
|
|
168
168
|
table.columns.forEach(function (col) {
|
|
169
169
|
var isPk = col.primaryKey;
|
|
170
170
|
var fieldLabel = esc(col.name) + (isPk ? ' (Primary Key - Optional/Auto)' : '');
|
|
171
|
-
var placeholder = col.type + (col.default ? '
|
|
171
|
+
var placeholder = col.type + (col.default ? ', default: ' + col.default : '');
|
|
172
172
|
var isLong = /json|map|array|text/i.test(col.type);
|
|
173
173
|
if (isLong) {
|
|
174
174
|
html += '<div class="field field-full"><label><b>' + fieldLabel + '</b></label><textarea class="input" name="' + esc(col.name) + '" placeholder="' + esc(placeholder) + '"></textarea></div>';
|
|
@@ -3,10 +3,11 @@ export const databaseConnectionScript = String.raw ` // ─── Database Stud
|
|
|
3
3
|
function renderModalStatus() {
|
|
4
4
|
['dev', 'prod'].forEach(function (env) {
|
|
5
5
|
var s = state.status && state.status[env];
|
|
6
|
-
|
|
6
|
+
var unset = !s || /No (Dev|Prod) database configured/.test(s.error || '');
|
|
7
|
+
$('mdot-' + env).className = 'dot ' + (s && s.connected ? 'on' : unset ? '' : 'off');
|
|
7
8
|
$('current-' + env).innerHTML = s && s.maskedUrl
|
|
8
9
|
? 'Current: <code>' + esc(s.maskedUrl) + '</code><span class="copy-wrap"><span class="tooltip">Copied!</span><button class="btn small" type="button" data-copy-masked="' + env + '">Copy</button></span>'
|
|
9
|
-
: 'Current: <span class="muted">
|
|
10
|
+
: 'Current: <span class="muted">Not set up</span>';
|
|
10
11
|
});
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -139,8 +140,8 @@ export const databaseConnectionScript = String.raw ` // ─── Database Stud
|
|
|
139
140
|
if (r.success) {
|
|
140
141
|
var count = r.entityCount != null ? r.entityCount : r.tableCount;
|
|
141
142
|
out.className = 'result ok';
|
|
142
|
-
out.textContent = 'Connected to ' + (ENGINE_LABEL[r.engine] || r.engine) + ' in ' + Math.round(r.pingMs) + 'ms
|
|
143
|
-
entityNoun({ engine: r.engine }, true).toLowerCase() + '
|
|
143
|
+
out.textContent = 'Connected to ' + (ENGINE_LABEL[r.engine] || r.engine) + ' in ' + Math.round(r.pingMs) + 'ms, ' + count + ' ' +
|
|
144
|
+
entityNoun({ engine: r.engine }, true).toLowerCase() + ', saved to session as ' + r.maskedUrl;
|
|
144
145
|
$('url-' + env).value = '';
|
|
145
146
|
$('ppass-' + env).value = '';
|
|
146
147
|
return true;
|
|
@@ -53,9 +53,12 @@ export const databaseDataScript = String.raw ` // ─── View 5: Database St
|
|
|
53
53
|
var label = env.toUpperCase();
|
|
54
54
|
if (!s) return '<span class="pill"><span class="dot"></span><b>' + label + ':</b> loading…</span>';
|
|
55
55
|
if (!s.connected) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
if (/No (Dev|Prod) database configured/.test(s.error || '')) {
|
|
57
|
+
return '<span class="pill"><span class="dot"></span><b>' + label + ':</b> ' +
|
|
58
|
+
'<span class="badge badge-neutral">Not set up</span></span>';
|
|
59
|
+
}
|
|
60
|
+
return '<span class="pill" title="' + esc(s.error || '') + '"><span class="dot off"></span><b>' + label + ':</b> offline' +
|
|
61
|
+
' <span class="badge badge-offline">Offline</span></span>';
|
|
59
62
|
}
|
|
60
63
|
var sourceBadge = s.synthesized && s.sourceKey
|
|
61
64
|
? ' <span class="badge badge-synth" title="Assembled in memory from fragmented .env keys">synthesized from ' + esc(keyFamily(s.sourceKey)) + '</span>'
|
|
@@ -63,7 +66,7 @@ export const databaseDataScript = String.raw ` // ─── View 5: Database St
|
|
|
63
66
|
? ' <span class="badge badge-source" title="Detected in ' + esc(s.exampleFile || '.env.example') + '">via ' + esc(s.sourceKey) + '</span>'
|
|
64
67
|
: (s.sourceKey ? ' <span class="badge badge-source">via ' + esc(s.sourceKey) + '</span>' : '');
|
|
65
68
|
return '<span class="pill" title="' + esc(s.maskedUrl) + '"><span class="dot on"></span><b>' + label + ':</b> ' +
|
|
66
|
-
esc(ENGINE_LABEL[s.engine] || s.engine) + (s.database ? '
|
|
69
|
+
esc(ENGINE_LABEL[s.engine] || s.engine) + (s.database ? ', ' + esc(s.database) : '') + ' (' + Math.round(s.pingMs) + 'ms): ' +
|
|
67
70
|
entityCount(s) + ' ' + entityNoun(s, true) + sourceBadge + ' <span class="badge badge-online">ONLINE</span></span>';
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -97,15 +100,8 @@ export const databaseDataScript = String.raw ` // ─── View 5: Database St
|
|
|
97
100
|
'<span class="diagnostic-tag">[DIAGNOSTIC]</span> ' +
|
|
98
101
|
'<span class="diagnostic-msg">' + esc(suggestion) + '</span>' +
|
|
99
102
|
'</div>' +
|
|
100
|
-
'<div class="diagnostic-actions">' +
|
|
101
|
-
'<button class="btn small" type="button" id="btn-diag-settings">Open Connection Settings</button>' +
|
|
102
|
-
'</div>' +
|
|
103
103
|
'</div>';
|
|
104
104
|
bannerEl.hidden = false;
|
|
105
|
-
var btnDiag = $('btn-diag-settings');
|
|
106
|
-
if (btnDiag) {
|
|
107
|
-
btnDiag.onclick = function () { openSettings(); };
|
|
108
|
-
}
|
|
109
105
|
} else {
|
|
110
106
|
bannerEl.innerHTML = '';
|
|
111
107
|
bannerEl.hidden = true;
|
|
@@ -158,8 +154,7 @@ export const databaseDataScript = String.raw ` // ─── View 5: Database St
|
|
|
158
154
|
'<path d="M128,24C74.17,24,32,48.6,32,80v96c0,31.4,42.17,56,96,56s96-24.6,96-56V80C224,48.6,181.83,24,128,24Zm80,104c0,9.62-7.88,19.43-21.61,26.92C170.93,163.35,150.19,168,128,168s-42.93-4.65-58.39-13.08C55.88,147.43,48,137.62,48,128V111.36c17.06,15,46.23,24.64,80,24.64s62.94-9.68,80-24.64ZM69.61,53.08C85.07,44.65,105.81,40,128,40s42.93,4.65,58.39,13.08C200.12,60.57,208,70.38,208,80s-7.88,19.43-21.61,26.92C170.93,115.35,150.19,120,128,120s-42.93-4.65-58.39-13.08C55.88,99.43,48,89.62,48,80S55.88,60.57,69.61,53.08ZM186.39,202.92C170.93,211.35,150.19,216,128,216s-42.93-4.65-58.39-13.08C55.88,195.43,48,185.62,48,176V159.36c17.06,15,46.23,24.64,80,24.64s62.94-9.68,80-24.64V176C208,185.62,200.12,195.43,186.39,202.92Z"/></svg>';
|
|
159
155
|
|
|
160
156
|
function emptyState(title, text) {
|
|
161
|
-
return '<div class="empty card">' + DB_ICON + '<h3>' + esc(title) + '</h3><p>' + esc(text) + '</p>'
|
|
162
|
-
'<button class="btn primary" type="button" data-action="open-settings">Connection Settings</button></div>';
|
|
157
|
+
return '<div class="empty card">' + DB_ICON + '<h3>' + esc(title) + '</h3><p>' + esc(text) + '</p></div>';
|
|
163
158
|
}
|
|
164
159
|
|
|
165
160
|
`;
|
|
@@ -292,7 +292,7 @@ export const databaseDriftScript = String.raw ` // ─── Database Studio: d
|
|
|
292
292
|
var lang = state.scriptLang;
|
|
293
293
|
var unit = lang === 'json' ? ' change(s)' : ' statement(s)';
|
|
294
294
|
return '<div class="sql-wrap card"><div class="sql-head"><span class="muted"><span class="lang">' + LANG_LABEL[lang] + '</span>' +
|
|
295
|
-
(destructive ? '<span class="badge badge-dropped">DESTRUCTIVE</span> ' : '') + state.scriptCount + unit + '
|
|
295
|
+
(destructive ? '<span class="badge badge-dropped">DESTRUCTIVE</span> ' : '') + state.scriptCount + unit + ', read-only preview</span>' +
|
|
296
296
|
'<span class="copy-wrap"><span class="tooltip">Copied!</span><button class="btn small" type="button" data-action="copy-sql">Copy ' +
|
|
297
297
|
(lang === 'sql' ? 'SQL' : lang === 'js' ? 'Script' : 'JSON') + '</button></span></div>' +
|
|
298
298
|
'<pre class="sql" tabindex="0" aria-label="Migration script (' + LANG_LABEL[lang] + ')"><code>' + highlightScript(state.sql, lang) + '</code></pre></div>';
|
|
@@ -123,8 +123,7 @@ export const databaseEventsScript = String.raw ` // ─── Events: Database
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
var action = el.getAttribute('data-action');
|
|
126
|
-
if (action === '
|
|
127
|
-
else if (action === 'copy-sql') copyText(state.sql, el);
|
|
126
|
+
if (action === 'copy-sql') copyText(state.sql, el);
|
|
128
127
|
else if (action === 'edit-record') openEditRecordModal(Number(el.getAttribute('data-row')));
|
|
129
128
|
else if (action === 'delete-record') handleDeleteRecord(Number(el.getAttribute('data-row')));
|
|
130
129
|
else if (action === 'preview-url') {
|
|
@@ -45,7 +45,7 @@ export const databaseExplorerScript = String.raw ` // ─── Database Studio
|
|
|
45
45
|
: '';
|
|
46
46
|
var docMeta = coll && (t.documentCount != null || t.sampledDocuments != null)
|
|
47
47
|
? '<p class="desc">' + (t.documentCount != null ? '~' + t.documentCount + ' documents' : 'Document count unavailable') +
|
|
48
|
-
(t.sampledDocuments != null ? '
|
|
48
|
+
(t.sampledDocuments != null ? ', fields inferred from ' + t.sampledDocuments + ' sampled' : '') + '</p>'
|
|
49
49
|
: '';
|
|
50
50
|
return '<details class="table-card"><summary><span class="tname">' + esc(t.name) + '</span>' +
|
|
51
51
|
(coll ? '<span class="badge badge-collection">COLLECTION</span>' : '') +
|
|
@@ -87,7 +87,7 @@ export const databaseExplorerScript = String.raw ` // ─── Database Studio
|
|
|
87
87
|
var parts = [m.mimeType || 'unknown type'];
|
|
88
88
|
if (m.width && m.height) parts.push(m.width + '×' + m.height);
|
|
89
89
|
if (m.sizeBytes != null) parts.push(formatBytes(m.sizeBytes));
|
|
90
|
-
return parts.join('
|
|
90
|
+
return parts.join(', ');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function showThumb(el) {
|
|
@@ -117,7 +117,7 @@ export const databaseExplorerScript = String.raw ` // ─── Database Studio
|
|
|
117
117
|
hideThumb();
|
|
118
118
|
var m = entry.media;
|
|
119
119
|
$('media-title').textContent = entry.table + '.' + entry.field;
|
|
120
|
-
$('media-sub').textContent = (m ? SOURCE_LABEL[m.source] || m.source : 'Image field') + '
|
|
120
|
+
$('media-sub').textContent = (m ? SOURCE_LABEL[m.source] || m.source : 'Image field') + ', ' + entry.type;
|
|
121
121
|
$('media-thumb').innerHTML = thumbHtml(m);
|
|
122
122
|
var rows = [];
|
|
123
123
|
if (m) {
|
|
@@ -144,7 +144,7 @@ export const databaseExplorerScript = String.raw ` // ─── Database Studio
|
|
|
144
144
|
var body;
|
|
145
145
|
if (!s || !s.connected) {
|
|
146
146
|
body = '<div class="empty" style="padding:28px 12px"><p>' + esc(s && s.error ? s.error : 'No database connected.') + '</p>' +
|
|
147
|
-
'<
|
|
147
|
+
'<p class="muted">Use Connection Settings at the top.</p></div>';
|
|
148
148
|
} else if (!tables.length) {
|
|
149
149
|
body = '<p class="muted">Connected, but this database has no ' + entityNoun(s, true).toLowerCase() + ' yet.</p>';
|
|
150
150
|
} else if (!filtered.length) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Plan layout: rounds, lanes, links and the highlight set. Pure, no DOM.
|
|
2
|
-
export const flowLayoutScript = String.raw ` var FLOW = { W: 206, H:
|
|
2
|
+
export const flowLayoutScript = String.raw ` var FLOW = { W: 206, H: 74, GX: 52, GY: 10, PAD: 14, LX: 128, TOP: 30 };
|
|
3
3
|
|
|
4
4
|
function flowAgentKey(t) { return t.agent || 'unassigned'; }
|
|
5
5
|
|
|
@@ -25,7 +25,7 @@ export const flowCss = String.raw `#panel-flow .seg { display: inline-flex; bord
|
|
|
25
25
|
#panel-flow .node .l1 { display: flex; align-items: center; gap: 6px; }
|
|
26
26
|
#panel-flow .node .l1 .d { margin-left: auto; font: 11px var(--mono); color: var(--fg-3); }
|
|
27
27
|
#panel-flow .node .l1 .mono { font-size: 11px; }
|
|
28
|
-
#panel-flow .node .title { font-size: 12.5px;
|
|
28
|
+
#panel-flow .node .title { font-size: 12.5px; line-height: 1.3; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
|
29
29
|
#panel-flow .canvas.focus .node:not(.hot) { opacity: .3; }
|
|
30
30
|
#panel-flow .node.sel { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
31
31
|
#panel-flow .legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--fg-2); align-items: center; }
|
|
@@ -51,7 +51,7 @@ export const flowTimelineScript = String.raw ` function flowStat(k, v, s) {
|
|
|
51
51
|
var cw = t.checkMs && t.status === 'completed' ? Math.min(w, t.checkMs * tm.sg.k) : 0;
|
|
52
52
|
var lx = w > 64 ? x1 + 6 : x2 + 4;
|
|
53
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 + '
|
|
54
|
+
var tip = ui.esc(t.id + ', ' + t.title) + ' ' + ui.esc(took);
|
|
55
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
56
|
'" width="' + w + '" height="21" rx="4"><title>' + tip + '</title></rect>' +
|
|
57
57
|
(cw > 1 ? '<rect class="bar-c" x="' + (x2 - cw) + '" y="' + (by + 17) + '" width="' + cw + '" height="4" rx="1"/>' : '') +
|
|
@@ -24,7 +24,7 @@ export const homeScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
24
24
|
var t = item.task;
|
|
25
25
|
if (t.status === 'blocked') return t.reason || 'Paused after 3 attempts.';
|
|
26
26
|
var e = item.escalation || {};
|
|
27
|
-
return e.reason || e.summary || e.details || '
|
|
27
|
+
return e.reason || e.summary || e.details || 'Needs your decision.';
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function needActions(item) {
|
|
@@ -55,16 +55,8 @@ export const homeScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
55
55
|
rightText(count) + '</div>' + n.map(needItem).join('') + '</section>';
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function isReady(model, t) {
|
|
59
|
-
if (t.status !== 'pending') return false;
|
|
60
|
-
return t.isAvailable || t.deps.every(function (d) {
|
|
61
|
-
var dep = model.byId[d];
|
|
62
|
-
return !dep || dep.status === 'completed';
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
58
|
function readyTasks(model) {
|
|
67
|
-
return model.tasks.filter(
|
|
59
|
+
return model.tasks.filter(Studio.status.isReady);
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
function dispatchBtn(id, label, cls) {
|
|
@@ -75,17 +67,18 @@ export const homeScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
75
67
|
function taskCells(t) {
|
|
76
68
|
return '<span class="mono id">' + esc(t.id) + '</span>' +
|
|
77
69
|
'<button type="button" class="t link" data-task="' + esc(t.id) + '">' + esc(t.title) + '</button>' +
|
|
78
|
-
'<span class="agent">' + esc(t.agent
|
|
70
|
+
'<span class="agent">' + esc(Studio.agentInfo(t.agent).label) + '</span>';
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
function runRow(t) {
|
|
82
74
|
var since = t.startT ? ' data-since="' + t.startT + '">' + Studio.fmt.dur(Studio.now() - t.startT) : '>';
|
|
83
75
|
var row = '<div class="row run-row">' + taskCells(t) +
|
|
84
|
-
'<span class="state"
|
|
76
|
+
'<span class="state">Running</span>' +
|
|
85
77
|
'<span class="mono num timer"' + since + '</span></div>';
|
|
86
78
|
var log = '<span class="mono log">' + esc(t.latestLog || '') + '</span>';
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
var watch = t.run ? '<button type="button" class="btn" data-act="home-console" data-id="' +
|
|
80
|
+
esc(t.id) + '">Watch live</button>' : '';
|
|
81
|
+
return row + '<div class="row sub-row">' + log + watch + '</div>';
|
|
89
82
|
}
|
|
90
83
|
|
|
91
84
|
function nowRunning(model) {
|
|
@@ -96,23 +89,18 @@ export const homeScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
96
89
|
return section('running', 'Now running', '', '<div class="row calm"><span>No agent is running.</span>' + btn + '</div>');
|
|
97
90
|
}
|
|
98
91
|
|
|
99
|
-
function
|
|
100
|
-
var
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function nextRow(model, t) {
|
|
105
|
-
var end = isReady(model, t) ? dispatchBtn(t.id, 'Dispatch') :
|
|
106
|
-
'<span class="wait">waits <span class="mono">' + esc(waitOn(model, t)) + '</span></span>';
|
|
92
|
+
function nextRow(t) {
|
|
93
|
+
var end = Studio.status.isReady(t) ? dispatchBtn(t.id, 'Dispatch') :
|
|
94
|
+
'<span class="wait">' + esc(Studio.status.waitText(t)) + '</span>';
|
|
107
95
|
return '<div class="row next-row">' + taskCells(t) + end + '</div>';
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
function upNext(model) {
|
|
111
99
|
var list = model.tasks.filter(function (t) { return t.status === 'pending'; });
|
|
112
|
-
var ready = list.filter(
|
|
113
|
-
var waiting = list.filter(function (t) { return !isReady(
|
|
100
|
+
var ready = list.filter(Studio.status.isReady);
|
|
101
|
+
var waiting = list.filter(function (t) { return !Studio.status.isReady(t); });
|
|
114
102
|
var rows = ready.concat(waiting).slice(0, UP_NEXT_CAP);
|
|
115
|
-
var body = rows.length ? rows.map(
|
|
103
|
+
var body = rows.length ? rows.map(nextRow).join('') :
|
|
116
104
|
'<div class="row calm">Nothing is waiting.</div>';
|
|
117
105
|
return section('next', 'Up next', '', body);
|
|
118
106
|
}
|
|
@@ -127,18 +115,27 @@ export const homeScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
127
115
|
return section('milestone', 'Milestone', '', body);
|
|
128
116
|
}
|
|
129
117
|
|
|
118
|
+
// One event per task that has started or finished; the state word comes from its status now.
|
|
130
119
|
function activityEvents(model) {
|
|
131
120
|
var out = [];
|
|
132
121
|
model.tasks.forEach(function (t) {
|
|
133
|
-
|
|
134
|
-
if (
|
|
122
|
+
var at = t.status === 'completed' ? (t.endT || t.startT) : t.startT;
|
|
123
|
+
if (at) out.push({ at: at, task: t });
|
|
135
124
|
});
|
|
136
125
|
return out.sort(function (a, b) { return b.at - a.at; }).slice(0, ACTIVITY_CAP);
|
|
137
126
|
}
|
|
138
127
|
|
|
128
|
+
function activityState(t) {
|
|
129
|
+
var word = Studio.status.word(Studio.status.bucket(t));
|
|
130
|
+
var took = t.status === 'completed' && t.durationMs ? ', took ' + Studio.fmt.dur(t.durationMs) : '';
|
|
131
|
+
return word + took;
|
|
132
|
+
}
|
|
133
|
+
|
|
139
134
|
function activityRow(e) {
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
var t = e.task;
|
|
136
|
+
return '<div class="row act-row" data-event="' + esc(t.id) + '"><span class="mono num when">' +
|
|
137
|
+
Studio.ui.when(e.at) + '</span><button type="button" class="t link" data-task="' + esc(t.id) + '">' +
|
|
138
|
+
esc(t.title) + '</button><span class="state">' + esc(activityState(t)) + '</span></div>';
|
|
142
139
|
}
|
|
143
140
|
|
|
144
141
|
function recentActivity(model) {
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
// Home page styles, all scoped under #panel-home. Ink: flat sections, one raised decision surface.
|
|
2
|
-
export const homeCss = String.raw `#panel-home .home {
|
|
2
|
+
export const homeCss = String.raw `#panel-home .home { display: flex; flex-direction: column; }
|
|
3
3
|
#panel-home .page-head h1 { font-size: 22px; font-weight: 600; margin: 0 0 12px; letter-spacing: -.01em; }
|
|
4
4
|
#panel-home .home-sec { border-top: 1px solid var(--line); padding: 12px 0; min-width: 0; }
|
|
5
5
|
#panel-home .sec-head { display: flex; align-items: center; gap: 8px; min-height: 28px; }
|
|
6
6
|
#panel-home .sec-head h2 { font-size: 13px; font-weight: 600; margin: 0; color: var(--fg-2); }
|
|
7
7
|
#panel-home .sec-head .right { margin-left: auto; font-size: 12px; color: var(--fg-3); }
|
|
8
8
|
#panel-home .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
|
|
9
|
-
#panel-home .row { display: flex; align-items: center; gap: 12px; min-height: 36px;
|
|
10
|
-
#panel-home .row:last-child { border-bottom: 0; }
|
|
9
|
+
#panel-home .row { display: flex; align-items: center; gap: 12px; min-height: 36px; }
|
|
11
10
|
#panel-home .row .id { width: 72px; flex: none; color: var(--fg-2); font-size: 12px; }
|
|
12
11
|
#panel-home .row .t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
13
12
|
#panel-home .link { background: none; border: 0; padding: 0; text-align: left; color: var(--fg); cursor: pointer; font: inherit; }
|
|
14
13
|
#panel-home .agent { width: 96px; flex: none; color: var(--fg-2); font-size: 12.5px; }
|
|
15
14
|
#panel-home .state { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; }
|
|
16
|
-
#panel-home .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--fg-3); }
|
|
17
|
-
#panel-home .dot.run { background: var(--run); }
|
|
18
15
|
#panel-home .timer { width: 64px; text-align: right; font-size: 12px; }
|
|
19
16
|
#panel-home .sub-row .log { flex: 1; min-width: 0; font-size: 12px; color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
20
17
|
#panel-home .wait { color: var(--fg-3); font-size: 12.5px; }
|
|
@@ -30,7 +27,6 @@ export const homeCss = String.raw `#panel-home .home { max-width: 960px; display
|
|
|
30
27
|
#panel-home .act-row .when { width: 72px; flex: none; font-size: 12px; color: var(--fg-3); }
|
|
31
28
|
#panel-home .skel-row .skel { height: 12px; width: 100%; border-radius: 6px; background: var(--surface-2); }
|
|
32
29
|
@media (max-width: 1024px) {
|
|
33
|
-
#panel-home .home { max-width: none; }
|
|
34
30
|
#panel-home .agent { display: none; }
|
|
35
31
|
}
|
|
36
32
|
`;
|
|
@@ -3,11 +3,7 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
3
3
|
var CAP = 5;
|
|
4
4
|
var view = { lastMs: null, showAll: false, open: {}, mode: null, filter: 'all', groups: {} };
|
|
5
5
|
var KEY = 'nativ-studio:tasks-view';
|
|
6
|
-
var FILTERS = [['all', 'All']
|
|
7
|
-
['done', 'Done']];
|
|
8
|
-
var DOT = { completed: 'ok', in_progress: 'run', blocked: 'bad', ready: 'fg', waiting: 'faint' };
|
|
9
|
-
var LABEL = { completed: 'Done', in_progress: 'Running', blocked: 'Blocked', ready: 'Ready',
|
|
10
|
-
waiting: 'Waiting' };
|
|
6
|
+
var FILTERS = [['all', 'All']].concat(Studio.status.buckets);
|
|
11
7
|
|
|
12
8
|
function readMode() {
|
|
13
9
|
try { return localStorage.getItem(KEY) === 'board' ? 'board' : 'list'; } catch (e) { return 'list'; }
|
|
@@ -16,30 +12,15 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
16
12
|
try { localStorage.setItem(KEY, mode); } catch (e) { /* private mode */ }
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
// Same notion of ready as Home and the sidebar: pending and every dependency done.
|
|
20
|
-
function isReady(model, t) {
|
|
21
|
-
if (t.status !== 'pending') return false;
|
|
22
|
-
return t.isAvailable || t.deps.every(function (d) {
|
|
23
|
-
var dep = model.byId && model.byId[d];
|
|
24
|
-
return !dep || dep.status === 'completed';
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function kindOf(model, t) {
|
|
28
|
-
if (t.status === 'pending') return isReady(model, t) ? 'ready' : 'waiting';
|
|
29
|
-
return t.status;
|
|
30
|
-
}
|
|
31
15
|
function matches(model, t, f) {
|
|
32
|
-
|
|
33
|
-
if (f === 'running') return t.status === 'in_progress';
|
|
34
|
-
if (f === 'blocked') return t.status === 'blocked';
|
|
35
|
-
if (f === 'done') return t.status === 'completed';
|
|
36
|
-
return isReady(model, t);
|
|
16
|
+
return f === 'all' || Studio.status.bucket(t) === f;
|
|
37
17
|
}
|
|
38
18
|
|
|
39
19
|
function toolbar(model) {
|
|
40
20
|
var chips = FILTERS.map(function (f) {
|
|
41
21
|
var on = view.filter === f[0];
|
|
42
22
|
var n = model.tasks.filter(function (t) { return matches(model, t, f[0]); }).length;
|
|
23
|
+
if (!n && !on && f[0] !== 'all') return '';
|
|
43
24
|
return '<button type="button" class="tf' + (on ? ' on' : '') + '" data-act="tasks-filter" data-filter="' +
|
|
44
25
|
f[0] + '" aria-pressed="' + on + '">' + f[1] + ' <span class="num">' + n + '</span></button>';
|
|
45
26
|
}).join('');
|
|
@@ -59,15 +40,17 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
59
40
|
|
|
60
41
|
// Rows use data-tid, not data-task, so the nested Dispatch button gets its own click.
|
|
61
42
|
function listRow(model, t, fmt) {
|
|
62
|
-
var
|
|
43
|
+
var bucket = Studio.status.bucket(t);
|
|
44
|
+
var wait = Studio.status.waitText(t);
|
|
63
45
|
var id = ui.esc(t.id);
|
|
64
|
-
var go =
|
|
46
|
+
var go = Studio.status.isReady(t) ? '<button type="button" class="tdispatch" data-act="tasks-dispatch" data-tid="' +
|
|
65
47
|
id + '">Dispatch</button>' : '';
|
|
66
48
|
return '<div class="trow" role="row" tabindex="0" data-act="tasks-open" data-tid="' + id + '">' +
|
|
67
49
|
'<span class="c-id mono" role="cell">' + id + '</span>' +
|
|
68
50
|
'<span class="c-title" role="cell" title="' + ui.esc(t.title) + '">' + ui.esc(t.title) + '</span>' +
|
|
69
|
-
'<span class="c-agent" role="cell">' + ui.esc(t.agent
|
|
70
|
-
'<span class="c-status st-' +
|
|
51
|
+
'<span class="c-agent" role="cell">' + ui.esc(Studio.agentInfo(t.agent).label) + '</span>' +
|
|
52
|
+
'<span class="c-status st-' + bucket + '" role="cell">' + Studio.status.word(bucket) +
|
|
53
|
+
(wait ? ' <span class="faint">' + ui.esc(wait) + '</span>' : '') + go + '</span>' +
|
|
71
54
|
'<span class="c-time mono" role="cell">' + ui.esc(timeOf(t, fmt)) + '</span></div>';
|
|
72
55
|
}
|
|
73
56
|
|
|
@@ -86,7 +69,7 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
86
69
|
return '<section class="tgroup" role="table" aria-label="' + ui.esc(m.name) + '">' +
|
|
87
70
|
'<button type="button" class="tg-head" data-act="tasks-group" data-group="' + ui.esc(m.id) +
|
|
88
71
|
'" aria-expanded="' + open + '"><span class="chev">' + ui.icon('chev', 2) + '</span>' +
|
|
89
|
-
'<span class="mono faint">' + ui.esc(m.id) + '</span><span class="t">' + ui.esc(m.name) + '</span>' +
|
|
72
|
+
'<span class="mono faint">' + ui.esc(m.id) + '</span><span class="t" title="' + ui.esc(m.name) + '">' + ui.esc(m.name) + '</span>' +
|
|
90
73
|
'<span class="num faint">' + m.done + ' of ' + m.total + '</span></button>' + body + '</section>';
|
|
91
74
|
}
|
|
92
75
|
|
|
@@ -124,13 +107,13 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
124
107
|
var el = e.target;
|
|
125
108
|
if (e.key !== 'Enter' || !el || !el.matches || !el.matches('.trow[data-tid]')) return;
|
|
126
109
|
e.preventDefault();
|
|
127
|
-
Studio.
|
|
110
|
+
Studio.openTask(el.getAttribute('data-tid'));
|
|
128
111
|
});
|
|
129
112
|
}
|
|
130
113
|
|
|
131
114
|
function head(m) {
|
|
132
115
|
var lead = m ? ui.esc(m.name) : 'The work of one milestone, by state.';
|
|
133
|
-
return '<div class="panel-head"><div class="panel-title"><h1>Tasks</h1><p class="lead">' + lead + '</p></div>' +
|
|
116
|
+
return '<div class="panel-head"><div class="panel-title"><h1>Tasks</h1><p class="lead" title="' + lead + '">' + lead + '</p></div>' +
|
|
134
117
|
'<div class="panel-actions">' + ui.milestoneSelect() + '</div></div>';
|
|
135
118
|
}
|
|
136
119
|
|
|
@@ -138,7 +121,8 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
138
121
|
|
|
139
122
|
function metaHtml(t, fmt) {
|
|
140
123
|
if (t.status === 'completed') {
|
|
141
|
-
|
|
124
|
+
var took = t.durationMs ? '<span>took ' + ui.esc(fmt.dur(t.durationMs)) + '</span>' : '';
|
|
125
|
+
return '<span>' + ui.esc(fmt.ago(t.endT)) + '</span>' + took;
|
|
142
126
|
}
|
|
143
127
|
if (t.status === 'in_progress') {
|
|
144
128
|
var live = t.startT ? '<span data-since="' + t.startT + '">' + fmt.dur(Studio.now() - t.startT) + '</span>' : '';
|
|
@@ -147,7 +131,7 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
147
131
|
if (t.status === 'blocked') {
|
|
148
132
|
return '<span class="bad">Paused after ' + (t.attempt || t.maxAttempts) + ' of ' + t.maxAttempts + ' attempts</span>';
|
|
149
133
|
}
|
|
150
|
-
if (t.waitingOn.length) return '<span>
|
|
134
|
+
if (t.waitingOn.length && !t.stale) return '<span>Waits for ' + t.waitingOn.map(ui.esc).join(', ') + '</span>';
|
|
151
135
|
return '<span>Ready to start</span>';
|
|
152
136
|
}
|
|
153
137
|
|
|
@@ -157,8 +141,9 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
157
141
|
'<div class="title">' + ui.esc(t.title) + '</div><div class="meta">' + metaHtml(t, fmt) + '</div></button>';
|
|
158
142
|
}
|
|
159
143
|
|
|
160
|
-
function column(label, status, list, shown,
|
|
161
|
-
var body = shown.length ? shown.map(function (t) { return card(t, fmt); }).join('')
|
|
144
|
+
function column(label, status, list, shown, extra, fmt) {
|
|
145
|
+
var body = shown.length ? shown.map(function (t) { return card(t, fmt); }).join('')
|
|
146
|
+
: '<p class="col-empty">None</p>';
|
|
162
147
|
return '<div class="col" data-col="' + status + '"><div class="col-head">' + ui.statusIcon(status) + label +
|
|
163
148
|
'<span class="count">' + list.length + '</span></div>' + body + (extra || '') + '</div>';
|
|
164
149
|
}
|
|
@@ -169,12 +154,12 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
169
154
|
var shown = view.showAll ? done : done.slice(0, CAP);
|
|
170
155
|
var more = done.length > CAP
|
|
171
156
|
? '<button type="button" class="more" data-act="tasks-more">' + (view.showAll ? 'Show fewer' : 'Show all ' + done.length) + '</button>' : '';
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
column(
|
|
176
|
-
|
|
177
|
-
|
|
157
|
+
var lanes = [['Up next', 'pending', by('pending'), null], ['Running', 'in_progress', by('in_progress'), null],
|
|
158
|
+
['Done', 'completed', done, shown], ['Stuck', 'blocked', by('blocked'), null]];
|
|
159
|
+
var cols = lanes.map(function (l) {
|
|
160
|
+
return column(l[0], l[1], l[2], l[3] || l[2], l[1] === 'completed' ? more : '', fmt);
|
|
161
|
+
}).join('');
|
|
162
|
+
return '<div class="board">' + cols + '</div>';
|
|
178
163
|
}
|
|
179
164
|
|
|
180
165
|
function histRow(m, fmt) {
|
|
@@ -224,7 +209,7 @@ export const tasksScript = String.raw ` var ui = Studio.ui;
|
|
|
224
209
|
},
|
|
225
210
|
'tasks-filter': function (el) { view.filter = el.getAttribute('data-filter'); Studio.repaint('tasks'); },
|
|
226
211
|
'tasks-clear': function () { view.filter = 'all'; Studio.repaint('tasks'); },
|
|
227
|
-
'tasks-open': function (el) { Studio.
|
|
212
|
+
'tasks-open': function (el) { Studio.openTask(el.getAttribute('data-tid')); },
|
|
228
213
|
'tasks-dispatch': function (el) { Studio.taskAction('dispatch', el.getAttribute('data-tid')); },
|
|
229
214
|
'tasks-group': function (el, ctx) {
|
|
230
215
|
var id = el.getAttribute('data-group');
|
|
@@ -3,7 +3,7 @@ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-te
|
|
|
3
3
|
#panel-tasks .col { background: var(--surface-2); border-radius: 6px; padding: 8px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
|
|
4
4
|
#panel-tasks .col-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 2px; font-weight: 500; font-size: 13px; }
|
|
5
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:
|
|
6
|
+
#panel-tasks .col-empty { margin: 0; font-size: 12.5px; color: var(--fg-3); padding: 4px 2px; }
|
|
7
7
|
#panel-tasks .tcard { display: flex; flex-direction: column; gap: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 10px; text-align: left; width: 100%; cursor: pointer; }
|
|
8
8
|
#panel-tasks .tcard:hover { border-color: var(--line-2); }
|
|
9
9
|
#panel-tasks .tcard.in_progress { border-color: color-mix(in srgb, var(--run) 45%, var(--line)); }
|
|
@@ -51,24 +51,24 @@ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-te
|
|
|
51
51
|
#panel-tasks .tg-head[aria-expanded="true"] .chev { transform: rotate(90deg); }
|
|
52
52
|
#panel-tasks .trow {
|
|
53
53
|
display: grid; grid-template-columns: 76px minmax(0, 1fr) 110px 150px 72px; gap: 12px;
|
|
54
|
-
align-items: center; height: 36px; padding: 0 8px;
|
|
54
|
+
align-items: center; height: 36px; padding: 0 8px;
|
|
55
55
|
font-size: 13px; cursor: pointer;
|
|
56
56
|
}
|
|
57
57
|
#panel-tasks .trow:hover, #panel-tasks .trow:focus-visible { background: var(--surface-2); }
|
|
58
58
|
#panel-tasks .trow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
59
|
-
#panel-tasks .trow.thead { cursor: default; color: var(--fg-3); font-size: 12px; background: none; }
|
|
59
|
+
#panel-tasks .trow.thead { border-bottom: 1px solid var(--line); cursor: default; color: var(--fg-3); font-size: 12px; background: none; }
|
|
60
60
|
#panel-tasks .trow .mono { font-variant-numeric: tabular-nums; font-size: 12px; }
|
|
61
61
|
#panel-tasks .c-id { color: var(--fg-3); }
|
|
62
62
|
#panel-tasks .c-title, #panel-tasks .c-agent { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
63
63
|
#panel-tasks .c-agent { color: var(--fg-2); }
|
|
64
64
|
#panel-tasks .c-time { text-align: right; color: var(--fg-2); }
|
|
65
65
|
#panel-tasks .c-status { display: flex; align-items: center; gap: 6px; }
|
|
66
|
-
#panel-tasks .c-status
|
|
67
|
-
#panel-tasks .st-
|
|
68
|
-
#panel-tasks .st-
|
|
69
|
-
#panel-tasks .st-
|
|
70
|
-
#panel-tasks .st-
|
|
71
|
-
#panel-tasks .
|
|
66
|
+
#panel-tasks .c-status { white-space: nowrap; overflow: hidden; }
|
|
67
|
+
#panel-tasks .st-done { color: var(--ok); }
|
|
68
|
+
#panel-tasks .st-running { color: var(--run); }
|
|
69
|
+
#panel-tasks .st-stuck { color: var(--bad); }
|
|
70
|
+
#panel-tasks .st-next { color: var(--fg); }
|
|
71
|
+
#panel-tasks .panel-title .lead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
|
72
72
|
#panel-tasks .tdispatch {
|
|
73
73
|
display: none; margin-left: auto; border: 1px solid var(--line-2); background: var(--surface);
|
|
74
74
|
color: var(--fg); border-radius: 6px; padding: 2px 8px; font-size: 12px; cursor: pointer;
|