@celestia-island/hikari 0.55.320 → 0.55.321

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.320",
3
+ "version": "0.55.321",
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",
@@ -113,8 +113,16 @@
113
113
  inset-inline-end: var(--space-10);
114
114
  bottom: var(--space-10);
115
115
  z-index: 6;
116
- // Defensive re-enable (the inner HkMinimap root asserts it too,
117
- // HkMinimap.scss:15): keeps the dock alive if a host ever parks the
118
- // board under an ancestor `pointer-events: none`.
116
+ // Defensive re-enable (the inner HkMinimap root asserts it too, in
117
+ // HkMinimap.scss's root rule): keeps the dock alive if a host ever
118
+ // parks the board under an ancestor `pointer-events: none`.
119
119
  pointer-events: auto;
120
120
  }
121
+
122
+ // The dock is the ONE anchor (same contract as HkNodeCanvas's): the
123
+ // docked card goes static so the dock's own corner inset is the only
124
+ // one applied — without this the card stacked its self-docking offset
125
+ // on top and sat twice as far from the corner as designed.
126
+ .hk-board-minimap > .hk-minimap {
127
+ position: static;
128
+ }
@@ -1,6 +1,13 @@
1
1
  @use "../styles/polyfill" as *;
2
2
 
3
3
  .hk-minimap {
4
+ // SELF-DOCKING form (position: absolute + corner insets): for hosts
5
+ // that mount the card DIRECTLY on a positioned surface (HkTreemapStage,
6
+ // HkBoard3D's minimap3d). Hosts that wrap the card in a positioned DOCK
7
+ // (HkNodeCanvas's `.hk-node-canvas-minimap`, HkBoard's
8
+ // `.hk-board-minimap`) reset the card to `position: static` in their
9
+ // own sheet — the dock owns the corner and the card must not stack a
10
+ // second absolute offset on top of it (the doubled inset bug).
4
11
  position: absolute;
5
12
  bottom: var(--space-12);
6
13
  // Corner overlay anchor mirrors in RTL like the sibling HkZoomToolbar
@@ -67,6 +67,16 @@
67
67
  inset-inline-start: var(--space-12);
68
68
  }
69
69
 
70
+ /* The wrapper is the ONE anchor: the docked card goes static so it sizes
71
+ the wrapper's shrink-to-fit box instead of stacking its own absolute
72
+ corner offset on top of the wrapper's (the doubled inset read as the
73
+ map floating oddly far and lopsided from its corner — one inset came
74
+ from here, a second from `.hk-minimap`'s self-docking rule). Works for
75
+ every placement: the wrapper's own data-placement carries the corner. */
76
+ .hk-node-canvas-minimap > .hk-minimap {
77
+ position: static;
78
+ }
79
+
70
80
  /* ── Interactive edges ───────────────────────────────────────────────
71
81
  The halo paths are always in the DOM when `interactiveEdges` is on but
72
82
  invisible until their edge is hovered or selected, so highlighting
@@ -217,7 +217,11 @@ export default defineComponent({
217
217
  * out with `false`. Needs `contentBounds` to know the hull — without
218
218
  * it there is nothing to clamp against and this is a no-op. */
219
219
  clampToBounds: { type: Boolean, default: true },
220
- /** Mount the minimap in the corner. */
220
+ /** Mount the minimap in the corner. The slot's card must be a
221
+ * DIRECT child of the dock wrapper (`.hk-node-canvas-minimap >
222
+ * .hk-minimap` is reset to static — the dock owns the corner inset;
223
+ * an intermediate wrapper around the card would re-stack the card's
224
+ * own self-docking offset on top of the dock's). */
221
225
  minimap: { type: Boolean, default: true },
222
226
  /** Which corner the minimap takes. */
