@azure-id/orc 1.4.0 → 1.4.2
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 +229 -0
- package/README-id.md +39 -2
- package/README.md +53 -63
- package/bin/cli.js +212 -15
- package/bin/webui/api.js +16 -0
- package/bin/webui/css/04-motion.css +61 -0
- package/bin/webui/css/06-responsive.css +17 -1
- package/bin/webui/css/panels/hookui.css +493 -98
- package/bin/webui/fixtures/hookui.js +431 -409
- package/bin/webui/i18n/en/hookui.json +119 -44
- package/bin/webui/i18n/en/tour.json +1 -1
- package/bin/webui/i18n/id/hookui.json +137 -62
- package/bin/webui/i18n/id/tour.json +1 -1
- package/bin/webui/js/panels/hookui.js +1266 -466
- package/package.json +1 -1
- package/templates/hooks/README.md +8 -1
- package/templates/hooks/orc-statusline.js +15 -4
- package/templates/hooks/orc-subagent-line.js +5 -2
package/bin/cli.js
CHANGED
|
@@ -34405,6 +34405,43 @@ const STATUSLINE_RENDERERS = {
|
|
|
34405
34405
|
pulse: { kind: "motif", sub: "pulse", needs: "motif" },
|
|
34406
34406
|
};
|
|
34407
34407
|
|
|
34408
|
+
// WHICH RENDERERS DRAW A LABEL. Seven of thirty-five, and the other
|
|
34409
|
+
// twenty-eight ignore `label` entirely — a bar with a word in front of it is a
|
|
34410
|
+
// different renderer (`plain`), which is the design. That was invisible in the
|
|
34411
|
+
// panel: the Name box was offered on every shape, took the text, wrote it to
|
|
34412
|
+
// disk correctly, and then nothing appeared on the bar. A control that cannot
|
|
34413
|
+
// do anything must SAY so.
|
|
34414
|
+
//
|
|
34415
|
+
// It is a mirror of the switch in `slLowerItem`, and a golden test reads that
|
|
34416
|
+
// switch and compares — the `DIY_STEPS` shape. Adding a `pushLabel` branch
|
|
34417
|
+
// without adding the name here fails the test rather than shipping a control
|
|
34418
|
+
// that lies.
|
|
34419
|
+
const SL_LABEL_RENDERERS = new Set(["plain", "label-value", "bracket", "angle", "badge", "pill", "stack"]);
|
|
34420
|
+
|
|
34421
|
+
// The item fields a renderer CONSUMES. Everything else on the chip is ignored
|
|
34422
|
+
// by that shape, and the panel greys it out with this as the reason.
|
|
34423
|
+
//
|
|
34424
|
+
// The split is the compiler's own: only a `text` (and `link`) renderer lowers
|
|
34425
|
+
// the FORMAT — `case`, `prefix`, `suffix`, `format`, `compact`, `min_width`,
|
|
34426
|
+
// `precision`, `truncate` all ride inside it — while a bar takes `width`,
|
|
34427
|
+
// `ramp` and `threshold`, a series takes `width`, and a state or a motif takes
|
|
34428
|
+
// neither. Colour and emphasis apply everywhere, so they are never listed as
|
|
34429
|
+
// unusable.
|
|
34430
|
+
function slRendererUses(name) {
|
|
34431
|
+
const rend = STATUSLINE_RENDERERS[name];
|
|
34432
|
+
if (!rend) return [];
|
|
34433
|
+
const out = [];
|
|
34434
|
+
if (SL_LABEL_RENDERERS.has(name)) out.push("label");
|
|
34435
|
+
if (rend.kind === "text" || rend.kind === "link")
|
|
34436
|
+
out.push("case", "prefix", "suffix", "format", "compact", "min_width", "precision", "truncate");
|
|
34437
|
+
if (rend.width) out.push("width");
|
|
34438
|
+
if (rend.kind === "bar" || name === "bar") {
|
|
34439
|
+
out.push("ramp");
|
|
34440
|
+
if (!out.includes("width")) out.push("width");
|
|
34441
|
+
}
|
|
34442
|
+
return out;
|
|
34443
|
+
}
|
|
34444
|
+
|
|
34408
34445
|
// ── The glyph sets (design-language.md §2) ──────────────────────────────────
|
|
34409
34446
|
// R1: every glyph here is EXACTLY one column in a monospaced terminal, and a
|
|
34410
34447
|
// test walks all of them. Fullwidth forms, bare emoji and combining marks are
|
|
@@ -34533,6 +34570,38 @@ const STATUSLINE_HIDE_WHEN = [
|
|
|
34533
34570
|
{ id: "wide", says: "hide above this component's max_cols" },
|
|
34534
34571
|
];
|
|
34535
34572
|
|
|
34573
|
+
// The separators a line can put BETWEEN its parts. A free-text box asked the
|
|
34574
|
+
// user to invent a rhythm and to type a character their keyboard may not have;
|
|
34575
|
+
// this is the closed set, and the panel renders it as a dropdown because the
|
|
34576
|
+
// panel names none of these itself. `value` is what is written into the layout,
|
|
34577
|
+
// `name` is what a person reads. A layout may still carry any string — a value
|
|
34578
|
+
// outside this set is kept and offered back, the way an unset `fixed_executor`
|
|
34579
|
+
// leads its own dropdown.
|
|
34580
|
+
const SL_SEPARATORS = [
|
|
34581
|
+
{ value: " · ", name: "middle dot" },
|
|
34582
|
+
{ value: " ", name: "one space" },
|
|
34583
|
+
{ value: " ", name: "two spaces" },
|
|
34584
|
+
{ value: " │ ", name: "upright line" },
|
|
34585
|
+
{ value: " — ", name: "long dash" },
|
|
34586
|
+
{ value: " - ", name: "hyphen" },
|
|
34587
|
+
{ value: " / ", name: "slash" },
|
|
34588
|
+
{ value: " • ", name: "bullet" },
|
|
34589
|
+
{ value: " ▸ ", name: "small arrow" },
|
|
34590
|
+
{ value: " » ", name: "double arrow" },
|
|
34591
|
+
{ value: ", ", name: "comma" },
|
|
34592
|
+
{ value: " | ", name: "pipe" },
|
|
34593
|
+
];
|
|
34594
|
+
|
|
34595
|
+
// What each colour set is FOR. The names are the CLI's own and are never
|
|
34596
|
+
// translated; this sentence is why a person would pick one, and without it the
|
|
34597
|
+
// picker is four words with no meaning.
|
|
34598
|
+
const SL_THEME_ABOUT = {
|
|
34599
|
+
terminal: "the default: your terminal's own colours, one accent",
|
|
34600
|
+
dim: "everything quiet — labels and values both greyed",
|
|
34601
|
+
"high-contrast": "the brightest slot of every colour, for a pale terminal",
|
|
34602
|
+
mono: "no colour at all; weight and shape carry every state",
|
|
34603
|
+
};
|
|
34604
|
+
|
|
34536
34605
|
const SL_FORMATS = ["percent", "ratio", "fraction", "decimal", "plain"];
|
|
34537
34606
|
const SL_COMPACT = ["off", "si", "bytes"];
|
|
34538
34607
|
const SL_CASES = ["none", "upper", "lower", "title"];
|
|
@@ -34787,6 +34856,15 @@ const SL_BY_ID = new Map(STATUSLINE_COMPONENTS.map((c) => [c.id, c]));
|
|
|
34787
34856
|
// compiler, the IR, every renderer, the glyph sets, the colour model and the
|
|
34788
34857
|
// validator — what differs is the component set, the file names and the config
|
|
34789
34858
|
// key. A second compiler is exactly what this table exists to prevent.
|
|
34859
|
+
// HOW MUCH ONE LINE MAY SAY (v1.4.2). Six, not five. The limit is about
|
|
34860
|
+
// reading, not about rendering: a line is scanned at a glance, and past about
|
|
34861
|
+
// half a dozen facts the glance becomes a read. Six is where people actually
|
|
34862
|
+
// stopped asking for one more — and it is ONE number, here, because a slot cap
|
|
34863
|
+
// spelled out at each of its five call sites is a cap that drifts on the first
|
|
34864
|
+
// change. A structural part (a spacer, a divider, a fill) is not a thing the
|
|
34865
|
+
// line SAYS and still does not count against it.
|
|
34866
|
+
const SL_MAX_PER_LINE = 6;
|
|
34867
|
+
|
|
34790
34868
|
const SL_BOARDS = {
|
|
34791
34869
|
status: {
|
|
34792
34870
|
id: "status",
|
|
@@ -34973,7 +35051,7 @@ function slValidate(layout) {
|
|
|
34973
35051
|
E(`line ${i + 1} holds ${rawCounts[i]} component${rawCounts[i] === 1 ? "" : "s"} but line ${i} is empty — fill line ${i} first`);
|
|
34974
35052
|
}
|
|
34975
35053
|
layout.lines.forEach((l, i) => {
|
|
34976
|
-
if (counts[i] >
|
|
35054
|
+
if (counts[i] > SL_MAX_PER_LINE) E(`line ${i + 1} holds ${counts[i]} components (max ${SL_MAX_PER_LINE}) — remove one`);
|
|
34977
35055
|
});
|
|
34978
35056
|
|
|
34979
35057
|
const seenIds = new Set();
|
|
@@ -35280,6 +35358,13 @@ function slCompile(layout) {
|
|
|
35280
35358
|
pad_r: (r.padding && r.padding[1]) || 0,
|
|
35281
35359
|
children,
|
|
35282
35360
|
};
|
|
35361
|
+
// WHETHER THIS ONE COUNTS AGAINST THE LINE'S CAP (v1.4.2). The hook's
|
|
35362
|
+
// cheap shape guard has to count the same way the validator does, and it
|
|
35363
|
+
// cannot: the compiled op carries an INSTANCE id, never a component type.
|
|
35364
|
+
// So the compiler — which is the one place that knows — says so. Without
|
|
35365
|
+
// it the guard counted spacers too, and a legal line of six plus a fill
|
|
35366
|
+
// fell back to the shipped lines with no visible reason.
|
|
35367
|
+
if (slIsStructural(item.type)) itemOp.s = 1;
|
|
35283
35368
|
if (r.min_cols) itemOp.min_cols = r.min_cols;
|
|
35284
35369
|
if (r.max_cols) itemOp.max_cols = r.max_cols;
|
|
35285
35370
|
ops.push(itemOp);
|
|
@@ -35343,6 +35428,13 @@ function slCompile(layout) {
|
|
|
35343
35428
|
providers: [...providers].sort(),
|
|
35344
35429
|
series: [...seriesWanted].sort(),
|
|
35345
35430
|
needs_refresh_interval: timeBased ? 5 : null,
|
|
35431
|
+
// THE CAP TRAVELS WITH THE PROGRAM (v1.4.2). A hook cannot call this CLI —
|
|
35432
|
+
// a status line re-renders on every keystroke — so before this it simply
|
|
35433
|
+
// held its own `5`, and raising the cap here left every legal six-part line
|
|
35434
|
+
// falling back to the shipped lines with the reason only in a ledger file.
|
|
35435
|
+
// The lock is already gated on `orc_version`, so a stale copy cannot
|
|
35436
|
+
// outlive the build that wrote it.
|
|
35437
|
+
max_per_line: SL_MAX_PER_LINE,
|
|
35346
35438
|
warnings: [],
|
|
35347
35439
|
};
|
|
35348
35440
|
return { compiled, lock };
|
|
@@ -36055,6 +36147,8 @@ function statusline() {
|
|
|
36055
36147
|
return slRemoveCmd(claudeDir, p);
|
|
36056
36148
|
case "line":
|
|
36057
36149
|
return slLineCmd(claudeDir, p);
|
|
36150
|
+
case "doc":
|
|
36151
|
+
return slDocCmd(claudeDir, p);
|
|
36058
36152
|
case "presets":
|
|
36059
36153
|
return slPresetsCmd(claudeDir);
|
|
36060
36154
|
case "apply":
|
|
@@ -36075,6 +36169,7 @@ function statusline() {
|
|
|
36075
36169
|
orc statusline move <from-line>:<pos> <to-line>:<pos>
|
|
36076
36170
|
orc statusline remove <line>:<pos>
|
|
36077
36171
|
orc statusline line <n> --separator "…" [--max-width N] [--theme T]
|
|
36172
|
+
orc statusline doc [--theme T] [--glyphs G] [--ansi auto|off] [--align-columns on|off]
|
|
36078
36173
|
orc statusline group <line>:<pos> <line>:<pos> [...] wrap 2-4 as one object
|
|
36079
36174
|
orc statusline expand <line>:<pos> a composite, or a group, back into its parts
|
|
36080
36175
|
orc statusline clone <line>:<pos>
|
|
@@ -36113,6 +36208,10 @@ function slComponentsCmd() {
|
|
|
36113
36208
|
binding: c.binding,
|
|
36114
36209
|
composite: c.composite || null,
|
|
36115
36210
|
time_based: !!c.time_based,
|
|
36211
|
+
// Whether this one eats a slot. A spacer is not a thing the line SAYS, so
|
|
36212
|
+
// it does not count against the five — and the panel cannot work that out
|
|
36213
|
+
// for itself without owning a second idea of the catalogue.
|
|
36214
|
+
structural: slIsStructural(c.id),
|
|
36116
36215
|
previews: slRendererPreviews(c),
|
|
36117
36216
|
};
|
|
36118
36217
|
out.push(row);
|
|
@@ -36126,7 +36225,12 @@ function slComponentsCmd() {
|
|
|
36126
36225
|
count: out.length,
|
|
36127
36226
|
groups: SL_GROUP_NAMES,
|
|
36128
36227
|
components: out,
|
|
36129
|
-
|
|
36228
|
+
// `uses` is the field list this shape actually consumes. The panel greys
|
|
36229
|
+
// out the controls it does not, rather than offering a Name box on a bar.
|
|
36230
|
+
renderers: Object.fromEntries(Object.entries(STATUSLINE_RENDERERS).map(([k, v]) => [k, { kind: v.kind, form: v.form || null, needs: v.needs, width: v.width || null, decoration: !!v.decoration, uses: slRendererUses(k) }])),
|
|
36231
|
+
// Which shapes DO draw a label, so a refusal can name them instead of
|
|
36232
|
+
// saying "not here".
|
|
36233
|
+
label_renderers: [...SL_LABEL_RENDERERS],
|
|
36130
36234
|
glyph_sets: Object.keys(STATUSLINE_GLYPHSETS),
|
|
36131
36235
|
ramps: Object.fromEntries(Object.entries(STATUSLINE_RAMPS).map(([k, v]) => [k, { stops: v.stops, colors: v.colors, why: v.why }])),
|
|
36132
36236
|
themes: STATUSLINE_THEMES,
|
|
@@ -36138,9 +36242,11 @@ function slComponentsCmd() {
|
|
|
36138
36242
|
emphasis: Object.keys(SL_EMPHASIS),
|
|
36139
36243
|
refused_emphasis: SL_REFUSED_EMPHASIS,
|
|
36140
36244
|
colors: Object.keys(SL_ANSI_SLOTS),
|
|
36245
|
+
separators: SL_SEPARATORS,
|
|
36246
|
+
themes_about: SL_THEME_ABOUT,
|
|
36141
36247
|
board: board.id,
|
|
36142
36248
|
boards: Object.keys(SL_BOARDS),
|
|
36143
|
-
max_per_line:
|
|
36249
|
+
max_per_line: SL_MAX_PER_LINE,
|
|
36144
36250
|
lines: board.lines,
|
|
36145
36251
|
config_key: board.key,
|
|
36146
36252
|
setting: board.setting,
|
|
@@ -36204,6 +36310,31 @@ function slLoadOrDefault(claudeDir) {
|
|
|
36204
36310
|
return slReadLayout(claudeDir, b) || slBuildPreset(b.id === "subagent" ? "agent-default" : "orc-default");
|
|
36205
36311
|
}
|
|
36206
36312
|
|
|
36313
|
+
// EVERY field a chip can carry, in the order the editor asks about them. It is
|
|
36314
|
+
// ONE list: `show --json` emits all of them, and the panel's editor reads them
|
|
36315
|
+
// back. A field that is settable and not listed here is a write with no way to
|
|
36316
|
+
// see it again, which is the v1.4.2 defect this exists to make impossible.
|
|
36317
|
+
const SL_ITEM_FIELDS = [
|
|
36318
|
+
"render", "label", "color", "label_color", "value_color", "bg", "ramp",
|
|
36319
|
+
"glyphs", "format", "case", "truncate", "compact", "prefix", "suffix",
|
|
36320
|
+
"emphasis", "hide_when", "width", "precision", "min_width", "min_cols",
|
|
36321
|
+
"max_cols", "priority", "max_len", "align", "draw_empty", "unknown",
|
|
36322
|
+
];
|
|
36323
|
+
// The ones whose absence is an empty LIST rather than a null — a control that
|
|
36324
|
+
// iterates a null throws, and a null here would read as "unknown" when it means
|
|
36325
|
+
// "nothing selected".
|
|
36326
|
+
const SL_ITEM_LIST_FIELDS = new Set(["emphasis", "hide_when"]);
|
|
36327
|
+
|
|
36328
|
+
function slAuthoredFields(it) {
|
|
36329
|
+
const out = {};
|
|
36330
|
+
for (const k of Object.keys(it || {})) {
|
|
36331
|
+
if (k === "id" || k === "type" || k === "children") continue;
|
|
36332
|
+
if (it[k] === null || it[k] === undefined) continue;
|
|
36333
|
+
out[k] = it[k];
|
|
36334
|
+
}
|
|
36335
|
+
return out;
|
|
36336
|
+
}
|
|
36337
|
+
|
|
36207
36338
|
function slShowCmd(claudeDir) {
|
|
36208
36339
|
const layout = slReadLayout(claudeDir);
|
|
36209
36340
|
const eff = layout || slBuildPreset("orc-default");
|
|
@@ -36221,23 +36352,29 @@ function slShowCmd(claudeDir) {
|
|
|
36221
36352
|
max_width: l.max_width || 0,
|
|
36222
36353
|
count: (l.items || []).length,
|
|
36223
36354
|
counted: (l.items || []).filter((it) => !slIsStructural(it.type)).length,
|
|
36224
|
-
full: (l.items || []).filter((it) => !slIsStructural(it.type)).length >=
|
|
36355
|
+
full: (l.items || []).filter((it) => !slIsStructural(it.type)).length >= SL_MAX_PER_LINE,
|
|
36356
|
+
// `--json is not a summary` (v0.49.1), found once more (v1.4.2). This
|
|
36357
|
+
// emitted TWELVE of the twenty-four fields a chip can carry, so a user who
|
|
36358
|
+
// set `case`, `prefix`, `min_cols` or `precision` got a write that landed on
|
|
36359
|
+
// disk and a panel that could never read it back — the editor reopened with
|
|
36360
|
+
// the control blank and the change looked like it had done nothing. Every
|
|
36361
|
+
// resolved field ships now, and `authored` carries the raw item so a
|
|
36362
|
+
// control can say which values are the USER'S rather than inherited.
|
|
36225
36363
|
items: (l.items || []).map((it, pi) => {
|
|
36226
36364
|
const r = slResolveItem(it, eff, l);
|
|
36227
|
-
|
|
36365
|
+
const row = {
|
|
36228
36366
|
pos: pi + 1,
|
|
36229
36367
|
id: it.id,
|
|
36230
36368
|
type: it.type,
|
|
36231
|
-
render: r ? r.render : it.render,
|
|
36232
|
-
label: r ? r.label : null,
|
|
36233
|
-
label_color: r ? r.label_color || null : null,
|
|
36234
|
-
value_color: r ? r.value_color || null : null,
|
|
36235
|
-
ramp: r ? r.ramp || null : null,
|
|
36236
|
-
emphasis: r ? r.emphasis || [] : [],
|
|
36237
|
-
hide_when: r ? r.hide_when || [] : [],
|
|
36238
|
-
unknown: r ? r.unknown : null,
|
|
36239
36369
|
known: SL_BY_ID.has(it.type),
|
|
36370
|
+
// WHAT THE USER SET, verbatim — never the resolution. A control that
|
|
36371
|
+
// cannot tell "you chose the theme's colour" from "you chose nothing"
|
|
36372
|
+
// cannot offer to clear it.
|
|
36373
|
+
authored: slAuthoredFields(it),
|
|
36240
36374
|
};
|
|
36375
|
+
for (const k of SL_ITEM_FIELDS) row[k] = r && r[k] !== undefined && r[k] !== null ? r[k] : SL_ITEM_LIST_FIELDS.has(k) ? [] : null;
|
|
36376
|
+
if (it.children) row.children = it.children.map((k) => ({ id: k.id, type: k.type, authored: slAuthoredFields(k) }));
|
|
36377
|
+
return row;
|
|
36241
36378
|
}),
|
|
36242
36379
|
}));
|
|
36243
36380
|
if (wantsJson())
|
|
@@ -36262,7 +36399,7 @@ function slShowCmd(claudeDir) {
|
|
|
36262
36399
|
console.log("");
|
|
36263
36400
|
for (const l of lines) {
|
|
36264
36401
|
const cells = l.items.map((i) => ui.color.cyan(i.type) + ui.color.gray("/" + i.render)).join(ui.color.gray(" · "));
|
|
36265
|
-
console.log(` ${ui.color.gray("line " + l.line)} ${cells || ui.color.gray("(empty)")} ${l.full ? ui.color.yellow("
|
|
36402
|
+
console.log(` ${ui.color.gray("line " + l.line)} ${cells || ui.color.gray("(empty)")} ${l.full ? ui.color.yellow(l.counted + "/" + SL_MAX_PER_LINE) : ui.color.gray(l.counted + "/" + SL_MAX_PER_LINE)}`);
|
|
36266
36403
|
}
|
|
36267
36404
|
if (preview) {
|
|
36268
36405
|
console.log("");
|
|
@@ -36318,7 +36455,17 @@ function slValidateCmd(claudeDir) {
|
|
|
36318
36455
|
}
|
|
36319
36456
|
|
|
36320
36457
|
function slPreviewCmd(claudeDir) {
|
|
36321
|
-
const
|
|
36458
|
+
const saved = slLoadOrDefault(claudeDir);
|
|
36459
|
+
// A RENDER-ONLY OVERRIDE (v1.4.2). `--theme` and `--glyphs` draw the SAVED
|
|
36460
|
+
// layout under a different colour set or symbol set WITHOUT writing one — so
|
|
36461
|
+
// the panel can show what each choice actually looks like instead of asking
|
|
36462
|
+
// somebody to pick a colour set from its name. It is the same engine and the
|
|
36463
|
+
// same layout; only the two document fields move, on a copy.
|
|
36464
|
+
const layout = JSON.parse(JSON.stringify(saved));
|
|
36465
|
+
const asTheme = flag("--theme");
|
|
36466
|
+
const asGlyphs = flag("--glyphs");
|
|
36467
|
+
if (typeof asTheme === "string" && STATUSLINE_THEMES[asTheme]) layout.theme = asTheme;
|
|
36468
|
+
if (typeof asGlyphs === "string" && STATUSLINE_GLYPHSETS[asGlyphs]) layout.glyphs = asGlyphs;
|
|
36322
36469
|
const v = slValidate(layout);
|
|
36323
36470
|
if (!v.ok) {
|
|
36324
36471
|
if (wantsJson()) return emitJson({ ok: false, reason: "invalid", errors: v.errors }, 1);
|
|
@@ -36357,6 +36504,11 @@ function slPreviewCmd(claudeDir) {
|
|
|
36357
36504
|
const out = {
|
|
36358
36505
|
ok: true,
|
|
36359
36506
|
width: cols,
|
|
36507
|
+
// WHAT THIS PICTURE WAS DRAWN WITH. The panel compares colour sets by
|
|
36508
|
+
// asking for the same layout several times; without these two it could not
|
|
36509
|
+
// label the answers.
|
|
36510
|
+
theme: layout.theme,
|
|
36511
|
+
glyphs: layout.glyphs,
|
|
36360
36512
|
state,
|
|
36361
36513
|
fixture: (SL_FIXTURES[state] || SL_FIXTURES.healthy).label,
|
|
36362
36514
|
fixtures: Object.fromEntries(Object.entries(SL_FIXTURES).map(([k, f]) => [k, f.label])),
|
|
@@ -36499,6 +36651,51 @@ function slLineCmd(claudeDir, p) {
|
|
|
36499
36651
|
return slSaveAndCompile(claudeDir, layout, { action: "line", line: n });
|
|
36500
36652
|
}
|
|
36501
36653
|
|
|
36654
|
+
// THE DOCUMENT-LEVEL SETTINGS, and they are a separate command on purpose.
|
|
36655
|
+
// `line <n> --theme` sets ONE line's override; the colour set a user picks in
|
|
36656
|
+
// the panel is the WHOLE layout's, and writing it onto line 1 changed one third
|
|
36657
|
+
// of the bar while `show --json` kept reporting the document theme — so the
|
|
36658
|
+
// picker looked dead and was in fact half working, which is worse. One command
|
|
36659
|
+
// per scope, and the panel calls the one that matches the control.
|
|
36660
|
+
function slDocCmd(claudeDir, p) {
|
|
36661
|
+
const layout = slLoadOrDefault(claudeDir);
|
|
36662
|
+
const th = flag("--theme");
|
|
36663
|
+
if (typeof th === "string") {
|
|
36664
|
+
if (!STATUSLINE_THEMES[th]) {
|
|
36665
|
+
if (wantsJson()) return emitJson({ ok: false, reason: "unknown-theme", theme: th, known: Object.keys(STATUSLINE_THEMES) }, 2);
|
|
36666
|
+
console.error(`unknown colour set "${th}" — known: ${Object.keys(STATUSLINE_THEMES).join(", ")}`);
|
|
36667
|
+
process.exit(2);
|
|
36668
|
+
}
|
|
36669
|
+
layout.theme = th;
|
|
36670
|
+
// A per-LINE override outranks the document, so a colour set chosen here
|
|
36671
|
+
// would be invisible on any line that carries one. The overrides are
|
|
36672
|
+
// cleared and the answer NAMES the lines it cleared — a setting silently
|
|
36673
|
+
// shadowed is the one failure this whole command exists to fix.
|
|
36674
|
+
for (const l of layout.lines) l.theme = null;
|
|
36675
|
+
}
|
|
36676
|
+
const gl = flag("--glyphs");
|
|
36677
|
+
if (typeof gl === "string") {
|
|
36678
|
+
if (!STATUSLINE_GLYPHSETS[gl]) {
|
|
36679
|
+
if (wantsJson()) return emitJson({ ok: false, reason: "unknown-glyphs", glyphs: gl, known: Object.keys(STATUSLINE_GLYPHSETS) }, 2);
|
|
36680
|
+
console.error(`unknown symbol set "${gl}" — known: ${Object.keys(STATUSLINE_GLYPHSETS).join(", ")}`);
|
|
36681
|
+
process.exit(2);
|
|
36682
|
+
}
|
|
36683
|
+
layout.glyphs = gl;
|
|
36684
|
+
}
|
|
36685
|
+
const an = flag("--ansi");
|
|
36686
|
+
if (typeof an === "string") {
|
|
36687
|
+
if (!["auto", "off"].includes(an)) {
|
|
36688
|
+
if (wantsJson()) return emitJson({ ok: false, reason: "unknown-ansi", ansi: an, known: ["auto", "off"] }, 2);
|
|
36689
|
+
console.error(`--ansi takes auto or off`);
|
|
36690
|
+
process.exit(2);
|
|
36691
|
+
}
|
|
36692
|
+
layout.ansi = an;
|
|
36693
|
+
}
|
|
36694
|
+
const align = flag("--align-columns");
|
|
36695
|
+
if (typeof align === "string") layout.align_columns = align === "on";
|
|
36696
|
+
return slSaveAndCompile(claudeDir, layout, { action: "doc", theme: layout.theme, glyphs: layout.glyphs, ansi: layout.ansi, align_columns: !!layout.align_columns });
|
|
36697
|
+
}
|
|
36698
|
+
|
|
36502
36699
|
function slPresetsCmd(claudeDir) {
|
|
36503
36700
|
const layout = slReadLayout(claudeDir);
|
|
36504
36701
|
const rows = slPresetsFor(slBoard().id).map(([name, p]) => {
|
package/bin/webui/api.js
CHANGED
|
@@ -269,6 +269,11 @@ const READS = {
|
|
|
269
269
|
const argv = ["statusline", "preview", ...slBoard(q)];
|
|
270
270
|
if (q.width) argv.push("--width", String(q.width));
|
|
271
271
|
if (q.state) argv.push("--state", String(q.state));
|
|
272
|
+
// v1.4.2 — a RENDER-ONLY override, so the panel can draw the same layout
|
|
273
|
+
// under each colour set and each symbol set. It writes nothing: the CLI
|
|
274
|
+
// applies these to a copy of the saved layout before it compiles.
|
|
275
|
+
if (q.theme) argv.push("--theme", String(q.theme));
|
|
276
|
+
if (q.glyphs) argv.push("--glyphs", String(q.glyphs));
|
|
272
277
|
return argv;
|
|
273
278
|
},
|
|
274
279
|
"/api/statusline/explain": (q) => ["statusline", "explain", String(q.at || "1:1"), ...slBoard(q)],
|
|
@@ -483,6 +488,17 @@ const WRITES = {
|
|
|
483
488
|
},
|
|
484
489
|
// A preset REPLACES the layout, so the panel always confirms it and names
|
|
485
490
|
// the loss — the `orc diy init --force` rule.
|
|
491
|
+
// THE DOCUMENT-LEVEL SETTINGS. `line` is per-LINE and `doc` is the whole
|
|
492
|
+
// layout; the colour set is a document fact, and routing it through `line`
|
|
493
|
+
// wrote one third of the bar while the picker read the other value back.
|
|
494
|
+
"/api/statusline/doc": (b) => {
|
|
495
|
+
const argv = ["statusline", "doc"];
|
|
496
|
+
if (b.theme) argv.push("--theme", String(b.theme));
|
|
497
|
+
if (b.glyphs) argv.push("--glyphs", String(b.glyphs));
|
|
498
|
+
if (b.ansi) argv.push("--ansi", String(b.ansi));
|
|
499
|
+
if (b.align_columns !== undefined) argv.push("--align-columns", b.align_columns ? "on" : "off");
|
|
500
|
+
return argv.concat(slBoard(b));
|
|
501
|
+
},
|
|
486
502
|
"/api/statusline/apply": (b) => ["statusline", "apply", String(b.name), ...slBoard(b)],
|
|
487
503
|
// v1.3.0 W5. `group` wraps 2-4 as one object, `expand` is its inverse and is
|
|
488
504
|
// also how a composite becomes editable, `clone` is for two `config` chips on
|
|
@@ -37,6 +37,61 @@
|
|
|
37
37
|
from { opacity: 0; transform: translateY(8px); }
|
|
38
38
|
to { opacity: 1; transform: none; }
|
|
39
39
|
}
|
|
40
|
+
/* ── the CLI Hook Interface (v1.4.1) ─────────────────────────────────────── */
|
|
41
|
+
/* The board is a thing you BUILD, so it moves the way a thing you build moves:
|
|
42
|
+
a chip arrives, a chip you just staged breathes once so you can find it, and
|
|
43
|
+
a row in the part picker slides in as the list narrows under your typing.
|
|
44
|
+
Every one of them is a FINITE animation with a real end state, so the cap in
|
|
45
|
+
the reduced-motion block below leaves each one at rest rather than frozen
|
|
46
|
+
part-way. */
|
|
47
|
+
.hk-chip { animation: hk-chip-in 200ms var(--ease) backwards; }
|
|
48
|
+
@keyframes hk-chip-in {
|
|
49
|
+
from { opacity: 0; transform: translateY(6px) scale(0.97); }
|
|
50
|
+
to { opacity: 1; transform: none; }
|
|
51
|
+
}
|
|
52
|
+
/* Staged, not written — and the one thing on the board that has to be findable
|
|
53
|
+
the instant it appears. Three pulses and it stops: an infinite one would have
|
|
54
|
+
to be REMOVED below rather than capped, and this one reads correctly at rest.
|
|
55
|
+
*/
|
|
56
|
+
.hk-chip-new { animation: hk-chip-in 200ms var(--ease) backwards, hk-chip-mark 900ms var(--ease) 3; }
|
|
57
|
+
@keyframes hk-chip-mark {
|
|
58
|
+
0%, 100% { box-shadow: inset 0 0 0 0 var(--accent); }
|
|
59
|
+
50% { box-shadow: inset 0 0 0 2px var(--accent); }
|
|
60
|
+
}
|
|
61
|
+
/* Position, drop marker, and the zone's own border: transitions rather than
|
|
62
|
+
animations, so an interrupted drag never leaves a chip mid-flight. */
|
|
63
|
+
.hk-chip, .hk-zone, .hk-add, .hk-act, .hk-pick-row, .hk-theme, .hk-sample {
|
|
64
|
+
transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease),
|
|
65
|
+
box-shadow var(--dur) var(--ease), opacity var(--dur) var(--ease),
|
|
66
|
+
transform var(--dur) var(--ease);
|
|
67
|
+
}
|
|
68
|
+
.hk-chip:hover:not(.hk-chip-dragging) { transform: translateY(-2px); }
|
|
69
|
+
.hk-add:hover:not(:disabled) { transform: translateY(-2px); }
|
|
70
|
+
.hk-pick-row { animation: hk-row-in 160ms var(--ease) backwards; }
|
|
71
|
+
@keyframes hk-row-in {
|
|
72
|
+
from { opacity: 0; transform: translateX(-6px); }
|
|
73
|
+
to { opacity: 1; transform: none; }
|
|
74
|
+
}
|
|
75
|
+
button.hk-pick-row:hover { transform: translateX(3px); }
|
|
76
|
+
|
|
77
|
+
/* NOTHING ARRIVES ON A REPAINT (v1.4.2). The CLI Hook Interface rebuilds its
|
|
78
|
+
whole panel when a change is STAGED — no request, no navigation, the same
|
|
79
|
+
cards with one value different — and the entrance animations above replayed
|
|
80
|
+
every time, on a 30ms stagger. With the network tab empty it read exactly
|
|
81
|
+
like the page reloading, on every keystroke.
|
|
82
|
+
|
|
83
|
+
`hk-quiet` is added by `hkPaint` from the SECOND paint onward and by the part
|
|
84
|
+
editor before every rebuild. It removes the ENTRANCES only: the transitions
|
|
85
|
+
further up are about the pointer rather than about arriving, so hover, the
|
|
86
|
+
drop marker and the staged edge are untouched. It must load after the rules
|
|
87
|
+
it switches off, which is why it is here and not in the panel's own file. */
|
|
88
|
+
.hk-quiet > *,
|
|
89
|
+
.hk-quiet .hk-chip,
|
|
90
|
+
.hk-quiet .hk-chip-new,
|
|
91
|
+
.hk-quiet .hk-pick-row {
|
|
92
|
+
animation: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
40
95
|
/* ---------------------------------------------------------- REDUCED MOTION */
|
|
41
96
|
/* Non-negotiable: every animation and transition above is off. */
|
|
42
97
|
|
|
@@ -55,6 +110,12 @@
|
|
|
55
110
|
nudges are removed outright rather than sped up. */
|
|
56
111
|
.nav a:hover, .btn:active:not(:disabled), .action:hover,
|
|
57
112
|
.todo:hover, .stat-link:hover, button.step:hover { transform: none !important; }
|
|
113
|
+
/* The board's own nudges, removed rather than shortened — a chip that lifts
|
|
114
|
+
under the pointer is an affordance, and a fast lift is still a lift. The
|
|
115
|
+
border and box-shadow changes stay: THEY are what says where a drop lands,
|
|
116
|
+
and a drop marker carried by motion alone would not exist here at all. */
|
|
117
|
+
.hk-chip:hover:not(.hk-chip-dragging), .hk-add:hover:not(:disabled),
|
|
118
|
+
button.hk-pick-row:hover { transform: none !important; }
|
|
58
119
|
/* The vault pulse is one of two INFINITE animations on the page. Capping it to
|
|
59
120
|
a single 1ms run would still leave a dash frozen mid-edge, so it is removed
|
|
60
121
|
outright and the edge reads as a plain line. */
|
|
@@ -124,12 +124,14 @@
|
|
|
124
124
|
way. The PREVIEW never collapses: it is a monospaced grid and squeezing it
|
|
125
125
|
would misreport the width the user is deciding. It scrolls instead. */
|
|
126
126
|
.hk-degrade-row,
|
|
127
|
+
.hk-compare-row,
|
|
127
128
|
.hk-field,
|
|
128
129
|
.hk-check {
|
|
129
130
|
grid-template-columns: 1fr;
|
|
130
131
|
gap: 4px;
|
|
131
132
|
}
|
|
132
|
-
.hk-degrade-label
|
|
133
|
+
.hk-degrade-label,
|
|
134
|
+
.hk-compare-label {
|
|
133
135
|
text-align: left;
|
|
134
136
|
}
|
|
135
137
|
.hk-gallery {
|
|
@@ -138,6 +140,20 @@
|
|
|
138
140
|
.hk-prow-id {
|
|
139
141
|
min-width: 0;
|
|
140
142
|
}
|
|
143
|
+
/* v1.4.1 — the editor's two-column field (a name plus the sentence that
|
|
144
|
+
explains it, then the control) stacks; and DRAG IS OFF below this width. A
|
|
145
|
+
40-pixel drop gap on a phone is not a target, so the three buttons on every
|
|
146
|
+
chip are the path — which is why they are buttons on the chip and not a
|
|
147
|
+
menu behind one. */
|
|
148
|
+
.hk-chip {
|
|
149
|
+
max-width: none;
|
|
150
|
+
flex-basis: 100%;
|
|
151
|
+
cursor: default;
|
|
152
|
+
}
|
|
153
|
+
.hk-grip { display: none; }
|
|
154
|
+
.hk-add { flex-basis: 100%; }
|
|
155
|
+
.hk-picker, .hk-editor { min-width: 0; }
|
|
156
|
+
.hk-theme { flex-basis: 100%; max-width: none; }
|
|
141
157
|
/* The caution KEEPS its two columns. The mark and the sentence belong to each
|
|
142
158
|
other, and stacking them puts a lone ✕ on a line of its own above the text
|
|
143
159
|
it is about. The mark column narrows instead. */
|