@celestia-island/hikari 0.55.166 → 0.55.168

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.166",
3
+ "version": "0.55.168",
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",
@@ -1,6 +1,10 @@
1
1
  @use "./scrim-fade" as sf;
2
2
 
3
3
  .hk-drawer-overlay {
4
+ /* Overlay-root contract: #hk-overlay-root fences pointer events
5
+ * (pointer-events: none) — restore them or the drawer's taps pass
6
+ * through to the page below (2026-10-15 defect). */
7
+ pointer-events: auto;
4
8
  position: fixed;
5
9
  inset: 0;
6
10
  background: var(--hk-drawer-overlay-bg, rgba(0, 0, 0, 0.45));
@@ -18,6 +22,7 @@
18
22
  );
19
23
 
20
24
  .hk-drawer-panel {
25
+ pointer-events: auto;
21
26
  position: fixed;
22
27
  display: flex;
23
28
  flex-direction: column;
@@ -264,3 +269,12 @@
264
269
  transition: none;
265
270
  }
266
271
  }
272
+
273
+ /* ── Overlay-root pointer-events contract ──────────────────────────
274
+ * #hk-overlay-root fences pointer events (pointer-events: none) and
275
+ * every teleported layer MUST restore them. This compiled-css guard
276
+ * makes the restoration a test-visible invariant rather than an
277
+ * implementation detail (2026-10-15: the mobile drawer painted above
278
+ * the page but every tap passed through because neither the overlay
279
+ * nor the panel restored pointer events after the overlay-root
280
+ * isolation migration). */
@@ -116,6 +116,8 @@
116
116
  * anchors narrower than 180px (the floor must lose to the inline style,
117
117
  * which can only happen on the same element). */
118
118
  .hk-select-popout-host {
119
+ /* Overlay-root contract: restore pointer events (see HkDrawer). */
120
+ pointer-events: auto;
119
121
  position: fixed;
120
122
  min-width: 180px;
121
123
 
@@ -280,6 +282,7 @@
280
282
  // popover/modal mobile dockings.
281
283
  // ------
282
284
  .hk-select-sheet-scrim {
285
+ pointer-events: auto;
283
286
  position: fixed;
284
287
  inset: 0;
285
288
  pointer-events: auto;
@@ -287,6 +290,7 @@
287
290
  }
288
291
 
289
292
  .hk-select-sheet-panel {
293
+ pointer-events: auto;
290
294
  position: fixed;
291
295
  left: 0;
292
296
  right: 0;
@@ -491,3 +495,7 @@
491
495
  transition: none;
492
496
  }
493
497
  }
498
+
499
+ /* ── Overlay-root pointer-events contract ──────────────────────────
500
+ * See HkDrawer.scss's guard note. The popout host, the sheet scrim
501
+ * and the sheet panel all live inside #hk-overlay-root. */
@@ -80,6 +80,17 @@ export const HkErrorReportingOverlay = defineComponent({
80
80
  if (!err) return null;
81
81
  const options = getErrorReportingOptions();
82
82
 
83
+ // Host-owned card: everything inside the takeover is the host's —
84
+ // content, actions, dismissal. The host keeps the capture channels
85
+ // and the crash-surviving mount; only the card is swapped.
86
+ if (options.landing) {
87
+ return (
88
+ <div class="hk-error-reporting-overlay" role="alertdialog" aria-live="assertive" aria-modal="true">
89
+ {options.landing(err)}
90
+ </div>
91
+ );
92
+ }
93
+
83
94
  const description = options.describe
84
95
  ? options.describe(err)
85
96
  : err.message || t("hikari::errors.unexpectedDesc", "An unhandled error occurred");
@@ -207,4 +207,75 @@ describe("createErrorReporting", () => {
207
207
  clearGlobalError();
208
208
  expect(overlayHost()).toBeNull();
209
209
  });
210
+
211
+ it("renders the host landing card instead of the built-in one", async () => {
212
+ vi.spyOn(console, "error").mockImplementation(() => {});
213
+ installPlugin({
214
+ landing: (err) =>
215
+ h("div", { class: "host-card", "data-code": err.name }, [
216
+ h("p", { class: "host-card__msg" }, err.message),
217
+ h(
218
+ "button",
219
+ { class: "host-card__dismiss", onClick: () => clearGlobalError() },
220
+ "Dismiss",
221
+ ),
222
+ ]),
223
+ });
224
+ reportGlobalError(new TypeError("host boom"));
225
+ await nextTick();
226
+
227
+ const host = overlayHost();
228
+ expect(host).not.toBeNull();
229
+ // The built-in card is fully replaced by the host's.
230
+ expect(host!.querySelector(".hk-error-landing")).toBeNull();
231
+ expect(host!.querySelector(".host-card")).not.toBeNull();
232
+ expect(host!.querySelector(".host-card")!.getAttribute("data-code")).toBe("TypeError");
233
+ expect(host!.querySelector(".host-card__msg")!.textContent).toBe("host boom");
234
+ // The host owns the actions: the built-in Home/Retry pair is gone.
235
+ expect(host!.textContent).not.toContain("Back to home");
236
+ expect(host!.textContent).not.toContain("Retry");
237
+ // The takeover shell stays (fixed, alert-dialog semantics).
238
+ expect(host!.querySelector(".hk-error-reporting-overlay")!.getAttribute("role")).toBe(
239
+ "alertdialog",
240
+ );
241
+ // The host card rides INSIDE the takeover shell (same fixed z-band), not
242
+ // beside it: a sibling placement would float outside --hk-z-error. The
243
+ // quadrant of these assertions that pins "same shell" is the ancestor
244
+ // relationship — descendants of the shell, not just of the data node.
245
+ expect(host!.querySelector(".hk-error-reporting-overlay .host-card")).not.toBeNull();
246
+ // The host's own dismissal tears the takeover down.
247
+ (host!.querySelector(".host-card__dismiss") as HTMLButtonElement).click();
248
+ await nextTick();
249
+ expect(overlayHost()).toBeNull();
250
+ });
251
+
252
+ it("lets landing step over title/describe and the built-in actions", async () => {
253
+ vi.spyOn(console, "error").mockImplementation(() => {});
254
+ installPlugin({
255
+ title: "ignored headline",
256
+ describe: () => "ignored description",
257
+ homeHref: "/should-not-be-used",
258
+ onHome: vi.fn(),
259
+ onRetry: vi.fn(),
260
+ landing: (err) =>
261
+ h("div", { class: "host-card", "data-msg": err.message }, [
262
+ h("p", { class: "host-card__msg" }, err.message),
263
+ ]),
264
+ });
265
+ reportGlobalError(new TypeError("own content"));
266
+ await nextTick();
267
+
268
+ const host = overlayHost()!;
269
+ // The host's card won outright; the built-in card (and its translated
270
+ // headline / description) never mounted.
271
+ expect(host.querySelector(".hk-error-landing")).toBeNull();
272
+ expect(host.querySelector(".host-card__msg")!.textContent).toBe("own content");
273
+ expect(host.textContent).not.toContain("ignored headline");
274
+ expect(host.textContent).not.toContain("ignored description");
275
+ expect(host.textContent).not.toContain("Back to home");
276
+ expect(host.textContent).not.toContain("Retry");
277
+ // The host card still receives the raw accepted record, not a
278
+ // title/describe-shaped one — the overrides never leak in.
279
+ expect(host.querySelector(".host-card")!.getAttribute("data-msg")).toBe("own content");
280
+ });
210
281
  });
