@promptctl/cc-candybar 1.2.0 → 1.3.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 +6 -6
- package/package.json +5 -5
- package/src/config/default-dsl-config.ts +38 -8
- package/src/daemon/cache/render.ts +3 -4
- package/src/demo/dsl.ts +0 -1
- package/src/dsl/render.ts +6 -5
- package/src/render/action.ts +5 -6
- package/src/render/picker.ts +0 -5
- package/src/var-system/sources.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/cc-candybar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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,10 +91,10 @@
|
|
|
91
91
|
"mobx": "^6.15.0"
|
|
92
92
|
},
|
|
93
93
|
"optionalDependencies": {
|
|
94
|
-
"@promptctl/cc-candybar-darwin-arm64": "1.
|
|
95
|
-
"@promptctl/cc-candybar-darwin-x64": "1.
|
|
96
|
-
"@promptctl/cc-candybar-linux-x64": "1.
|
|
97
|
-
"@promptctl/cc-candybar-linux-arm64": "1.
|
|
94
|
+
"@promptctl/cc-candybar-darwin-arm64": "1.3.0",
|
|
95
|
+
"@promptctl/cc-candybar-darwin-x64": "1.3.0",
|
|
96
|
+
"@promptctl/cc-candybar-linux-x64": "1.3.0",
|
|
97
|
+
"@promptctl/cc-candybar-linux-arm64": "1.3.0"
|
|
98
98
|
},
|
|
99
99
|
"pnpm": {
|
|
100
100
|
"supportedArchitectures": {
|
|
@@ -159,6 +159,14 @@ export const DEFAULT_DSL_CONFIG = {
|
|
|
159
159
|
path: "workspace.project_dir",
|
|
160
160
|
default: "",
|
|
161
161
|
},
|
|
162
|
+
// Transcript path (a top-level hookData field, spread onto the payload
|
|
163
|
+
// root by buildRenderPayload). Read by the quick-action tray's
|
|
164
|
+
// openTranscript action — pass-through, no projection.
|
|
165
|
+
transcript_path: {
|
|
166
|
+
kind: "input",
|
|
167
|
+
path: "transcript_path",
|
|
168
|
+
default: "",
|
|
169
|
+
},
|
|
162
170
|
"model.display_name": {
|
|
163
171
|
kind: "input",
|
|
164
172
|
path: "model.display_name",
|
|
@@ -497,10 +505,16 @@ export const DEFAULT_DSL_CONFIG = {
|
|
|
497
505
|
fg: "foreground",
|
|
498
506
|
when: '{{ ne .git.branch "" }}',
|
|
499
507
|
},
|
|
508
|
+
// Quick-action tray — copy the session id / cwd, open the project dir /
|
|
509
|
+
// transcript in the editor. [LAW:locality-or-seam] The glyph is the
|
|
510
|
+
// REPRESENTATION; the named action (below) is the BEHAVIOR; the action
|
|
511
|
+
// name is the seam between them. Re-glyph without touching behavior;
|
|
512
|
+
// re-target without touching this template. Each `{{ action … }}` emits
|
|
513
|
+
// one OSC-8 clickable region whose URL the wire codec owns end-to-end.
|
|
500
514
|
toolbar: {
|
|
501
515
|
template:
|
|
502
|
-
' {{
|
|
503
|
-
' {{
|
|
516
|
+
' {{ action "copySession" "⎘ id" }} {{ action "copyDir" "⎘ cwd" }}' +
|
|
517
|
+
' {{ action "openProject" "↗ proj" }} {{ action "openTranscript" "↗ log" }} ',
|
|
504
518
|
bg: "surface",
|
|
505
519
|
fg: "foreground",
|
|
506
520
|
},
|
|
@@ -612,7 +626,7 @@ export const DEFAULT_DSL_CONFIG = {
|
|
|
612
626
|
},
|
|
613
627
|
|
|
614
628
|
// Default layout — a single horizontal row of segment refs.
|
|
615
|
-
// A-grammar equivalent: { h: ["directory","git","model","session","today","context"] }
|
|
629
|
+
// A-grammar equivalent: { h: ["directory","git","model","session","today","context","toolbar"] }
|
|
616
630
|
// [LAW:one-source-of-truth] The bundled default now authors the same terse
|
|
617
631
|
// surface every user config lowers to, so the default is the reference spelling.
|
|
618
632
|
// Adding rows = wrapping in { kind:"container", direction:"vertical", children:[...] };
|
|
@@ -627,14 +641,30 @@ export const DEFAULT_DSL_CONFIG = {
|
|
|
627
641
|
{ kind: "segment", name: "session" },
|
|
628
642
|
{ kind: "segment", name: "today" },
|
|
629
643
|
{ kind: "segment", name: "context" },
|
|
644
|
+
{ kind: "segment", name: "toolbar" },
|
|
630
645
|
],
|
|
631
646
|
},
|
|
632
647
|
|
|
633
|
-
// [LAW:locality-or-seam]
|
|
634
|
-
// the
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
|
|
648
|
+
// [LAW:locality-or-seam] The quick-action tray's behaviors, decoupled by NAME
|
|
649
|
+
// from the `toolbar` segment's glyphs above. copy/open evaluate a Go-template
|
|
650
|
+
// against the live render scope at click time and write NO SessionState, so
|
|
651
|
+
// they derive no state validator (no gate) — they are pure click effects.
|
|
652
|
+
//
|
|
653
|
+
// [LAW:single-enforcer] Each template emits a RAW value; the click-wire codec
|
|
654
|
+
// (effectsUrl → encodeSegments) owns ALL percent-encoding and the verb's
|
|
655
|
+
// `oneArg` owns the single matching decode — so the template never hand-rolls
|
|
656
|
+
// a `urlEncode`, and the path round-trips untouched through one codec.
|
|
657
|
+
//
|
|
658
|
+
// open* route through the open-vscode verb (`open -a "Visual Studio Code"
|
|
659
|
+
// <path>`), so they pass a bare filesystem path — a directory or a file the
|
|
660
|
+
// editor opens directly — NOT a `vscode://` URL (which `open -a` would treat
|
|
661
|
+
// as a literal filename, not a deep link).
|
|
662
|
+
actions: {
|
|
663
|
+
copySession: { copy: "{{ .session.id }}" },
|
|
664
|
+
copyDir: { copy: "{{ .current_dir }}" },
|
|
665
|
+
openProject: { open: "{{ .project_dir }}" },
|
|
666
|
+
openTranscript: { open: "{{ .transcript_path }}" },
|
|
667
|
+
},
|
|
638
668
|
|
|
639
669
|
// [LAW:single-enforcer] / [LAW:one-source-of-truth] Display-formatting policy
|
|
640
670
|
// for the cost/token/budget family lives here as named template helpers, each
|
|
@@ -300,12 +300,11 @@ export class RenderCache {
|
|
|
300
300
|
// reloadInto preserves the prior last-known-good with nothing half-installed.
|
|
301
301
|
const validatorDisposers: Array<() => void> = [];
|
|
302
302
|
try {
|
|
303
|
-
// [LAW:
|
|
304
|
-
//
|
|
305
|
-
//
|
|
303
|
+
// [LAW:one-source-of-truth] The action runtime reads session.id + current
|
|
304
|
+
// picker values from registry.variableStore — the same store this entry's
|
|
305
|
+
// registry declares into — so no store reference is threaded separately.
|
|
306
306
|
compiled = registerDslConfig(config, registry, {
|
|
307
307
|
cwd: entry.cwd,
|
|
308
|
-
store,
|
|
309
308
|
});
|
|
310
309
|
// [LAW:one-source-of-truth] Derive the writable-key validators from the
|
|
311
310
|
// config's action table (the sole interaction authority) through one
|
package/src/demo/dsl.ts
CHANGED
package/src/dsl/render.ts
CHANGED
|
@@ -252,7 +252,7 @@ function compileHelperPreamble(
|
|
|
252
252
|
export function registerDslConfig(
|
|
253
253
|
config: ValidatedConfig,
|
|
254
254
|
registry: SourceRegistry,
|
|
255
|
-
opts?: { cwd?: string;
|
|
255
|
+
opts?: { cwd?: string; clock?: () => Date },
|
|
256
256
|
): CompiledConfig {
|
|
257
257
|
const cwd = opts?.cwd ?? process.cwd();
|
|
258
258
|
|
|
@@ -262,11 +262,12 @@ export function registerDslConfig(
|
|
|
262
262
|
// because the action set is config-scoped. The runtime holder is populated below
|
|
263
263
|
// — the `action`/`picker` funcs reference the engine, and the compiled actions
|
|
264
264
|
// reference the engine, so the holder breaks that cycle.
|
|
265
|
-
// [LAW:
|
|
266
|
-
//
|
|
267
|
-
//
|
|
265
|
+
// [LAW:one-source-of-truth] The action runtime reads through the SAME store the
|
|
266
|
+
// registry declares into and the renderer reads back — sourced from the registry
|
|
267
|
+
// itself, not a redundant opts field a caller could forget (or pass a divergent
|
|
268
|
+
// store for). Every config has a registry, so the action store is never null.
|
|
268
269
|
const actionRuntime: ActionRuntime = {
|
|
269
|
-
store:
|
|
270
|
+
store: registry.variableStore,
|
|
270
271
|
compiled: new Map(),
|
|
271
272
|
};
|
|
272
273
|
// [LAW:one-way-deps] Inject action + picker feature funcs as data — the engine
|
package/src/render/action.ts
CHANGED
|
@@ -117,7 +117,11 @@ export function optionDomain(src: OptionSource): readonly string[] {
|
|
|
117
117
|
// reads session.id and the current value from the same source the rest of the
|
|
118
118
|
// render does.
|
|
119
119
|
export interface ActionRuntime {
|
|
120
|
-
|
|
120
|
+
// [LAW:types-are-the-program] Always present — registerDslConfig sources it
|
|
121
|
+
// from the registry it is handed (registry.variableStore), so "no store" is
|
|
122
|
+
// structurally unrepresentable. The action reads session.id and current
|
|
123
|
+
// values from the same store the renderer reads.
|
|
124
|
+
store: VariableStore;
|
|
121
125
|
compiled: CompiledActions;
|
|
122
126
|
}
|
|
123
127
|
|
|
@@ -416,11 +420,6 @@ export function renderAction(
|
|
|
416
420
|
throw new Error(`action "${name}" is not declared in this config`);
|
|
417
421
|
}
|
|
418
422
|
const store = runtime.store;
|
|
419
|
-
if (!store) {
|
|
420
|
-
throw new Error(
|
|
421
|
-
`action "${name}" rendered without a VariableStore — registerDslConfig was not given one`,
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
423
|
const { display, boundValue } = selectDisplay(name, action, displays, store);
|
|
425
424
|
const sessionId = readVar(store, "session.id");
|
|
426
425
|
const { effect, active } = realize(
|
package/src/render/picker.ts
CHANGED
|
@@ -144,11 +144,6 @@ function renderPicker(
|
|
|
144
144
|
"an int action ({ set, int: true })",
|
|
145
145
|
);
|
|
146
146
|
const store = runtime.store;
|
|
147
|
-
if (!store) {
|
|
148
|
-
throw new Error(
|
|
149
|
-
`picker "${applyName}" rendered without a VariableStore — registerDslConfig was not given one`,
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
147
|
const sessionId = readVar(store, "session.id");
|
|
153
148
|
const current = readVar(store, apply.stateVar);
|
|
154
149
|
const widths = apply.options.map(cellWidth);
|
|
@@ -580,6 +580,15 @@ export class SourceRegistry {
|
|
|
580
580
|
this.sessionState = sessionState;
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
// [LAW:one-source-of-truth] The registry IS the owner of its store — every
|
|
584
|
+
// variable it declares lives there, and the renderer reads back through it.
|
|
585
|
+
// Exposing it read-only lets a caller that already holds the registry obtain
|
|
586
|
+
// the one store without threading a second reference that could diverge (the
|
|
587
|
+
// action runtime reads session.id/current values from this exact store).
|
|
588
|
+
get variableStore(): VariableStore {
|
|
589
|
+
return this.store;
|
|
590
|
+
}
|
|
591
|
+
|
|
583
592
|
// ─── Synchronous source kinds ─────────────────────────────────────────────
|
|
584
593
|
|
|
585
594
|
// literal: type inferred from value; box written once at declaration and never again.
|