@promptctl/cc-candybar 1.11.0 → 1.12.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 +50 -50
- package/package.json +5 -5
- package/schema/cc-candybar.schema.json +8 -0
- package/src/config/dsl-types.ts +14 -1
- package/src/config/loader/globals.ts +38 -1
- package/src/daemon/server.ts +24 -8
- package/src/demo/dsl.ts +5 -1
- package/src/render/strip.ts +19 -4
- package/src/themes/policy.ts +26 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/cc-candybar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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.12.0",
|
|
95
|
+
"@promptctl/cc-candybar-darwin-x64": "1.12.0",
|
|
96
|
+
"@promptctl/cc-candybar-linux-x64": "1.12.0",
|
|
97
|
+
"@promptctl/cc-candybar-linux-arm64": "1.12.0"
|
|
98
98
|
},
|
|
99
99
|
"pnpm": {
|
|
100
100
|
"supportedArchitectures": {
|
package/src/config/dsl-types.ts
CHANGED
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
// references it here. The dependency is one-way (this file → action.ts), never
|
|
15
15
|
// the reverse, so that shape can be lifted out without a cycle.
|
|
16
16
|
import type { ActionDecl } from "./action.js";
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
Charset,
|
|
19
|
+
ColorCompatibility,
|
|
20
|
+
StripStyle,
|
|
21
|
+
} from "../themes/policy.js";
|
|
18
22
|
|
|
19
23
|
// [LAW:types-are-the-program] Three stages, three names.
|
|
20
24
|
//
|
|
@@ -213,6 +217,15 @@ export interface Globals {
|
|
|
213
217
|
// [config-only] The daemon resolves `globals.charset ?? "unicode"` into
|
|
214
218
|
// renderOpts.charset; no SessionState/click half.
|
|
215
219
|
readonly charset?: Charset;
|
|
220
|
+
|
|
221
|
+
// The legacy display.colorCompatibility knob: the color depth rich-js
|
|
222
|
+
// downsamples output to. Default "truecolor" (current behavior — NOT the
|
|
223
|
+
// legacy "auto" default, which would change rendering for existing users).
|
|
224
|
+
// The type excludes "auto" entirely: the daemon is detached, so env
|
|
225
|
+
// detection would read the wrong terminal — see COLOR_COMPATIBILITIES.
|
|
226
|
+
// [config-only] The daemon resolves `globals.colorCompatibility ??
|
|
227
|
+
// "truecolor"` into renderOpts.colorCompatibility; no SessionState/click half.
|
|
228
|
+
readonly colorCompatibility?: ColorCompatibility;
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
// [LAW:one-type-per-behavior] One discriminated union covers every source
|
|
@@ -4,19 +4,54 @@
|
|
|
4
4
|
// add a key to GLOBALS_SCHEMA and Globals; the engine does the rest.
|
|
5
5
|
|
|
6
6
|
import { type Globals } from "../dsl-types.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
CHARSETS,
|
|
9
|
+
COLOR_COMPATIBILITIES,
|
|
10
|
+
STRIP_STYLES,
|
|
11
|
+
type ColorCompatibility,
|
|
12
|
+
} from "../../themes/policy.js";
|
|
8
13
|
import {
|
|
9
14
|
optionalBooleanSpec,
|
|
15
|
+
optionalEnum,
|
|
10
16
|
optionalEnumSpec,
|
|
11
17
|
optionalIntSpec,
|
|
12
18
|
optionalStringSpec,
|
|
13
19
|
paletteSpec,
|
|
14
20
|
record,
|
|
15
21
|
recordJson,
|
|
22
|
+
type FieldSpec,
|
|
16
23
|
type JsonNode,
|
|
17
24
|
type RecordSchema,
|
|
18
25
|
type ValidateCtx,
|
|
19
26
|
} from "./validate-core.js";
|
|
27
|
+
import { findKeyLine } from "./diagnostics.js";
|
|
28
|
+
|
|
29
|
+
// [LAW:types-are-the-program] Closed enum like `charset`, plus one
|
|
30
|
+
// migration-pointing rejection: "auto" was the LEGACY default, so migrating
|
|
31
|
+
// configs will carry it — but the daemon runs detached, so rich-js env
|
|
32
|
+
// detection would downsample against the daemon's terminal, not the client's.
|
|
33
|
+
// Rather than ship that silent lie [LAW:no-silent-failure], "auto" is outside
|
|
34
|
+
// the ColorCompatibility domain and gets an error that says why (same species
|
|
35
|
+
// as the removed-layout migration errors in layout.ts). The json emit and the
|
|
36
|
+
// membership check derive from the same COLOR_COMPATIBILITIES literal.
|
|
37
|
+
const colorCompatibilitySpec: FieldSpec<ColorCompatibility> = {
|
|
38
|
+
required: false,
|
|
39
|
+
json: { enum: [...COLOR_COMPATIBILITIES] },
|
|
40
|
+
parse: (ctx, path, field, raw) => {
|
|
41
|
+
if (raw[field] === "auto") {
|
|
42
|
+
ctx.issues.push({
|
|
43
|
+
path: `${path}.${field}`,
|
|
44
|
+
message:
|
|
45
|
+
`${path}.${field}: "auto" is not supported — the render daemon runs detached, ` +
|
|
46
|
+
`so terminal detection would read the daemon's environment, not your terminal's. ` +
|
|
47
|
+
`Pick an explicit depth: ${COLOR_COMPATIBILITIES.join(", ")}`,
|
|
48
|
+
line: findKeyLine(ctx.source, [...path.split("."), field]),
|
|
49
|
+
});
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return optionalEnum(ctx, path, raw, field, COLOR_COMPATIBILITIES);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
20
55
|
|
|
21
56
|
const GLOBALS_SCHEMA: RecordSchema<Globals> = {
|
|
22
57
|
noun: "globals key",
|
|
@@ -41,6 +76,8 @@ const GLOBALS_SCHEMA: RecordSchema<Globals> = {
|
|
|
41
76
|
// vocabularies pickJoiner can render — validates by membership, emits a
|
|
42
77
|
// JSON-Schema `enum` from the same CHARSETS literal.
|
|
43
78
|
charset: optionalEnumSpec(CHARSETS),
|
|
79
|
+
// Closed enum with a bespoke "auto" rejection — see colorCompatibilitySpec.
|
|
80
|
+
colorCompatibility: colorCompatibilitySpec,
|
|
44
81
|
},
|
|
45
82
|
};
|
|
46
83
|
|
package/src/daemon/server.ts
CHANGED
|
@@ -47,11 +47,13 @@ import {
|
|
|
47
47
|
import {
|
|
48
48
|
renderStripCells,
|
|
49
49
|
DEFAULT_CHARSET,
|
|
50
|
+
DEFAULT_COLOR_COMPATIBILITY,
|
|
50
51
|
DEFAULT_PADDING,
|
|
51
52
|
DEFAULT_TERMINAL_WIDTH,
|
|
52
53
|
DEFAULT_WRAP,
|
|
53
54
|
type BuildLineOptions,
|
|
54
55
|
type Charset,
|
|
56
|
+
type ColorCompatibility,
|
|
55
57
|
} from "../render/strip.js";
|
|
56
58
|
import { applyClaudeCodeReserve } from "../utils/terminal-width.js";
|
|
57
59
|
import type { RichText } from "@promptctl/rich-js";
|
|
@@ -788,6 +790,13 @@ async function handleRequest(req: Request): Promise<HandledRequest> {
|
|
|
788
790
|
// [LAW:one-source-of-truth]
|
|
789
791
|
renderOpts.charset =
|
|
790
792
|
entry.state.config.globals.charset ?? DEFAULT_CHARSET;
|
|
793
|
+
// [config-only] globals.colorCompatibility, same shape as charset:
|
|
794
|
+
// the config global over the base floor is the whole resolution —
|
|
795
|
+
// the ONE home of the color-depth choice rich-js downsamples to.
|
|
796
|
+
// [LAW:one-source-of-truth]
|
|
797
|
+
renderOpts.colorCompatibility =
|
|
798
|
+
entry.state.config.globals.colorCompatibility ??
|
|
799
|
+
DEFAULT_COLOR_COMPATIBILITY;
|
|
791
800
|
// [LAW:single-enforcer] renderDsl internally calls
|
|
792
801
|
// `registry.applyInput(payload)` as its first step (see step 1 in
|
|
793
802
|
// src/dsl/render.ts). The daemon must not pre-apply — doing so
|
|
@@ -895,6 +904,8 @@ async function handleRequest(req: Request): Promise<HandledRequest> {
|
|
|
895
904
|
? serializeSegmentCells(
|
|
896
905
|
dbgEntry.lastRenderCellsBySegment,
|
|
897
906
|
dbgEntry.config.globals.charset ?? DEFAULT_CHARSET,
|
|
907
|
+
dbgEntry.config.globals.colorCompatibility ??
|
|
908
|
+
DEFAULT_COLOR_COMPATIBILITY,
|
|
898
909
|
)
|
|
899
910
|
: EMPTY_RENDER_MAP,
|
|
900
911
|
};
|
|
@@ -1080,7 +1091,7 @@ const verbCtx = { sessionState, dlog };
|
|
|
1080
1091
|
// is rendered standalone (wrap doesn't apply to a one-segment projection).
|
|
1081
1092
|
const RENDER_OPTS_BASE = {
|
|
1082
1093
|
style: "powerline" as const,
|
|
1083
|
-
colorCompatibility:
|
|
1094
|
+
colorCompatibility: DEFAULT_COLOR_COMPATIBILITY,
|
|
1084
1095
|
wrap: DEFAULT_WRAP,
|
|
1085
1096
|
padding: DEFAULT_PADDING,
|
|
1086
1097
|
charset: DEFAULT_CHARSET,
|
|
@@ -1095,21 +1106,26 @@ const DEBUG_RENDER_OPTS: BuildLineOptions = {
|
|
|
1095
1106
|
// the DaemonDslState type requires the field.
|
|
1096
1107
|
const EMPTY_RENDER_MAP = new Map<string, string>();
|
|
1097
1108
|
|
|
1098
|
-
// [LAW:one-source-of-truth] The joiner glyph vocabulary
|
|
1099
|
-
//
|
|
1100
|
-
//
|
|
1101
|
-
// live session-over-config resolution needs a
|
|
1102
|
-
// carry. The caller threads the
|
|
1103
|
-
// re-defaults
|
|
1109
|
+
// [LAW:one-source-of-truth] The joiner glyph vocabulary and the color depth
|
|
1110
|
+
// are serialization-time choices, and both are config-only (no SessionState
|
|
1111
|
+
// half) — so the faithful values are fully derivable from the sampled entry's
|
|
1112
|
+
// config, unlike style, whose live session-over-config resolution needs a
|
|
1113
|
+
// session a debug request doesn't carry. The caller threads the
|
|
1114
|
+
// entry-resolved values; this serializer never re-defaults them.
|
|
1104
1115
|
function serializeSegmentCells(
|
|
1105
1116
|
cells: ReadonlyMap<string, readonly RichText[]>,
|
|
1106
1117
|
charset: Charset,
|
|
1118
|
+
colorCompatibility: ColorCompatibility,
|
|
1107
1119
|
): Map<string, string> {
|
|
1108
1120
|
const out = new Map<string, string>();
|
|
1109
1121
|
for (const [name, segCells] of cells) {
|
|
1110
1122
|
out.set(
|
|
1111
1123
|
name,
|
|
1112
|
-
renderStripCells(segCells, {
|
|
1124
|
+
renderStripCells(segCells, {
|
|
1125
|
+
...DEBUG_RENDER_OPTS,
|
|
1126
|
+
charset,
|
|
1127
|
+
colorCompatibility,
|
|
1128
|
+
}),
|
|
1113
1129
|
);
|
|
1114
1130
|
}
|
|
1115
1131
|
return out;
|
package/src/demo/dsl.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { effectiveThemeName, resolverForThemeName } from "../themes/index.js";
|
|
|
33
33
|
import { registerDslConfig, renderDsl } from "../dsl/render.js";
|
|
34
34
|
import {
|
|
35
35
|
DEFAULT_CHARSET,
|
|
36
|
+
DEFAULT_COLOR_COMPATIBILITY,
|
|
36
37
|
DEFAULT_PADDING,
|
|
37
38
|
DEFAULT_TERMINAL_WIDTH,
|
|
38
39
|
DEFAULT_WRAP,
|
|
@@ -108,7 +109,10 @@ try {
|
|
|
108
109
|
basePalette,
|
|
109
110
|
{
|
|
110
111
|
style: "powerline",
|
|
111
|
-
|
|
112
|
+
// Same resolution the daemon applies: the config global over the
|
|
113
|
+
// truecolor default floor.
|
|
114
|
+
colorCompatibility:
|
|
115
|
+
config.globals.colorCompatibility ?? DEFAULT_COLOR_COMPATIBILITY,
|
|
112
116
|
// [LAW:one-source-of-truth] Demo applies the same Claude-Code-UI
|
|
113
117
|
// reserve the daemon does so demo output matches the bytes a real
|
|
114
118
|
// statusline would emit at the same terminal width.
|
package/src/render/strip.ts
CHANGED
|
@@ -8,11 +8,14 @@ import {
|
|
|
8
8
|
FlexStrip,
|
|
9
9
|
renderToString,
|
|
10
10
|
type Joiner,
|
|
11
|
-
type ColorSystemSpec,
|
|
12
11
|
type PowerlineJoinerOptions,
|
|
13
12
|
type CapsuleJoinerOptions,
|
|
14
13
|
} from "@promptctl/rich-js";
|
|
15
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
Charset,
|
|
16
|
+
ColorCompatibility,
|
|
17
|
+
StripStyle,
|
|
18
|
+
} from "../themes/policy.js";
|
|
16
19
|
|
|
17
20
|
export interface RenderedSegmentLike {
|
|
18
21
|
type: string;
|
|
@@ -25,7 +28,7 @@ export interface RenderedSegmentLike {
|
|
|
25
28
|
// in themes/policy.ts (the render-identifier policy module, importable by the
|
|
26
29
|
// option-source machinery without a render→template-engine cycle). Re-exported
|
|
27
30
|
// here so render-layer consumers can keep importing them from the strip module.
|
|
28
|
-
export type { Charset, StripStyle };
|
|
31
|
+
export type { Charset, ColorCompatibility, StripStyle };
|
|
29
32
|
|
|
30
33
|
// [LAW:one-source-of-truth] Raw terminal cols we assume when the wire
|
|
31
34
|
// didn't give us one (older client, env-stripped spawn). RAW — not
|
|
@@ -51,9 +54,21 @@ export const DEFAULT_PADDING = 1;
|
|
|
51
54
|
// (`globals.charset ?? DEFAULT_CHARSET`) derives from this constant.
|
|
52
55
|
export const DEFAULT_CHARSET: Charset = "unicode";
|
|
53
56
|
|
|
57
|
+
// [LAW:one-source-of-truth] The one statement of the globals.colorCompatibility
|
|
58
|
+
// default (truecolor — the CURRENT pinned value, deliberately NOT the legacy
|
|
59
|
+
// "auto" default, which would change rendering for existing users). Every
|
|
60
|
+
// resolver of the config global (`globals.colorCompatibility ??
|
|
61
|
+
// DEFAULT_COLOR_COMPATIBILITY`) derives from this constant.
|
|
62
|
+
export const DEFAULT_COLOR_COMPATIBILITY: ColorCompatibility = "truecolor";
|
|
63
|
+
|
|
54
64
|
export interface BuildLineOptions {
|
|
55
65
|
style: StripStyle;
|
|
56
|
-
|
|
66
|
+
// [LAW:types-are-the-program] Narrower than rich-js ColorSystemSpec on
|
|
67
|
+
// purpose: the four explicit depths only. "auto"/null never reach a render —
|
|
68
|
+
// the daemon is detached, so env detection would read the wrong terminal;
|
|
69
|
+
// the loader rejects "auto" at the trust boundary (see COLOR_COMPATIBILITIES
|
|
70
|
+
// in themes/policy.ts), and every construction site states a resolved depth.
|
|
71
|
+
colorCompatibility: ColorCompatibility;
|
|
57
72
|
separator?: string;
|
|
58
73
|
// [LAW:types-are-the-program] Every render carries a width. Required (not
|
|
59
74
|
// optional) so callers cannot silently drop the wire's value.
|
package/src/themes/policy.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// PaletteResolver, no ColorRgba, no hex. The semantic/anchor knowledge
|
|
6
6
|
// (which tokens keep their hue) stays in rich-js (ANCHORED_ROOTS), not here.
|
|
7
7
|
|
|
8
|
-
import { listThemePalettes } from "@promptctl/rich-js";
|
|
8
|
+
import { listThemePalettes, type ColorSystemSpec } from "@promptctl/rich-js";
|
|
9
9
|
|
|
10
10
|
// --- Theme name aliasing ---
|
|
11
11
|
|
|
@@ -96,3 +96,28 @@ export function effectiveStripStyle(
|
|
|
96
96
|
// is the whole resolution.
|
|
97
97
|
export const CHARSETS = ["unicode", "ascii"] as const;
|
|
98
98
|
export type Charset = (typeof CHARSETS)[number];
|
|
99
|
+
|
|
100
|
+
// --- Color-depth identifiers ---
|
|
101
|
+
|
|
102
|
+
// [LAW:one-source-of-truth][LAW:types-are-the-program] The single canonical set
|
|
103
|
+
// of color depths a config can pin (the legacy display.colorCompatibility).
|
|
104
|
+
// Same species as CHARSETS: a closed render-vocabulary enum hosted in this leaf
|
|
105
|
+
// policy module so the config loader (validation + JSON-schema emit) and the
|
|
106
|
+
// render layer both derive from one literal without a config↔render cycle
|
|
107
|
+
// [LAW:one-way-deps]. `satisfies` ties every member to rich-js's
|
|
108
|
+
// ColorSystemSpec at compile time WITHOUT widening the derived union — if
|
|
109
|
+
// rich-js renames a depth, this literal fails to compile rather than drifting.
|
|
110
|
+
//
|
|
111
|
+
// Deliberately NARROWER than ColorSystemSpec: "auto" (and null) are excluded.
|
|
112
|
+
// The daemon is long-lived and detached, so its process env is NOT the client
|
|
113
|
+
// terminal's — rich-js env detection would silently downsample against the
|
|
114
|
+
// wrong terminal [LAW:no-silent-failure]. Honoring "auto" needs a client
|
|
115
|
+
// capability hint over the wire (the termCols pattern); until that lands, the
|
|
116
|
+
// loader rejects "auto" with a pointer instead of shipping a lie.
|
|
117
|
+
export const COLOR_COMPATIBILITIES = [
|
|
118
|
+
"truecolor",
|
|
119
|
+
"256",
|
|
120
|
+
"ansi",
|
|
121
|
+
"none",
|
|
122
|
+
] as const satisfies readonly ColorSystemSpec[];
|
|
123
|
+
export type ColorCompatibility = (typeof COLOR_COMPATIBILITIES)[number];
|