@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.55.94",
3
+ "version": "0.55.95",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -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, 1rem);
73
- height: var(--jt-toggle, 1rem);
72
+ width: var(--jt-toggle, 14px);
73
+ height: var(--jt-toggle, 14px);
74
74
  flex-shrink: 0;
75
- margin-inline-end: var(--space-2, 0.125rem);
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 instead of letting it join the row's baseline group with a
78
- synthesized border-box baseline. */
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.35em;
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.75em;
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
- * This is the exact renderer the chat card flow uses for tool results
134
- * (extracted verbatim from HkToolBlock, same DOM and `s-jt-*` stylesheet, so
135
- * the two can never drift). Feed it either pre-parsed `value` or a raw JSON
136
- * `text`; non-container input renders nothing.
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",