223
227
  minimapPlacement: {
@@ -0,0 +1,82 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { compile } from "sass";
6
+ import { describe, expect, it } from "vitest";
7
+
8
+ /**
9
+ * Compiled-stylesheet guard for the minimap DOCK contract: a positioned
10
+ * dock wrapper (HkNodeCanvas's `.hk-node-canvas-minimap`, HkBoard's
11
+ * `.hk-board-minimap`) owns the corner inset, and the docked card is
12
+ * reset to `position: static` so it sizes the dock's shrink-to-fit box.
13
+ * Without the reset the card stacked its own self-docking absolute
14
+ * offset (`.hk-minimap`'s bottom + inset-inline-end) on top of the
15
+ * dock's, and the map sat twice as far from its corner as designed —
16
+ * measurably lopsided against the undocked axes. Vitest stubs CSS, so
17
+ * the runtime suites can never catch a lost rule; this reads the
18
+ * compiled sheet.
19
+ */
20
+
21
+ const componentDir = resolve(dirname(fileURLToPath(import.meta.url)));
22
+
23
+ function compileSheet(name: string): string {
24
+ return compile(resolve(componentDir, name), { style: "expanded" }).css;
25
+ }
26
+
27
+ const canvasCss = compileSheet("HkNodeCanvas.scss");
28
+ const boardCss = compileSheet("HkBoard.scss");
29
+
30
+ function ruleFor(css: string, selector: string): string {
31
+ const start = css.indexOf(selector);
32
+ expect(start, `${selector} present in the compiled sheet`).toBeGreaterThanOrEqual(0);
33
+ const open = css.indexOf("{", start);
34
+ const close = css.indexOf("}", open);
35
+ return css.slice(start, close + 1);
36
+ }
37
+
38
+ describe("minimap dock stylesheet contract", () => {
39
+ it("HkNodeCanvas's wrapper is the single anchor: the docked card is static", () => {
40
+ const rule = ruleFor(canvasCss, ".hk-node-canvas-minimap > .hk-minimap");
41
+ expect(rule).toContain("position: static");
42
+ });
43
+
44
+ it("HkBoard's dock is the single anchor: the docked card is static", () => {
45
+ const rule = ruleFor(boardCss, ".hk-board-minimap > .hk-minimap");
46
+ expect(rule).toContain("position: static");
47
+ });
48
+
49
+ it("the docks keep their own corner insets (the anchor that remains)", () => {
50
+ // The wrapper still carries the placement insets — the static card
51
+ // has not REPLACED the dock's geometry, only stopped doubling it.
52
+ // (Compiled attribute selectors lose their quotes: bottom-right.)
53
+ expect(canvasCss).toContain(".hk-node-canvas-minimap[data-placement=bottom-right]");
54
+ const dock = ruleFor(boardCss, ".hk-board-minimap");
55
+ expect(dock).toContain("position: absolute");
56
+ expect(dock).toContain("inset-inline-end");
57
+ expect(dock).toContain("bottom");
58
+ });
59
+
60
+ it("the card's self-docking form survives for direct mounts", () => {
61
+ // TreemapStage / HkBoard3D mount the card directly on a positioned
62
+ // surface with no dock wrapper — the root rule must keep its own
63
+ // absolute corner for them.
64
+ const minimapCss = compileSheet("HkMinimap.scss");
65
+ const root = ruleFor(minimapCss, ".hk-minimap {");
66
+ expect(root).toContain("position: absolute");
67
+ });
68
+
69
+ it("the static reset survives sheet read round-trips (no @extend loss)", () => {
70
+ // The selector is plain and direct (no nesting tricks), so a future
71
+ // Sass rewrite cannot silently drop it from the compiled output.
72
+ expect(canvasCss.match(/position: static/g)?.length ?? 0).toBeGreaterThanOrEqual(1);
73
+ expect(boardCss.match(/position: static/g)?.length ?? 0).toBeGreaterThanOrEqual(1);
74
+ });
75
+
76
+ it("board scss raw source still documents the single-anchor reason", () => {
77
+ // The WHY lives next to the rule (comments are stripped from the
78
+ // compiled output, so guard the source text).
79
+ const src = readFileSync(resolve(componentDir, "HkBoard.scss"), "utf8");
80
+ expect(src).toContain("ONE anchor");
81
+ });
82
+ });
@@ -76,10 +76,18 @@ function flaggedRules(source: string): Array<{ selector: string; body: string }>
76
76
  return out;
77
77
  }
78
78
 
79
- /** The block's own transition declaration (multi-line shorthands joined). */
80
- function transitionOf(block: string): string {
81
- const match = block.match(/transition:[^;]+;/);
82
- return match?.[0]?.replace(/\s+/g, " ").trim() ?? "";
79
+ /** Every transition declaration in the block (multi-line shorthands
80
+ * joined, in order). Reading ALL of them closes the coverage hole the
81
+ * mutation round proved (H1): a stray second `transition:` declaration
82
+ * silently overrides the first in real CSS (last wins), so leg checks
83
+ * scoped to the first would pass while the real grammar went rogue.
84
+ * The flagged-rule assertions below additionally require EXACTLY one
85
+ * declaration — two shorthands in one rule is an authoring hazard the
86
+ * mechanical contract refuses outright. */
87
+ function transitionOf(block: string): string[] {
88
+ return [...block.matchAll(/transition:[^;]+;/g)].map((m) =>
89
+ m[0].replace(/\s+/g, " ").trim(),
90
+ );
83
91
  }
84
92
 
85
93
  describe("sheet family morph-flag ↔ transition-grammar contract", () => {
@@ -94,10 +102,15 @@ describe("sheet family morph-flag ↔ transition-grammar contract", () => {
94
102
  it("every flagged rule carries a clip-path leg and no dead height/max-height legs", () => {
95
103
  for (const sheet of FAMILY_SHEETS) {
96
104
  for (const rule of flaggedRules(read(sheet))) {
97
- const tr = transitionOf(rule.body);
105
+ const declarations = transitionOf(rule.body);
106
+ expect(
107
+ declarations.length,
108
+ `${sheet} ${rule.selector} carries exactly one transition declaration (a stray second silently overrides the first in real CSS)`,
109
+ ).toBe(1);
110
+ const tr = declarations[0] ?? "";
98
111
  expect(tr, `${sheet} ${rule.selector} has a transition`).toContain("clip-path");
99
112
  expect(tr, `${sheet} ${rule.selector} height leg is dead in clip mode`).not.toMatch(
100
- /\bheight\b/,
113
+ /(?<!max-)\bheight\b/,
101
114
  );
102
115
  expect(
103
116
  tr,