@celestia-island/hikari 0.55.94 → 0.55.95
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/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Source contract: the JSON tree's spacing rhythm is token-based, not
|
|
8
|
+
* ad-hoc. The 2026-09-30 normalization replaced the off-scale gaps
|
|
9
|
+
* (2px toggle margin, 0.35em colon, 0.75em type hint) with design
|
|
10
|
+
* units and added the toggle's first-line centering formula — this
|
|
11
|
+
* contract keeps the file from drifting back to magic values, which is
|
|
12
|
+
* exactly the "crooked collapsed/expanded alignment" regression class
|
|
13
|
+
* the normalization fixed.
|
|
14
|
+
*/
|
|
15
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const scss = readFileSync(join(here, "HkJsonTree.scss"), "utf-8");
|
|
17
|
+
|
|
18
|
+
describe("HkJsonTree spacing contract", () => {
|
|
19
|
+
it("keeps the toggle exactly one spacing unit from the row text", () => {
|
|
20
|
+
const block = scss.slice(scss.indexOf(".s-jt-toggle {"), scss.indexOf(".s-jt-toggle[data-parent]"));
|
|
21
|
+
expect(block).toContain("margin-inline-end: var(--space-4");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("centers the toggle box on the first text line by formula", () => {
|
|
25
|
+
const block = scss.slice(scss.indexOf(".s-jt-toggle {"), scss.indexOf(".s-jt-toggle[data-parent]"));
|
|
26
|
+
expect(block).toContain("align-self: flex-start");
|
|
27
|
+
expect(block).toContain("margin-block-start: max(0px, calc((1.55em - var(--jt-toggle, 14px)) / 2))");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("keeps every --jt-toggle fallback at the 14px the formula assumes", () => {
|
|
31
|
+
// Regex sweep, not a blacklist: ANY fallback other than 14px (today
|
|
32
|
+
// or added later) would de-center the box by the delta.
|
|
33
|
+
const fallbacks = [...scss.matchAll(/var\(--jt-toggle,\s*([^)]+)\)/g)].map((m) => m[1].trim());
|
|
34
|
+
expect(fallbacks.length, "fallback sites were scanned").toBeGreaterThan(2);
|
|
35
|
+
expect(fallbacks.every((f) => f === "14px"), `fallbacks = ${fallbacks}`).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("pins the depth step to exactly one toggle width", () => {
|
|
39
|
+
const tsx = readFileSync(join(here, "HkJsonTree.tsx"), "utf-8");
|
|
40
|
+
expect(tsx).toContain("const JT_INDENT = JT_TOGGLE;");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("keeps the key colon gap on one spacing unit", () => {
|
|
44
|
+
const block = scss.slice(scss.indexOf(".s-jt-colon {"), scss.indexOf(".s-jv-null"));
|
|
45
|
+
expect(block).toContain("margin-inline-end: var(--space-4");
|
|
46
|
+
expect(block).not.toContain("0.35em");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("keeps the type hint gap on two spacing units", () => {
|
|
50
|
+
const block = scss.slice(scss.indexOf(".s-jt-type {"), scss.indexOf(".s-jt-badge {"));
|
|
51
|
+
expect(block).toContain("margin-inline-start: var(--space-8");
|
|
52
|
+
expect(block).not.toContain("0.75em");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("leaves no off-scale magic gaps anywhere in the tree stylesheet", () => {
|
|
56
|
+
expect(scss).not.toMatch(/margin-inline-end: 0\.\d+em/);
|
|
57
|
+
expect(scss).not.toMatch(/margin-inline-start: 0\.\d+em/);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -69,14 +69,20 @@
|
|
|
69
69
|
display: inline-flex;
|
|
70
70
|
align-items: center;
|
|
71
71
|
justify-content: center;
|
|
72
|
-
width: var(--jt-toggle,
|
|
73
|
-
height: var(--jt-toggle,
|
|
72
|
+
width: var(--jt-toggle, 14px);
|
|
73
|
+
height: var(--jt-toggle, 14px);
|
|
74
74
|
flex-shrink: 0;
|
|
75
|
-
|
|
75
|
+
/* Toggle → text is ONE spacing unit everywhere (leaf, collapsed and
|
|
76
|
+
expanded parents alike) — the collapsed row and its children read
|
|
77
|
+
on the same rhythm instead of ad-hoc gaps. */
|
|
78
|
+
margin-inline-end: var(--space-4, 0.25rem);
|
|
76
79
|
/* The toggle box has no text baseline of its own — pin it to the line
|
|
77
|
-
top
|
|
78
|
-
|
|
80
|
+
top and center it on the FIRST text line by formula (line-height
|
|
81
|
+
1.55 × font-size vs the fixed box), so the chevron/leaf sits on the
|
|
82
|
+
text's optical center in collapsed and expanded rows alike instead
|
|
83
|
+
of floating above the baseline group. */
|
|
79
84
|
align-self: flex-start;
|
|
85
|
+
margin-block-start: max(0px, calc((1.55em - var(--jt-toggle, 14px)) / 2));
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
.s-jt-toggle[data-parent] {
|
|
@@ -117,7 +123,7 @@
|
|
|
117
123
|
|
|
118
124
|
.s-jt-colon {
|
|
119
125
|
color: rgb(var(--color-muted));
|
|
120
|
-
margin-inline-end: 0.
|
|
126
|
+
margin-inline-end: var(--space-4, 0.25rem);
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
.s-jv-null {
|
|
@@ -152,7 +158,7 @@
|
|
|
152
158
|
font-size: var(--text-3xs, 0.5rem);
|
|
153
159
|
font-style: italic;
|
|
154
160
|
font-weight: 400;
|
|
155
|
-
margin-inline-start: 0.
|
|
161
|
+
margin-inline-start: var(--space-8, 0.5rem);
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
.s-jt-badge {
|
|
@@ -130,14 +130,24 @@ function initialExpandedSet(root: HJsonNode): Set<number> {
|
|
|
130
130
|
/**
|
|
131
131
|
* HkJsonTree — the interactive expandable JSON tree.
|
|
132
132
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* `
|
|
133
|
+
* A first-class family component (same tier as the rich-text surfaces):
|
|
134
|
+
* the chat tool blocks, the chest error landing's raw-details pane and
|
|
135
|
+
* any host pane render through THIS component — HkToolBlock consumes it
|
|
136
|
+
* directly, and the `s-jt-*` stylesheet single-sources the visual
|
|
137
|
+
* language, so the two can never drift. Feed it either pre-parsed
|
|
138
|
+
* `value` or a raw JSON `text`; non-container input renders nothing.
|
|
137
139
|
*
|
|
138
140
|
* The root keeps the legacy `s-tool-json-tree` class: hosts (including
|
|
139
141
|
* HkToolBlock's deferred scrollbar pass) target it, and the pane chrome
|
|
140
142
|
* (mono font, capped height, hidden native scrollbars) lives there.
|
|
143
|
+
*
|
|
144
|
+
* Spacing contract (pinned by HkJsonTree.contract.test.ts): the toggle
|
|
145
|
+
* box is 14px and sits ONE `--space-4` from the row text — identical for
|
|
146
|
+
* leaf dots, collapsed previews and expanded parents — and is centered
|
|
147
|
+
* on the first text line by formula, so collapsed and expanded states
|
|
148
|
+
* align with the same left rhythm; `key:` gaps are one unit, type hints
|
|
149
|
+
* two; the depth step is one toggle width (JT_INDENT = JT_TOGGLE,
|
|
150
|
+
* pinned alongside the stylesheet contract).
|
|
141
151
|
*/
|
|
142
152
|
export const HkJsonTree = defineComponent({
|
|
143
153
|
name: "HkJsonTree",
|