@promptctl/cc-candybar 1.42.1 → 1.43.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.
Files changed (126) hide show
  1. package/dist/index.mjs +72 -71
  2. package/package.json +5 -6
  3. package/src/check.ts +0 -478
  4. package/src/cli-flags.ts +0 -8
  5. package/src/click/wire.ts +0 -158
  6. package/src/config/action.ts +0 -329
  7. package/src/config/cli.ts +0 -71
  8. package/src/config/default-dsl-config.ts +0 -1645
  9. package/src/config/disclosure.ts +0 -170
  10. package/src/config/dsl-loader.ts +0 -339
  11. package/src/config/dsl-types.ts +0 -581
  12. package/src/config/edit-chrome.ts +0 -559
  13. package/src/config/help.ts +0 -151
  14. package/src/config/ident.ts +0 -22
  15. package/src/config/layout-ops.ts +0 -177
  16. package/src/config/loader/actions.ts +0 -972
  17. package/src/config/loader/cache.ts +0 -206
  18. package/src/config/loader/cross-ref.ts +0 -714
  19. package/src/config/loader/cycles.ts +0 -148
  20. package/src/config/loader/diagnostics.ts +0 -99
  21. package/src/config/loader/discovery.ts +0 -182
  22. package/src/config/loader/edit-mode.ts +0 -137
  23. package/src/config/loader/emit-schema.ts +0 -68
  24. package/src/config/loader/globals.ts +0 -269
  25. package/src/config/loader/helpers.ts +0 -48
  26. package/src/config/loader/layout.ts +0 -693
  27. package/src/config/loader/looks.ts +0 -96
  28. package/src/config/loader/menu-synth.ts +0 -435
  29. package/src/config/loader/merge.ts +0 -115
  30. package/src/config/loader/persist-target.ts +0 -67
  31. package/src/config/loader/presets.ts +0 -119
  32. package/src/config/loader/refs.ts +0 -100
  33. package/src/config/loader/reserved-namespace.ts +0 -38
  34. package/src/config/loader/segments.ts +0 -120
  35. package/src/config/loader/validate-core.ts +0 -737
  36. package/src/config/loader/variables.ts +0 -260
  37. package/src/config/menu-keys.ts +0 -139
  38. package/src/config/option-domain.ts +0 -164
  39. package/src/config/presets.ts +0 -326
  40. package/src/config/settings-menu.ts +0 -775
  41. package/src/daemon/acquire.ts +0 -684
  42. package/src/daemon/cache/git.ts +0 -649
  43. package/src/daemon/cache/render.ts +0 -623
  44. package/src/daemon/cache/session-usage-store.ts +0 -720
  45. package/src/daemon/cache/watchers.ts +0 -249
  46. package/src/daemon/client-debug.ts +0 -120
  47. package/src/daemon/client-stats.ts +0 -130
  48. package/src/daemon/client-transport.ts +0 -273
  49. package/src/daemon/client.ts +0 -78
  50. package/src/daemon/config-overrides-store.ts +0 -663
  51. package/src/daemon/debug-types.ts +0 -91
  52. package/src/daemon/debug.ts +0 -264
  53. package/src/daemon/fork-bomb-breaker.ts +0 -351
  54. package/src/daemon/limits.ts +0 -211
  55. package/src/daemon/log.ts +0 -81
  56. package/src/daemon/parent-watchdog.ts +0 -87
  57. package/src/daemon/paths.ts +0 -211
  58. package/src/daemon/process-fingerprint.ts +0 -146
  59. package/src/daemon/protocol.ts +0 -292
  60. package/src/daemon/render-payload.ts +0 -1256
  61. package/src/daemon/server.ts +0 -1330
  62. package/src/daemon/session-state-file.ts +0 -108
  63. package/src/daemon/session-state.ts +0 -237
  64. package/src/daemon/socket-lease.ts +0 -209
  65. package/src/daemon/socket-ownership.ts +0 -209
  66. package/src/daemon/stats.ts +0 -235
  67. package/src/daemon/verbs/config-validators.ts +0 -250
  68. package/src/daemon/verbs/index.ts +0 -706
  69. package/src/daemon/verbs/state-validators.ts +0 -249
  70. package/src/daemon/verbs/validator-registry.ts +0 -457
  71. package/src/demo/dsl.ts +0 -143
  72. package/src/demo/mock-data.ts +0 -67
  73. package/src/demo/statusline.json5 +0 -94
  74. package/src/dsl/node-registry.ts +0 -374
  75. package/src/dsl/render.ts +0 -803
  76. package/src/help-text.ts +0 -90
  77. package/src/index.ts +0 -210
  78. package/src/install/currency.ts +0 -197
  79. package/src/install/index.ts +0 -557
  80. package/src/proc/launch.ts +0 -459
  81. package/src/proc/stats-handle.ts +0 -13
  82. package/src/render/action.ts +0 -883
  83. package/src/render/active-segment.ts +0 -78
  84. package/src/render/diagnostic-style.ts +0 -23
  85. package/src/render/diagnostic-text.ts +0 -77
  86. package/src/render/error-glyph.ts +0 -53
  87. package/src/render/menu.ts +0 -257
  88. package/src/render/outcome-plan.ts +0 -45
  89. package/src/render/picker.ts +0 -372
  90. package/src/render/segment-color.ts +0 -74
  91. package/src/render/split-lines.ts +0 -51
  92. package/src/render/strip.ts +0 -228
  93. package/src/segments/cache.ts +0 -131
  94. package/src/segments/context.ts +0 -190
  95. package/src/segments/git.ts +0 -1084
  96. package/src/segments/metrics.ts +0 -187
  97. package/src/segments/pricing.ts +0 -452
  98. package/src/segments/session.ts +0 -23
  99. package/src/segments/tmux.ts +0 -74
  100. package/src/template-engine/cells.ts +0 -90
  101. package/src/template-engine/colors.ts +0 -124
  102. package/src/template-engine/engine.ts +0 -108
  103. package/src/template-engine/funcs.ts +0 -232
  104. package/src/template-engine/index.ts +0 -11
  105. package/src/template-engine/layout.ts +0 -133
  106. package/src/template-engine/scope.ts +0 -62
  107. package/src/template-engine/sparkline.ts +0 -79
  108. package/src/themes/index.ts +0 -20
  109. package/src/themes/palette-resolvers.ts +0 -84
  110. package/src/themes/policy.ts +0 -393
  111. package/src/utils/cache.ts +0 -206
  112. package/src/utils/claude.ts +0 -683
  113. package/src/utils/color-support.ts +0 -118
  114. package/src/utils/formatters.ts +0 -99
  115. package/src/utils/logger.ts +0 -5
  116. package/src/utils/outcome.ts +0 -33
  117. package/src/utils/schema-validator.ts +0 -126
  118. package/src/utils/single-flight.ts +0 -57
  119. package/src/utils/terminal-width.ts +0 -51
  120. package/src/utils/terminal.ts +0 -11
  121. package/src/utils/transcript-fs.ts +0 -279
  122. package/src/var-system/index.ts +0 -24
  123. package/src/var-system/sources.ts +0 -1047
  124. package/src/var-system/store.ts +0 -223
  125. package/src/var-system/types.ts +0 -57
  126. package/src/version.ts +0 -17
