@celestia-island/hikari 0.55.232 → 0.55.233

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.232",
3
+ "version": "0.55.233",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari UI library, Vue 3 runtime — one design system for Vue 3 and Rust/WASM applications",
@@ -16,6 +16,9 @@ import { HkAboutBody } from "./HkAboutModal";
16
16
  * - the default slot appends host content after the copyright footer
17
17
  * - without a backdrop factory no backdrop layer renders (a body host may
18
18
  * not want the decorative canvas at all)
19
+ * - a version row's `metaTone: "danger"` reaches the pill as
20
+ * `data-tone="danger"`, and an EMPTY `value` drops the mono number cell
21
+ * entirely (the "component not wired up" row shows only its red label)
19
22
  *
20
23
  * (Repo test convention: raw createApp + document queries, no
21
24
  * @vue/test-utils dependency.)
@@ -121,4 +124,36 @@ describe("HkAboutBody extraction", () => {
121
124
 
122
125
  expect(document.body.querySelector(".s-about-modal-backdrop")).toBeNull();
123
126
  });
127
+
128
+ it("tints a version row's meta pill with the danger tone", async () => {
129
+ mountBody({
130
+ componentVersions: [
131
+ { label: "Edge engine", value: "1.4.0", meta: "Not connected", metaTone: "danger" },
132
+ ],
133
+ });
134
+ await flush();
135
+
136
+ const pill = query(".s-about-modal-row-meta");
137
+ expect(pill.getAttribute("data-tone")).toBe("danger");
138
+ expect(pill.textContent).toBe("Not connected");
139
+ });
140
+
141
+ it("drops the mono number cell when a row's value is empty", async () => {
142
+ mountBody({
143
+ componentVersions: [
144
+ // A host with nothing to report names the state in `meta` and leaves
145
+ // `value` empty (the "not wired up" row).
146
+ { label: "Edge engine", value: "", meta: "Not connected", metaTone: "danger" },
147
+ ],
148
+ });
149
+ await flush();
150
+
151
+ // No blank tabular slot: the mono cell is absent, not just empty.
152
+ expect(document.body.querySelector(".s-about-modal-row-number")).toBeNull();
153
+ // The label and the pill still carry the row.
154
+ expect(query(".s-about-modal-row-label").textContent).toContain("Edge engine");
155
+ const pill = query(".s-about-modal-row-meta");
156
+ expect(pill.textContent).toBe("Not connected");
157
+ expect(pill.getAttribute("data-tone")).toBe("danger");
158
+ });
124
159
  });
@@ -228,6 +228,14 @@
228
228
  color: rgb(var(--color-warning));
229
229
  }
230
230
 
231
+ // Red state: the component is not wired up / unreachable. The row then
232
+ // carries no version at all, so this pill is the whole value cell.
233
+ .s-about-modal-row-meta[data-tone="danger"] {
234
+ border-color: rgb(var(--color-error) / 32%);
235
+ background: rgb(var(--color-error) / 10%);
236
+ color: rgb(var(--color-error));
237
+ }
238
+
231
239
  /* ── Links: licenses, external links, credits names and filings ───────── */
232
240
 