@@ -32,6 +32,11 @@ export { HkErrorReportingOverlay } from "./HkErrorReportingOverlay";
32
32
  * with Home / Retry actions. Because the overlay lives on its own tiny app
33
33
  * instance, it still shows when the host tree itself is what crashed.
34
34
  *
35
+ * Hosts whose error landing carries app-specific semantics pass
36
+ * `options.landing(err)`: the takeover then renders the host's card inside
37
+ * the same crash-surviving shell — capture channels and the takeover
38
+ * guarantee stay, the built-in card and its actions step aside.
39
+ *
35
40
  * Any pre-existing `app.config.errorHandler` is chained, not discarded.
36
41
  * All capture channels are optional; `shouldReport` filters, `onError` is
37
42
  * the telemetry hook.
@@ -1,4 +1,4 @@
1
- import { ref, type Ref } from "vue";
1
+ import { ref, type Ref, type VNodeChild } from "vue";
2
2
 
3
3
  import { reportHkRuntime, type HkRuntimeHandle } from "../runtime/registry";
4
4
 
@@ -17,11 +17,23 @@ export interface HkReportedError {
17
17
  }
18
18
 
19
19
  export interface HkErrorReportingOptions {
20
- /** Headline override; defaults to `hikari::errors.defaultTitle`. */
20
+ /** Headline override; defaults to `hikari::errors.defaultTitle`.
21
+ * Ignored when `landing` is set (the host owns the whole card). */
21
22
  title?: string;
22
23
  /** Description builder; defaults to the error's message, falling back to
23
- * `hikari::errors.unexpectedDesc` when the error carries none. */
24
+ * `hikari::errors.unexpectedDesc` when the error carries none.
25
+ * Ignored when `landing` is set. */
24
26
  describe?: (err: HkReportedError) => string;
27
+ /** Host-provided takeover card — the unification hatch for hosts whose
28
+ * error landing carries app-specific semantics (localized per-code
29
+ * headlines, raw wire payloads, a Dismiss action). When set, the overlay
30
+ * renders `landing(err)` INSTEAD of the built-in HkErrorLanding, inside
31
+ * the same crash-surviving host (dedicated body app, --hk-z-error): the
32
+ * host keeps every capture channel and the takeover guarantee and owns
33
+ * the card's content and actions outright — `title`, `describe`,
34
+ * `homeHref`, `onHome` and `onRetry` are all ignored. Resolve app-shaped
35
+ * payloads from `err.error` (e.g. an Error subclass carrying them). */
36
+ landing?: (err: HkReportedError) => VNodeChild;
25
37
  /** Target of the "back to home" action. `false` hides the button.
26
38
  * Ignored when `onHome` is set. Default `/`. */
27
39
  homeHref?: string | false;