@promptctl/cc-candybar 1.15.0 → 1.17.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 +5 -5
- package/package.json +5 -5
- package/src/config/default-dsl-config.ts +12 -0
- package/src/daemon/render-payload.ts +22 -13
- package/src/daemon/server.ts +17 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/cc-candybar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.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.17.0",
|
|
95
|
+
"@promptctl/cc-candybar-darwin-x64": "1.17.0",
|
|
96
|
+
"@promptctl/cc-candybar-linux-x64": "1.17.0",
|
|
97
|
+
"@promptctl/cc-candybar-linux-arm64": "1.17.0"
|
|
98
98
|
},
|
|
99
99
|
"pnpm": {
|
|
100
100
|
"supportedArchitectures": {
|
|
@@ -185,6 +185,18 @@ export const DEFAULT_DSL_CONFIG = {
|
|
|
185
185
|
},
|
|
186
186
|
"session.id": { kind: "input", path: "session_id", default: "" },
|
|
187
187
|
version: { kind: "input", path: "version", default: "" },
|
|
188
|
+
// [LAW:one-source-of-truth] The daemon-resolved effective theme name —
|
|
189
|
+
// effectiveThemeName(sessionState.theme, globals.palette), the SAME name the
|
|
190
|
+
// rendered basePalette is built from. A theme-picker config's trigger reads
|
|
191
|
+
// `{{ .theme.effective }}` to show the active theme, so the label and the
|
|
192
|
+
// colors trace to one resolution and cannot drift — no per-config restating
|
|
193
|
+
// of the initial theme (which JSON5, being inert data, cannot derive). The
|
|
194
|
+
// daemon always provides it; the "" default is the unreachable-absence floor.
|
|
195
|
+
"theme.effective": {
|
|
196
|
+
kind: "input",
|
|
197
|
+
path: "theme.effective",
|
|
198
|
+
default: "",
|
|
199
|
+
},
|
|
188
200
|
|
|
189
201
|
// [LAW:one-source-of-truth] The usable terminal width for THIS render —
|
|
190
202
|
// the exact post-reserve cell count FlexStrip wraps to. renderDsl injects
|
|
@@ -33,7 +33,6 @@ import type { ContextProvider } from "../segments/context.js";
|
|
|
33
33
|
import type { MetricsProvider } from "../segments/metrics.js";
|
|
34
34
|
import type { TmuxService } from "../segments/tmux.js";
|
|
35
35
|
import type { GitDataProvider } from "./cache/git.js";
|
|
36
|
-
import type { SessionStateRW } from "./session-state.js";
|
|
37
36
|
|
|
38
37
|
// ─── Augmented payload shape ─────────────────────────────────────────────────
|
|
39
38
|
|
|
@@ -54,7 +53,16 @@ export interface RenderPayload extends ClaudeHookData {
|
|
|
54
53
|
|
|
55
54
|
readonly git?: GitPayload;
|
|
56
55
|
readonly tmux?: { readonly session: string };
|
|
57
|
-
|
|
56
|
+
// [LAW:one-source-of-truth] The daemon-resolved effective theme name —
|
|
57
|
+
// effectiveThemeName(sessionState.theme, globals.palette). The SAME value the
|
|
58
|
+
// rendered basePalette is built from, surfaced so a trigger label can display
|
|
59
|
+
// the active theme WITHOUT the config restating it (the label and the colors
|
|
60
|
+
// trace to one resolution and cannot drift).
|
|
61
|
+
// [LAW:types-are-the-program] REQUIRED, not optional: the daemon resolves it
|
|
62
|
+
// every render and buildRenderPayload includes it unconditionally, so the
|
|
63
|
+
// domain truth is "always present". A `?` here would let a callsite believe it
|
|
64
|
+
// could be undefined and guard defensively against an impossibility.
|
|
65
|
+
readonly theme: { readonly effective: string };
|
|
58
66
|
|
|
59
67
|
// Usage-family. Each provider returns null when it has no data (no
|
|
60
68
|
// transcript yet, no rate-limit window active, etc.); we drop the field
|
|
@@ -206,7 +214,6 @@ export interface RenderPayloadDeps {
|
|
|
206
214
|
readonly contextProvider: ContextProvider;
|
|
207
215
|
readonly metricsProvider: MetricsProvider;
|
|
208
216
|
readonly tmuxService: TmuxService;
|
|
209
|
-
readonly sessionState: SessionStateRW;
|
|
210
217
|
// [LAW:single-enforcer] The log capability for every provider lane:
|
|
211
218
|
// buildRenderPayload is the ONE place lane failures are logged, so the
|
|
212
219
|
// providers' interiors never log and never double-log.
|
|
@@ -568,6 +575,13 @@ export async function buildRenderPayload(
|
|
|
568
575
|
// registration; passing it in (rather than recomputing per render) keeps
|
|
569
576
|
// the hot path free of the BFS + extractTemplateRefs cost.
|
|
570
577
|
neededInputPaths: ReadonlySet<string>,
|
|
578
|
+
// [LAW:one-source-of-truth] The effective theme name, resolved ONCE by the
|
|
579
|
+
// daemon (effectiveThemeName(sessionState.theme, globals.palette)) and used
|
|
580
|
+
// for BOTH the rendered basePalette and this payload field — so a trigger
|
|
581
|
+
// label reading `.theme.effective` can never disagree with the colors. Passed
|
|
582
|
+
// in (not re-resolved here) because the daemon already computes it for the
|
|
583
|
+
// palette; this is that same value, threaded to the sole payload assembler.
|
|
584
|
+
effectiveTheme: string,
|
|
571
585
|
): Promise<RenderPayload> {
|
|
572
586
|
const wants = (prefix: string): boolean =>
|
|
573
587
|
anyPathStartsWith(neededInputPaths, prefix);
|
|
@@ -718,15 +732,6 @@ export async function buildRenderPayload(
|
|
|
718
732
|
}
|
|
719
733
|
: undefined;
|
|
720
734
|
|
|
721
|
-
// [LAW:one-source-of-truth] The theme variable surfaces the session's
|
|
722
|
-
// resolved theme so the toolbar/tray DSL templates can encode it into
|
|
723
|
-
// cc-candybar:// URLs without re-resolving. SessionState owns the value;
|
|
724
|
-
// we mirror it onto the payload, not redeclare it.
|
|
725
|
-
const themeRaw = wants("theme")
|
|
726
|
-
? deps.sessionState.get(hookData.session_id, "theme")
|
|
727
|
-
: undefined;
|
|
728
|
-
const theme = typeof themeRaw === "string" ? themeRaw : undefined;
|
|
729
|
-
|
|
730
735
|
// home is always available — it's a single env-var read, no I/O cost.
|
|
731
736
|
// Letting the gate skip it would add a branch with no win.
|
|
732
737
|
// [LAW:single-enforcer] All path-shaped payload fields are normalized
|
|
@@ -778,7 +783,11 @@ export async function buildRenderPayload(
|
|
|
778
783
|
...(home !== undefined && { home }),
|
|
779
784
|
...(gitProjection.git !== undefined && { git: gitProjection.git }),
|
|
780
785
|
...(tmuxValue !== undefined && { tmux: { session: tmuxValue } }),
|
|
781
|
-
|
|
786
|
+
// [LAW:one-source-of-truth] Always present — the daemon resolves the
|
|
787
|
+
// effective theme every render (for basePalette), and this is that value.
|
|
788
|
+
// No `wants` gate: it costs nothing (a string already in hand) and a
|
|
789
|
+
// config that reads `.theme.effective` must always find it.
|
|
790
|
+
theme: { effective: effectiveTheme },
|
|
782
791
|
...(sessionPayload !== undefined && { session: sessionPayload }),
|
|
783
792
|
...(todayPayload !== undefined && { today: todayPayload }),
|
|
784
793
|
...(costPerHour !== undefined && { burn: { costPerHour } }),
|
package/src/daemon/server.ts
CHANGED
|
@@ -745,24 +745,30 @@ async function handleRequest(req: Request): Promise<HandledRequest> {
|
|
|
745
745
|
// No special-case branches — same composition every render.
|
|
746
746
|
let body = "";
|
|
747
747
|
if (entry.state !== null) {
|
|
748
|
+
// [LAW:one-source-of-truth] Resolve the effective theme ONCE — the
|
|
749
|
+
// session's chosen theme (SessionState) over the config default. This
|
|
750
|
+
// single name drives BOTH the payload's `theme.effective` field (the
|
|
751
|
+
// trigger label reads it) AND the rendered basePalette below, so a label
|
|
752
|
+
// and the colors can never disagree. Resolved here, before the payload
|
|
753
|
+
// build, so it can be threaded into the sole payload assembler.
|
|
754
|
+
const effectiveTheme = effectiveThemeName(
|
|
755
|
+
sessionState.get(req.hookData.session_id, "theme"),
|
|
756
|
+
entry.state.config.globals.palette,
|
|
757
|
+
);
|
|
748
758
|
const payload = await buildRenderPayload(
|
|
749
759
|
req.hookData,
|
|
750
760
|
payloadDeps,
|
|
751
761
|
req.cwd,
|
|
752
762
|
entry.state.neededInputPaths,
|
|
763
|
+
effectiveTheme,
|
|
753
764
|
);
|
|
754
765
|
// [LAW:one-source-of-truth][LAW:dataflow-not-control-flow] basePalette
|
|
755
|
-
// is derived
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
//
|
|
760
|
-
const basePalette = resolverForThemeName(
|
|
761
|
-
effectiveThemeName(
|
|
762
|
-
sessionState.get(req.hookData.session_id, "theme"),
|
|
763
|
-
entry.state.config.globals.palette,
|
|
764
|
-
),
|
|
765
|
-
);
|
|
766
|
+
// is derived from the same effective theme resolved above — so a theme
|
|
767
|
+
// click recolors the whole bar on the next render. Not frozen on the
|
|
768
|
+
// cache entry (one entry serves many sessions). resolverForThemeName
|
|
769
|
+
// memoizes, so the per-render cost is one Map lookup once the theme is
|
|
770
|
+
// warm.
|
|
771
|
+
const basePalette = resolverForThemeName(effectiveTheme);
|
|
766
772
|
// [LAW:one-type-per-behavior][LAW:dataflow-not-control-flow] The powerline
|
|
767
773
|
// cap/separator SHAPE, resolved per render the exact way the theme is: the
|
|
768
774
|
// session's clicked style (SessionState) over the config default over the
|
|
@@ -1139,7 +1145,6 @@ const payloadDeps = {
|
|
|
1139
1145
|
contextProvider,
|
|
1140
1146
|
metricsProvider,
|
|
1141
1147
|
tmuxService,
|
|
1142
|
-
sessionState,
|
|
1143
1148
|
// [LAW:single-enforcer] buildRenderPayload is the one log site for the
|
|
1144
1149
|
// outcome-carrying provider lanes (git, cache).
|
|
1145
1150
|
log: dlog,
|