@celestia-island/hikari 0.55.89 → 0.55.91

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.89",
3
+ "version": "0.55.91",
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,53 @@
1
+ /**
2
+ * Source contract: the inline landing sits FLUSH under its context. The
3
+ * `.is-inline` variant must drop the viewport takeover's block-axis
4
+ * padding entirely (the host pane already pads and a sibling header sits
5
+ * immediately above — the old 1.25rem top padding floated the card a
6
+ * full padding-frame below its context) and slim the card's page-grade
7
+ * top padding (2.25rem desktop / 1.75rem mobile) to one space step. The
8
+ * horizontal inset stays so a narrow pane never seats the card
9
+ * edge-to-edge. A class-name test cannot see this; the compiled contract
10
+ * lives here.
11
+ */
12
+ import { describe, expect, it } from "vitest";
13
+ import { readFileSync } from "node:fs";
14
+ import { dirname, join } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const here = dirname(fileURLToPath(import.meta.url));
18
+
19
+ /** Extract one rule's body with nesting-aware brace matching. */
20
+ function ruleBody(css: string, selector: string): string {
21
+ const start = css.indexOf(`${selector} {`);
22
+ expect(start, `rule ${selector} present`).toBeGreaterThanOrEqual(0);
23
+ const open = css.indexOf("{", start);
24
+ let depth = 0;
25
+ for (let i = open; i < css.length; i++) {
26
+ if (css[i] === "{") depth++;
27
+ else if (css[i] === "}") {
28
+ depth--;
29
+ if (depth === 0) return css.slice(open + 1, i);
30
+ }
31
+ }
32
+ throw new Error(`unbalanced braces in ${selector}`);
33
+ }
34
+
35
+ describe("HkErrorLanding inline contract", () => {
36
+ const css = readFileSync(join(here, "HkErrorLanding.scss"), "utf-8");
37
+ const inline = ruleBody(css, ".hk-error-landing.is-inline");
38
+
39
+ it("drops the block-axis padding entirely and keeps only the horizontal inset", () => {
40
+ expect(inline).toContain("min-height: 0");
41
+ expect(inline).toContain("padding: 0 var(--space-12, 0.75rem)");
42
+ // The takeover padding must not survive in any form.
43
+ expect(inline).not.toContain("1.25rem");
44
+ });
45
+
46
+ it("slims the card's top padding below the page-grade value", () => {
47
+ const card = ruleBody(inline, ".hk-error-landing__card");
48
+ // The shorthand's block-start value IS the slim step, not the
49
+ // page-grade 2.25rem (desktop) / 1.75rem (≤480px media) top.
50
+ expect(card).toContain("padding: var(--space-16, 1rem) ");
51
+ expect(card).not.toContain("2.25rem");
52
+ });
53
+ });
@@ -260,11 +260,26 @@
260
260
 
261
261
  // Inline variant: the same card without the viewport takeover — used when
262
262
  // HkErrorBoundary captures a crash inside a pane and swaps in the landing
263
- // in-place instead of covering the whole screen.
263
+ // in-place instead of covering the whole screen, or when a host view
264
+ // renders it as its failed content region right under a sibling header.
264
265
  .hk-error-landing.is-inline {
265
266
  min-height: 0;
266
- padding: var(--space-20, 1.25rem) var(--space-12, 0.75rem);
267
+ // In-pane, the host region already pads and a sibling header usually
268
+ // sits immediately above: the takeover's block-axis padding floated
269
+ // the card a full padding-frame below its context. The vertical
270
+ // padding is dropped entirely (the card's own slimmer top padding
271
+ // below supplies the breathing room); the horizontal inset stays so a
272
+ // narrow pane never seats the card edge-to-edge.
273
+ padding: 0 var(--space-12, 0.75rem);
267
274
  background: transparent;
275
+
276
+ // The card's page-grade top padding (2.25rem desktop / 1.75rem mobile)
277
+ // reads as a detached float inside a pane — slim it to one space step.
278
+ // Specificity (0,3,0) outranks the ≤480px media rule (0,1,0), so this
279
+ // holds on phones too.
280
+ .hk-error-landing__card {
281
+ padding: var(--space-16, 1rem) var(--space-28, 1.75rem) var(--space-24, 1.5rem);
282
+ }
268
283
  }
