@liminis/editor 0.2.2 → 0.4.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/LICENSE +0 -13
- package/README.md +158 -103
- package/dist/app/editor/CorrectionPanelPlugin.js +10 -11
- package/dist/app/editor/DragHandlePlugin.js +1 -1
- package/dist/app/editor/Editor.js +17 -5
- package/dist/app/editor/SelectionContextMenuPlugin.js +4 -4
- package/dist/app/editor/nodes/C4Component.js +8 -10
- package/dist/app/editor/nodes/C4Node.d.ts +1 -1
- package/dist/app/editor/nodes/DiagramContextMenu.js +5 -5
- package/dist/headless.d.ts +3 -5
- package/dist/headless.js +2 -4
- package/dist/index.d.ts +1 -1
- package/dist/styles.css +428 -325
- package/docs/decisions/adr-92-lexical-peer-range-policy.md +152 -0
- package/docs/decisions/adr-93-liminis-editor-defined-aliases.md +315 -0
- package/docs/decisions/adr-98-invert-token-direction.md +309 -0
- package/package.json +28 -27
- package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +0 -35
- package/dist/app/editor/c4/C4InteractiveRenderer.js +0 -299
- package/dist/app/editor/c4/edge-clipping.d.ts +0 -24
- package/dist/app/editor/c4/edge-clipping.js +0 -139
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +0 -38
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +0 -112
- package/dist/app/editor/c4/layout.d.ts +0 -25
- package/dist/app/editor/c4/layout.js +0 -839
- package/dist/app/editor/c4/parser.d.ts +0 -19
- package/dist/app/editor/c4/parser.js +0 -410
- package/dist/app/editor/c4/render-to-string.d.ts +0 -24
- package/dist/app/editor/c4/render-to-string.js +0 -34
- package/dist/app/editor/c4/renderer.d.ts +0 -64
- package/dist/app/editor/c4/renderer.js +0 -569
- package/dist/app/editor/c4/types.d.ts +0 -203
- package/dist/app/editor/c4/types.js +0 -43
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# ADR-92: The Lexical Peer Range Bumps Forward by a Narrow Single Caret, Never a Wide Band
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-20
|
|
4
|
+
**Status:** Accepted
|
|
5
|
+
**Supersedes:** none
|
|
6
|
+
**Amends:** none
|
|
7
|
+
**Issue:** #92 (verveguy/liminis-editor)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Every published version of `@liminis/editor`, including `0.2.2`, declared its
|
|
12
|
+
Lexical peer range as `^0.44.0` across all twelve Lexical family packages
|
|
13
|
+
(`lexical` and eleven `@lexical/*` scoped packages). Lexical itself moved on
|
|
14
|
+
to `0.49.0`. Because Lexical is a `0.x` package, `^0.44.0` resolves under
|
|
15
|
+
semver to `>=0.44.0 <0.45.0` — it admits patch releases of 0.44 only, and
|
|
16
|
+
excludes every 0.45+ release regardless of whether that particular release
|
|
17
|
+
was actually breaking. The practical effect: this package pinned every host
|
|
18
|
+
application to a Lexical line five minors behind, with no published version
|
|
19
|
+
offering a path forward.
|
|
20
|
+
|
|
21
|
+
This surfaced while refreshing `liminis-app`'s dependencies
|
|
22
|
+
(verveguy/liminis#1026): the Lexical family was bumped 0.44.0 → 0.49.0 and
|
|
23
|
+
tested in isolation there. It passed `liminis-app`'s own typecheck, lint,
|
|
24
|
+
build, and 3275-test suite — and was still wrong, because `liminis-app` would
|
|
25
|
+
have shipped in violation of this package's declared peer contract. That
|
|
26
|
+
app's suite does not exercise the editor deeply enough for a green run to be
|
|
27
|
+
evidence of Lexical compatibility; the only suite that can answer that
|
|
28
|
+
question is this package's own.
|
|
29
|
+
|
|
30
|
+
The peer-range shape was an open question, not a foregone conclusion: widen
|
|
31
|
+
the range to admit 0.44 through 0.49 (matching the issue's own suggested
|
|
32
|
+
example), or narrow it to `^0.49.0` and drop 0.44–0.48 support outright.
|
|
33
|
+
|
|
34
|
+
## Decision
|
|
35
|
+
|
|
36
|
+
**The twelve Lexical peer ranges move to `^0.49.0` — a narrow single-caret
|
|
37
|
+
bump, not a wide multi-version band.** This is a deliberate, permanent
|
|
38
|
+
policy choice about how this package treats Lexical peer ranges going
|
|
39
|
+
forward, not a one-time bump: every future Lexical minor this package adopts
|
|
40
|
+
gets the same treatment — the range moves forward by exactly one caret,
|
|
41
|
+
paired with running the full suite against the new version, rather than
|
|
42
|
+
widening to claim a span of untested minors as compatible.
|
|
43
|
+
|
|
44
|
+
### Why narrow, not wide
|
|
45
|
+
|
|
46
|
+
**Widening a peer range later is patch-safe; narrowing one is breaking.**
|
|
47
|
+
Starting narrow keeps both options open — if a wide band is ever wanted, it
|
|
48
|
+
can be added without breaking anyone. Starting wide commits the package to a
|
|
49
|
+
claim it can only walk back by breaking whoever relied on the wide end.
|
|
50
|
+
|
|
51
|
+
**This package's existing range philosophy for `react` does not transfer to
|
|
52
|
+
Lexical.** The `//peerDependencies` manifest comment already documents that
|
|
53
|
+
ranges are "the compatibility contract, not the tested matrix," and that
|
|
54
|
+
narrowing `react`'s range to match its `devDependency` version "would only
|
|
55
|
+
reject working consumers." That reasoning is sound for React specifically
|
|
56
|
+
*because* React's untested span sits inside a **stable major** — a
|
|
57
|
+
regression there is a bug in React, not a permitted outcome of its
|
|
58
|
+
versioning scheme. Lexical is `0.x`, where semver explicitly permits every
|
|
59
|
+
minor release to be breaking. A range like `>=0.44.0 <0.50.0` would assert
|
|
60
|
+
compatibility with six minors, five of them never run against this
|
|
61
|
+
package's own suite — a materially different, and unjustified, claim.
|
|
62
|
+
|
|
63
|
+
**No known consumer wants Lexical 0.44.** Both known consumers of this
|
|
64
|
+
package (`liminis-app` and the package's own test/build pipeline) are moving
|
|
65
|
+
*toward* 0.49, not sitting on 0.44 and wanting to stay there. The wide
|
|
66
|
+
band's benefit — "don't reject a working consumer on an old version" — is
|
|
67
|
+
close to hypothetical here, while its cost — asserting untested compatibility
|
|
68
|
+
across four intermediate minors known to contain breaking changes (see
|
|
69
|
+
Consequences) — is real.
|
|
70
|
+
|
|
71
|
+
A wide band remains available later, but only backed by CI matrix testing
|
|
72
|
+
across the band's endpoints — that is separate work, out of scope for this
|
|
73
|
+
decision.
|
|
74
|
+
|
|
75
|
+
### What was verified before this decision was acted on
|
|
76
|
+
|
|
77
|
+
The twelve packages were bumped to `0.49.0` in both `peerDependencies` and
|
|
78
|
+
`devDependencies` (kept in lockstep, per this package's existing pairing
|
|
79
|
+
between the tested version and the declared floor), and the full
|
|
80
|
+
verification chain — `pnpm typecheck`, `pnpm lint`, `pnpm build`,
|
|
81
|
+
`pnpm test` (1591 tests), and `pnpm verify:package` (the packed-tarball
|
|
82
|
+
install-and-typecheck check against a fresh consumer) — ran clean with **no
|
|
83
|
+
source changes required**, despite Lexical's own changelog listing several
|
|
84
|
+
breaking changes in the 0.45–0.49 window (a `config()` protocol rework for
|
|
85
|
+
node classes in 0.49.0; `insertNodes` linebreak-handling changes and a
|
|
86
|
+
deprecated-export removal in 0.46.0; selection/backspace/reconcile changes
|
|
87
|
+
in 0.45.0). This package's sixteen custom Lexical node classes all define
|
|
88
|
+
explicit `getType`/`clone`/`importJSON` statics rather than relying on
|
|
89
|
+
auto-synthesis, which structurally avoided the highest-risk part of the
|
|
90
|
+
0.49.0 change; the rest of the changelog's items apparently do not intersect
|
|
91
|
+
this package's actual usage patterns, since the suite exercises them and
|
|
92
|
+
passed unmodified.
|
|
93
|
+
|
|
94
|
+
## Consequences
|
|
95
|
+
|
|
96
|
+
**Positive:**
|
|
97
|
+
|
|
98
|
+
- Consumers can now install this package alongside the current Lexical
|
|
99
|
+
release; the multi-minor stall the issue described cannot recur until
|
|
100
|
+
Lexical ships another minor this package hasn't yet adopted.
|
|
101
|
+
- The Lexical peer range's compatibility claim is now backed by an actual CI
|
|
102
|
+
run against the exact version it names — peer and devDependency are both
|
|
103
|
+
`0.49.0`, so no untested span exists in it. (React's own range still has
|
|
104
|
+
an intentionally untested lower half — peer `^19.2.0` vs. devDependency
|
|
105
|
+
`^19.2.5` — per the `//peerDependencies` note; that is unchanged by this
|
|
106
|
+
decision and follows the opposite, stable-major reasoning explained
|
|
107
|
+
above.)
|
|
108
|
+
- The policy is written down (`//lexicalPeerPolicy` in `package.json`, this
|
|
109
|
+
ADR, and the README's install section), so the next Lexical minor is a
|
|
110
|
+
known, bounded task rather than a rediscovery of this issue's reasoning.
|
|
111
|
+
|
|
112
|
+
**Negative:**
|
|
113
|
+
|
|
114
|
+
- **This is a breaking change.** `^0.44.0` and `^0.49.0` are non-overlapping
|
|
115
|
+
ranges; any consumer still on Lexical 0.44.0–0.48.x will see an
|
|
116
|
+
unmet-peer-dependency warning on installing the next release and must
|
|
117
|
+
upgrade its Lexical family to 0.49.0 first. That is the intended effect of
|
|
118
|
+
narrowing, not a regression.
|
|
119
|
+
- **The stall this issue describes will recur at every future Lexical
|
|
120
|
+
minor**, by design: this package's range never gets ahead of what it has
|
|
121
|
+
actually tested. The mitigation is that each recurrence is now a known,
|
|
122
|
+
bounded task (bump twelve versions, run the suite, adjust the range) with
|
|
123
|
+
a written rationale, rather than an accumulating five-minor gap discovered
|
|
124
|
+
incidentally by a downstream consumer's dependency refresh.
|
|
125
|
+
|
|
126
|
+
**Neutral:**
|
|
127
|
+
|
|
128
|
+
- `react`/`react-dom` peer ranges and all non-Lexical dependencies are
|
|
129
|
+
unaffected; this decision only concerns the Lexical family, and does not
|
|
130
|
+
reopen or alter the existing React range philosophy.
|
|
131
|
+
- No Lexical-API-floor guard test (the kind `tests/package-manifest-contract.test.ts`
|
|
132
|
+
already has for React) was added. That guard exists for React because its
|
|
133
|
+
range makes an untested-lower-half claim that needs policing; the narrow
|
|
134
|
+
Lexical range makes no such claim, so there is nothing analogous to guard
|
|
135
|
+
against.
|
|
136
|
+
|
|
137
|
+
## References
|
|
138
|
+
|
|
139
|
+
- Issue #92 (this decision), verveguy/liminis#1026 (the originating
|
|
140
|
+
`liminis-app` dependency refresh that surfaced the stall)
|
|
141
|
+
- `package.json` — the `//peerDependencies` and `//lexicalPeerPolicy`
|
|
142
|
+
manifest comments; the twelve `peerDependencies`/`devDependencies` entries
|
|
143
|
+
- `README.md` — "Install" section, the Lexical `pnpm add` block and the
|
|
144
|
+
peer-range policy callout
|
|
145
|
+
- `CHANGELOG.md` — the `## Unreleased` / `### Breaking changes` entry
|
|
146
|
+
recording this bump
|
|
147
|
+
- `docs/decisions/adr-078.md`, `docs/decisions/adr-079.md` — prior ADRs on
|
|
148
|
+
consumer-facing package surface, cited as precedent for treating this as
|
|
149
|
+
ADR-worthy
|
|
150
|
+
- Related: #78 (a required member made 0.2.0 type-level breaking), #79
|
|
151
|
+
(token definitions documented as public API) — prior instances of
|
|
152
|
+
consumer-facing surface needing explicit treatment
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# ADR-93: `--liminis-editor-*` Names Are Defined as Aliases of Their Legacy Names
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-20
|
|
4
|
+
**Status:** Accepted (amended by ADR-98 — see the note marked *Amended by ADR-98* below; the alias direction this ADR chose is reversed)
|
|
5
|
+
**Supersedes:** none
|
|
6
|
+
**Amends:** none
|
|
7
|
+
**Amended by:** ADR-98 (#98 inverts this ADR's alias direction — the legacy names stop carrying the real defaults, and become shims to `--liminis-editor-*` instead)
|
|
8
|
+
**Issue:** #93 (verveguy/liminis-editor)
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
ADR-087 (#51) renamed every *consumption* site to `--liminis-editor-*`,
|
|
13
|
+
deliberately leaving every *definition* site (`:root`, `.dark`, the
|
|
14
|
+
`@media print` block) declaring the old, pre-`0.2.0` names. ADR-092 (#79)
|
|
15
|
+
built a checked-in baseline guarding those definitions, and explicitly
|
|
16
|
+
deferred a third item: whether the package should ever *define*
|
|
17
|
+
`--liminis-editor-*` names directly, with the legacy names kept as an
|
|
18
|
+
alias layer underneath.
|
|
19
|
+
|
|
20
|
+
That gap is not theoretical. Zusammen, a downstream host, reads this
|
|
21
|
+
package's definitions directly — `var(--vscode-background)`,
|
|
22
|
+
`var(--vscode-border)`, etc. — mapping its own design-system tokens onto
|
|
23
|
+
them rather than overriding them
|
|
24
|
+
([verveguy/zusammen#129](https://github.com/verveguy/zusammen/issues/129)).
|
|
25
|
+
It wants to migrate those reads onto `--liminis-editor-*` instead, but
|
|
26
|
+
before this change there was nothing to migrate to: every row in the
|
|
27
|
+
README's token table read "Has a default: No (inline fallback only)."
|
|
28
|
+
|
|
29
|
+
**The naive fix is the trap ADR-087 already reasoned through once, now on
|
|
30
|
+
the definition side.** Adding `--liminis-editor-foreground: <default>;` to
|
|
31
|
+
the package's own `:root` unconditionally would resolve every one of the
|
|
32
|
+
250 consumption chains (`var(--liminis-editor-foreground, var(--vscode-foreground))`)
|
|
33
|
+
at the new name before ever reaching the old one — silently disabling the
|
|
34
|
+
ADR-087 fallback layer. A host like `liminis-app`, which supplies only
|
|
35
|
+
`--color-primary`/`--color-muted-foreground` (its own Tailwind `@theme`
|
|
36
|
+
tokens, coincidentally the fallback names `C4Component.tsx` already uses),
|
|
37
|
+
would stop tracking its own overrides with no error and no failing test —
|
|
38
|
+
exactly the failure mode ADR-092 exists to guard against.
|
|
39
|
+
|
|
40
|
+
Two further, non-obvious problems surfaced during implementation, not
|
|
41
|
+
anticipated by the plan:
|
|
42
|
+
|
|
43
|
+
1. **A definition-side alias's own `var()` reference is textually
|
|
44
|
+
indistinguishable from a consumption site.** `scripts/lib/theming-tokens.mjs`'s
|
|
45
|
+
`consumptionSitesIn()` scans raw text for depth-0 `var(--x` occurrences
|
|
46
|
+
with no awareness of whether the enclosing statement is a custom-property
|
|
47
|
+
*declaration* (`--liminis-editor-foreground: var(--vscode-foreground);`)
|
|
48
|
+
or an ordinary property/JS-style value (`color: var(--liminis-editor-foreground, ...)`).
|
|
49
|
+
Adding the alias block as originally planned would have made every one
|
|
50
|
+
of the ~40 flat-aliased legacy names register as a newly "consumed"
|
|
51
|
+
token, growing the README table and `TOKEN_DESCRIPTIONS` maintenance
|
|
52
|
+
surface for tokens no host-facing code actually reads — a regression
|
|
53
|
+
against this issue's own explicit Out of Scope ("does not add or remove
|
|
54
|
+
rows" from the consumed/documented table).
|
|
55
|
+
2. **A CSS block comment containing a literal `*/` substring inside its
|
|
56
|
+
text terminates early.** An early draft of the new `:root` block's
|
|
57
|
+
header comment read `--vscode-*/--slashmd-*/--checkbox-*` — the `*/`
|
|
58
|
+
embedded in `--vscode-*/--slashmd-*` closed the comment there, both for
|
|
59
|
+
real CSS parsers and for this repo's own `stripCssComments()`, leaving
|
|
60
|
+
the remainder of the intended comment as live (invalid) CSS text. This
|
|
61
|
+
would have shipped a real authoring bug, not just a test artifact.
|
|
62
|
+
|
|
63
|
+
## Decision
|
|
64
|
+
|
|
65
|
+
> **Amended 2026-08-22 by ADR-98 (#98) — this clause no longer holds.** The
|
|
66
|
+
> direction chosen here is reversed: `--liminis-editor-*` names now carry the
|
|
67
|
+
> real defaults, and the legacy names below become one-line shims
|
|
68
|
+
> (`<legacy>: var(--liminis-editor-x);`). This ADR's own reasoning for why
|
|
69
|
+
> the *opposite* direction was unsafe — "adding `--liminis-editor-foreground:
|
|
70
|
+
> <default>` unconditionally would resolve every consumption chain at the
|
|
71
|
+
> new name before ever reaching the old one" — no longer applies once every
|
|
72
|
+
> internal consumption site reads `--liminis-editor-*` only (ADR-98's FR-003),
|
|
73
|
+
> which removes the nested legacy fallback this ADR's Decision explicitly
|
|
74
|
+
> chose to leave in place. This also reverses this ADR's own non-breaking
|
|
75
|
+
> guarantee below for one direction: a host that only *reads* a legacy name
|
|
76
|
+
> still gets a real value (the shim resolves fine), but a host that only
|
|
77
|
+
> *sets* a legacy name no longer themes the editor, since nothing internal
|
|
78
|
+
> reads that name to pick the override up — verified empirically, not
|
|
79
|
+
> assumed, and accepted as an intentional breaking change. See ADR-98's
|
|
80
|
+
> "Verified, not assumed" section for the full rationale. The rest of this
|
|
81
|
+
> section is kept as the historical record of what was decided and why in
|
|
82
|
+
> 2026-08-20, not edited to match the new direction.
|
|
83
|
+
|
|
84
|
+
**Legacy names keep carrying the real defaults. Each `--liminis-editor-*`
|
|
85
|
+
name becomes a new declaration that reads its legacy name via `var()`** —
|
|
86
|
+
`--liminis-editor-foreground: var(--vscode-foreground);` — added in a new
|
|
87
|
+
`:root`/`.dark` block placed immediately after the package's existing
|
|
88
|
+
`:root`/`.dark` blocks in `src/styles.css`. This is the direction the
|
|
89
|
+
spec's Background illustrated and Research confirmed by inspecting this
|
|
90
|
+
file's cascade structure directly: a CSS custom property resolves against
|
|
91
|
+
the *element's own* cascade at the point of use, not at declaration time,
|
|
92
|
+
so a single `:root`-level alias re-resolves correctly through `.dark`'s and
|
|
93
|
+
the `@media print` block's overrides of the *legacy* name it points to —
|
|
94
|
+
no per-block alias duplication is needed, and neither `.dark` nor the print
|
|
95
|
+
block needed to change for the ~40 tokens with a real existing declaration.
|
|
96
|
+
|
|
97
|
+
**All 59 renamed tokens (`PREVIOUS_NAME`'s full set) get a real declaration
|
|
98
|
+
in this pass, not only the two the originating issue named.** FR-001 says
|
|
99
|
+
"every token it currently consumes"; inspection of every consumption site
|
|
100
|
+
found 19 tokens with no existing `:root`/`.dark` legacy declaration to
|
|
101
|
+
alias to, not 2, split into three groups:
|
|
102
|
+
|
|
103
|
+
- **2 tokens** (`--vscode-input-bg`, `--vscode-foreground-muted`) resolve
|
|
104
|
+
today through an existing *nested* fallback at their own consumption
|
|
105
|
+
site. Their alias preserves that chain rather than flattening it:
|
|
106
|
+
`--liminis-editor-input-bg: var(--vscode-input-bg, var(--vscode-code-bg));`.
|
|
107
|
+
- **13 tokens** have no CSS custom property backing anywhere — only a
|
|
108
|
+
literal inline fallback at each consumption site (e.g.
|
|
109
|
+
`var(--vscode-focus-border, #007acc)`). Their alias reproduces that
|
|
110
|
+
literal as the alias's own fallback:
|
|
111
|
+
`--liminis-editor-focus-border: var(--vscode-focus-border, #007acc);` —
|
|
112
|
+
a host supplying nothing sees the same literal as before (FR-002); a
|
|
113
|
+
host supplying the legacy name still wins (FR-003). 6 of these 13 vary by
|
|
114
|
+
theme via a JS `isDark`/`dark` ternary at their consumption sites
|
|
115
|
+
(`button-background`, all 5 `menu-*` tokens); those 6 get a matching
|
|
116
|
+
`.dark` override in the new block, mirroring the same light/dark split
|
|
117
|
+
their consumption sites already express in JS.
|
|
118
|
+
- **4 tokens** (`--color-primary`, `--color-primary-100`,
|
|
119
|
+
`--color-muted-foreground`, `--color-muted-100`) are `liminis-app`'s own
|
|
120
|
+
Tailwind `@theme` brand tokens, for which the package only ever supplied
|
|
121
|
+
a literal final fallback, never asserted ownership. They are aliased
|
|
122
|
+
identically to the 13 above — the mechanism doesn't newly assert
|
|
123
|
+
ownership, it makes the existing fallback literal reachable under the new
|
|
124
|
+
name too.
|
|
125
|
+
|
|
126
|
+
**Two of the 17 no-backing tokens have a real cross-site literal
|
|
127
|
+
inconsistency, discovered by direct inspection (one — `--vscode-errorForeground`
|
|
128
|
+
— was already flagged by Specify; the second — `--vscode-toolbar-hoverBackground`
|
|
129
|
+
— was not):**
|
|
130
|
+
|
|
131
|
+
| Token | Sites | Canonical value chosen |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `--vscode-errorForeground` | `#f14c4c` (2 sites: `.editor-link-broken`, `:hover`) vs. `#f44336` (2 sites: `.block-delete-button:hover .block-delete-icon`, `.search-close-button:hover`) | `#f14c4c` — tie broken toward the more semantically central "broken link" indicator over two hover-only chrome details |
|
|
134
|
+
| `--vscode-toolbar-hoverBackground` | `rgba(128, 128, 128, 0.15)` (2 sites) vs. `rgba(128, 128, 128, 0.1)` (1 site, `.drag-handle:hover .drag-handle-icon`) | `rgba(128, 128, 128, 0.15)` — majority (2 of 3) |
|
|
135
|
+
|
|
136
|
+
Once a token has a single real definition, it necessarily wins over every
|
|
137
|
+
site's own inline fallback (fallback only applies when the property is
|
|
138
|
+
genuinely unset) — there is no mechanism that both defines the token
|
|
139
|
+
(FR-001) and preserves two different resolved values at different sites.
|
|
140
|
+
**This unifies 3 sites' rendered color to the canonical value, a small,
|
|
141
|
+
real, intentional visual change**, called out here per this repository's
|
|
142
|
+
convention of recording what a decision costs. The three affected call
|
|
143
|
+
sites (`src/styles.css:1148`, `:1188`, `:1704`) each carry a one-line
|
|
144
|
+
comment noting they are superseded by the new canonical default. The
|
|
145
|
+
spec's Out of Scope section explicitly permits this as a side effect of
|
|
146
|
+
choosing FR-001's value, not as a design goal in its own right.
|
|
147
|
+
|
|
148
|
+
> **Amended 2026-08-22 by ADR-98 (#98) — this clause no longer holds.** Every
|
|
149
|
+
> consumption site's nested legacy fallback, called "provably dead code"
|
|
150
|
+
> below, is removed by ADR-98 — precisely because leaving it in place would
|
|
151
|
+
> mean this package still textually reads the legacy vocabulary ADR-98's
|
|
152
|
+
> FR-003 forbids. The "materially larger, coordinated change deferred out of
|
|
153
|
+
> this issue's scope" this paragraph anticipated is ADR-98 itself.
|
|
154
|
+
|
|
155
|
+
**Consumption sites are left untouched.** All 224 `styles.css` sites and 26
|
|
156
|
+
`.tsx` sites keep their existing `var(--liminis-editor-x, var(--old-name-x, ...))`
|
|
157
|
+
chains. Simplifying them to a single `var(--liminis-editor-x)` call was
|
|
158
|
+
identified as optional cleanup (spec Assumptions) but would strip every
|
|
159
|
+
renamed token's `immediateFallback`, breaking `resolvesToPreviousName()`
|
|
160
|
+
(FR-011's existing drift guard) for all 59 tokens unless that guard were
|
|
161
|
+
also redesigned to check the *definition* site's alias target instead —
|
|
162
|
+
a materially larger, coordinated change deferred out of this issue's scope.
|
|
163
|
+
The now-redundant nested legacy fallback at each site is provably dead code
|
|
164
|
+
once its definition-side alias exists, left in place rather than removed.
|
|
165
|
+
|
|
166
|
+
**`scripts/lib/theming-tokens.mjs`'s `consumptionSitesIn()` gains a
|
|
167
|
+
declaration-value check.** A new helper,
|
|
168
|
+
`isCustomPropertyDeclarationValue(text, varStartIndex)`, walks back from a
|
|
169
|
+
depth-0 `var(` to the nearest statement boundary (`;`, `{`, or `}`) and
|
|
170
|
+
tests whether everything between that boundary and the `var(` is exactly
|
|
171
|
+
`--name:` — the shape only a custom-property declaration's value position
|
|
172
|
+
can have (an ordinary CSS property name never starts with `--`). Each
|
|
173
|
+
site now carries an `isDeclarationValue` flag; `consumedTokens()`'s Pass 1
|
|
174
|
+
skips depth-0 sites where it is true. This keeps the "consumed" inventory —
|
|
175
|
+
and therefore the README table's row set and the `TOKEN_DESCRIPTIONS`
|
|
176
|
+
maintenance surface — exactly as it was before this issue, satisfying its
|
|
177
|
+
Out of Scope constraint, while still letting `defaultedTokens()` (a
|
|
178
|
+
separate, unchanged function that just scans for any `--name:` declaration)
|
|
179
|
+
pick up all 59 new aliases as newly "defined."
|
|
180
|
+
|
|
181
|
+
**No other change to `theming-tokens.mjs` or its `.d.mts`.**
|
|
182
|
+
`isCustomPropertyDeclarationValue` is internal, not exported;
|
|
183
|
+
`defaultedTokens()` needed no change to recognize the new aliases; the
|
|
184
|
+
ADR-092 baseline guard, `resolvesWithoutHost()`, and `resolvesToPreviousName()`
|
|
185
|
+
all continue to operate over the same "consumed"/"defined" definitions they
|
|
186
|
+
already had, now simply seeing a larger defined set.
|
|
187
|
+
|
|
188
|
+
## Consequences
|
|
189
|
+
|
|
190
|
+
**Good:**
|
|
191
|
+
|
|
192
|
+
- A host can now read `--liminis-editor-x` directly and get a real value in
|
|
193
|
+
every override state — the entire point of this issue and the
|
|
194
|
+
prerequisite for zusammen#129's read migration (FR-001, SC-001, SC-006).
|
|
195
|
+
- A host supplying only a legacy name **at `:root`/`document.documentElement`**
|
|
196
|
+
is unaffected: the alias's `var()` re-resolves through that name's own
|
|
197
|
+
`.dark`/`@media print` overrides at the point of use, verified directly
|
|
198
|
+
against this file's actual cascade structure rather than assumed from the
|
|
199
|
+
general CSS-custom-property theory alone (FR-002, FR-003, User Story 2).
|
|
200
|
+
This is the standard integration point and the one this package's own
|
|
201
|
+
e2e coverage exercises. It does not extend to a legacy override scoped to
|
|
202
|
+
an arbitrary descendant element — see Bad/accepted below.
|
|
203
|
+
- The ADR-092 baseline guard now protects all 59 new alias declarations the
|
|
204
|
+
same way it already protected the legacy ones — a future accidental
|
|
205
|
+
deletion of an alias fails `pnpm test`, naming the token (FR-005, US4).
|
|
206
|
+
- The declaration-value fix to `consumptionSitesIn()` is general: it holds
|
|
207
|
+
for any future alias declaration of this shape without further
|
|
208
|
+
special-casing, not just the 59 added here.
|
|
209
|
+
- The premature-comment-close bug was caught by the same regenerated test
|
|
210
|
+
suite that caught the consumption-site pollution, before either shipped —
|
|
211
|
+
the CI guards this issue extends are what surfaced both problems.
|
|
212
|
+
|
|
213
|
+
**Bad / accepted:**
|
|
214
|
+
|
|
215
|
+
- **The `:root`-only alias does not track a legacy-name override scoped to
|
|
216
|
+
a descendant element below `:root`** (e.g. a per-widget wrapper div a
|
|
217
|
+
host applies its own theme vars to, rather than `document.documentElement`
|
|
218
|
+
itself) — found during PR review (CodeRabbit), confirmed empirically in
|
|
219
|
+
a running Electron app, not merely reasoned about. A custom property
|
|
220
|
+
inherits its parent's already-*computed* value, not the `var()`
|
|
221
|
+
expression that produced it, so the alias's `var(--legacy-x)` is fixed
|
|
222
|
+
using `:root`'s own value of `--legacy-x` and does not re-evaluate for a
|
|
223
|
+
change made further down the tree. Two alternatives were tried and
|
|
224
|
+
rejected, each empirically, in a running app:
|
|
225
|
+
- **Re-declaring the alias on every element (`*`)** does fix the
|
|
226
|
+
descendant case, but then an element's own `*`-matched declaration
|
|
227
|
+
shadows whatever `--liminis-editor-x` value it would otherwise have
|
|
228
|
+
inherited from an ancestor — including a host's own direct
|
|
229
|
+
`--liminis-editor-x` override set on that ancestor. This broke User
|
|
230
|
+
Story 3 (a directly-set `--liminis-editor-*` name must win), which is
|
|
231
|
+
higher-confidence/already-tested behavior — confirmed by a real,
|
|
232
|
+
reproducible test failure, not a theoretical concern.
|
|
233
|
+
- **Scoping the redeclaration to the package's own root wrapper class**
|
|
234
|
+
(`.editor-app-root`) reduces but does not eliminate that same
|
|
235
|
+
shadowing (still breaks a `--liminis-editor-*` override set above
|
|
236
|
+
`.editor-app-root`), and is additionally unreliable on its own terms:
|
|
237
|
+
not every integration renders that wrapper — this repo's own
|
|
238
|
+
`examples/electron` shell mounts a lower-level component without it,
|
|
239
|
+
so the alias would go undeclared there, confirmed by inspecting the
|
|
240
|
+
actual DOM in a running instance.
|
|
241
|
+
- No mechanism was found that recomputes a legacy-name change made
|
|
242
|
+
anywhere below `:root` (User Story 2) while also respecting a
|
|
243
|
+
`--liminis-editor-*` override set on any ancestor of the read element
|
|
244
|
+
(User Story 3) — CSS custom-property inheritance passes down an
|
|
245
|
+
already-resolved value, not a live formula, so a property can prefer
|
|
246
|
+
"inherit if my ancestor set one" or "always recompute from a
|
|
247
|
+
dependency," but not both conditionally per-element without a
|
|
248
|
+
mechanism this package doesn't have (e.g. `@property` plus JS
|
|
249
|
+
reconciliation, well beyond this issue's scope). **The `:root`-only
|
|
250
|
+
design is kept, and this scope limitation is accepted and documented**
|
|
251
|
+
here and in `src/styles.css`'s alias-block comment, per this
|
|
252
|
+
repository's convention of recording what a decision costs rather than
|
|
253
|
+
silently narrowing FR-002/FR-003's stated guarantee. Extending it to
|
|
254
|
+
arbitrary DOM scope, if a specific host needs it, is deferred to a
|
|
255
|
+
follow-up issue — it is a materially larger design problem than this
|
|
256
|
+
ADR anticipated.
|
|
257
|
+
- Three consumption sites (`--vscode-errorForeground` at 2 sites,
|
|
258
|
+
`--vscode-toolbar-hoverBackground` at 1 site) render a different,
|
|
259
|
+
intentionally-unified color than before this change. A host relying on
|
|
260
|
+
the specific pre-existing `#f44336`/`0.1`-alpha rendering at exactly
|
|
261
|
+
those sites will see a small visual shift. This is an unavoidable
|
|
262
|
+
consequence of giving these two tokens a single real definition (FR-001)
|
|
263
|
+
when their own consumption sites already disagreed with each other —
|
|
264
|
+
there is no mechanism that satisfies FR-001 without picking one value.
|
|
265
|
+
- The checked-in baseline grows from 41 to 100 entries in one change. Per
|
|
266
|
+
ADR-092's own design the guard is one-directional (`missing` fails,
|
|
267
|
+
`added` never does), so this is a mechanical `pnpm docs:theming-baseline`
|
|
268
|
+
diff, not a CI risk — but it is a large diff for a human reviewer to
|
|
269
|
+
read line-by-line.
|
|
270
|
+
- Every one of the 250 existing consumption sites keeps a nested legacy
|
|
271
|
+
fallback that is now provably redundant dead code (the definition-side
|
|
272
|
+
alias means the fallback branch can never be reached in practice). Removing
|
|
273
|
+
it was explicitly deferred (see Decision) rather than done here, so this
|
|
274
|
+
verbosity persists in `styles.css` and the five `.tsx` files until a
|
|
275
|
+
future change picks it up.
|
|
276
|
+
- `consumptionSitesIn()`'s declaration-value detection is a text-boundary
|
|
277
|
+
heuristic (nearest `;`/`{`/`}` before the `var(`), not a real CSS parser.
|
|
278
|
+
It is correct for every declaration in this codebase today (all
|
|
279
|
+
single-line), but a future multi-line custom-property declaration split
|
|
280
|
+
across a statement boundary could evade it.
|
|
281
|
+
|
|
282
|
+
**Neutral:**
|
|
283
|
+
|
|
284
|
+
- The `@media print` block needed no changes: its `:root, .dark { ... }`
|
|
285
|
+
selector already overrides every legacy name it touches, and each new
|
|
286
|
+
alias's `var()` reference re-resolves through that override the same way
|
|
287
|
+
it re-resolves through `.dark`'s.
|
|
288
|
+
- `liminis-app`'s own `--color-primary`/`--color-muted-foreground` overrides
|
|
289
|
+
are unaffected either way — the 4 brand-token aliases only make their
|
|
290
|
+
existing fallback literal reachable under the new name too, without
|
|
291
|
+
changing what either name resolves to when the host supplies its usual
|
|
292
|
+
overrides (SC-003).
|
|
293
|
+
|
|
294
|
+
## References
|
|
295
|
+
|
|
296
|
+
- Issue #93 (this decision); Issue #79 / ADR-092's "Not decided here" —
|
|
297
|
+
this decision's direct origin (Item 3)
|
|
298
|
+
- `docs/decisions/adr-087.md` — the consumption-side rename and fallback
|
|
299
|
+
design this decision extends to the definition side, and the trap
|
|
300
|
+
("that alternative is broken...") this decision's alias direction avoids
|
|
301
|
+
repeating on the definition side
|
|
302
|
+
- `docs/decisions/adr-092.md` — the checked-in baseline guard this
|
|
303
|
+
decision's new declarations are added to
|
|
304
|
+
- `src/styles.css` — the new `:root`/`.dark` alias block (after the
|
|
305
|
+
existing `.dark` block); the three superseded-literal comments at the
|
|
306
|
+
drag-handle-hover, block-delete-hover, and search-close-hover sites
|
|
307
|
+
- `scripts/lib/theming-tokens.mjs` — `isCustomPropertyDeclarationValue()`,
|
|
308
|
+
`consumptionSitesIn()`'s new `isDeclarationValue` field, and
|
|
309
|
+
`consumedTokens()`'s Pass 1 filter
|
|
310
|
+
- `scripts/lib/theming-defined-tokens-baseline.json` — regenerated via
|
|
311
|
+
`pnpm docs:theming-baseline`, 41 → 100 entries
|
|
312
|
+
- `README.md`, "Theming: CSS custom properties" — regenerated table and
|
|
313
|
+
hand-rewritten prose describing the alias mechanism
|
|
314
|
+
- [verveguy/zusammen#129](https://github.com/verveguy/zusammen/issues/129)
|
|
315
|
+
— the downstream migration this decision unblocks
|