@promptctl/cc-candybar 1.30.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +80 -80
- package/package.json +5 -5
- package/schema/cc-candybar.schema.json +24 -0
- package/src/click/wire.ts +9 -0
- package/src/config/action.ts +40 -2
- package/src/config/loader/actions.ts +45 -0
- package/src/config/loader/cross-ref.ts +16 -7
- package/src/daemon/config-overrides-store.ts +308 -17
- package/src/daemon/verbs/index.ts +40 -2
- package/src/render/action.ts +27 -2
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
isGlobalsField,
|
|
38
38
|
loadConfigOverrides,
|
|
39
39
|
loadOverrides,
|
|
40
|
+
redoLastOverride,
|
|
41
|
+
undoLastOverride,
|
|
40
42
|
writeConfigOverride,
|
|
41
43
|
} from "../config-overrides-store";
|
|
42
44
|
import { configOverridesPath } from "../paths";
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
VERB_DISPATCH,
|
|
50
52
|
VERB_OPEN_VSCODE,
|
|
51
53
|
VERB_LOAD_CONFIG,
|
|
54
|
+
VERB_REDO,
|
|
52
55
|
VERB_RESET_CONFIG,
|
|
53
56
|
VERB_SET_CONFIG,
|
|
54
57
|
VERB_SET_STATE,
|
|
@@ -57,6 +60,7 @@ import {
|
|
|
57
60
|
VERB_SHOW_CONFIG_ERROR,
|
|
58
61
|
VERB_SHOW_CONFIG_WARNING,
|
|
59
62
|
VERB_TOOLBAR_TOGGLE,
|
|
63
|
+
VERB_UNDO,
|
|
60
64
|
} from "../../click/wire";
|
|
61
65
|
|
|
62
66
|
export interface VerbContext {
|
|
@@ -489,6 +493,35 @@ const applyLayoutOp: VerbHandler = (rawValue, ctx) => {
|
|
|
489
493
|
);
|
|
490
494
|
};
|
|
491
495
|
|
|
496
|
+
// [LAW:one-source-of-truth] `reset`'s fine-grained sibling: step the ONE
|
|
497
|
+
// global history over the overrides layer back one entry. No key, no value —
|
|
498
|
+
// the history store (config-overrides-store.ts) owns which entry moves and
|
|
499
|
+
// what it restores; this handler is pure plumbing between the wire and it.
|
|
500
|
+
// [LAW:no-silent-failure] An empty stack is a loud BAD_REQUEST (dispatch's
|
|
501
|
+
// aggregator turns it into a transient click.error), never a silent no-op —
|
|
502
|
+
// the ticket's own done-gate.
|
|
503
|
+
const undoConfig: VerbHandler = (value, ctx) => {
|
|
504
|
+
const [sessionId = ""] = decodeWire(() => decodeSegments(value));
|
|
505
|
+
const sid = requireSessionId(sessionId);
|
|
506
|
+
const entry = undoLastOverride(configOverridesPath(), ctx.dlog);
|
|
507
|
+
if (entry === null) {
|
|
508
|
+
throw new BadVerbArgs("undo: history is empty, nothing to undo");
|
|
509
|
+
}
|
|
510
|
+
ctx.dlog("info", `undo: ${entry.key} (session=${sid})`);
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// [LAW:one-source-of-truth] undo's mirror — steps the same global history
|
|
514
|
+
// forward one entry.
|
|
515
|
+
const redoConfig: VerbHandler = (value, ctx) => {
|
|
516
|
+
const [sessionId = ""] = decodeWire(() => decodeSegments(value));
|
|
517
|
+
const sid = requireSessionId(sessionId);
|
|
518
|
+
const entry = redoLastOverride(configOverridesPath(), ctx.dlog);
|
|
519
|
+
if (entry === null) {
|
|
520
|
+
throw new BadVerbArgs("redo: nothing to redo");
|
|
521
|
+
}
|
|
522
|
+
ctx.dlog("info", `redo: ${entry.key} (session=${sid})`);
|
|
523
|
+
};
|
|
524
|
+
|
|
492
525
|
// ─── Registry ───────────────────────────────────────────────────────────────
|
|
493
526
|
|
|
494
527
|
// [LAW:one-source-of-truth] The LEAF verbs — every click effect that does real
|
|
@@ -551,6 +584,8 @@ const LEAF_VERBS = new Map<string, VerbHandler>([
|
|
|
551
584
|
[VERB_STEP_CONFIG, stepConfig],
|
|
552
585
|
[VERB_RESET_CONFIG, resetConfig],
|
|
553
586
|
[VERB_APPLY_LAYOUT_OP, applyLayoutOp],
|
|
587
|
+
[VERB_UNDO, undoConfig],
|
|
588
|
+
[VERB_REDO, redoConfig],
|
|
554
589
|
[VERB_SHOW_CONFIG_ERROR, showConfigError],
|
|
555
590
|
[VERB_SHOW_CONFIG_WARNING, showConfigWarning],
|
|
556
591
|
[VERB_TOOLBAR_TOGGLE, toolbarToggle],
|
|
@@ -583,8 +618,9 @@ const dispatch: VerbHandler = (rawValue, ctx) => {
|
|
|
583
618
|
for (const { verb, value } of parseEffects(rawValue)) {
|
|
584
619
|
// Extract session ID from the first session-bearing effect for error display.
|
|
585
620
|
// set-state, step-state, set-config, step-config, reset-config,
|
|
586
|
-
// apply-layout-op, and toolbar-toggle all carry the session id
|
|
587
|
-
// first segment, so a failing step surfaces in the bar like any
|
|
621
|
+
// apply-layout-op, undo, redo, and toolbar-toggle all carry the session id
|
|
622
|
+
// as their first segment, so a failing step surfaces in the bar like any
|
|
623
|
+
// other.
|
|
588
624
|
if (
|
|
589
625
|
!sessionId &&
|
|
590
626
|
(verb === VERB_SET_STATE ||
|
|
@@ -593,6 +629,8 @@ const dispatch: VerbHandler = (rawValue, ctx) => {
|
|
|
593
629
|
verb === VERB_STEP_CONFIG ||
|
|
594
630
|
verb === VERB_RESET_CONFIG ||
|
|
595
631
|
verb === VERB_APPLY_LAYOUT_OP ||
|
|
632
|
+
verb === VERB_UNDO ||
|
|
633
|
+
verb === VERB_REDO ||
|
|
596
634
|
verb === VERB_TOOLBAR_TOGGLE)
|
|
597
635
|
) {
|
|
598
636
|
const parts = decodeSegments(value);
|
package/src/render/action.ts
CHANGED
|
@@ -34,11 +34,13 @@ import {
|
|
|
34
34
|
VERB_APPLY_LAYOUT_OP,
|
|
35
35
|
VERB_COPY,
|
|
36
36
|
VERB_OPEN_VSCODE,
|
|
37
|
+
VERB_REDO,
|
|
37
38
|
VERB_RESET_CONFIG,
|
|
38
39
|
VERB_SET_CONFIG,
|
|
39
40
|
VERB_SET_STATE,
|
|
40
41
|
VERB_STEP_CONFIG,
|
|
41
42
|
VERB_STEP_STATE,
|
|
43
|
+
VERB_UNDO,
|
|
42
44
|
type Effect,
|
|
43
45
|
} from "../click/wire.js";
|
|
44
46
|
|
|
@@ -142,7 +144,13 @@ export type CompiledActionDecl =
|
|
|
142
144
|
// arms. Fully literal at compile time (the op IS the declaration — no
|
|
143
145
|
// template-bound option, unlike persist-option), so `op` is precomputed
|
|
144
146
|
// here rather than reconstructed from raw fields at every realize() call.
|
|
145
|
-
| { readonly kind: "layout-op"; readonly key: string; readonly op: LayoutOp }
|
|
147
|
+
| { readonly kind: "layout-op"; readonly key: string; readonly op: LayoutOp }
|
|
148
|
+
// [LAW:one-source-of-truth] brandon-layout-edit-2gc.2's global history
|
|
149
|
+
// step over the overrides layer — `reset`'s fine-grained sibling. No key:
|
|
150
|
+
// there is nothing to carry, since the history stack (not this action) is
|
|
151
|
+
// what decides which entry moves.
|
|
152
|
+
| { readonly kind: "undo" }
|
|
153
|
+
| { readonly kind: "redo" };
|
|
146
154
|
|
|
147
155
|
export type CompiledActions = ReadonlyMap<string, CompiledActionDecl>;
|
|
148
156
|
|
|
@@ -338,7 +346,10 @@ function compileAction(
|
|
|
338
346
|
target: parseActionTemplate(parse, action.open, name),
|
|
339
347
|
};
|
|
340
348
|
}
|
|
341
|
-
|
|
349
|
+
if ("reset" in action) {
|
|
350
|
+
return { kind: "reset", key: action.reset };
|
|
351
|
+
}
|
|
352
|
+
return "undo" in action ? { kind: "undo" } : { kind: "redo" };
|
|
342
353
|
}
|
|
343
354
|
|
|
344
355
|
function parseActionTemplate(
|
|
@@ -548,6 +559,20 @@ function realize(
|
|
|
548
559
|
effect: { verb: VERB_RESET_CONFIG, args: [sessionId, c.key] },
|
|
549
560
|
active: false,
|
|
550
561
|
};
|
|
562
|
+
// [LAW:one-source-of-truth] No key to carry — the click just says "step
|
|
563
|
+
// the history", and which entry moves is entirely server-side state
|
|
564
|
+
// (never wire input, so there is nothing here to gate). Never "active":
|
|
565
|
+
// a history step is a one-shot trigger, not a current-selection toggle.
|
|
566
|
+
case "undo":
|
|
567
|
+
return {
|
|
568
|
+
effect: { verb: VERB_UNDO, args: [sessionId] },
|
|
569
|
+
active: false,
|
|
570
|
+
};
|
|
571
|
+
case "redo":
|
|
572
|
+
return {
|
|
573
|
+
effect: { verb: VERB_REDO, args: [sessionId] },
|
|
574
|
+
active: false,
|
|
575
|
+
};
|
|
551
576
|
// [LAW:one-source-of-truth] The op is fixed at compile time (see
|
|
552
577
|
// compileAction) — the click just delivers it. `apply-layout-op`'s
|
|
553
578
|
// handler does read-current-append-write (see verbs/index.ts), unlike
|