233
241
  .s-about-modal-chips {
@@ -29,6 +29,36 @@ function classesFrom(source: string): Set<string> {
29
29
  return found;
30
30
  }
31
31
 
32
+ /**
33
+ * Body of one compiled rule, by exact selector (Sass drops the quotes around
34
+ * an attribute value: `[data-face=plain]`).
35
+ *
36
+ * The selector must compile exactly once: a second copy — say inside a media
37
+ * query, or a later override — would otherwise stay invisible here while
38
+ * still winning the cascade.
39
+ */
40
+ function ruleBody(sheet: string, selector: string): string {
41
+ const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
42
+ const matches = [...sheet.matchAll(new RegExp(`${escaped}\\s*\\{([^}]*)\\}`, "g"))];
43
+ expect(matches, `${selector} compiles exactly once`).toHaveLength(1);
44
+ return matches[0]?.[1] ?? "";
45
+ }
46
+
47
+ /**
48
+ * One declaration's value inside a rule body.
49
+ *
50
+ * A property declared twice is a failure rather than a coincidence: CSS
51
+ * resolves a repeat last-wins, so a first-match reader would report the stale
52
+ * value and happily accept the override.
53
+ */
54
+ function declaration(body: string, property: string): string {
55
+ const values = [
56
+ ...body.matchAll(new RegExp(`(?:^|;)\\s*${property}\\s*:([^;]*)`, "g")),
57
+ ].map((match) => match[1]!.trim());
58
+ expect(values.length, `${property} is declared at most once`).toBeLessThanOrEqual(1);
59
+ return values[0] ?? "";
60
+ }
61
+
32
62
  describe("HkAboutModal stylesheet contract", () => {
33
63
  const tsx = readFileSync(resolve(componentDir, "HkAboutModal.tsx"), "utf8");
34
64
  const scss = readFileSync(resolve(componentDir, "HkAboutModal.scss"), "utf8");
@@ -67,36 +97,6 @@ describe("HkAboutModal stylesheet contract", () => {
67
97
  * into chips. These assertions read the compiled declarations instead.
68
98
  */
69
99
  describe("link faces", () => {
70
- /**
71
- * Body of one compiled rule, by exact selector (Sass drops the quotes
72
- * around an attribute value: `[data-face=plain]`).
73
- *
74
- * The selector must compile exactly once: a second copy — say inside a
75
- * media query, or a later override — would otherwise stay invisible here
76
- * while still winning the cascade.
77
- */
78
- function ruleBody(sheet: string, selector: string): string {
79
- const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
80
- const matches = [...sheet.matchAll(new RegExp(`${escaped}\\s*\\{([^}]*)\\}`, "g"))];
81
- expect(matches, `${selector} compiles exactly once`).toHaveLength(1);
82
- return matches[0]?.[1] ?? "";
83
- }
84
-
85
- /**
86
- * One declaration's value inside a rule body.
87
- *
88
- * A property declared twice is a failure rather than a coincidence: CSS
89
- * resolves a repeat last-wins, so a first-match reader would report the
90
- * stale value and happily accept the override.
91
- */
92
- function declaration(body: string, property: string): string {
93
- const values = [
94
- ...body.matchAll(new RegExp(`(?:^|;)\\s*${property}\\s*:([^;]*)`, "g")),
95
- ].map((match) => match[1]!.trim());
96
- expect(values.length, `${property} is declared at most once`).toBeLessThanOrEqual(1);
97
- return values[0] ?? "";
98
- }
99
-
100
100
  const PLAIN = ".s-about-modal-link[data-face=plain]";
101
101
 
102
102
  it("keeps the plain face a bare text link", () => {
@@ -229,4 +229,32 @@ describe("HkAboutModal stylesheet contract", () => {
229
229
  expect(iconAt).toBeGreaterThan(plainAt);
230
230
  });
231
231
  });
232
+
233
+ /**
234
+ * Row-meta tones are self-contained rules, so the class-name checks above
235
+ * cannot see them: reverting a tone to `neutral` (or dropping its rule)
236
+ * leaves the `data-tone` attribute intact and the runtime suite green while
237
+ * the pill silently loses its colour — the whole point of the tone.
238
+ */
239
+ describe("row meta tones", () => {
240
+ const TONE = (tone: string) => `.s-about-modal-row-meta[data-tone=${tone}]`;
241
+
242
+ it("paints the danger tone in the theme's error colour", () => {
243
+ const danger = ruleBody(css, TONE("danger"));
244
+ expect(danger, "the danger-tone rule must survive compilation").not.toBe("");
245
+ expect({
246
+ borderColor: declaration(danger, "border-color"),
247
+ background: declaration(danger, "background"),
248
+ color: declaration(danger, "color"),
249
+ }).toEqual({
250
+ // Same geometry as the positive / caution tones, only the channel
251
+ // differs — `--color-error` is the theme's existing danger token.
252
+ // (Dart Sass compiles the seed-relative form without spaces around
253
+ // the slash.)
254
+ borderColor: "rgb(var(--color-error)/32%)",
255
+ background: "rgb(var(--color-error)/10%)",
256
+ color: "rgb(var(--color-error))",
257
+ });
258
+ });
259
+ });
232
260
  });
@@ -73,12 +73,19 @@ export interface HAboutCredit {
73
73
  * rendered as a separate muted pill so it never melts into the version
74
74
  * string. `metaTone` tints that pill when the tag carries a status
75
75
  * meaning (e.g. a production environment).
76
+ *
77
+ * `danger` is the red "not wired up / unreachable" state: a host whose
78
+ * component (e.g. an edge master node engine) has no version to report
79
+ * passes an EMPTY `value` and names the state in `meta` with
80
+ * `metaTone: "danger"`. The empty value then renders the pill alone —
81
+ * the mono number cell is dropped rather than leaving a blank tabular
82
+ * slot — so the row reads as one red label instead of `unknown`.
76
83
  */
77
84
  export interface HAboutComponentVersion {
78
85
  label: string;
79
86
  value: string;
80
87
  meta?: string;
81
- metaTone?: "neutral" | "positive" | "caution";
88
+ metaTone?: "neutral" | "positive" | "caution" | "danger";
82
89
  }
83
90
 
84
91
  /**
@@ -288,7 +295,14 @@ export const HkAboutBody = defineComponent({
288
295
  <div class="s-about-modal-row" key={component.label}>
289
296
  <span class="s-about-modal-row-label">{component.label}</span>
290
297
  <span class="s-about-modal-row-value">
291
- <span class="s-about-modal-row-number">{component.value}</span>
298
+ {/* An empty value means the host has no version to
299
+ report (e.g. the component is not wired up) and
300
+ carries the state in `meta` instead — skip the mono
301
+ cell so the row does not open with a blank tabular
302
+ slot and a stray gap. */}
303
+ {component.value && (
304
+ <span class="s-about-modal-row-number">{component.value}</span>
305
+ )}
292
306
  {component.meta && (
293
307
  <span
294
308
  class="s-about-modal-row-meta"