@@ -1,326 +0,0 @@
1
- // [LAW:one-source-of-truth] The preset RESOLUTION seam: the three questions a
2
- // render asks about presets — which one is active, what layout does it stage,
3
- // what display globals does it carry — answered in one place, from one map.
4
- //
5
- // A preset is to configuration what a look is to a theme, and that is the
6
- // implementation instruction, not an analogy: the selection rides the SAME
7
- // per-config-member seam looks rides (effectiveMemberName in themes/policy.ts),
8
- // the domain is threaded as DATA through the same perConfigDomainsFor the click
9
- // gate and the rendered options both read, and an unknown name collapses to the
10
- // same kind of always-present floor. Nothing here is parallel machinery
11
- // [LAW:one-type-per-behavior].
12
- //
13
- // WHERE THE PRESET LAYER SITS — the five-layer precedence chain, documented in
14
- // full in docs/interaction-authoring.md ("persist / reset"), which is the ONE
15
- // place it is written down:
16
- //
17
- // bundled default < user config file < persisted overrides
18
- // < ACTIVE PRESET < session pick < EDIT MODE
19
- //
20
- // The preset's position is forced by its lifetime, not chosen. Everything to
21
- // its left is resolved once per RenderCache entry (an entry serves many
22
- // sessions: the user file and the overrides file are both read in buildState);
23
- // everything from the preset rightward is resolved per render, because the pick
24
- // is per session. The chain is therefore monotonic in "how late is this
25
- // decided", which is why a preset overrides a persisted default (switching to a
26
- // "compact" arrangement must actually change padding, even for a user who once
27
- // persisted a padding they liked) while a session's own click still wins over
28
- // the preset (a click is later still).
29
- //
30
- // The same rule places the last rung (candybar-settings-ui-aok.5): edit mode's
31
- // `editGlobals` fragment is decided later than ANY session pick — a user picks
32
- // a style, and only afterwards enters edit mode — so it is the new rightmost
33
- // layer, and a session pick of "capsule" cannot survive into a mode whose whole
34
- // job is to stop segments reading as one continuous strip. It differs from
35
- // every rung to its left in LIFETIME rather than in kind: nothing writes it to
36
- // SessionState or the overrides layer, so leaving edit mode restores the
37
- // previous look with no save/restore path — the session's own pick was never
38
- // overwritten, only out-ranked [LAW:dataflow-not-control-flow]. The rung itself
39
- // is the `staged` parameter of effectiveGlobal (themes/policy.ts); this comment
40
- // is the ONE place the order is written down.
41
-
42
- // [LAW:one-way-deps] Type-only, so nothing is emitted and option-domain.ts (a
43
- // leaf that deliberately never imports dsl-types.ts) can import PRESET_NAMES
44
- // from here without a runtime cycle.
45
- import type {
46
- DslConfig,
47
- Globals,
48
- LayoutNode,
49
- PresetDecl,
50
- } from "./dsl-types.js";
51
- import { effectiveMemberName } from "../themes/policy.js";
52
- import { applyLayoutOps, decodeLayoutOp } from "./layout-ops.js";
53
-
54
- // [LAW:one-source-of-truth] The floor preset's name, spelled once. `looks` has
55
- // `"none"` (the identity adaptation); presets have `"default"` (the identity
56
- // fragment — the empty PresetDecl, i.e. the config's own root and globals
57
- // unchanged). The bundled default declares it and merge-by-name cannot remove
58
- // it, so every merged DslConfig carries it by construction.
59
- export const PRESET_FLOOR = "default";
60
-
61
- // [LAW:dataflow-not-control-flow] The floor's fragment is the EMPTY one — no
62
- // alternative root, no globals delta — which is exactly what "no preset chosen"
63
- // already means. It is a value the lookup below starts from rather than a case
64
- // the lookup handles, so the floor resolves whether or not any config declares
65
- // it. The bundled default declares `default: {}` for a different job: to put the
66
- // floor in the DOMAIN, so a `{{ menu }}` lists it and the derived click gate
67
- // admits a click that returns to it. Domain membership and resolvability are two
68
- // guarantees, and this one does not lean on the other.
69
- const FLOOR_FRAGMENT: PresetDecl = {};
70
-
71
- // [LAW:one-source-of-truth] THE preset domain: every arrangement selectable in
72
- // this config — the declared alternatives with the floor always among them. The
73
- // three readers that each need "the preset names" (perConfigDomainsFor, which
74
- // feeds the rendered options AND the derived click gate; registerDslConfig's
75
- // per-preset compile; the `presets` template binding) call this rather than
76
- // spelling `Object.keys(config.presets)` themselves, so the menu you can see,
77
- // the click the wire admits, and the layouts that were compiled are the same
78
- // set by construction — a click returning to the floor cannot be rejected by a
79
- // gate that forgot the floor was selectable.
80
- //
81
- // This is deliberately STRONGER than the looks seam it otherwise mirrors, where
82
- // "none" is in the domain only because the bundled stdlib ships it. The floor's
83
- // membership is a fact about the resolution, not about any config, so it is
84
- // stated here once rather than depending on a merge going right.
85
- export function presetNames(
86
- presets: Readonly<Record<string, unknown>>,
87
- ): readonly string[] {
88
- return [...new Set([PRESET_FLOOR, ...Object.keys(presets)])];
89
- }
90
-
91
- // [LAW:one-type-per-behavior] The preset domain's instance of the shared
92
- // per-config-member resolver — the same call shape effectiveLookName makes, one
93
- // dimension over. A stale or deleted name collapses to PRESET_FLOOR rather than
94
- // throwing, and the caller publishes this RESOLVED name as `preset.effective`
95
- // so the bar's label and the bar's layout can never disagree
96
- // [LAW:no-silent-failure].
97
- export function effectivePresetName(
98
- sessionPreset: string | null,
99
- globalsPreset: string | undefined,
100
- declaredPresets: Readonly<Record<string, PresetDecl>>,
101
- ): string {
102
- // [LAW:types-are-the-program] No staged rung, and the absence is enforced
103
- // rather than assumed: every globals fragment that could stage one — a
104
- // preset's own `globals`, edit mode's `editGlobals` — has `preset` swapped for
105
- // a rejection in its schema (loader/globals.ts), so "a fragment selected a
106
- // preset" is unrepresentable and there is nothing here to resolve against.
107
- // Which preset is active keeps exactly one authority.
108
- return effectiveMemberName(
109
- undefined,
110
- sessionPreset,
111
- globalsPreset,
112
- PRESET_FLOOR,
113
- declaredPresets,
114
- );
115
- }
116
-
117
- // [LAW:single-enforcer] The one place an effective preset NAME becomes the
118
- // fragment a render reads — a declared preset, or the floor's identity fragment
119
- // layered under them so the floor never depends on being declared. By the time
120
- // a name reaches here it must be one of those: effectivePresetName collapses
121
- // unknown names to the floor.
122
- // [LAW:no-defensive-null-guards] the throw is the loud failure for that broken
123
- // invariant (a caller that skipped the resolution and passed a raw session
124
- // string), never a silent empty-fragment fallback that would render one
125
- // arrangement while the bar's label named another — the exact contract
126
- // lookKeyByName holds for looks.
127
- export function presetByName(
128
- presets: Readonly<Record<string, PresetDecl>>,
129
- name: string,
130
- ): PresetDecl {
131
- const preset = { [PRESET_FLOOR]: FLOOR_FRAGMENT, ...presets }[name];
132
- if (preset === undefined) {
133
- throw new Error(
134
- `Preset "${name}" is not declared in this config — effectivePresetName ` +
135
- `collapses unknown names to "${PRESET_FLOOR}", which always resolves; ` +
136
- `a miss here means a raw name reached this function without going ` +
137
- `through that resolution`,
138
- );
139
- }
140
- return preset;
141
- }
142
-
143
- // A preset's layout AND the config path that layout was authored at, as a total
144
- // function of the name: a preset that declares no `root` stages the config's own
145
- // root, which lives at `root` and not under this preset's name.
146
- //
147
- // [LAW:one-source-of-truth] Both halves come from ONE decision on purpose. The
148
- // fallback used to be resolved here while the diagnostic path was spelled
149
- // separately at the compile site as `presets.<name>.root`, so the two disagreed
150
- // for exactly the configs that never opted into presets at all: a plain config
151
- // with no `presets:` block reported its own root's template errors under
152
- // `presets.default.root`, naming a node the author never wrote. Returning the
153
- // tree together with where it came from makes that drift unrepresentable rather
154
- // than merely fixed [FRAMING:representation].
155
- export function presetRoot(
156
- config: DslConfig,
157
- name: string,
158
- ): { readonly node: LayoutNode; readonly path: string } {
159
- // [LAW:dataflow-not-control-flow] A projection returning DATA, not a branch
160
- // around an operation: both arms yield the same shape, and the discriminator
161
- // (did this preset declare a root?) is a fact the fragment already carries.
162
- const own = presetByName(config.presets, name).root;
163
- return own === undefined
164
- ? { node: config.root, path: "root" }
165
- : { node: own, path: `presets.${name}.root` };
166
- }
167
-
168
- // [LAW:dataflow-not-control-flow] A preset's display globals, as a total
169
- // function of the name: the config's globals with the preset's shallow-merged
170
- // over them, per field — the SAME per-field cascade mergeWithDefault applies to
171
- // `globals` everywhere else in the loader, so a preset naming `padding` says
172
- // nothing about `charset`. The empty fragment yields the config's globals
173
- // unchanged, so again no floor-shaped branch.
174
- export function presetGlobals(config: DslConfig, name: string): Globals {
175
- return { ...config.globals, ...presetByName(config.presets, name).globals };
176
- }
177
-
178
- // [LAW:no-silent-failure] The persisted-overrides analogue of
179
- // applySegmentPaletteOverrides' stale-segment skip (src/config/loader/
180
- // merge.ts), one seam over: `configOverridesPath()` is ONE file shared by
181
- // every project on the machine (candybar-config-engine-71o.2), but `presets`
182
- // is a PER-CONFIG domain — the one other per-config domain besides `looks`.
183
- // A `persist: "preset"` click writes a name valid for the config it was
184
- // clicked against; loading a DIFFERENT project whose config never declared
185
- // that name must not fail the whole render. validateConfig's cross-ref check
186
- // (loader/cross-ref.ts) stays fatal for a hand-AUTHORED `globals.preset` —
187
- // that is a typo in a file the author can see and fix — but a value that
188
- // only exists because the overrides layer supplied it gets the SAME
189
- // treatment a stale SessionState pick already gets from effectivePresetName:
190
- // an undeclared name is not a pick at all, so the resolution moves on to the
191
- // next rung of the precedence chain — visibly, never a throw. (For a stale
192
- // SESSION pick that next rung is the config's own `globals.preset`, and only
193
- // then the floor; for the field dropped here it is whatever the config file
194
- // itself declares. Same rule, different starting rung —
195
- // see effectiveGlobal in themes/policy.ts.) Dropping the field here (so it
196
- // never reaches validateConfig) is what applies that rule to this layer too,
197
- // instead of a fatal error replacing the whole bar.
198
- export function sanitizePersistedPresetOverride(
199
- globalsOverride: Partial<Globals>,
200
- declaredPresets: Readonly<Record<string, PresetDecl>>,
201
- ): Partial<Globals> {
202
- const preset = globalsOverride.preset;
203
- if (preset === undefined || presetNames(declaredPresets).includes(preset)) {
204
- return globalsOverride;
205
- }
206
- // [LAW:no-defensive-null-guards] Not a broken-invariant throw: an
207
- // overrides file legitimately outlives any one config's preset names, so
208
- // this IS the expected shape, not a caller bug — a plain drop, mirroring
209
- // applySegmentPaletteOverrides' `continue` for the identical reason.
210
- const rest = { ...globalsOverride };
211
- delete rest.preset;
212
- return rest;
213
- }
214
-
215
- // [LAW:no-silent-failure] sanitizePersistedPresetOverride's twin for the
216
- // SAME machine-global overrides file, one field over: `presets.<name>.
217
- // rootOps` entries persist by NAME, and that name only has meaning against
218
- // the config it was clicked against. Loading a DIFFERENT project's config
219
- // — one that never declared "compact", say — would otherwise reach
220
- // applyPresetRootOpsOverrides -> presetRoot -> presetByName, which THROWS
221
- // for an undeclared name (correctly so for a hand-AUTHORED preset root: a
222
- // typo in a file the author can see). A stale overrides entry is not that
223
- // — it is the expected shape of a file that outlives any one project's
224
- // preset names — so it gets the SAME treatment sanitizePersistedPresetOverride
225
- // already gives `globals.preset`: dropped before replay ever sees it,
226
- // never a fatal error replacing an unrelated project's entire bar
227
- // (brandon-layout-edit-2gc.5 PR review).
228
- export function sanitizePersistedPresetRootOps(
229
- presetRootOps: Readonly<Record<string, readonly string[]>>,
230
- declaredPresets: Readonly<Record<string, PresetDecl>>,
231
- ): Readonly<Record<string, readonly string[]>> {
232
- const known = new Set(presetNames(declaredPresets));
233
- const entries = Object.entries(presetRootOps).filter(([name]) =>
234
- known.has(name),
235
- );
236
- // [LAW:carrying-cost] Identity return when nothing was dropped — the
237
- // common case (this daemon serves one project, or every persisted name
238
- // happens to be declared) costs one Set build and no new allocation.
239
- if (entries.length === Object.keys(presetRootOps).length) {
240
- return presetRootOps;
241
- }
242
- return Object.fromEntries(entries);
243
- }
244
-
245
- // [LAW:one-source-of-truth] brandon-layout-edit-2gc.1's replay step — the
246
- // SAME "patch an already-merged config" shape applySegmentPaletteOverrides
247
- // (src/config/loader/merge.ts) uses one field over, run at the SAME point in
248
- // RenderCache.buildState (after the globals/segment-palette overrides, before
249
- // validateConfig): for every preset with an accumulated op log, resolve its
250
- // CURRENT root the normal way (presetRoot — bundled/user root, or the
251
- // preset's own declared fragment) and replay the ops on top, writing the
252
- // result back as that preset's `root`. Every later reader (presetRoot,
253
- // registerDslConfig's per-preset compile, validateConfig's cross-ref walk)
254
- // sees the patched tree as if it had been authored that way — no second
255
- // resolution path [LAW:locality-or-seam].
256
- //
257
- // [LAW:no-silent-failure] exception: an op whose target/anchor names a
258
- // segment absent from the CURRENT tree is a no-op (layout-ops.ts's own
259
- // documented policy) — a validated action can only ever name a segment the
260
- // config declares at the time it was clicked, so a miss here only happens
261
- // after a LATER edit (a config change, or an earlier op in the same list)
262
- // already removed it. A malformed individual token (decodeLayoutOp -> null;
263
- // can only arise from hand-edited or previous-version state, never from this
264
- // process's own encodeLayoutOp) is filtered the same way, never applied.
265
- // [LAW:one-source-of-truth] brandon-layout-edit-2gc.5's diagnostic seam — the
266
- // ONE definition of "does this preset's rendered layout currently differ
267
- // from what's literally declared in the config file", read from the SAME
268
- // raw op-log record applyPresetRootOpsOverrides replays (never re-derived by
269
- // diffing the replayed tree against a fresh presetRoot() call, which would
270
- // be a second, weaker definition — a structural coincidence between two
271
- // unrelated edits could equal zero net ops and still disagree with the raw
272
- // log's own length). A name absent from the record (never edited) and a
273
- // name present with an empty token list (edited down to nothing, e.g. by
274
- // undo) are the SAME "not customized" — presence in the record only ever
275
- // means "this reload's overrides file had an entry", not "and it's non-
276
- // empty" [LAW:no-defensive-null-guards].
277
- //
278
- // Decodes each token the SAME way applyPresetRootOpsOverrides does, rather
279
- // than trusting the raw token COUNT — a token list where every token
280
- // decodes to null (malformed, or written by a previous protocol version)
281
- // is exactly the case applyPresetRootOpsOverrides itself treats as "no ops
282
- // to replay" (`ops.length === 0 → continue`, leaving the root untouched).
283
- // Counting raw tokens would disagree with that: "customized" would read
284
- // true over a tree that is, in fact, byte-identical to the literal
285
- // declared root — the inverted form of the drift this diagnostic exists to
286
- // catch.
287
- //
288
- // [LAW:carrying-cost] Verifies DECODE only, not whether the decoded op's
289
- // target/anchor still resolves against the CURRENT tree — that check would
290
- // need this to duplicate applyLayoutOps' own stale-target resolution
291
- // (a real walk of the tree) just to COUNT, on every render, a fact only a
292
- // LATER hand-authored config edit can produce (removing a segment a stored
293
- // op still names — see the stale-op comment on applyPresetRootOpsOverrides
294
- // above). A rare, later-edit-triggered false "customized" is the honest,
295
- // documented cost of keeping this an O(tokens) check rather than an O(tree)
296
- // one; the doc at docs/interaction-authoring.md's "Knowing when a preset's
297
- // layout has been edited" section states this same limit.
298
- export function presetIsCustomized(
299
- presetRootOps: Readonly<Record<string, readonly string[]>>,
300
- name: string,
301
- ): boolean {
302
- const tokens = presetRootOps[name];
303
- if (tokens === undefined) return false;
304
- return tokens.some((t) => decodeLayoutOp(t) !== null);
305
- }
306
-
307
- export function applyPresetRootOpsOverrides(
308
- config: DslConfig,
309
- presetRootOps: Readonly<Record<string, readonly string[]>>,
310
- ): DslConfig {
311
- const entries = Object.entries(presetRootOps).filter(
312
- ([, tokens]) => tokens.length > 0,
313
- );
314
- if (entries.length === 0) return config;
315
- const presets: Record<string, PresetDecl> = { ...config.presets };
316
- for (const [name, tokens] of entries) {
317
- const ops = tokens.map(decodeLayoutOp).filter((op) => op !== null);
318
- if (ops.length === 0) continue;
319
- const { node } = presetRoot(config, name);
320
- presets[name] = {
321
- ...presetByName(config.presets, name),
322
- root: applyLayoutOps(node, ops),
323
- };
324
- }
325
- return { ...config, presets };
326
- }