269
284
 
270
285
  @media (max-width: 480px) {
@@ -1,4 +1,7 @@
1
1
  import { afterEach, describe, expect, it } from "vitest";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
2
5
  import { createApp, defineComponent, h } from "vue";
3
6
 
4
7
  import HkIconButton from "./HkIconButton";
@@ -118,3 +121,33 @@ describe("HkIconButton", () => {
118
121
  expect(propAndSlot.querySelector(".hk-icon")).toBeNull();
119
122
  });
120
123
  });
124
+
125
+ /**
126
+ * Static pin for the header-chrome glyph step (user report 2026-09-29:
127
+ * the search / refresh / filter glyphs in the top-right clusters read
128
+ * visibly smaller than HkThemeToggle's sun beside them). The `.svg` rule
129
+ * in HkIconButton.scss forces ANY slotted glyph to
130
+ * `--hi-icon-button-icon-size`, so the 28 step must map that var to the
131
+ * sm token (16px — the theme toggle's own glyph size). happy-dom has no
132
+ * cascade here that a DOM test could measure, so — tokenInventory style —
133
+ * the declarations themselves are the contract; a deliberate remap is
134
+ * expected to update this file.
135
+ */
136
+ describe("HkIconButton glyph step", () => {
137
+ const here = dirname(fileURLToPath(import.meta.url));
138
+ const vars = readFileSync(join(here, "HkIconButtonVars.scss"), "utf8");
139
+
140
+ it("maps the 28 header step to the sm glyph (16px), matching the theme toggle", () => {
141
+ const block = vars.slice(vars.indexOf(".hk-icon-button-28"), vars.indexOf(".hk-icon-button-32"));
142
+ expect(block).toContain("--hi-icon-button-size: 28px;");
143
+ expect(block).toContain("--hi-icon-button-icon-size: var(--hi-icon-size-sm);");
144
+ expect(block).not.toContain("var(--hi-icon-size-xs)");
145
+ });
146
+
147
+ it("keeps the dense steps (16/24) on the xs glyph", () => {
148
+ const block16 = vars.slice(vars.indexOf(".hk-icon-button-16"), vars.indexOf(".hk-icon-button-24"));
149
+ const block24 = vars.slice(vars.indexOf(".hk-icon-button-24"), vars.indexOf(".hk-icon-button-28"));
150
+ expect(block16).toContain("var(--hi-icon-size-xs)");
151
+ expect(block24).toContain("var(--hi-icon-size-xs)");
152
+ });
153
+ });
@@ -74,7 +74,15 @@
74
74
 
75
75
  .hk-icon-button-28 {
76
76
  --hi-icon-button-size: 28px;
77
- --hi-icon-button-icon-size: var(--hi-icon-size-xs);
77
+ /* The header-chrome step carries the sm glyph (16px): its consumers are
78
+ * the header clusters — global search, the pages' refresh, the filter
79
+ * trigger — whose visual sibling is HkThemeToggle's own trigger buttons
80
+ * (28px boxes with direct 16px lucide glyphs). The xs mapping (12px)
81
+ * rendered those glyphs visibly smaller than the sun beside them
82
+ * (user report 2026-09-29: 不像一个尺寸的图标按钮) — the `.svg` rule
83
+ * in HkIconButton.scss forces ANY slotted glyph to this var, so the
84
+ * slot's size-16 attribute never reached the screen. */
85
+ --hi-icon-button-icon-size: var(--hi-icon-size-sm);
78
86
  }
79
87
 
80
88
  .hk-icon-button-32 {