@promptctl/cc-candybar 1.36.0 → 1.38.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptctl/cc-candybar",
3
- "version": "1.36.0",
3
+ "version": "1.38.0",
4
4
  "description": "Statusline renderer for Claude Code — a JSON5-configurable DSL with daemon-cached data sources, byte-clean palette-aware composition, and OSC8 click verbs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -91,9 +91,9 @@
91
91
  "mobx": "^6.15.0"
92
92
  },
93
93
  "optionalDependencies": {
94
- "@promptctl/cc-candybar-darwin-arm64": "1.36.0",
95
- "@promptctl/cc-candybar-darwin-x64": "1.36.0",
96
- "@promptctl/cc-candybar-linux-x64": "1.36.0",
97
- "@promptctl/cc-candybar-linux-arm64": "1.36.0"
94
+ "@promptctl/cc-candybar-darwin-arm64": "1.38.0",
95
+ "@promptctl/cc-candybar-darwin-x64": "1.38.0",
96
+ "@promptctl/cc-candybar-linux-x64": "1.38.0",
97
+ "@promptctl/cc-candybar-linux-arm64": "1.38.0"
98
98
  }
99
99
  }
@@ -1405,6 +1405,130 @@
1405
1405
  ],
1406
1406
  "additionalProperties": false
1407
1407
  },
