@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 +1 -1
- package/src/components/HkDrawer.scss +14 -0
- package/src/components/HkSelect.scss +8 -0
- package/src/errorReporting/HkErrorReportingOverlay.tsx +11 -0
- package/src/errorReporting/index.test.tsx +71 -0
- package/src/errorReporting/index.ts +5 -0
- package/src/errorReporting/state.ts +15 -3
package/package.json
CHANGED
|
@@ -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;
|