@yemi33/minions 0.1.2150 → 0.1.2151
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/bin/minions.js +99 -6
- package/dashboard/docs/typography-audit.md +128 -0
- package/dashboard/docs/typography.md +114 -0
- package/dashboard/js/render-agents.js +1 -1
- package/dashboard/js/utils.js +1 -1
- package/dashboard/slim/body.html +2 -2
- package/dashboard/slim/styles.css +112 -77
- package/dashboard/styles.css +37 -3
- package/engine/cli.js +5 -3
- package/engine/dispatch.js +26 -1
- package/package.json +6 -1
package/bin/minions.js
CHANGED
|
@@ -64,6 +64,10 @@ const PKG_ROOT = path.resolve(__dirname, '..');
|
|
|
64
64
|
const shared = require(path.join(PKG_ROOT, 'engine', 'shared'));
|
|
65
65
|
const { openUrlInBrowser } = shared;
|
|
66
66
|
const { waitForRestartHealth, formatRestartHealthError } = require(path.join(PKG_ROOT, 'engine', 'restart-health'));
|
|
67
|
+
// Dev-mode dashboard port. `--dev` mode (see argv parser below) retargets
|
|
68
|
+
// MINIONS_HOME at PKG_ROOT and routes the dashboard onto this port so a dev
|
|
69
|
+
// checkout coexists with a global npm install on the normal port (7331).
|
|
70
|
+
const DEFAULT_DEV_DASH_PORT = 7332;
|
|
67
71
|
|
|
68
72
|
/**
|
|
69
73
|
* Resolve the dashboard port for any CLI command. Precedence (W-mq5nwl9l):
|
|
@@ -71,7 +75,7 @@ const { waitForRestartHealth, formatRestartHealthError } = require(path.join(PKG
|
|
|
71
75
|
* ACTUALLY bound to. Wins over the chain so status/dash/restart-health
|
|
72
76
|
* probes the right port even after an EADDRINUSE auto-fallback.
|
|
73
77
|
* 2. Explicit `--port N` CLI flag if present in `argv`.
|
|
74
|
-
* 3. env MINIONS_DASHBOARD_PORT.
|
|
78
|
+
* 3. env MINIONS_DASHBOARD_PORT (dev mode pre-seeds this with devPort).
|
|
75
79
|
* 4. config.json#engine.dashboardPort.
|
|
76
80
|
* 5. shared.DEFAULT_DASHBOARD_PORT (7331).
|
|
77
81
|
*
|
|
@@ -578,7 +582,50 @@ function resolveMinionsHome(forInit = false) {
|
|
|
578
582
|
return shared.resolveMinionsHome(forInit);
|
|
579
583
|
}
|
|
580
584
|
|
|
581
|
-
|
|
585
|
+
// `--dev` runs the in-place checkout AS the runtime root, on a separate
|
|
586
|
+
// dashboard port (default 7332) so it can coexist with a global npm install on
|
|
587
|
+
// 7331. Only valid when bin/minions.js sits inside a git checkout — the
|
|
588
|
+
// `.git` probe stops the global shim (whose PKG_ROOT lives under
|
|
589
|
+
// node_modules/) from accidentally activating dev mode.
|
|
590
|
+
//
|
|
591
|
+
// We strip --dev / --dev-port <n> from argv BEFORE the cmd/rest split so the
|
|
592
|
+
// flag can appear in any position (`minions --dev help`, `minions help --dev`,
|
|
593
|
+
// `minions --dev start --foo` all work). Otherwise `--dev` as the first arg
|
|
594
|
+
// would be misread as the subcommand.
|
|
595
|
+
const { cmd, rest, devMode, devPort } = (() => {
|
|
596
|
+
const raw = process.argv.slice(2);
|
|
597
|
+
const out = [];
|
|
598
|
+
let dev = process.env.MINIONS_DEV === '1';
|
|
599
|
+
let port = null;
|
|
600
|
+
for (let i = 0; i < raw.length; i++) {
|
|
601
|
+
const arg = raw[i];
|
|
602
|
+
if (arg === '--dev') { dev = true; continue; }
|
|
603
|
+
if (arg === '--dev-port') {
|
|
604
|
+
const next = raw[i + 1];
|
|
605
|
+
const n = Number(next);
|
|
606
|
+
if (!Number.isInteger(n) || n <= 0 || n > 65535) {
|
|
607
|
+
console.error(`\n --dev-port requires an integer 1-65535, got: ${next || '(missing)'}\n`);
|
|
608
|
+
process.exit(2);
|
|
609
|
+
}
|
|
610
|
+
port = n; dev = true; i++; continue;
|
|
611
|
+
}
|
|
612
|
+
if (arg && arg.startsWith('--dev-port=')) {
|
|
613
|
+
const n = Number(arg.slice('--dev-port='.length));
|
|
614
|
+
if (!Number.isInteger(n) || n <= 0 || n > 65535) {
|
|
615
|
+
console.error(`\n --dev-port=<n> requires an integer 1-65535, got: ${arg}\n`);
|
|
616
|
+
process.exit(2);
|
|
617
|
+
}
|
|
618
|
+
port = n; dev = true; continue;
|
|
619
|
+
}
|
|
620
|
+
out.push(arg);
|
|
621
|
+
}
|
|
622
|
+
if (dev && port == null) {
|
|
623
|
+
const envPort = Number(process.env.MINIONS_DEV_PORT);
|
|
624
|
+
if (Number.isInteger(envPort) && envPort > 0 && envPort <= 65535) port = envPort;
|
|
625
|
+
}
|
|
626
|
+
const [firstCmd, ...restArgs] = out;
|
|
627
|
+
return { cmd: firstCmd, rest: restArgs, devMode: dev, devPort: port || DEFAULT_DEV_DASH_PORT };
|
|
628
|
+
})();
|
|
582
629
|
let force = rest.includes('--force');
|
|
583
630
|
const skipScan = rest.includes('--skip-scan');
|
|
584
631
|
const skipStart = rest.includes('--skip-start') || rest.includes('--no-start');
|
|
@@ -587,8 +634,46 @@ const skipStart = rest.includes('--skip-start') || rest.includes('--no-start');
|
|
|
587
634
|
// when the heuristic mis-fires (e.g. you closed your dashboard tab seconds before
|
|
588
635
|
// running restart and its final heartbeat was still inside the 45s window).
|
|
589
636
|
const forceOpen = rest.includes('--open') || process.env.MINIONS_FORCE_OPEN === '1';
|
|
590
|
-
|
|
637
|
+
// When --dev is set, PKG_ROOT MUST be a git checkout. Without the .git probe a
|
|
638
|
+
// user who typed `minions --dev` from any directory would silently retarget the
|
|
639
|
+
// globally-installed package as its own dev workspace.
|
|
640
|
+
if (devMode && !fs.existsSync(path.join(PKG_ROOT, '.git'))) {
|
|
641
|
+
console.error(`
|
|
642
|
+
--dev requires running from a git checkout of minions.
|
|
643
|
+
This bin/minions.js lives under: ${PKG_ROOT}
|
|
644
|
+
No .git directory was found there — refusing to retarget runtime files.
|
|
645
|
+
|
|
646
|
+
To use dev mode, clone the repo and run from inside it:
|
|
647
|
+
git clone https://github.com/opg-microsoft/minions.git
|
|
648
|
+
cd minions && node bin/minions.js --dev restart
|
|
649
|
+
`);
|
|
650
|
+
process.exit(2);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const MINIONS_HOME = devMode ? PKG_ROOT : resolveMinionsHome(cmd === 'init');
|
|
591
654
|
process.env.MINIONS_HOME = MINIONS_HOME;
|
|
655
|
+
// When --dev is set, force the dashboard chain onto the dev port (7332 by
|
|
656
|
+
// default or --dev-port N) so a dev checkout coexists with a global install
|
|
657
|
+
// on 7331. We seed MINIONS_DASHBOARD_PORT so resolveDashboardPort()'s env
|
|
658
|
+
// step treats devPort as the fallback while still respecting an existing dev
|
|
659
|
+
// runtime-file at PKG_ROOT/engine/dashboard-port.json (runtime-file wins).
|
|
660
|
+
if (devMode) process.env.MINIONS_DASHBOARD_PORT = String(devPort);
|
|
661
|
+
// Effective dashboard port for this CLI invocation. Honors the W-mq5nwl9l
|
|
662
|
+
// chain (runtime-file → --port → env → config → DEFAULT_DASHBOARD_PORT/7331);
|
|
663
|
+
// --dev overrides via the env seed above. Consumed by the watchdog tick
|
|
664
|
+
// callsite below and propagated to children via MINIONS_PORT.
|
|
665
|
+
// Named DASHBOARD_PORT (not the deprecated DASH-underscore-PORT identifier) so
|
|
666
|
+
// the W-mq5nwl9l source-inspection guard in
|
|
667
|
+
// test/unit/dashboard-port-resolution.test.js keeps holding: that test pins
|
|
668
|
+
// that no static `\bDASH_PORT\b` survives, because anything other than the
|
|
669
|
+
// dynamic chain re-introduces the 7331 bug W-mq5nwl9l was opened to fix.
|
|
670
|
+
const DASHBOARD_PORT = resolveDashboardPort(rest).port;
|
|
671
|
+
// Propagate the port to every child (engine, dashboard, delegated subcommands).
|
|
672
|
+
// engine/cli.js status reads MINIONS_PORT; dashboard.js reads PORT. We export
|
|
673
|
+
// MINIONS_PORT for the engine + watch-actions / pipeline / managed-spawn
|
|
674
|
+
// loopback callers that already honor it, and pass PORT explicitly when
|
|
675
|
+
// spawning dashboard.js.
|
|
676
|
+
process.env.MINIONS_PORT = String(DASHBOARD_PORT);
|
|
592
677
|
const POST_UPDATE_INIT_TIMEOUT_MS = 120000;
|
|
593
678
|
const POST_UPDATE_RESTART_TIMEOUT_MS = 60000;
|
|
594
679
|
|
|
@@ -1051,8 +1136,16 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
1051
1136
|
minions watchdog uninstall Remove the scheduled task (idempotent)
|
|
1052
1137
|
minions watchdog status Show registration + last-run details from the OS scheduler
|
|
1053
1138
|
minions watchdog tick One-shot probe + recovery (used by the scheduler; safe to run by hand)
|
|
1054
|
-
|
|
1055
|
-
|
|
1139
|
+
${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
|
|
1140
|
+
Dev mode (this checkout, contributors only):
|
|
1141
|
+
minions --dev <cmd> Run against this checkout instead of ~/.minions/
|
|
1142
|
+
MINIONS_HOME=<checkout>, dashboard on :7332.
|
|
1143
|
+
Coexists with the global install on :7331.
|
|
1144
|
+
minions --dev --dev-port <n> <cmd> Override the dev dashboard port.
|
|
1145
|
+
npm run dev Shortcut for: node bin/minions.js --dev restart
|
|
1146
|
+
` : ''}
|
|
1147
|
+
Runtime root: ${MINIONS_HOME}${devMode ? ' [DEV]' : ''}${devMode ? `
|
|
1148
|
+
Dashboard port: ${DASHBOARD_PORT} [DEV]` : ''}
|
|
1056
1149
|
`);
|
|
1057
1150
|
} else if (cmd === 'init') {
|
|
1058
1151
|
init();
|
|
@@ -1365,7 +1458,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
1365
1458
|
watchdog.tick({
|
|
1366
1459
|
minionsHome: MINIONS_HOME,
|
|
1367
1460
|
minionsBin,
|
|
1368
|
-
dashPort:
|
|
1461
|
+
dashPort: DASHBOARD_PORT,
|
|
1369
1462
|
readEnginePid,
|
|
1370
1463
|
isPortListening,
|
|
1371
1464
|
isStopIntentSet: shared.isStopIntentSet || (() => false),
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Dashboard typography audit (W-mq1c8og40003a7cf)
|
|
2
|
+
|
|
3
|
+
This document is the pre-migration audit that justified the token set
|
|
4
|
+
now declared in `dashboard/styles.css` + `dashboard/slim/styles.css`.
|
|
5
|
+
See `dashboard/docs/typography.md` for the contract.
|
|
6
|
+
|
|
7
|
+
## Findings
|
|
8
|
+
|
|
9
|
+
### Baseline before this WI
|
|
10
|
+
|
|
11
|
+
| Surface | font-size declarations | Already on tokens? |
|
|
12
|
+
| ----------------------------- | ---------------------: | ----------------------------- |
|
|
13
|
+
| `dashboard/styles.css` (main) | 179 | ✅ all `var(--text-*)` except `.prd-item-id { font-size: 0.9em }` (slipped past PR #66 tripwire because the pattern was px-only) |
|
|
14
|
+
| `dashboard/layout.html` | 20 | ✅ all `var(--text-*)` |
|
|
15
|
+
| `dashboard/pages/*.html` | 32 | ✅ all `var(--text-*)` |
|
|
16
|
+
| `dashboard/js/*.js` | ~530 | ✅ all `var(--text-*)` except `utils.js:263` `font-size:0.9em` (same em-pattern blind spot as above) |
|
|
17
|
+
| `dashboard/slim/styles.css` | 82 | ❌ all literals (slim was explicitly out of scope for PR #66) |
|
|
18
|
+
| `dashboard/slim/body.html` | 2 | ❌ inline literals |
|
|
19
|
+
| **Total** | **~845** | **~84 raw / non-tokenized** |
|
|
20
|
+
|
|
21
|
+
The main dashboard was already ~99% tokenized by PR #66
|
|
22
|
+
(`refactor(dashboard): anchor typography scale at 12px, migrate raw px
|
|
23
|
+
to tokens`). Two `0.9em` declarations slipped past its `\d+\s*px`
|
|
24
|
+
tripwire because the regex didn't catch the em form.
|
|
25
|
+
|
|
26
|
+
The slim UX prototype (`dashboard/slim/*`) was deliberately out of scope
|
|
27
|
+
for PR #66 — slim is a feature-flagged playground anchored at 16px
|
|
28
|
+
body, and its CSS is served from disk on every request so the human can
|
|
29
|
+
hand-edit it (`dashboard/slim/styles.css` header comment).
|
|
30
|
+
|
|
31
|
+
### Distinct font-size values seen across the audit
|
|
32
|
+
|
|
33
|
+
(Unique px values, sorted.)
|
|
34
|
+
|
|
35
|
+
| px | Count | Maps to | Where |
|
|
36
|
+
| -: | -----:| ------------------ | ---------------------------------------------------------- |
|
|
37
|
+
| 10 | many | `--text-xs` | main dashboard chips/badges |
|
|
38
|
+
| 11 | many | `--text-sm` | main dashboard status badges |
|
|
39
|
+
| 12 | ~30 | `--text-base` | main dashboard captions + most slim chips/labels |
|
|
40
|
+
| 13 | ~26 | `--text-md` | main dashboard default + slim default |
|
|
41
|
+
| 14 | ~10 | `--text-lg` | main dashboard body + slim body |
|
|
42
|
+
| 15 | 1 | `--text-lg` (round −1) | slim `.topbar-title` |
|
|
43
|
+
| 16 | ~7 | `--text-xl` | main dashboard body, slim chat input + msg |
|
|
44
|
+
| 17 | 1 | `--text-2xl` (round +1) | slim `.agent-detail-name` |
|
|
45
|
+
| 18 | many | `--text-2xl` | main dashboard h2 / modal headers |
|
|
46
|
+
| 20 | 1 | `--text-2xl` (round −2) | slim `.icon-btn` |
|
|
47
|
+
| 22 | many | `--text-stat` | main + slim stat readouts |
|
|
48
|
+
| 24 | 1 | `--text-stat` (round −2) | slim `.member-emoji` (decorative emoji glyph) |
|
|
49
|
+
| 28 | many | `--text-stat-lg` | main + slim large readouts |
|
|
50
|
+
| 32 | many | `--text-display` | main dashboard hero / H1 |
|
|
51
|
+
| 34 | 1 | `--text-display` (round −2) | slim `.agent-detail-emoji` (decorative emoji) |
|
|
52
|
+
| em | | | |
|
|
53
|
+
| 0.9em | 3 | `--text-code` | inline `<code>` in main + slim |
|
|
54
|
+
|
|
55
|
+
### Rounding decisions (5 sites)
|
|
56
|
+
|
|
57
|
+
The task contract says: *"prefer rounding to the nearest token over
|
|
58
|
+
inventing a new token. If rounding would visibly change the UI in a way
|
|
59
|
+
you can't justify, flag it in the PR description."*
|
|
60
|
+
|
|
61
|
+
All five rounded sites are within ±2px of their assigned token:
|
|
62
|
+
|
|
63
|
+
| Selector | Was | Now (token) | Δ | Justification |
|
|
64
|
+
| ------------------------------------- | --: | ------------------ | ---: | -------------------------------------------------------------- |
|
|
65
|
+
| `slim .topbar-title` | 15 | 14 `--text-lg` | −1px | Topbar title — 1px reduction not perceptible at glance |
|
|
66
|
+
| `slim .agent-detail-name` | 17 | 18 `--text-2xl` | +1px | Detail-modal heading — 1px increase matches modal-h3 elsewhere |
|
|
67
|
+
| `slim .icon-btn` | 20 | 18 `--text-2xl` | −2px | Hamburger / close icon — emoji-like glyph |
|
|
68
|
+
| `slim .member-emoji` | 24 | 22 `--text-stat` | −2px | Decorative emoji avatar |
|
|
69
|
+
| `slim .agent-detail-emoji` | 34 | 32 `--text-display`| −2px | Decorative emoji in detail modal |
|
|
70
|
+
|
|
71
|
+
Each rounded site has a `/* Rounded from Npx → Mpx … */` inline comment.
|
|
72
|
+
All five sit inside the feature-flagged slim UX, so the blast radius is
|
|
73
|
+
minimal even if the human decides to revert. If any rounding visibly
|
|
74
|
+
breaks the slim layout, define a one-off custom property scoped to that
|
|
75
|
+
selector and reference it instead of expanding the global token set.
|
|
76
|
+
|
|
77
|
+
### Tokens chosen — and rejected
|
|
78
|
+
|
|
79
|
+
**Chosen** (7 role tokens + 1 code token, all alias size primitives):
|
|
80
|
+
|
|
81
|
+
`--text-role-display`, `--text-heading`, `--text-subheading`,
|
|
82
|
+
`--text-body`, `--text-meta`, `--text-caption`, `--text-micro`,
|
|
83
|
+
`--text-code`.
|
|
84
|
+
|
|
85
|
+
**Rejected**:
|
|
86
|
+
|
|
87
|
+
- `--text-h1` / `--text-h2` / `--text-h3` — semantic HTML headings don't
|
|
88
|
+
map 1:1 to visual roles in this dashboard (`<h1>` in `header` is 18px;
|
|
89
|
+
PRD section h1s render at 32px). Role names beat heading names.
|
|
90
|
+
- `--text-label` — would overlap with `--text-caption`. Same role.
|
|
91
|
+
- `--text-large` / `--text-medium` / `--text-small` — pure-size names
|
|
92
|
+
duplicate the primitives without adding semantics.
|
|
93
|
+
- Slim-specific tokens (e.g. `--text-slim-body: 15px`) — would let slim
|
|
94
|
+
drift further from main. Better to round once and converge.
|
|
95
|
+
- A separate `--icon-*` family for the decorative emoji glyphs — only
|
|
96
|
+
3 sites, all single-use, and they all happen to round cleanly to an
|
|
97
|
+
existing primitive. Not worth a parallel token family yet.
|
|
98
|
+
|
|
99
|
+
### Out of scope (deliberately)
|
|
100
|
+
|
|
101
|
+
- font-weight, line-height, letter-spacing, color — explicitly out of
|
|
102
|
+
scope per the task contract; this WI is size-only.
|
|
103
|
+
- font-family — no change.
|
|
104
|
+
- SVG `font-size='90'` inside the favicon `data:image/svg+xml` URL —
|
|
105
|
+
data URLs can't resolve CSS variables. Allowlisted.
|
|
106
|
+
- The 4 `:root[data-font-size="…"] { --minions-font-scale: … }` rules
|
|
107
|
+
declare the dashboard zoom scale; they're attribute selectors, not
|
|
108
|
+
text-size declarations. Allowlisted.
|
|
109
|
+
|
|
110
|
+
## Regression guard
|
|
111
|
+
|
|
112
|
+
Extended `test/unit/dashboard-font-size-tokens.test.js`:
|
|
113
|
+
|
|
114
|
+
- Already covered: main `dashboard/styles.css`, `dashboard/layout.html`,
|
|
115
|
+
`dashboard/pages/*.html`, `dashboard/js/*.js` for raw `Npx` font-sizes.
|
|
116
|
+
- New in this WI:
|
|
117
|
+
- Extended file set to include `dashboard/slim/styles.css` and
|
|
118
|
+
`dashboard/slim/body.html`.
|
|
119
|
+
- Added pattern for `font-size: Nem|Nrem|N%` (catches the `0.9em`
|
|
120
|
+
blind spot that hid the two pre-existing inline-code declarations).
|
|
121
|
+
- Allowlisted the `--text-code: 0.9em` token declaration itself.
|
|
122
|
+
- Asserted that slim's typography tokens match the main ones
|
|
123
|
+
(drift-detection between the two stylesheets).
|
|
124
|
+
|
|
125
|
+
The test runs in `npm test` and `npm run test:sequential`. ESLint
|
|
126
|
+
remains scoped to XSS-prevention (`eslint.config.js`); we don't add a
|
|
127
|
+
second lint plugin for CSS literals because the pure-Node tripwire
|
|
128
|
+
already covers every file type.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Dashboard typography tokens
|
|
2
|
+
|
|
3
|
+
> Single source of truth for text sizing across the Minions dashboard.
|
|
4
|
+
> Centralized in W-mq1c8og40003a7cf (follow-up to PR #66 size primitives
|
|
5
|
+
> and PR #80 `.btn-add` consolidation).
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
The dashboard SPA renders text from ~30 modules (`dashboard/js/render-*.js`,
|
|
10
|
+
`dashboard/pages/*.html`, `dashboard/styles.css`, plus the slim UX prototype
|
|
11
|
+
in `dashboard/slim/`). Without a shared token system, every callsite picks
|
|
12
|
+
its own pixel size. Cards drift, captions disagree, headers don't line up
|
|
13
|
+
across pages, and changing the design language costs ~700 edits.
|
|
14
|
+
|
|
15
|
+
Two pieces solve this together:
|
|
16
|
+
|
|
17
|
+
1. **Size primitives** (`--text-xs` ... `--text-display`) — raw px values.
|
|
18
|
+
The 10 declared sizes cover every visual role the dashboard currently
|
|
19
|
+
uses. Anchored at `--text-base = 12px`. PR #66 migrated ~664 callsites
|
|
20
|
+
from raw `Npx` to these tokens.
|
|
21
|
+
2. **Role aliases** (`--text-heading`, `--text-body`, etc.) — semantic
|
|
22
|
+
names that point at size primitives. Added in W-mq1c8og40003a7cf so
|
|
23
|
+
new code talks in roles, not px. The size primitive stays the single
|
|
24
|
+
source of truth; role names are aliases.
|
|
25
|
+
|
|
26
|
+
## Files this lives in
|
|
27
|
+
|
|
28
|
+
| File | What it owns |
|
|
29
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `dashboard/styles.css` | Canonical token declarations (`:root`), utility classes, and main-dashboard CSS rules. |
|
|
31
|
+
| `dashboard/slim/styles.css` | Duplicate token declarations + utility classes for the feature-flagged slim UX (self-contained stylesheet). |
|
|
32
|
+
| `dashboard/docs/typography.md` | This file — the contract. |
|
|
33
|
+
| `dashboard/docs/typography-audit.md` | The pre-migration audit + rounding decisions. |
|
|
34
|
+
| `test/unit/dashboard-font-size-tokens.test.js` | Tripwire that fails CI if raw `Npx` / `Nem` font-sizes leak back in. |
|
|
35
|
+
|
|
36
|
+
A tripwire asserts the slim token VALUES match the main values so the
|
|
37
|
+
two stay in lockstep.
|
|
38
|
+
|
|
39
|
+
## Size primitives
|
|
40
|
+
|
|
41
|
+
| Token | Value | Common use |
|
|
42
|
+
| ------------------ | ----- | --------------------------------------------------------- |
|
|
43
|
+
| `--text-xs` | 10px | Smallest legitimate text; sidebar badges, count pills |
|
|
44
|
+
| `--text-sm` | 11px | Status badges, chip / tag labels |
|
|
45
|
+
| `--text-base` | 12px | Captions, secondary metadata, dense table cells |
|
|
46
|
+
| `--text-md` | 13px | Default for slim chat / detail tables / button labels |
|
|
47
|
+
| `--text-lg` | 14px | Body text, modal textarea, card titles |
|
|
48
|
+
| `--text-xl` | 16px | Main `<body>` font, secondary headers, chat input |
|
|
49
|
+
| `--text-2xl` | 18px | Section headers, page headers, modal h3 |
|
|
50
|
+
| `--text-stat` | 22px | Stat counter readouts, member emoji glyphs |
|
|
51
|
+
| `--text-stat-lg` | 28px | Larger stat readouts, completions-card type emoji |
|
|
52
|
+
| `--text-display` | 32px | Page hero / markdown H1, agent-detail emoji |
|
|
53
|
+
| `--text-code` | 0.9em | Inline `<code>` — relative so it scales with parent text |
|
|
54
|
+
|
|
55
|
+
> `--text-code` is intentionally relative (`em`, not `px`). Inline code
|
|
56
|
+
> should read 10% smaller than whatever surrounds it. This is the one
|
|
57
|
+
> token that breaks the "all px" rule and is explicitly allowlisted in
|
|
58
|
+
> the tripwire.
|
|
59
|
+
|
|
60
|
+
## Role aliases (preferred for new code)
|
|
61
|
+
|
|
62
|
+
Use the role token when adding new UI. It survives a future size-scale
|
|
63
|
+
redesign without callsite edits.
|
|
64
|
+
|
|
65
|
+
| Role token | Aliases | Intended use |
|
|
66
|
+
| ---------------------- | ------------------- | ----------------------------------------- |
|
|
67
|
+
| `--text-role-display` | `--text-display` | Page hero, dashboard title |
|
|
68
|
+
| `--text-heading` | `--text-2xl` (18px) | Section + modal headers |
|
|
69
|
+
| `--text-subheading` | `--text-xl` (16px) | Secondary headers, card titles |
|
|
70
|
+
| `--text-body` | `--text-lg` (14px) | Default body text, paragraphs |
|
|
71
|
+
| `--text-meta` | `--text-md` (13px) | Captions, timestamps, secondary metadata |
|
|
72
|
+
| `--text-caption` | `--text-base` (12px)| Small labels, table cells |
|
|
73
|
+
| `--text-micro` | `--text-sm` (11px) | Chip / tag pill labels |
|
|
74
|
+
| `--text-code` | (`0.9em`) | Inline code |
|
|
75
|
+
|
|
76
|
+
## Utility classes
|
|
77
|
+
|
|
78
|
+
Each role token has a matching utility class for callsites that can't
|
|
79
|
+
write a CSS rule (inline `class="text-meta"`, template strings, etc.).
|
|
80
|
+
Only `font-size` is set — weight, color, line-height stay with the caller.
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<span class="text-meta">3 min ago</span>
|
|
84
|
+
<h3 class="text-heading">Active dispatches</h3>
|
|
85
|
+
<code class="text-code">npm run lint</code>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Available classes: `.text-display`, `.text-heading`, `.text-subheading`,
|
|
89
|
+
`.text-body`, `.text-meta`, `.text-caption`, `.text-micro`, `.text-code`.
|
|
90
|
+
|
|
91
|
+
## How to add a new text style
|
|
92
|
+
|
|
93
|
+
1. Pick an existing role token. The 7 roles + 1 code token cover every
|
|
94
|
+
case the audit found.
|
|
95
|
+
2. If your value doesn't fit any role, round to the nearest primitive
|
|
96
|
+
and document it inline. Inventing a new token requires changing
|
|
97
|
+
`dashboard/styles.css`, `dashboard/slim/styles.css`, this doc, AND
|
|
98
|
+
the tripwire.
|
|
99
|
+
3. NEVER hand-roll `font-size: 14px` in CSS / inline style / JS template
|
|
100
|
+
string. The tripwire test `test/unit/dashboard-font-size-tokens.test.js`
|
|
101
|
+
fails CI.
|
|
102
|
+
|
|
103
|
+
## Exceptions / allowlist
|
|
104
|
+
|
|
105
|
+
These declarations are intentional and exempt from the tripwire:
|
|
106
|
+
|
|
107
|
+
- `:root[data-font-size="…"]` rules in `dashboard/styles.css` — these
|
|
108
|
+
declare the user-level zoom scale, not text size.
|
|
109
|
+
- SVG `font-size='90'` inside the `data:image/svg+xml` favicon link in
|
|
110
|
+
`dashboard/layout.html` and `dashboard/slim/layout.html` — runtime
|
|
111
|
+
data URL, CSS variables don't resolve inside it.
|
|
112
|
+
- The `--text-*` token declarations themselves in `:root` blocks.
|
|
113
|
+
|
|
114
|
+
Everything else MUST go through a token.
|
|
@@ -82,7 +82,7 @@ async function openAgentDetail(id) {
|
|
|
82
82
|
// guarantees no HTML interpretation even if the escape function were ever bypassed.
|
|
83
83
|
const nameEl = document.getElementById('detail-agent-name');
|
|
84
84
|
const emojiSpan = document.createElement('span');
|
|
85
|
-
emojiSpan.style.fontSize = '
|
|
85
|
+
emojiSpan.style.fontSize = 'var(--text-stat)';
|
|
86
86
|
emojiSpan.textContent = agent.emoji || '';
|
|
87
87
|
// Runtime tag \u2014 uses the inline-SVG logo from the same RUNTIME_TAGS map the
|
|
88
88
|
// card uses, so the visual is consistent. The container's user-controlled
|
package/dashboard/js/utils.js
CHANGED
|
@@ -280,7 +280,7 @@ function _renderMdCore(s) {
|
|
|
280
280
|
}).join('\n');
|
|
281
281
|
|
|
282
282
|
html = html.replace(/`([^`\n]+)`/g, function(_, code) {
|
|
283
|
-
codeSlots.push('<code style="background:var(--bg);padding:1px 4px;border-radius:3px;font-size:
|
|
283
|
+
codeSlots.push('<code style="background:var(--bg);padding:1px 4px;border-radius:3px;font-size:var(--text-code)">' + code + '</code>');
|
|
284
284
|
return '\x00CB' + (codeSlots.length - 1) + '\x00';
|
|
285
285
|
});
|
|
286
286
|
|
package/dashboard/slim/body.html
CHANGED
|
@@ -300,10 +300,10 @@
|
|
|
300
300
|
<div class="modal-body">
|
|
301
301
|
<p>Point to a local git repository. Minions will read its remote, name, and main branch automatically.</p>
|
|
302
302
|
<div style="display:flex; gap:8px; align-items:stretch">
|
|
303
|
-
<input id="slim-add-project-path" type="text" placeholder="C:\path\to\repo" style="flex:1; padding:8px 10px; background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); color:var(--text); font-size:
|
|
303
|
+
<input id="slim-add-project-path" type="text" placeholder="C:\path\to\repo" style="flex:1; padding:8px 10px; background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); color:var(--text); font-size:var(--text-md); font-family:inherit">
|
|
304
304
|
<button id="slim-add-project-browse" class="btn-secondary" type="button">Browse…</button>
|
|
305
305
|
</div>
|
|
306
|
-
<div id="slim-add-project-msg" style="margin-top:10px; font-size:
|
|
306
|
+
<div id="slim-add-project-msg" style="margin-top:10px; font-size:var(--text-base); min-height:16px"></div>
|
|
307
307
|
</div>
|
|
308
308
|
<div class="modal-footer">
|
|
309
309
|
<button id="slim-add-project-cancel" class="btn-secondary" type="button">Cancel</button>
|
|
@@ -22,8 +22,38 @@
|
|
|
22
22
|
--amber: #d29922;
|
|
23
23
|
--red: #f85149;
|
|
24
24
|
--radius: 6px;
|
|
25
|
+
|
|
26
|
+
/* Typography — mirrors dashboard/styles.css :root tokens
|
|
27
|
+
(W-mq1c8og40003a7cf). Slim ships its CSS independently
|
|
28
|
+
(template-baked into slim/layout.html), so we duplicate the
|
|
29
|
+
token VALUES here verbatim — see dashboard/docs/typography.md.
|
|
30
|
+
A tripwire test asserts the slim and main tokens stay in
|
|
31
|
+
sync (test/unit/dashboard-font-size-tokens.test.js). */
|
|
32
|
+
--text-xs: 10px; --text-sm: 11px; --text-base: 12px;
|
|
33
|
+
--text-md: 13px; --text-lg: 14px; --text-xl: 16px; --text-2xl: 18px;
|
|
34
|
+
--text-stat: 22px; --text-stat-lg: 28px; --text-display: 32px;
|
|
35
|
+
--text-role-display: var(--text-display);
|
|
36
|
+
--text-heading: var(--text-2xl);
|
|
37
|
+
--text-subheading: var(--text-xl);
|
|
38
|
+
--text-body: var(--text-lg);
|
|
39
|
+
--text-meta: var(--text-md);
|
|
40
|
+
--text-caption: var(--text-base);
|
|
41
|
+
--text-micro: var(--text-sm);
|
|
42
|
+
--text-code: 0.9em;
|
|
25
43
|
}
|
|
26
44
|
|
|
45
|
+
/* Typography utility classes — see dashboard/styles.css for the
|
|
46
|
+
canonical declarations; slim duplicates because the two
|
|
47
|
+
stylesheets are served independently. */
|
|
48
|
+
.text-display { font-size: var(--text-role-display); }
|
|
49
|
+
.text-heading { font-size: var(--text-heading); }
|
|
50
|
+
.text-subheading { font-size: var(--text-subheading); }
|
|
51
|
+
.text-body { font-size: var(--text-body); }
|
|
52
|
+
.text-meta { font-size: var(--text-meta); }
|
|
53
|
+
.text-caption { font-size: var(--text-caption); }
|
|
54
|
+
.text-micro { font-size: var(--text-micro); }
|
|
55
|
+
.text-code { font-size: var(--text-code); }
|
|
56
|
+
|
|
27
57
|
* { box-sizing: border-box; }
|
|
28
58
|
|
|
29
59
|
/* Typography baseline (slim UX foundation phase).
|
|
@@ -41,7 +71,7 @@
|
|
|
41
71
|
background: var(--bg);
|
|
42
72
|
color: var(--text);
|
|
43
73
|
font-family: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
44
|
-
font-size:
|
|
74
|
+
font-size: var(--text-xl);
|
|
45
75
|
line-height: 1.5;
|
|
46
76
|
}
|
|
47
77
|
|
|
@@ -89,7 +119,8 @@
|
|
|
89
119
|
}
|
|
90
120
|
.topbar-title {
|
|
91
121
|
font-weight: 600;
|
|
92
|
-
|
|
122
|
+
/* Rounded from 15px → 14px (--text-lg) in W-mq1c8og40003a7cf typography centralization. */
|
|
123
|
+
font-size: var(--text-lg);
|
|
93
124
|
color: var(--text);
|
|
94
125
|
letter-spacing: 0.3px;
|
|
95
126
|
}
|
|
@@ -97,7 +128,7 @@
|
|
|
97
128
|
display: inline-block;
|
|
98
129
|
margin-left: 8px;
|
|
99
130
|
padding: 1px 6px;
|
|
100
|
-
font-size:
|
|
131
|
+
font-size: var(--text-base);
|
|
101
132
|
font-weight: 700;
|
|
102
133
|
letter-spacing: 0.5px;
|
|
103
134
|
color: var(--blue);
|
|
@@ -125,7 +156,8 @@
|
|
|
125
156
|
border: none;
|
|
126
157
|
cursor: pointer;
|
|
127
158
|
color: var(--muted);
|
|
128
|
-
|
|
159
|
+
/* Rounded from 20px → 18px (--text-2xl) in W-mq1c8og40003a7cf typography centralization. */
|
|
160
|
+
font-size: var(--text-2xl);
|
|
129
161
|
line-height: 1;
|
|
130
162
|
padding: 6px 10px;
|
|
131
163
|
border-radius: var(--radius);
|
|
@@ -164,7 +196,7 @@
|
|
|
164
196
|
}
|
|
165
197
|
.panel-header {
|
|
166
198
|
padding: 9px 14px;
|
|
167
|
-
font-size:
|
|
199
|
+
font-size: var(--text-base);
|
|
168
200
|
font-weight: 700;
|
|
169
201
|
letter-spacing: 0.7px;
|
|
170
202
|
text-transform: uppercase;
|
|
@@ -180,7 +212,7 @@
|
|
|
180
212
|
letter-spacing: 0;
|
|
181
213
|
font-weight: 400;
|
|
182
214
|
color: var(--muted);
|
|
183
|
-
font-size:
|
|
215
|
+
font-size: var(--text-base);
|
|
184
216
|
}
|
|
185
217
|
.panel-body {
|
|
186
218
|
flex: 1;
|
|
@@ -191,7 +223,7 @@
|
|
|
191
223
|
.panel-placeholder {
|
|
192
224
|
color: var(--muted);
|
|
193
225
|
font-style: italic;
|
|
194
|
-
font-size:
|
|
226
|
+
font-size: var(--text-lg);
|
|
195
227
|
}
|
|
196
228
|
|
|
197
229
|
.panel-actions { grid-area: actions; }
|
|
@@ -223,7 +255,7 @@
|
|
|
223
255
|
}
|
|
224
256
|
.chat-empty {
|
|
225
257
|
color: var(--muted);
|
|
226
|
-
font-size:
|
|
258
|
+
font-size: var(--text-lg);
|
|
227
259
|
font-style: italic;
|
|
228
260
|
text-align: center;
|
|
229
261
|
margin-top: 24px;
|
|
@@ -234,7 +266,7 @@
|
|
|
234
266
|
border-radius: var(--radius);
|
|
235
267
|
white-space: pre-wrap;
|
|
236
268
|
word-wrap: break-word;
|
|
237
|
-
font-size:
|
|
269
|
+
font-size: var(--text-xl);
|
|
238
270
|
}
|
|
239
271
|
.chat-msg.user {
|
|
240
272
|
align-self: flex-end;
|
|
@@ -251,7 +283,7 @@
|
|
|
251
283
|
align-self: center;
|
|
252
284
|
background: transparent;
|
|
253
285
|
color: var(--muted);
|
|
254
|
-
font-size:
|
|
286
|
+
font-size: var(--text-md);
|
|
255
287
|
font-style: italic;
|
|
256
288
|
}
|
|
257
289
|
.chat-msg.error {
|
|
@@ -259,7 +291,7 @@
|
|
|
259
291
|
background: rgba(248, 81, 73, 0.1);
|
|
260
292
|
color: var(--red);
|
|
261
293
|
border: 1px solid var(--red);
|
|
262
|
-
font-size:
|
|
294
|
+
font-size: var(--text-lg);
|
|
263
295
|
}
|
|
264
296
|
.chat-input-wrap {
|
|
265
297
|
display: flex;
|
|
@@ -290,14 +322,14 @@
|
|
|
290
322
|
max-width: 800px;
|
|
291
323
|
margin-left: auto;
|
|
292
324
|
margin-right: auto;
|
|
293
|
-
font-size:
|
|
325
|
+
font-size: var(--text-base);
|
|
294
326
|
color: var(--muted);
|
|
295
327
|
}
|
|
296
328
|
.chat-context-strip .context-label {
|
|
297
329
|
text-transform: uppercase;
|
|
298
330
|
letter-spacing: 0.5px;
|
|
299
331
|
font-weight: 700;
|
|
300
|
-
font-size:
|
|
332
|
+
font-size: var(--text-base);
|
|
301
333
|
}
|
|
302
334
|
.chat-context-strip select {
|
|
303
335
|
background: var(--bg);
|
|
@@ -306,7 +338,7 @@
|
|
|
306
338
|
border-radius: var(--radius);
|
|
307
339
|
padding: 4px 8px;
|
|
308
340
|
font-family: inherit;
|
|
309
|
-
font-size:
|
|
341
|
+
font-size: var(--text-base);
|
|
310
342
|
cursor: pointer;
|
|
311
343
|
}
|
|
312
344
|
.chat-context-strip select:focus { outline: none; border-color: var(--blue); }
|
|
@@ -324,7 +356,7 @@
|
|
|
324
356
|
border: 1px solid var(--border);
|
|
325
357
|
border-radius: var(--radius);
|
|
326
358
|
padding: 3px 9px;
|
|
327
|
-
font-size:
|
|
359
|
+
font-size: var(--text-base);
|
|
328
360
|
cursor: pointer;
|
|
329
361
|
line-height: 1;
|
|
330
362
|
}
|
|
@@ -339,7 +371,7 @@
|
|
|
339
371
|
color: var(--text);
|
|
340
372
|
border: 1px solid var(--border);
|
|
341
373
|
border-radius: var(--radius);
|
|
342
|
-
font-size:
|
|
374
|
+
font-size: var(--text-xl);
|
|
343
375
|
font-family: inherit;
|
|
344
376
|
resize: none;
|
|
345
377
|
/* >= 2 lines visible by default. 16px font * 1.5 line-height = 24px/line,
|
|
@@ -354,7 +386,7 @@
|
|
|
354
386
|
color: #fff;
|
|
355
387
|
border: none;
|
|
356
388
|
border-radius: var(--radius);
|
|
357
|
-
font-size:
|
|
389
|
+
font-size: var(--text-lg);
|
|
358
390
|
font-weight: 600;
|
|
359
391
|
cursor: pointer;
|
|
360
392
|
align-self: flex-end;
|
|
@@ -367,7 +399,7 @@
|
|
|
367
399
|
color: var(--red);
|
|
368
400
|
border: 1px solid var(--border);
|
|
369
401
|
border-radius: var(--radius);
|
|
370
|
-
font-size:
|
|
402
|
+
font-size: var(--text-md);
|
|
371
403
|
cursor: pointer;
|
|
372
404
|
align-self: flex-end;
|
|
373
405
|
display: none;
|
|
@@ -413,7 +445,7 @@
|
|
|
413
445
|
}
|
|
414
446
|
.chat-tool {
|
|
415
447
|
color: var(--muted);
|
|
416
|
-
font-size:
|
|
448
|
+
font-size: var(--text-base);
|
|
417
449
|
line-height: 1.5;
|
|
418
450
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
419
451
|
white-space: nowrap;
|
|
@@ -425,7 +457,7 @@
|
|
|
425
457
|
/* Tool-calls modal: full list, wrapped so long commands are readable. */
|
|
426
458
|
.tools-modal-line {
|
|
427
459
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
428
|
-
font-size:
|
|
460
|
+
font-size: var(--text-base);
|
|
429
461
|
color: var(--text);
|
|
430
462
|
padding: 5px 0;
|
|
431
463
|
border-bottom: 1px solid var(--border);
|
|
@@ -434,7 +466,7 @@
|
|
|
434
466
|
}
|
|
435
467
|
.tools-modal-line::before { content: "● "; color: var(--muted); }
|
|
436
468
|
.tools-modal-line:last-child { border-bottom: none; }
|
|
437
|
-
.tools-modal-empty { color: var(--muted); font-style: italic; font-size:
|
|
469
|
+
.tools-modal-empty { color: var(--muted); font-style: italic; font-size: var(--text-md); }
|
|
438
470
|
/* Wide enough to span the Actions + Status columns (820 + 10 gap + 548). */
|
|
439
471
|
#slim-tools-modal .modal {
|
|
440
472
|
width: 1378px;
|
|
@@ -458,13 +490,13 @@
|
|
|
458
490
|
a.tile-item:hover { border-color: var(--blue); }
|
|
459
491
|
.tile-item-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
460
492
|
.tile-item-title {
|
|
461
|
-
font-size:
|
|
493
|
+
font-size: var(--text-lg); font-weight: 600; color: var(--text);
|
|
462
494
|
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
463
495
|
}
|
|
464
|
-
.tile-item-meta { font-size:
|
|
496
|
+
.tile-item-meta { font-size: var(--text-base); color: var(--muted); margin-top: 3px; word-break: break-word; }
|
|
465
497
|
.tile-chip {
|
|
466
498
|
flex: 0 0 auto;
|
|
467
|
-
font-size:
|
|
499
|
+
font-size: var(--text-base); font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
|
|
468
500
|
padding: 1px 7px; border-radius: 10px;
|
|
469
501
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted);
|
|
470
502
|
}
|
|
@@ -472,7 +504,7 @@
|
|
|
472
504
|
.tile-chip.amber { background: rgba(210, 153, 34, 0.15); color: var(--amber); border-color: var(--amber); }
|
|
473
505
|
.tile-chip.red { background: rgba(248, 81, 73, 0.15); color: var(--red); border-color: var(--red); }
|
|
474
506
|
.tile-chip.blue { background: rgba(88, 166, 255, 0.12); color: var(--blue); border-color: var(--blue); }
|
|
475
|
-
.tile-empty { color: var(--muted); font-style: italic; font-size:
|
|
507
|
+
.tile-empty { color: var(--muted); font-style: italic; font-size: var(--text-md); }
|
|
476
508
|
|
|
477
509
|
/* Pinned-context list rows (slim-pinned-modal). */
|
|
478
510
|
.pinned-row {
|
|
@@ -499,7 +531,7 @@
|
|
|
499
531
|
|
|
500
532
|
.chat-thinking {
|
|
501
533
|
color: var(--muted);
|
|
502
|
-
font-size:
|
|
534
|
+
font-size: var(--text-md);
|
|
503
535
|
font-style: italic;
|
|
504
536
|
}
|
|
505
537
|
.chat-thinking-dots {
|
|
@@ -526,7 +558,7 @@
|
|
|
526
558
|
align-self: flex-start;
|
|
527
559
|
padding: 5px 11px;
|
|
528
560
|
border-radius: 4px;
|
|
529
|
-
font-size:
|
|
561
|
+
font-size: var(--text-base);
|
|
530
562
|
border: 1px dashed var(--border);
|
|
531
563
|
color: var(--muted);
|
|
532
564
|
}
|
|
@@ -543,14 +575,14 @@
|
|
|
543
575
|
background: var(--bg);
|
|
544
576
|
padding: 1px 4px;
|
|
545
577
|
border-radius: 3px;
|
|
546
|
-
font-size:
|
|
578
|
+
font-size: var(--text-code);
|
|
547
579
|
}
|
|
548
580
|
.chat-msg.assistant pre {
|
|
549
581
|
background: var(--bg);
|
|
550
582
|
padding: 10px;
|
|
551
583
|
border-radius: 4px;
|
|
552
584
|
overflow-x: auto;
|
|
553
|
-
font-size:
|
|
585
|
+
font-size: var(--text-lg);
|
|
554
586
|
margin: 6px 0;
|
|
555
587
|
}
|
|
556
588
|
.chat-msg.assistant pre code { background: none; padding: 0; }
|
|
@@ -577,7 +609,7 @@
|
|
|
577
609
|
border-radius: var(--radius);
|
|
578
610
|
padding: 10px 12px;
|
|
579
611
|
cursor: pointer;
|
|
580
|
-
font-size:
|
|
612
|
+
font-size: var(--text-md);
|
|
581
613
|
font-family: inherit;
|
|
582
614
|
display: flex;
|
|
583
615
|
flex-direction: column;
|
|
@@ -602,28 +634,28 @@
|
|
|
602
634
|
.act-ic-link { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20d%3D%22M9%207C9.55228%207%2010%207.44772%2010%208C10%208.51284%209.61396%208.93551%209.11662%208.99327L9%209H7C5.34315%209%204%2010.3431%204%2012C4%2013.5977%205.24892%2014.9037%206.82373%2014.9949L7%2015H9C9.55228%2015%2010%2015.4477%2010%2016C10%2016.5128%209.61396%2016.9355%209.11662%2016.9933L9%2017H7C4.23858%2017%202%2014.7614%202%2012C2%209.31125%204.12231%207.11818%206.78311%207.00462L7%207H9ZM17%207C19.7614%207%2022%209.23858%2022%2012C22%2014.6888%2019.8777%2016.8818%2017.2169%2016.9954L17%2017H15C14.4477%2017%2014%2016.5523%2014%2016C14%2015.4872%2014.386%2015.0645%2014.8834%2015.0067L15%2015H17C18.6569%2015%2020%2013.6569%2020%2012C20%2010.4023%2018.7511%209.09634%2017.1763%209.00509L17%209H15C14.4477%209%2014%208.55228%2014%208C14%207.48716%2014.386%207.06449%2014.8834%207.00673L15%207H17ZM7%2011H17C17.5523%2011%2018%2011.4477%2018%2012C18%2012.5128%2017.614%2012.9355%2017.1166%2012.9933L17%2013H7C6.44772%2013%206%2012.5523%206%2012C6%2011.4872%206.38604%2011.0645%206.88338%2011.0067L7%2011H17H7Z%22%20fill%3D%22%23212121%22%2F%3E%20%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20d%3D%22M9%207C9.55228%207%2010%207.44772%2010%208C10%208.51284%209.61396%208.93551%209.11662%208.99327L9%209H7C5.34315%209%204%2010.3431%204%2012C4%2013.5977%205.24892%2014.9037%206.82373%2014.9949L7%2015H9C9.55228%2015%2010%2015.4477%2010%2016C10%2016.5128%209.61396%2016.9355%209.11662%2016.9933L9%2017H7C4.23858%2017%202%2014.7614%202%2012C2%209.31125%204.12231%207.11818%206.78311%207.00462L7%207H9ZM17%207C19.7614%207%2022%209.23858%2022%2012C22%2014.6888%2019.8777%2016.8818%2017.2169%2016.9954L17%2017H15C14.4477%2017%2014%2016.5523%2014%2016C14%2015.4872%2014.386%2015.0645%2014.8834%2015.0067L15%2015H17C18.6569%2015%2020%2013.6569%2020%2012C20%2010.4023%2018.7511%209.09634%2017.1763%209.00509L17%209H15C14.4477%209%2014%208.55228%2014%208C14%207.48716%2014.386%207.06449%2014.8834%207.00673L15%207H17ZM7%2011H17C17.5523%2011%2018%2011.4477%2018%2012C18%2012.5128%2017.614%2012.9355%2017.1166%2012.9933L17%2013H7C6.44772%2013%206%2012.5523%206%2012C6%2011.4872%206.38604%2011.0645%206.88338%2011.0067L7%2011H17H7Z%22%20fill%3D%22%23212121%22%2F%3E%20%3C%2Fsvg%3E"); }
|
|
603
635
|
.act-ic-pin { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20d%3D%22M21.0682%207.75765L16.2425%202.93189C14.9152%201.60462%2012.6777%201.96772%2011.8382%203.6466L9.40281%208.51748C9.31512%208.69287%209.16223%208.82694%208.97688%208.89096L4.81061%2010.3302C3.93791%2010.6317%203.682%2011.7427%204.33487%2012.3956L7.43936%2015.5001L3.00008%2019.9394L3%2021.0001H4.06074L8.50002%2016.5607L11.6045%2019.6653C12.2574%2020.3181%2013.3684%2020.0622%2013.6699%2019.1895L15.1092%2015.0232C15.1732%2014.8379%2015.3073%2014.685%2015.4826%2014.5973L20.3535%2012.1619C22.0324%2011.3224%2022.3955%209.08491%2021.0682%207.75765Z%22%20fill%3D%22%23212121%22%2F%3E%20%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20d%3D%22M21.0682%207.75765L16.2425%202.93189C14.9152%201.60462%2012.6777%201.96772%2011.8382%203.6466L9.40281%208.51748C9.31512%208.69287%209.16223%208.82694%208.97688%208.89096L4.81061%2010.3302C3.93791%2010.6317%203.682%2011.7427%204.33487%2012.3956L7.43936%2015.5001L3.00008%2019.9394L3%2021.0001H4.06074L8.50002%2016.5607L11.6045%2019.6653C12.2574%2020.3181%2013.3684%2020.0622%2013.6699%2019.1895L15.1092%2015.0232C15.1732%2014.8379%2015.3073%2014.685%2015.4826%2014.5973L20.3535%2012.1619C22.0324%2011.3224%2022.3955%209.08491%2021.0682%207.75765Z%22%20fill%3D%22%23212121%22%2F%3E%20%3C%2Fsvg%3E"); }
|
|
604
636
|
.link-pr-btn .link-pr-label { font-weight: 600; }
|
|
605
|
-
.link-pr-btn .link-pr-sub { font-size:
|
|
637
|
+
.link-pr-btn .link-pr-sub { font-size: var(--text-base); color: var(--muted); }
|
|
606
638
|
|
|
607
639
|
/* Link-PR dialog fields + the "+ Link PR" chip (tile corner + modal header). */
|
|
608
|
-
.linkpr-label { display: block; color: var(--text); font-size:
|
|
640
|
+
.linkpr-label { display: block; color: var(--text); font-size: var(--text-md); margin-bottom: 10px; }
|
|
609
641
|
.linkpr-input {
|
|
610
642
|
display: block; width: 100%; margin-top: 4px; padding: 7px 9px;
|
|
611
643
|
background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
|
|
612
|
-
color: var(--text); font-size:
|
|
644
|
+
color: var(--text); font-size: var(--text-md); font-family: inherit; resize: vertical;
|
|
613
645
|
}
|
|
614
646
|
.linkpr-input:focus { outline: none; border-color: var(--blue); }
|
|
615
|
-
.linkpr-check { display: flex; align-items: center; gap: 8px; color: var(--text); font-size:
|
|
647
|
+
.linkpr-check { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: var(--text-md); cursor: pointer; margin-bottom: 4px; }
|
|
616
648
|
.linkpr-check input { width: 16px; height: 16px; accent-color: var(--blue); }
|
|
617
649
|
.linkpr-hint-inline { color: var(--muted); font-weight: 400; }
|
|
618
|
-
.linkpr-hint { font-size:
|
|
650
|
+
.linkpr-hint { font-size: var(--text-base); color: var(--muted); padding-left: 24px; }
|
|
619
651
|
.linkpr-warning {
|
|
620
652
|
padding: 8px 12px; margin-bottom: 10px;
|
|
621
653
|
background: rgba(210, 153, 34, 0.15); border: 1px solid rgba(210, 153, 34, 0.3);
|
|
622
|
-
border-radius: var(--radius); font-size:
|
|
654
|
+
border-radius: var(--radius); font-size: var(--text-md); line-height: 1.5; color: var(--text);
|
|
623
655
|
}
|
|
624
656
|
.linkpr-warning-title { font-weight: 700; margin-bottom: 2px; color: var(--amber); }
|
|
625
657
|
.linkpr-chip {
|
|
626
|
-
font-size:
|
|
658
|
+
font-size: var(--text-base); font-weight: 600;
|
|
627
659
|
padding: 1px 8px; border-radius: 10px;
|
|
628
660
|
background: rgba(63, 185, 80, 0.12); color: var(--green); border: 1px solid var(--green);
|
|
629
661
|
cursor: pointer; font-family: inherit;
|
|
@@ -671,7 +703,7 @@
|
|
|
671
703
|
background: rgba(248, 81, 73, 0.08);
|
|
672
704
|
}
|
|
673
705
|
.cockpit-label {
|
|
674
|
-
font-size:
|
|
706
|
+
font-size: var(--text-base);
|
|
675
707
|
font-weight: 700;
|
|
676
708
|
letter-spacing: 0.6px;
|
|
677
709
|
text-transform: uppercase;
|
|
@@ -692,14 +724,14 @@
|
|
|
692
724
|
.lit-amber .cockpit-dot { background: var(--amber); box-shadow: 0 0 6px var(--amber); }
|
|
693
725
|
.lit-red .cockpit-dot { background: var(--red); box-shadow: 0 0 6px var(--red); }
|
|
694
726
|
.cockpit-value {
|
|
695
|
-
font-size:
|
|
727
|
+
font-size: var(--text-stat);
|
|
696
728
|
font-weight: 700;
|
|
697
729
|
color: var(--text);
|
|
698
730
|
line-height: 1;
|
|
699
731
|
}
|
|
700
732
|
.cockpit-value.dim { color: var(--muted); }
|
|
701
733
|
.cockpit-detail {
|
|
702
|
-
font-size:
|
|
734
|
+
font-size: var(--text-base);
|
|
703
735
|
color: var(--muted);
|
|
704
736
|
line-height: 1.3;
|
|
705
737
|
min-height: 14px;
|
|
@@ -726,7 +758,7 @@
|
|
|
726
758
|
.member-empty {
|
|
727
759
|
grid-column: 1 / -1;
|
|
728
760
|
color: var(--muted);
|
|
729
|
-
font-size:
|
|
761
|
+
font-size: var(--text-md);
|
|
730
762
|
font-style: italic;
|
|
731
763
|
}
|
|
732
764
|
.member-card {
|
|
@@ -750,9 +782,10 @@
|
|
|
750
782
|
.member-card.working { border-color: rgba(210, 153, 34, 0.6); }
|
|
751
783
|
.member-card.done { border-color: rgba(63, 185, 80, 0.6); }
|
|
752
784
|
.member-card.error { border-color: rgba(248, 81, 73, 0.6); }
|
|
753
|
-
|
|
785
|
+
/* Rounded from 24px → 22px (--text-stat) in W-mq1c8og40003a7cf typography centralization. Emoji glyph; 2px diff is visually negligible. */
|
|
786
|
+
.member-emoji { font-size: var(--text-stat); line-height: 1; }
|
|
754
787
|
.member-name {
|
|
755
|
-
font-size:
|
|
788
|
+
font-size: var(--text-md);
|
|
756
789
|
font-weight: 600;
|
|
757
790
|
line-height: 1.2;
|
|
758
791
|
color: var(--text);
|
|
@@ -762,7 +795,7 @@
|
|
|
762
795
|
white-space: nowrap;
|
|
763
796
|
}
|
|
764
797
|
.member-role {
|
|
765
|
-
font-size:
|
|
798
|
+
font-size: var(--text-base);
|
|
766
799
|
line-height: 1.2;
|
|
767
800
|
color: var(--muted);
|
|
768
801
|
max-width: 100%;
|
|
@@ -774,7 +807,7 @@
|
|
|
774
807
|
status-badge but tuned to the slim token palette. */
|
|
775
808
|
.member-status {
|
|
776
809
|
margin-top: 2px;
|
|
777
|
-
font-size:
|
|
810
|
+
font-size: var(--text-base);
|
|
778
811
|
font-weight: 700;
|
|
779
812
|
letter-spacing: 0.5px;
|
|
780
813
|
text-transform: uppercase;
|
|
@@ -812,19 +845,21 @@
|
|
|
812
845
|
gap: 12px;
|
|
813
846
|
margin-bottom: 14px;
|
|
814
847
|
}
|
|
815
|
-
|
|
816
|
-
.agent-detail-
|
|
817
|
-
|
|
848
|
+
/* Rounded from 34px → 32px (--text-display) in W-mq1c8og40003a7cf typography centralization. Emoji glyph; 2px diff is visually negligible. */
|
|
849
|
+
.agent-detail-emoji { font-size: var(--text-display); line-height: 1; }
|
|
850
|
+
/* Rounded from 17px → 18px (--text-2xl) in W-mq1c8og40003a7cf typography centralization. */
|
|
851
|
+
.agent-detail-name { font-size: var(--text-2xl); font-weight: 600; color: var(--text); }
|
|
852
|
+
.agent-detail-role { font-size: var(--text-md); color: var(--muted); }
|
|
818
853
|
.agent-detail-row { margin-bottom: 12px; }
|
|
819
854
|
.agent-detail-key {
|
|
820
|
-
font-size:
|
|
855
|
+
font-size: var(--text-base);
|
|
821
856
|
font-weight: 700;
|
|
822
857
|
letter-spacing: 0.5px;
|
|
823
858
|
text-transform: uppercase;
|
|
824
859
|
color: var(--muted);
|
|
825
860
|
margin-bottom: 3px;
|
|
826
861
|
}
|
|
827
|
-
.agent-detail-val { font-size:
|
|
862
|
+
.agent-detail-val { font-size: var(--text-md); color: var(--text); white-space: pre-wrap; word-break: break-word; }
|
|
828
863
|
.agent-detail-val.muted { color: var(--muted); font-style: italic; }
|
|
829
864
|
|
|
830
865
|
/* ── History panel: event log ─────────────────────────────── */
|
|
@@ -851,27 +886,27 @@
|
|
|
851
886
|
justify-content: space-between;
|
|
852
887
|
align-items: baseline;
|
|
853
888
|
gap: 8px;
|
|
854
|
-
font-size:
|
|
889
|
+
font-size: var(--text-md);
|
|
855
890
|
}
|
|
856
891
|
.history-kind {
|
|
857
892
|
font-weight: 700;
|
|
858
893
|
letter-spacing: 0.4px;
|
|
859
894
|
text-transform: uppercase;
|
|
860
|
-
font-size:
|
|
895
|
+
font-size: var(--text-base);
|
|
861
896
|
color: var(--muted);
|
|
862
897
|
}
|
|
863
898
|
.history-time {
|
|
864
|
-
font-size:
|
|
899
|
+
font-size: var(--text-base);
|
|
865
900
|
color: var(--muted);
|
|
866
901
|
white-space: nowrap;
|
|
867
902
|
}
|
|
868
903
|
.history-title {
|
|
869
|
-
font-size:
|
|
904
|
+
font-size: var(--text-md);
|
|
870
905
|
color: var(--text);
|
|
871
906
|
word-break: break-word;
|
|
872
907
|
}
|
|
873
908
|
.history-meta {
|
|
874
|
-
font-size:
|
|
909
|
+
font-size: var(--text-base);
|
|
875
910
|
color: var(--muted);
|
|
876
911
|
word-break: break-word;
|
|
877
912
|
}
|
|
@@ -879,7 +914,7 @@
|
|
|
879
914
|
.history-empty {
|
|
880
915
|
color: var(--muted);
|
|
881
916
|
font-style: italic;
|
|
882
|
-
font-size:
|
|
917
|
+
font-size: var(--text-md);
|
|
883
918
|
}
|
|
884
919
|
|
|
885
920
|
/* ╔══════════════════════════════════════════════════════════════════╗
|
|
@@ -941,7 +976,7 @@
|
|
|
941
976
|
justify-content: center;
|
|
942
977
|
gap: 4px;
|
|
943
978
|
color: var(--muted);
|
|
944
|
-
font-size:
|
|
979
|
+
font-size: var(--text-base);
|
|
945
980
|
}
|
|
946
981
|
.completions-card-body-top {
|
|
947
982
|
/* parent grid cell (col 2, row 1) — wraps title and optional
|
|
@@ -958,7 +993,7 @@
|
|
|
958
993
|
relative time so the rail's bottom row reads as a single quiet line:
|
|
959
994
|
`✓ · 8h ago`. */
|
|
960
995
|
.completions-card-status-icon {
|
|
961
|
-
font-size:
|
|
996
|
+
font-size: var(--text-base);
|
|
962
997
|
line-height: 1;
|
|
963
998
|
color: var(--muted);
|
|
964
999
|
}
|
|
@@ -1036,7 +1071,7 @@
|
|
|
1036
1071
|
but a larger font so it reads as the rail's primary type marker. */
|
|
1037
1072
|
.completions-card-type-word {
|
|
1038
1073
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
1039
|
-
font-size:
|
|
1074
|
+
font-size: var(--text-lg);
|
|
1040
1075
|
font-weight: 700;
|
|
1041
1076
|
letter-spacing: 0.5px;
|
|
1042
1077
|
line-height: 1;
|
|
@@ -1049,12 +1084,12 @@
|
|
|
1049
1084
|
cursor: help;
|
|
1050
1085
|
}
|
|
1051
1086
|
.completions-card-time {
|
|
1052
|
-
font-size:
|
|
1087
|
+
font-size: var(--text-base);
|
|
1053
1088
|
color: var(--muted);
|
|
1054
1089
|
white-space: nowrap;
|
|
1055
1090
|
}
|
|
1056
1091
|
.completions-card-title {
|
|
1057
|
-
font-size:
|
|
1092
|
+
font-size: var(--text-xl);
|
|
1058
1093
|
color: var(--text);
|
|
1059
1094
|
line-height: 1.35;
|
|
1060
1095
|
/* Always reserve two lines so cards have a consistent vertical
|
|
@@ -1070,7 +1105,7 @@
|
|
|
1070
1105
|
/* Byline — "Agent · Role" under the title. Quiet (muted, small) so the
|
|
1071
1106
|
title stays primary, but always present so every row names its owner. */
|
|
1072
1107
|
.completions-card-byline {
|
|
1073
|
-
font-size:
|
|
1108
|
+
font-size: var(--text-base);
|
|
1074
1109
|
color: var(--muted);
|
|
1075
1110
|
white-space: nowrap;
|
|
1076
1111
|
overflow: hidden;
|
|
@@ -1078,7 +1113,7 @@
|
|
|
1078
1113
|
min-width: 0;
|
|
1079
1114
|
}
|
|
1080
1115
|
.completions-card-fail-line {
|
|
1081
|
-
font-size:
|
|
1116
|
+
font-size: var(--text-base);
|
|
1082
1117
|
color: var(--red);
|
|
1083
1118
|
word-break: break-word;
|
|
1084
1119
|
}
|
|
@@ -1092,7 +1127,7 @@
|
|
|
1092
1127
|
above the chips is supplied by the parent card grid's row gap. */
|
|
1093
1128
|
.completions-card-lineage {
|
|
1094
1129
|
grid-column: 2; /* cell 2,2 of the parent card grid */
|
|
1095
|
-
font-size:
|
|
1130
|
+
font-size: var(--text-base);
|
|
1096
1131
|
color: var(--muted);
|
|
1097
1132
|
display: grid;
|
|
1098
1133
|
grid-template-columns: 1fr 1fr 1fr;
|
|
@@ -1101,7 +1136,7 @@
|
|
|
1101
1136
|
}
|
|
1102
1137
|
.completions-card-chip {
|
|
1103
1138
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
1104
|
-
font-size:
|
|
1139
|
+
font-size: var(--text-base);
|
|
1105
1140
|
background: var(--bg);
|
|
1106
1141
|
border: 1px solid var(--border);
|
|
1107
1142
|
border-radius: 3px;
|
|
@@ -1146,7 +1181,7 @@
|
|
|
1146
1181
|
.completions-modal-section { margin-bottom: 14px; }
|
|
1147
1182
|
.completions-modal-section:last-child { margin-bottom: 0; }
|
|
1148
1183
|
.completions-modal-label {
|
|
1149
|
-
font-size:
|
|
1184
|
+
font-size: var(--text-base);
|
|
1150
1185
|
font-weight: 700;
|
|
1151
1186
|
letter-spacing: 0.4px;
|
|
1152
1187
|
text-transform: uppercase;
|
|
@@ -1154,14 +1189,14 @@
|
|
|
1154
1189
|
margin-bottom: 4px;
|
|
1155
1190
|
}
|
|
1156
1191
|
.completions-modal-value {
|
|
1157
|
-
font-size:
|
|
1192
|
+
font-size: var(--text-md);
|
|
1158
1193
|
color: var(--text);
|
|
1159
1194
|
word-break: break-word;
|
|
1160
1195
|
white-space: pre-wrap;
|
|
1161
1196
|
}
|
|
1162
1197
|
.completions-modal-report {
|
|
1163
1198
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
1164
|
-
font-size:
|
|
1199
|
+
font-size: var(--text-base);
|
|
1165
1200
|
background: var(--bg);
|
|
1166
1201
|
border: 1px solid var(--border);
|
|
1167
1202
|
border-radius: var(--radius);
|
|
@@ -1209,9 +1244,9 @@
|
|
|
1209
1244
|
padding: 12px 16px;
|
|
1210
1245
|
border-bottom: 1px solid var(--border);
|
|
1211
1246
|
}
|
|
1212
|
-
.modal-header h3 { margin: 0; font-size:
|
|
1247
|
+
.modal-header h3 { margin: 0; font-size: var(--text-xl); color: var(--blue); }
|
|
1213
1248
|
.modal-body { padding: 14px 18px; overflow-y: auto; }
|
|
1214
|
-
.modal-body p { color: var(--muted); margin: 4px 0 12px; font-size:
|
|
1249
|
+
.modal-body p { color: var(--muted); margin: 4px 0 12px; font-size: var(--text-md); }
|
|
1215
1250
|
.modal-footer {
|
|
1216
1251
|
padding: 10px 16px;
|
|
1217
1252
|
border-top: 1px solid var(--border);
|
|
@@ -1226,7 +1261,7 @@
|
|
|
1226
1261
|
border: none;
|
|
1227
1262
|
border-radius: var(--radius);
|
|
1228
1263
|
cursor: pointer;
|
|
1229
|
-
font-size:
|
|
1264
|
+
font-size: var(--text-md);
|
|
1230
1265
|
font-weight: 600;
|
|
1231
1266
|
}
|
|
1232
1267
|
.btn-secondary {
|
|
@@ -1236,7 +1271,7 @@
|
|
|
1236
1271
|
border: 1px solid var(--border);
|
|
1237
1272
|
border-radius: var(--radius);
|
|
1238
1273
|
cursor: pointer;
|
|
1239
|
-
font-size:
|
|
1274
|
+
font-size: var(--text-md);
|
|
1240
1275
|
}
|
|
1241
1276
|
.btn-primary:hover, .btn-secondary:hover { filter: brightness(1.1); }
|
|
1242
1277
|
|
|
@@ -1248,9 +1283,9 @@
|
|
|
1248
1283
|
border-bottom: 1px solid var(--border);
|
|
1249
1284
|
}
|
|
1250
1285
|
.flag-row:last-child { border-bottom: none; }
|
|
1251
|
-
.flag-name { font-size:
|
|
1252
|
-
.flag-desc { font-size:
|
|
1253
|
-
.flag-toggle { display: flex; align-items: center; gap: 8px; font-size:
|
|
1286
|
+
.flag-name { font-size: var(--text-lg); font-weight: 600; }
|
|
1287
|
+
.flag-desc { font-size: var(--text-base); color: var(--muted); margin-top: 2px; }
|
|
1288
|
+
.flag-toggle { display: flex; align-items: center; gap: 8px; font-size: var(--text-md); }
|
|
1254
1289
|
.flag-toggle input { cursor: pointer; }
|
|
1255
1290
|
.settings-link {
|
|
1256
1291
|
display: block;
|
|
@@ -1261,7 +1296,7 @@
|
|
|
1261
1296
|
border-radius: var(--radius);
|
|
1262
1297
|
color: var(--blue);
|
|
1263
1298
|
text-decoration: none;
|
|
1264
|
-
font-size:
|
|
1299
|
+
font-size: var(--text-md);
|
|
1265
1300
|
text-align: center;
|
|
1266
1301
|
}
|
|
1267
1302
|
.settings-link:hover { filter: brightness(1.1); }
|
|
@@ -1274,7 +1309,7 @@
|
|
|
1274
1309
|
color: var(--text);
|
|
1275
1310
|
border: 1px solid var(--border);
|
|
1276
1311
|
border-radius: var(--radius);
|
|
1277
|
-
font-size:
|
|
1312
|
+
font-size: var(--text-lg);
|
|
1278
1313
|
font-family: inherit;
|
|
1279
1314
|
resize: vertical;
|
|
1280
1315
|
}
|
package/dashboard/styles.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[data-font-size] on <html>. 'small' is the historic default (1.0) so
|
|
4
4
|
existing users see no change. Body-level CSS `zoom` is the cheapest
|
|
5
5
|
way to scale every px/em/rem rule across the SPA (typography tokens
|
|
6
|
-
below, slim.html, and the many inline
|
|
6
|
+
below, slim.html, and the many inline font-size Npx styles) without
|
|
7
7
|
rewriting every rule. Scales modals, drawers, and fixed-position
|
|
8
8
|
elements because they all live inside <body>. */
|
|
9
9
|
--minions-font-scale: 1;
|
|
@@ -15,11 +15,30 @@
|
|
|
15
15
|
--space-1: 2px; --space-2: 4px; --space-3: 6px; --space-4: 8px;
|
|
16
16
|
--space-5: 10px; --space-6: 12px; --space-7: 16px; --space-8: 20px; --space-9: 24px;
|
|
17
17
|
|
|
18
|
-
/* Typography
|
|
18
|
+
/* Typography — size primitives (raw px). Source of truth for the
|
|
19
|
+
~700 callsites migrated in PR #66; do NOT introduce new raw `Npx`
|
|
20
|
+
font-sizes outside this block. Tripwire:
|
|
21
|
+
test/unit/dashboard-font-size-tokens.test.js */
|
|
19
22
|
--text-xs: 10px; --text-sm: 11px; --text-base: 12px;
|
|
20
23
|
--text-md: 13px; --text-lg: 14px; --text-xl: 16px; --text-2xl: 18px;
|
|
21
24
|
--text-stat: 22px; --text-stat-lg: 28px; --text-display: 32px;
|
|
22
25
|
|
|
26
|
+
/* Typography — role aliases (W-mq1c8og40003a7cf). Prefer these for
|
|
27
|
+
NEW code: the role names survive a size-scale redesign while the
|
|
28
|
+
primitives do not. Existing callsites that still reference the
|
|
29
|
+
size primitives directly are kept (they're correct by
|
|
30
|
+
construction — every role alias is just an indirection). See
|
|
31
|
+
dashboard/docs/typography.md for the role → primitive map and
|
|
32
|
+
intended use. */
|
|
33
|
+
--text-role-display: var(--text-display); /* 32px — hero / page title */
|
|
34
|
+
--text-heading: var(--text-2xl); /* 18px — section + modal headers */
|
|
35
|
+
--text-subheading: var(--text-xl); /* 16px — secondary headers, card titles */
|
|
36
|
+
--text-body: var(--text-lg); /* 14px — default body text */
|
|
37
|
+
--text-meta: var(--text-md); /* 13px — captions, timestamps, secondary metadata */
|
|
38
|
+
--text-caption: var(--text-base); /* 12px — small labels, table cells */
|
|
39
|
+
--text-micro: var(--text-sm); /* 11px — chip / tag pill labels */
|
|
40
|
+
--text-code: 0.9em; /* inline code; relative so it scales with the surrounding text */
|
|
41
|
+
|
|
23
42
|
/* Border radius */
|
|
24
43
|
--radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px; --radius-xl: 10px; --radius-full: 50%;
|
|
25
44
|
|
|
@@ -43,6 +62,21 @@
|
|
|
43
62
|
html, body { height: 100%; margin: 0; overflow: hidden; }
|
|
44
63
|
body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; font-size: var(--text-xl); display: flex; flex-direction: column; zoom: var(--minions-font-scale, 1); }
|
|
45
64
|
|
|
65
|
+
/* Typography utility classes (W-mq1c8og40003a7cf).
|
|
66
|
+
One class per role token. Apply directly on any element
|
|
67
|
+
(`<span class="text-meta">`) so JS/HTML callsites don't need to
|
|
68
|
+
hand-roll inline `style="font-size:var(--text-*)"`. The classes
|
|
69
|
+
ONLY set font-size — weight, color, line-height stay with the
|
|
70
|
+
caller. Mirrors the `.btn-add` consolidation in spirit. */
|
|
71
|
+
.text-display { font-size: var(--text-role-display); }
|
|
72
|
+
.text-heading { font-size: var(--text-heading); }
|
|
73
|
+
.text-subheading { font-size: var(--text-subheading); }
|
|
74
|
+
.text-body { font-size: var(--text-body); }
|
|
75
|
+
.text-meta { font-size: var(--text-meta); }
|
|
76
|
+
.text-caption { font-size: var(--text-caption); }
|
|
77
|
+
.text-micro { font-size: var(--text-micro); }
|
|
78
|
+
.text-code { font-size: var(--text-code); }
|
|
79
|
+
|
|
46
80
|
header {
|
|
47
81
|
background: var(--surface); border-bottom: 1px solid var(--border);
|
|
48
82
|
padding: var(--space-6) 14px; display: flex; align-items: center; justify-content: space-between;
|
|
@@ -240,7 +274,7 @@
|
|
|
240
274
|
.prd-item-row.st-needs-human-review { border-left-color: var(--orange); }
|
|
241
275
|
.prd-item-row.st-updated { border-left-color: var(--purple); }
|
|
242
276
|
.prd-item-row.st-paused { border-left-color: var(--muted); opacity: 0.5; }
|
|
243
|
-
.prd-item-id { font-family: Consolas, monospace; color: var(--muted); min-width: 36px; font-size:
|
|
277
|
+
.prd-item-id { font-family: Consolas, monospace; color: var(--muted); min-width: 36px; font-size: var(--text-code); }
|
|
244
278
|
.prd-item-name { flex: 1; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
245
279
|
.prd-item-priority { font-size: var(--text-sm); padding: var(--space-1) var(--space-3); border-radius: var(--radius-lg); }
|
|
246
280
|
.prd-item-priority.high { background: rgba(248,81,73,0.15); color: var(--red); }
|
package/engine/cli.js
CHANGED
|
@@ -1304,15 +1304,17 @@ const commands = {
|
|
|
1304
1304
|
console.log(`Engine: ${control.state} (PID ${control.pid || 'N/A'})`);
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
|
-
// Dashboard check
|
|
1307
|
+
// Dashboard check. Honors MINIONS_PORT so `minions --dev status` probes the
|
|
1308
|
+
// dev dashboard (7332 by default) rather than the binary install's 7331.
|
|
1308
1309
|
const http = require('http');
|
|
1310
|
+
const dashPort = Number(process.env.MINIONS_PORT) || 7331;
|
|
1309
1311
|
const dashCheck = new Promise(resolve => {
|
|
1310
|
-
const req = http.get(
|
|
1312
|
+
const req = http.get(`http://localhost:${dashPort}/api/health`, { timeout: 2000 }, () => resolve(true));
|
|
1311
1313
|
req.on('error', () => resolve(false));
|
|
1312
1314
|
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
1313
1315
|
});
|
|
1314
1316
|
dashCheck.then(dashUp => {
|
|
1315
|
-
if (dashUp) console.log(
|
|
1317
|
+
if (dashUp) console.log(`Dashboard: running (http://localhost:${dashPort})`);
|
|
1316
1318
|
else console.log('Dashboard: not running — start with: minions dash');
|
|
1317
1319
|
}).catch(() => {});
|
|
1318
1320
|
|
package/engine/dispatch.js
CHANGED
|
@@ -18,6 +18,18 @@ const { getConfig, INBOX_DIR } = queries;
|
|
|
18
18
|
|
|
19
19
|
const MINIONS_DIR = shared.MINIONS_DIR;
|
|
20
20
|
|
|
21
|
+
// Dispatch types that do not push code to a PR branch. These remain dispatchable
|
|
22
|
+
// against `_contextOnly: true` PRs because casting a review vote, posting a
|
|
23
|
+
// comment, asking a question, or running a read-only explore never mutates the
|
|
24
|
+
// PR's source branch. Auto-discovery (engine.js#discoverFromPrs) is still
|
|
25
|
+
// gated on `_contextOnly` separately — only explicit WIs (dashboard, watches,
|
|
26
|
+
// CC) can target a context-only PR, and only via these types.
|
|
27
|
+
const NON_MUTATING_DISPATCH_TYPES = new Set([
|
|
28
|
+
WORK_TYPE.REVIEW,
|
|
29
|
+
WORK_TYPE.ASK,
|
|
30
|
+
WORK_TYPE.EXPLORE,
|
|
31
|
+
]);
|
|
32
|
+
|
|
21
33
|
// Lazy require to break circular dependency with engine.js
|
|
22
34
|
let _lifecycle = null;
|
|
23
35
|
function lifecycle() { if (!_lifecycle) _lifecycle = require('./lifecycle'); return _lifecycle; }
|
|
@@ -358,7 +370,19 @@ function getStalePrDispatchReason(entry, config) {
|
|
|
358
370
|
const prLabel = entry.meta.pr?.id || entry.meta.pr?.url || entry.id;
|
|
359
371
|
if (!tracked) return `PR ${prLabel} is no longer tracked`;
|
|
360
372
|
if (tracked.status !== PR_STATUS.ACTIVE) return `PR ${tracked.id || prLabel} is ${tracked.status || 'missing status'}`;
|
|
361
|
-
|
|
373
|
+
// Combined gate (W-mq5v612m001g1545 + #3126):
|
|
374
|
+
// 1. `entry.meta?.source !== 'work-item'` — blocks any non-work-item dispatch
|
|
375
|
+
// (e.g. legacy `pr`/`pr-human-feedback` discovery paths) from running
|
|
376
|
+
// against a context-only PR. Auto-discovery in engine.js#discoverFromPrs
|
|
377
|
+
// already skips _contextOnly PRs at the source, so this is defense-in-depth.
|
|
378
|
+
// 2. `!NON_MUTATING_DISPATCH_TYPES.has(entry.type)` — even an explicit
|
|
379
|
+
// work-item dispatch is dropped if it's a mutating type (fix, implement,
|
|
380
|
+
// test, verify, decompose, docs). Only review/ask/explore — which never
|
|
381
|
+
// push to the PR's source branch — are allowed through.
|
|
382
|
+
if (tracked._contextOnly
|
|
383
|
+
&& (entry.meta?.source !== 'work-item' || !NON_MUTATING_DISPATCH_TYPES.has(entry.type))) {
|
|
384
|
+
return `PR ${tracked.id || prLabel} is context-only`;
|
|
385
|
+
}
|
|
362
386
|
|
|
363
387
|
const queuedBranch = entry.meta.branch || entry.meta.pr?.branch || '';
|
|
364
388
|
const trackedBranch = tracked.branch || '';
|
|
@@ -1048,4 +1072,5 @@ module.exports = {
|
|
|
1048
1072
|
findActivePrOrBranchLock,
|
|
1049
1073
|
normalizeRetryableDecision,
|
|
1050
1074
|
isCompletedWorkItemForFailure,
|
|
1075
|
+
NON_MUTATING_DISPATCH_TYPES,
|
|
1051
1076
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2151",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
"package:check": "node tools/check-package-boundary.js public",
|
|
10
10
|
"package:prepare:public": "node tools/prepare-package.js public",
|
|
11
11
|
"package:prepare:internal": "node tools/prepare-package.js internal",
|
|
12
|
+
"dev": "node bin/minions.js --dev restart",
|
|
13
|
+
"dev:start": "node bin/minions.js --dev start",
|
|
14
|
+
"dev:stop": "node bin/minions.js --dev stop",
|
|
15
|
+
"dev:dash": "node bin/minions.js --dev dash",
|
|
16
|
+
"dev:status": "node bin/minions.js --dev status",
|
|
12
17
|
"test": "node test/run-parallel.js",
|
|
13
18
|
"test:sequential": "node test/unit.test.js",
|
|
14
19
|
"test:unit": "node test/run-parallel.js",
|