1408
+ {
1409
+ "type": "object",
1410
+ "properties": {
1411
+ "set": {
1412
+ "type": "string"
1413
+ },
1414
+ "persist": {
1415
+ "type": "string"
1416
+ },
1417
+ "persistWhen": {
1418
+ "type": "string"
1419
+ },
1420
+ "to": {
1421
+ "type": "string"
1422
+ }
1423
+ },
1424
+ "required": [
1425
+ "set",
1426
+ "persist",
1427
+ "persistWhen",
1428
+ "to"
1429
+ ],
1430
+ "additionalProperties": false
1431
+ },
1432
+ {
1433
+ "type": "object",
1434
+ "properties": {
1435
+ "set": {
1436
+ "type": "string"
1437
+ },
1438
+ "persist": {
1439
+ "type": "string"
1440
+ },
1441
+ "persistWhen": {
1442
+ "type": "string"
1443
+ },
1444
+ "from": {
1445
+ "anyOf": [
1446
+ {
1447
+ "type": "string",
1448
+ "minLength": 1
1449
+ },
1450
+ {
1451
+ "type": "array",
1452
+ "items": {
1453
+ "type": "string",
1454
+ "minLength": 1
1455
+ },
1456
+ "minItems": 1,
1457
+ "uniqueItems": true
1458
+ }
1459
+ ]
1460
+ }
1461
+ },
1462
+ "required": [
1463
+ "set",
1464
+ "persist",
1465
+ "persistWhen",
1466
+ "from"
1467
+ ],
1468
+ "additionalProperties": false
1469
+ },
1470
+ {
1471
+ "type": "object",
1472
+ "properties": {
1473
+ "set": {
1474
+ "type": "string"
1475
+ },
1476
+ "persist": {
1477
+ "type": "string"
1478
+ },
1479
+ "persistWhen": {
1480
+ "type": "string"
1481
+ },
1482
+ "min": {
1483
+ "type": "integer"
1484
+ },
1485
+ "max": {
1486
+ "type": "integer"
1487
+ },
1488
+ "by": {
1489
+ "type": "integer"
1490
+ }
1491
+ },
1492
+ "required": [
1493
+ "set",
1494
+ "persist",
1495
+ "persistWhen",
1496
+ "min",
1497
+ "max",
1498
+ "by"
1499
+ ],
1500
+ "additionalProperties": false
1501
+ },
1502
+ {
1503
+ "type": "object",
1504
+ "properties": {
1505
+ "set": {
1506
+ "type": "string"
1507
+ },
1508
+ "persist": {
1509
+ "type": "string"
1510
+ },
1511
+ "persistWhen": {
1512
+ "type": "string"
1513
+ },
1514
+ "cycle": {
1515
+ "type": "array",
1516
+ "items": {
1517
+ "type": "string",
1518
+ "minLength": 1
1519
+ },
1520
+ "minItems": 2,
1521
+ "uniqueItems": true
1522
+ }
1523
+ },
1524
+ "required": [
1525
+ "set",
1526
+ "persist",
1527
+ "persistWhen",
1528
+ "cycle"
1529
+ ],
1530
+ "additionalProperties": false
1531
+ },
1408
1532
  {
1409
1533
  "type": "object",
1410
1534
  "properties": {
package/src/check.ts CHANGED
@@ -36,14 +36,14 @@ import {
36
36
  effectiveLookName,
37
37
  lookKeyByName,
38
38
  effectiveStripStyle,
39
+ effectiveAutoWrap,
40
+ effectivePadding,
39
41
  } from "./themes/policy.js";
40
42
  import { paletteForThemeName } from "./themes/palette-resolvers.js";
41
43
  import { effectivePresetName, presetGlobals } from "./config/presets.js";
42
44
  import {
43
45
  DEFAULT_CHARSET,
44
46
  DEFAULT_COLOR_COMPATIBILITY,
45
- DEFAULT_PADDING,
46
- DEFAULT_WRAP,
47
47
  } from "./render/strip.js";
48
48
  import type { EffectiveGlobals } from "./daemon/render-payload.js";
49
49
 
@@ -321,8 +321,8 @@ function loadRegisterRender(
321
321
  theme: effectiveThemeName(null, globals.palette),
322
322
  look: effectiveLookName(null, globals.look, config.looks),
323
323
  style: effectiveStripStyle(null, globals.style),
324
- autoWrap: globals.autoWrap ?? DEFAULT_WRAP,
325
- padding: globals.padding ?? DEFAULT_PADDING,
324
+ autoWrap: effectiveAutoWrap(null, globals.autoWrap),
325
+ padding: effectivePadding(null, globals.padding),
326
326
  charset: globals.charset ?? DEFAULT_CHARSET,
327
327
  colorCompatibility:
328
328
  globals.colorCompatibility ?? DEFAULT_COLOR_COMPATIBILITY,
package/src/click/wire.ts CHANGED
@@ -57,10 +57,19 @@ export const VERB_LOAD_CONFIG = "load-config";
57
57
  // (candybar-config-engine-71o.2). Args: `[sessionId, key, value]` — the
58
58
  // sessionId is carried only for click.error surfacing, exactly like
59
59
  // set-state; the write itself is daemon-global, not session-scoped.
60
+ // A durable write takes an OPTIONAL trailing segment: the SessionState key to
61
+ // RELEASE once the write has succeeded. A dual-destination control
62
+ // (candybar-settings-ui-aok.3) commits "make this the durable default AND stop
63
+ // overriding it in this session" — one intent, whose session half must not
64
+ // happen if the durable half failed. Carried as one more segment on the write
65
+ // itself rather than as a second effect beside it, because `dispatch` runs
66
+ // every effect in a click by design; a pair would let a rejected write still
67
+ // wipe the user's pick. Args: `[sessionId, key, value, releaseKey?]`.
60
68
  export const VERB_SET_CONFIG = "set-config";
61
69
  // [LAW:types-are-the-program] A RELATIVE nudge to a bounded config-overrides
62
70
  // key (e.g. a padding stepper) — the config twin of step-state. Args:
63
- // `[sessionId, key, by]`.
71
+ // `[sessionId, key, by, releaseKey?]` — the same optional release segment
72
+ // set-config takes, for the same reason.
64
73
  export const VERB_STEP_CONFIG = "step-config";
65
74
  // [LAW:one-source-of-truth] The gated undo for `persist`: clears one
66
75
  // config-overrides key, restoring the user-file/bundled-default value on the
@@ -185,7 +185,116 @@ export type ActionDecl =
185
185
  | { readonly open: string }
186
186
  | { readonly reset: string }
187
187
  | { readonly undo: true }
188
- | { readonly redo: true };
188
+ | { readonly redo: true }
189
+ | DualActionDecl;
190
+
191
+ // [LAW:one-source-of-truth] The key whose PRESENCE makes an action
192
+ // dual-destination, and whose VALUE names the SessionState key that chooses
193
+ // the destination at click time. Spelled once: the loader dispatches on it,
194
+ // the compiler reads it, and the settings menu's synthesis writes it.
195
+ export const PERSIST_WHEN = "persistWhen";
196
+
197
+ // [LAW:types-are-the-program] ONE setting, ONE control, TWO stores
198
+ // (candybar-settings-ui-aok.3). Before this arm, a setting with both a
199
+ // session and a durable half cost the author two declarations and the reader
200
+ // two controls — `{{ menu "applyTheme" }}` beside `📌{{ menu
201
+ // "applyThemeForever" }}` — a second representation of one setting that had
202
+ // to be reconciled at every glance.
203
+ //
204
+ // A dual decl carries BOTH destination keys (`set` = the SessionState key,
205
+ // `persist` = the config-overrides key — they differ where history made them
206
+ // differ, e.g. session "theme" over globals field "palette") and ONE value
207
+ // source shared by both. `persistWhen` names the session key whose boolean
208
+ // value SELECTS the destination; the value written is identical either way,
209
+ // so the destination is the only thing that varies, and it varies as DATA
210
+ // [LAW:dataflow-not-control-flow].
211
+ //
212
+ // [LAW:single-enforcer] This adds NO gate surface. `actionDestinations`
213
+ // below explodes one dual decl into exactly the two single-destination decls
214
+ // it is equivalent to, and both validator derivations (state-validators.ts,
215
+ // config-validators.ts) fold over that explosion — so the writable (key,
216
+ // spec) pairs stay statically enumerable from the declarations, derived by
217
+ // the same code that has always derived them.
218
+ //
219
+ // The `int` / `removeSegment` / `insertSegment*` sources are deliberately
220
+ // absent: `int` is a UI-only page cursor with no durable meaning, and the
221
+ // structural edits are persist-only by design (see the arms above), so
222
+ // neither has two destinations to choose between.
223
+ export type DualActionDecl =
224
+ | {
225
+ readonly set: string;
226
+ readonly persist: string;
227
+ readonly persistWhen: string;
228
+ readonly to: string;
229
+ }
230
+ | {
231
+ readonly set: string;
232
+ readonly persist: string;
233
+ readonly persistWhen: string;
234
+ readonly from: OptionDomain;
235
+ }
236
+ | {
237
+ readonly set: string;
238
+ readonly persist: string;
239
+ readonly persistWhen: string;
240
+ readonly cycle: readonly string[];
241
+ }
242
+ | {
243
+ readonly set: string;
244
+ readonly persist: string;
245
+ readonly persistWhen: string;
246
+ readonly min: number;
247
+ readonly max: number;
248
+ readonly by: number;
249
+ };
250
+
251
+ // [LAW:parse-dont-validate] The one discriminator for the dual arm, and the
252
+ // reason every `"set" in a` consumer stays correct: a dual decl carries both
253
+ // `set` and `persist`, so a consumer that must treat the two destinations
254
+ // separately asks THIS first (or folds through actionDestinations below), and
255
+ // a consumer that only asks "does this bind a session write" (actionBindsSet)
256
+ // keeps its existing answer with no change at all.
257
+ export function actionIsDual(a: ActionDecl): a is DualActionDecl {
258
+ return PERSIST_WHEN in a;
259
+ }
260
+
261
+ // [LAW:one-source-of-truth] THE explosion of an action into the
262
+ // single-destination declarations it writes through — `[a]` for every
263
+ // ordinary action, `[session, durable]` for a dual. Every consumer that
264
+ // reasons about DESTINATIONS (both validator derivations) flatMaps through
265
+ // this instead of learning the dual shape, so the gate a dual derives is by
266
+ // construction the union of the gates its two halves would have derived
267
+ // separately — there is no second derivation to keep in agreement.
268
+ export function actionDestinations(a: ActionDecl): readonly ActionDecl[] {
269
+ if (!actionIsDual(a)) return [a];
270
+ // [LAW:types-are-the-program] Written out per value source rather than
271
+ // spread-and-cast: the two halves are then ordinary, fully-typed `set` and
272
+ // `persist` members — the exact declarations an author would have written —
273
+ // and adding a value source to DualActionDecl fails to compile here until
274
+ // its explosion is stated.
275
+ if ("to" in a) {
276
+ return [
277
+ { set: a.set, to: a.to },
278
+ { persist: a.persist, to: a.to },
279
+ ];
280
+ }
281
+ if ("from" in a) {
282
+ return [
283
+ { set: a.set, from: a.from },
284
+ { persist: a.persist, from: a.from },
285
+ ];
286
+ }
287
+ if ("cycle" in a) {
288
+ return [
289
+ { set: a.set, cycle: a.cycle },
290
+ { persist: a.persist, cycle: a.cycle },
291
+ ];
292
+ }
293
+ return [
294
+ { set: a.set, min: a.min, max: a.max, by: a.by },
295
+ { persist: a.persist, min: a.min, max: a.max, by: a.by },
296
+ ];
297
+ }
189
298
 
190
299
  // [LAW:dataflow-not-control-flow] Does this action write a SessionState key? A
191
300
  // `set` action composes a set-state click URL whose first segment is session.id;