@promptctl/cc-candybar 1.30.0 → 1.32.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 +65 -0
- package/src/click/wire.ts +9 -0
- package/src/config/action.ts +65 -2
- package/src/config/dsl-loader.ts +18 -1
- package/src/config/edit-chrome.ts +329 -0
- package/src/config/layout-ops.ts +21 -0
- package/src/config/loader/actions.ts +97 -13
- package/src/config/loader/cross-ref.ts +29 -9
- package/src/config/loader/edit-mode.ts +111 -0
- package/src/daemon/config-overrides-store.ts +308 -17
- package/src/daemon/verbs/config-validators.ts +39 -1
- package/src/daemon/verbs/index.ts +40 -2
- package/src/dsl/render.ts +10 -1
- package/src/render/action.ts +76 -2
- package/src/render/picker.ts +71 -38
package/src/render/picker.ts
CHANGED
|
@@ -28,7 +28,13 @@ import type { FuncMap } from "@promptctl/go-template-js";
|
|
|
28
28
|
import { toNumber } from "../var-system/types.js";
|
|
29
29
|
import { stripChromeCols } from "./strip.js";
|
|
30
30
|
import { TERM_COLS_VAR } from "../config/dsl-types.js";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
effectsUrl,
|
|
33
|
+
VERB_APPLY_LAYOUT_OP,
|
|
34
|
+
VERB_SET_CONFIG,
|
|
35
|
+
VERB_SET_STATE,
|
|
36
|
+
} from "../click/wire.js";
|
|
37
|
+
import { encodeLayoutOp } from "../config/layout-ops.js";
|
|
32
38
|
import {
|
|
33
39
|
linkFragment,
|
|
34
40
|
readVar,
|
|
@@ -136,26 +142,33 @@ function requireKind<K extends CompiledActionDecl["kind"]>(
|
|
|
136
142
|
return action as Extract<CompiledActionDecl, { kind: K }>;
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
// [LAW:one-source-of-truth] The apply action a picker grid binds to is
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
145
|
+
// [LAW:one-source-of-truth] The apply action a picker grid binds to is one of
|
|
146
|
+
// THREE option-domain-driven kinds (src/render/action.ts): set-option/
|
|
147
|
+
// persist-option (a picked value is WRITTEN VERBATIM — set-option's two
|
|
148
|
+
// durability twins, differing only in wire verb VERB_SET_CONFIG vs
|
|
149
|
+
// VERB_SET_STATE) or layout-op-option (a picked value is ENCODED into a
|
|
150
|
+
// structural LayoutOp before writing — brandon-layout-edit-2gc.3's
|
|
151
|
+
// `insertSegmentFrom`). All three share the same option-domain gate
|
|
152
|
+
// (deriveActionValidators/deriveConfigActionValidators) and the same "pick a
|
|
153
|
+
// cell, apply it" shape; only WHAT the click writes differs, which is
|
|
154
|
+
// realize()'s job, not the picker's. Rejecting any of the three here would be
|
|
155
|
+
// an artificial gap — there is nothing about "picker" that excludes one kind.
|
|
148
156
|
function requireOptionKind(
|
|
149
157
|
runtime: ActionRuntime,
|
|
150
158
|
name: string,
|
|
151
|
-
): Extract<
|
|
159
|
+
): Extract<
|
|
160
|
+
CompiledActionDecl,
|
|
161
|
+
{ kind: "set-option" | "persist-option" | "layout-op-option" }
|
|
162
|
+
> {
|
|
152
163
|
const action = runtime.compiled.get(name);
|
|
153
164
|
if (
|
|
154
165
|
!action ||
|
|
155
|
-
(action.kind !== "set-option" &&
|
|
166
|
+
(action.kind !== "set-option" &&
|
|
167
|
+
action.kind !== "persist-option" &&
|
|
168
|
+
action.kind !== "layout-op-option")
|
|
156
169
|
) {
|
|
157
170
|
throw new Error(
|
|
158
|
-
`picker references action "${name}" which must be a set-option or
|
|
171
|
+
`picker references action "${name}" which must be a set-option, persist-option, or layout-op-option action ({ set, from }, { persist, from }, or { persist, insertSegmentFrom, anchor, relation }), got ${action ? `a ${action.kind} action` : "no such action"}`,
|
|
159
172
|
);
|
|
160
173
|
}
|
|
161
174
|
return action;
|
|
@@ -183,7 +196,13 @@ export function renderPicker(
|
|
|
183
196
|
const apply = requireOptionKind(runtime, applyName);
|
|
184
197
|
const store = runtime.store;
|
|
185
198
|
const sessionId = readVar(store, "session.id");
|
|
186
|
-
|
|
199
|
+
// [LAW:no-defensive-null-guards] layout-op-option carries no `stateVar` —
|
|
200
|
+
// a structural insert is a one-shot trigger, not a persisted single value,
|
|
201
|
+
// so there is no "current selection" to mark. `undefined` here (never a
|
|
202
|
+
// magic sentinel string) makes every option's `option === current` compare
|
|
203
|
+
// false below, structurally rather than by accident.
|
|
204
|
+
const current =
|
|
205
|
+
"stateVar" in apply ? readVar(store, apply.stateVar) : undefined;
|
|
187
206
|
const widths = apply.options.map(cellWidth);
|
|
188
207
|
|
|
189
208
|
// ✕ is always present; ←/→ appear only on a multi-page menu. Reserve arrow
|
|
@@ -243,30 +262,44 @@ export function renderPicker(
|
|
|
243
262
|
{ verb: VERB_SET_STATE, args: [sessionId, ...closeFlat] },
|
|
244
263
|
]);
|
|
245
264
|
// [LAW:one-source-of-truth] A set-option apply folds its closeOnPick pairs
|
|
246
|
-
// into ONE set-state batch (setState is variadic — see daemon/verbs).
|
|
247
|
-
// persist-option
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
265
|
+
// into ONE set-state batch (setState is variadic — see daemon/verbs).
|
|
266
|
+
// persist-option and layout-op-option cannot: setConfig/apply-layout-op
|
|
267
|
+
// each take exactly one (key, value) pair, so their close pairs (always
|
|
268
|
+
// SessionState — open/page live there regardless of the apply's
|
|
269
|
+
// durability) ride as a SECOND effect in the same dispatch, still one
|
|
270
|
+
// atomic click via effectsUrl's array. layout-op-option's "value" is the
|
|
271
|
+
// ENCODED op (segment=option, anchor/relation from the compiled action),
|
|
272
|
+
// not the option verbatim — the one place this kind's write differs from
|
|
273
|
+
// persist-option's.
|
|
274
|
+
const closeEffect = closeOnPick
|
|
275
|
+
? [{ verb: VERB_SET_STATE, args: [sessionId, ...closeFlat] }]
|
|
276
|
+
: [];
|
|
277
|
+
const optionUrl = (option: string): string => {
|
|
278
|
+
if (apply.kind === "persist-option") {
|
|
279
|
+
return effectsUrl([
|
|
280
|
+
{ verb: VERB_SET_CONFIG, args: [sessionId, apply.key, option] },
|
|
281
|
+
...closeEffect,
|
|
282
|
+
]);
|
|
283
|
+
}
|
|
284
|
+
if (apply.kind === "layout-op-option") {
|
|
285
|
+
const op = encodeLayoutOp({
|
|
286
|
+
op: "insert",
|
|
287
|
+
segment: option,
|
|
288
|
+
anchor: apply.anchor,
|
|
289
|
+
relation: apply.relation,
|
|
290
|
+
});
|
|
291
|
+
return effectsUrl([
|
|
292
|
+
{ verb: VERB_APPLY_LAYOUT_OP, args: [sessionId, apply.key, op] },
|
|
293
|
+
...closeEffect,
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
return effectsUrl([
|
|
297
|
+
{
|
|
298
|
+
verb: VERB_SET_STATE,
|
|
299
|
+
args: [sessionId, apply.key, option, ...(closeOnPick ? closeFlat : [])],
|
|
300
|
+
},
|
|
301
|
+
]);
|
|
302
|
+
};
|
|
270
303
|
|
|
271
304
|
const frags: RichText[] = [linkFragment(PICKER_CLOSE, closeUrl, false)];
|
|
272
305
|
if (pageIdx > 0) {
|