@celestia-island/hikari 0.55.313 → 0.55.314
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/animation/registerAnimations.ts +4 -0
- package/src/components/HkBadge.scss +23 -0
- package/src/components/HkBadge.test.tsx +71 -0
- package/src/components/HkBadge.tsx +4 -0
- package/src/components/HkErrorLanding.scss +2 -1
- package/src/components/HkSplash.scss +3 -1
- package/src/components/HkTitleBar.scss +5 -1
- package/src/plugins/bootLandingCss.mjs +1 -1
- package/src/plugins/bootLandingCss.test.ts +7 -0
- package/src/scale.scss +1 -1
- package/src/styles/theme/scale.scss +1 -1
- package/src/theme/fontStacks.contract.test.ts +98 -0
package/package.json
CHANGED
|
@@ -143,3 +143,7 @@ registerCssAnimation("hk-screen-identify-in");
|
|
|
143
143
|
// brushed window still reads as live. Reduced motion keeps the dashed
|
|
144
144
|
// pending look and drops the loop (house pattern, see HkSkeleton).
|
|
145
145
|
registerCssAnimation("hk-range-ribbon-pulse", { infinite: true });
|
|
146
|
+
// HkBadge's presence pulse (opacity-only, loops forever): the `pulse`
|
|
147
|
+
// prop's dot breathes while its state is in-flight — a settled dot sits
|
|
148
|
+
// static. Reduced motion drops the loop (house pattern, see HkSkeleton).
|
|
149
|
+
registerCssAnimation("hk-badge-dot-pulse", { infinite: true });
|
|
@@ -64,6 +64,29 @@
|
|
|
64
64
|
flex-shrink: 0;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/* Presence pulse (data-pulse, dot required): an in-flight / live state
|
|
68
|
+
breathes instead of sitting static. Opacity-only so the pill's layout
|
|
69
|
+
box never moves; consumers wanting a glow layer their own shadow. */
|
|
70
|
+
@keyframes hk-badge-dot-pulse {
|
|
71
|
+
0%,
|
|
72
|
+
100% {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
}
|
|
75
|
+
50% {
|
|
76
|
+
opacity: 0.4;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.hk-badge[data-pulse] [data-el="dot"] {
|
|
81
|
+
animation: hk-badge-dot-pulse 1.6s ease-in-out infinite;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (prefers-reduced-motion: reduce) {
|
|
85
|
+
.hk-badge[data-pulse] [data-el="dot"] {
|
|
86
|
+
animation: none;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
67
90
|
/* dot-only: strip the pill padding/border/background so it's just the
|
|
68
91
|
indicator dot, not a dot wrapped in a bordered pill. */
|
|
69
92
|
.hk-badge[data-dot-only] {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createApp, h } from "vue";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { compile } from "sass";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import HkBadge from "./HkBadge";
|
|
9
|
+
|
|
10
|
+
const mounts: Array<{ app: ReturnType<typeof createApp>; container: HTMLElement }> = [];
|
|
11
|
+
|
|
12
|
+
function mount(renderNode: () => ReturnType<typeof h>) {
|
|
13
|
+
const container = document.createElement("div");
|
|
14
|
+
document.body.appendChild(container);
|
|
15
|
+
const app = createApp({ render: renderNode });
|
|
16
|
+
app.mount(container);
|
|
17
|
+
mounts.push({ app, container });
|
|
18
|
+
return container;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
for (const { app, container } of mounts.splice(0)) {
|
|
23
|
+
app.unmount();
|
|
24
|
+
container.remove();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Vitest stubs CSS, so the runtime suite can never catch a lost rule —
|
|
29
|
+
// the pulse animation contract is additionally pinned by the compiled
|
|
30
|
+
// stylesheet guard at the bottom of this file (HkCornerBadge pattern).
|
|
31
|
+
describe("HkBadge pulse", () => {
|
|
32
|
+
it("renders the dot and data-pulse only when both dot and pulse are set", () => {
|
|
33
|
+
const container = mount(() => h(HkBadge, { dot: true, pulse: true }, () => "Creating"));
|
|
34
|
+
expect(container.querySelector(".hk-badge [data-el='dot']")).toBeTruthy();
|
|
35
|
+
expect(container.querySelector(".hk-badge[data-pulse]")).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("never sets data-pulse without a dot — a pulsing pill with no dot is meaningless", () => {
|
|
39
|
+
const container = mount(() => h(HkBadge, { pulse: true }, () => "Creating"));
|
|
40
|
+
expect(container.querySelector(".hk-badge")).toBeTruthy();
|
|
41
|
+
expect(container.querySelector("[data-pulse]")).toBeNull();
|
|
42
|
+
expect(container.querySelector("[data-el='dot']")).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("keeps the static dot the default — pulse is opt-in per call site", () => {
|
|
46
|
+
const container = mount(() => h(HkBadge, { dot: true }, () => "Idle"));
|
|
47
|
+
expect(container.querySelector(".hk-badge [data-el='dot']")).toBeTruthy();
|
|
48
|
+
expect(container.querySelector("[data-pulse]")).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("HkBadge pulse stylesheet contract", () => {
|
|
53
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
const css = compile(resolve(here, "HkBadge.scss"), { style: "expanded" }).css;
|
|
55
|
+
|
|
56
|
+
it("drives the pulse through the data-pulse selector on the dot only", () => {
|
|
57
|
+
// Sass compiles quoted attribute selectors unquoted ([data-el=dot]).
|
|
58
|
+
expect(css).toContain(".hk-badge[data-pulse] [data-el=dot]");
|
|
59
|
+
// The animation must never ride the pill itself — layout stays frozen.
|
|
60
|
+
expect(css.match(/\.hk-badge\s*\{[^}]*animation/)).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("names the keyframes and runs them infinitely", () => {
|
|
64
|
+
expect(css).toContain("@keyframes hk-badge-dot-pulse");
|
|
65
|
+
expect(/\.hk-badge\[data-pulse\] \[data-el=dot\]\s*\{[^}]*animation:\s*hk-badge-dot-pulse[^;]*infinite/.test(css)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("switches the pulse off under prefers-reduced-motion", () => {
|
|
69
|
+
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)\s*\{[^@]*\.hk-badge\[data-pulse\] \[data-el=dot\]\s*\{[^}]*animation:\s*none/);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -15,6 +15,9 @@ export default defineComponent({
|
|
|
15
15
|
props: {
|
|
16
16
|
variant: { type: String as PropType<BadgeVariant>, default: "default" },
|
|
17
17
|
dot: { type: Boolean, default: false },
|
|
18
|
+
/** Animate the dot with a presence pulse (in-flight / live state).
|
|
19
|
+
* Meaningful only with `dot`; honors prefers-reduced-motion. */
|
|
20
|
+
pulse: { type: Boolean, default: false },
|
|
18
21
|
size: { type: String as PropType<"sm" | "md">, default: "md" },
|
|
19
22
|
mono: { type: Boolean, default: false },
|
|
20
23
|
uppercase: { type: Boolean, default: false },
|
|
@@ -38,6 +41,7 @@ export default defineComponent({
|
|
|
38
41
|
data-size={props.size}
|
|
39
42
|
data-pill={props.pill ? undefined : "false"}
|
|
40
43
|
data-mono={props.mono ? "" : undefined}
|
|
44
|
+
data-pulse={props.pulse && props.dot ? "" : undefined}
|
|
41
45
|
data-uppercase={props.uppercase ? "" : undefined}
|
|
42
46
|
data-dot-only={(props.dot && !slots.default) || undefined}
|
|
43
47
|
style={Object.keys(style.value).length ? style.value : undefined}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// host themes (dark mode etc.) when the vars exist.
|
|
7
7
|
|
|
8
8
|
@use "../styles/polyfill" as *;
|
|
9
|
+
@use "./hikari-vars" as vars;
|
|
9
10
|
|
|
10
11
|
.hk-error-landing {
|
|
11
12
|
--hel-background: var(--color-background, 246 247 249);
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
padding: var(--space-20, 1.25rem);
|
|
35
36
|
background: rgb(var(--hel-background));
|
|
36
37
|
color: rgb(var(--hel-text));
|
|
37
|
-
font-family: var(--font-sans,
|
|
38
|
+
font-family: var(--font-sans, vars.$hikari-font-family-sans);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
.hk-error-landing__card {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use "./hikari-vars" as vars;
|
|
2
|
+
|
|
1
3
|
.hk-splash {
|
|
2
4
|
display: flex;
|
|
3
5
|
flex-direction: column;
|
|
@@ -6,7 +8,7 @@
|
|
|
6
8
|
min-height: 100vh;
|
|
7
9
|
background: var(--hi-color-bg-canvas, #0d1117);
|
|
8
10
|
color: var(--hi-color-text-primary, #1e1e1e);
|
|
9
|
-
font-family: var(--font-sans, -
|
|
11
|
+
font-family: var(--font-sans, vars.$hikari-font-family-sans);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
.hk-splash-main {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// Text selection is chrome-disabled across the whole bar.
|
|
7
7
|
|
|
8
8
|
@use "../styles/polyfill" as *;
|
|
9
|
+
@use "./hikari-vars" as vars;
|
|
9
10
|
|
|
10
11
|
.hk-titlebar {
|
|
11
12
|
--hk-tb-height: 32px;
|
|
@@ -42,7 +43,10 @@
|
|
|
42
43
|
background: var(--hk-tb-bg);
|
|
43
44
|
@include backdrop-filter(blur(var(--blur-md, 16px)));
|
|
44
45
|
border-bottom: 1px solid var(--hk-tb-border);
|
|
45
|
-
|
|
46
|
+
// The token, not a literal (docs/en/guides/fonts.md): this sheet used
|
|
47
|
+
// to hand-roll a CJK-less sans, dropping CJK titlebar text onto a
|
|
48
|
+
// platform fallback on Linux.
|
|
49
|
+
font-family: var(--font-sans, vars.$hikari-font-family-sans);
|
|
46
50
|
-webkit-app-region: drag;
|
|
47
51
|
app-region: drag;
|
|
48
52
|
}
|
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
// HkErrorLanding, so it ships the component's real stylesheet instead of a
|
|
12
12
|
// hand-copied palette. bootLandingCss.test.ts recompiles the entry and fails
|
|
13
13
|
// on drift.
|
|
14
|
-
export const BOOT_LANDING_CSS = ":root{--c-primary-faint: rgb(var(--color-primary, 122 162 247) / 5%);--c-primary-subtle: rgb(var(--color-primary, 122 162 247) / 8%);--c-primary-dim: rgb(var(--color-primary, 122 162 247) / 10%);--c-primary-light: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-overlay: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-medium: rgb(var(--color-primary, 122 162 247) / 25%);--c-primary-strong: rgb(var(--color-primary, 122 162 247) / 40%);--c-primary-intense: rgb(var(--color-primary, 122 162 247) / 40%);--c-error-dim: rgb(var(--color-error, 247 118 142) / 10%);--c-error-overlay: rgb(var(--color-error, 247 118 142) / 20%);--c-error-strong: rgb(var(--color-error, 247 118 142) / 50%);--shadow-elevated: 0 8px 32px rgb(0 0 0 / 15%);--shadow-dropdown: 0 4px 24px rgb(0 0 0 / 12%), 0 0 0 1px rgb(255 255 255 / 4%);--shadow-button: 0 4px 14px rgb(var(--color-primary, 122 162 247) / 35%);--shadow-button-danger: 0 4px 14px rgb(var(--color-error, 247 118 142) / 35%);--shadow-focus: 0 0 0 3px rgb(var(--color-primary, 122 162 247) / 12%);--shadow-focus-error: 0 0 0 3px rgb(var(--color-error, 247 118 142) / 12%);--border-input: rgb(var(--color-border, 100 100 100) / 15%);--border-faint: rgb(var(--color-border, 100 100 100) / 10%);--border-subtle: rgb(var(--color-border, 100 100 100) / 12%);--hi-icon-color: rgb(var(--color-text, 30 30 30));--hi-icon-size-xs: 0.75rem;--hi-icon-size-sm: 1rem;--hi-icon-size-md: 1.25rem;--hi-icon-size-lg: 1.5rem;--hi-duration-instant: var(--duration-instant, 0.1s);--hi-border-color-focus: rgb(var(--color-primary, 122 162 247));--hi-glow-color: transparent;--hi-bg-surface-dark: rgb(var(--color-muted, 108 108 108) / 12%);--hi-color-primary-dark: color-mix(in srgb, rgb(var(--color-primary, 122 162 247)) 82%, #000);--hi-color-secondary-dark: color-mix(in srgb, rgb(var(--color-secondary, 81 154 115)) 82%, #000);--hi-color-danger-dark: color-mix(in srgb, rgb(var(--color-error, 247 118 142)) 82%, #000);--hi-color-success-dark: color-mix(in srgb, rgb(var(--color-success, 14 184 64)) 82%, #000);--hi-color-text-on-secondary: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-danger: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-success: rgb(var(--color-on-solid, 255 255 255))}:root{--color-primary: 122 162 247;--color-on-primary: 255 255 255;--color-on-solid: 255 255 255;--color-on-solid-text: 255 255 255;--color-on-solid-icon: 255 255 255;--color-secondary: 81 154 115;--color-accent: 247 181 0;--color-text: 30 30 30;--color-text-secondary: 102 102 102;--color-text-tertiary: 140 140 140;--color-muted: 108 108 108;--color-border: 100 100 100;--color-focused-border: 122 162 247;--color-background: 255 255 255;--color-surface: 255 255 255;--color-selected-bg: 191 218 242;--color-selected-text: 30 30 30;--color-status-bar-bg: 240 244 248;--color-success: 14 184 64;--color-error: 247 118 142;--color-warning: 238 214 119;--color-info: 106 190 214;--hi-color-primary: rgb(var(--color-primary));--hi-color-secondary: rgb(var(--color-secondary));--hi-color-surface: rgb(var(--color-surface));--hi-color-background: rgb(var(--color-background));--hi-color-border: rgb(var(--color-border) / 15%);--hi-color-text-primary: rgb(var(--color-text));--hi-color-text-secondary: rgb(var(--color-text-secondary));--hi-color-muted: rgb(var(--color-muted));--hi-color-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-solid: rgb(var(--color-on-solid-text, 255 255 255));--hi-color-icon-on-solid: rgb(var(--color-on-solid-icon, 255 255 255));--hi-color-success: rgb(var(--color-success));--hi-color-error: rgb(var(--color-error));--hi-color-warning: rgb(var(--color-warning));--hi-color-info: rgb(var(--color-info));--hi-color-bg-subtle: rgb(var(--color-surface) / 0.5);--hi-color-bg-elevated: rgb(var(--color-surface));--hi-color-bg-canvas: rgb(var(--color-background));--hi-secondary-bg: rgb(var(--color-surface) / 0.5);--hi-color-accent: rgb(var(--color-accent));--hi-primary: var(--hi-color-primary);--hi-surface: var(--hi-color-surface);--hi-card-bg: var(--hi-color-surface)}:root{--space-1: 0.0625rem;--space-2: 0.125rem;--space-4: 0.25rem;--space-6: 0.375rem;--space-8: 0.5rem;--space-10: 0.625rem;--space-12: 0.75rem;--space-14: 0.875rem;--space-16: 1rem;--space-20: 1.25rem;--space-24: 1.5rem;--space-28: 1.75rem;--space-32: 2rem;--space-40: 2.5rem;--text-3xs: 0.5rem;--text-2xs: 0.625rem;--text-xs: 0.75rem;--text-sm: 0.8125rem;--text-base: 0.875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--text-2xl: 1.5rem;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--radius-full: 9999px;--blur-xs: 4px;--blur-sm: 8px;--blur-md: 16px;--blur-lg: 24px;--blur-xl: 24px;--opacity-half: 0.92;--duration-instant: 0.1s;--duration-short: 0.15s;--duration-fast: 0.15s;--duration-normal: 0.3s;--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);--ease-in: cubic-bezier(0.4, 0, 1, 1);--ease-elastic: cubic-bezier(0.16, 1, 0.3, 1);--hi-duration-fast: 0.15s;--hi-ease-default: cubic-bezier(0.4, 0, 0.2, 1);--hk-z-base: 0;--hk-z-above: 1;--hk-z-scrollbar: 10;--hk-z-fab: 40;--hk-z-toolbar: 50;--hk-z-header: 100;--hk-z-sidebar: 900;--hk-z-drag: 950;--hk-z-breadcrumb: 2500;--hk-z-float: 3000;--hk-z-toast: 4000;--hk-z-veil: 4100;--hk-z-modal: 5000;--hk-z-tooltip: 6000;--hk-z-overlay-root: 90000;--hk-z-hud: 90050;--hk-z-error: 95000;--hk-z-titlebar: 100001;--viewport-gutter: 16px;--font-sans: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif;--font-reading: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif;--font-mono: ui-monospace, \"SF Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", \"PingFang SC\", \"Microsoft YaHei\", monospace}@media(max-width: 767px){:root{--viewport-gutter: 8px}}.hk-error-landing{--hel-background: var(--color-background, 246 247 249);--hel-surface: var(--color-surface, 255 255 255);--hel-text: var(--color-text, 30 32 36);--hel-muted: var(--color-muted, 118 122 128);--hel-border: var(--color-border, 222 224 228);--hel-primary: var(--color-primary, 58 118 236);--hel-error: var(--color-error, 226 84 84);--hel-warning: var(--color-warning, 222 168 62);width:100%;min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center;padding:var(--space-20, 1.25rem);background:rgb(var(--hel-background));color:rgb(var(--hel-text));font-family:var(--font-sans, system-ui, -apple-system, \"Segoe UI\", sans-serif)}.hk-error-landing__card{position:relative;overflow:hidden;width:min(440px,100%);background:rgb(var(--hel-surface));border:1px solid rgb(var(--hel-border)/70%);border-radius:var(--radius-xl, 20px);box-shadow:var(--shadow-lg, 0 18px 48px rgba(0, 0, 0, 0.08));padding:2.25rem var(--space-28, 1.75rem) var(--space-28, 1.75rem);text-align:center}.hk-error-landing__card::before{content:\"\";position:absolute;inset:0 0 auto;height:150px;background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-error)/8%), transparent 72%);pointer-events:none}.hk-error-landing.is-warning .hk-error-landing__card::before{background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-warning)/10%), transparent 72%)}.hk-error-landing.is-info .hk-error-landing__card::before{background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-primary)/7%), transparent 72%)}.hk-error-landing__brand{position:relative;display:flex;justify-content:center;margin-bottom:var(--space-16, 1rem)}.hk-error-landing__brand:empty{display:none}.hk-error-landing__icon{position:relative;width:64px;height:64px;margin:0 auto var(--space-14, 0.875rem);display:flex;align-items:center;justify-content:center;border-radius:50%}.hk-error-landing__icon,.hk-error-landing__icon svg{color:rgb(var(--hel-error))}.hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-error)/18%), rgb(var(--hel-error)/6%));border:1px solid rgb(var(--hel-error)/26%);box-shadow:0 0 0 8px rgb(var(--hel-error)/5%),0 10px 28px -12px rgb(var(--hel-error)/45%)}.hk-error-landing.is-warning .hk-error-landing__icon,.hk-error-landing.is-warning .hk-error-landing__icon svg{color:rgb(var(--hel-warning))}.hk-error-landing.is-warning .hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-warning)/20%), rgb(var(--hel-warning)/7%));border-color:rgb(var(--hel-warning)/28%);box-shadow:0 0 0 8px rgb(var(--hel-warning)/6%),0 10px 28px -12px rgb(var(--hel-warning)/45%)}.hk-error-landing.is-info .hk-error-landing__icon,.hk-error-landing.is-info .hk-error-landing__icon svg{color:rgb(var(--hel-primary))}.hk-error-landing.is-info .hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-primary)/16%), rgb(var(--hel-primary)/5%));border-color:rgb(var(--hel-primary)/24%);box-shadow:0 0 0 8px rgb(var(--hel-primary)/5%),0 10px 28px -12px rgb(var(--hel-primary)/40%)}.hk-error-landing__meta{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--space-6, 0.375rem);margin-bottom:var(--space-8, 0.5rem)}.hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-error));--hk-badge-bg: rgb(var(--hel-error) / 10%);--hk-badge-border: rgb(var(--hel-error) / 22%)}.hk-error-landing.is-warning .hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-warning));--hk-badge-bg: rgb(var(--hel-warning) / 12%);--hk-badge-border: rgb(var(--hel-warning) / 24%)}.hk-error-landing.is-info .hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-primary));--hk-badge-bg: rgb(var(--hel-primary) / 10%);--hk-badge-border: rgb(var(--hel-primary) / 20%)}.hk-error-landing__meta .hk-badge-muted{--hk-badge-text: rgb(var(--hel-muted));--hk-badge-bg: rgb(var(--hel-muted) / 8%);--hk-badge-border: rgb(var(--hel-border) / 80%)}.hk-error-landing__title{position:relative;margin:0;font-size:var(--text-xl, 1.25rem);font-weight:650;letter-spacing:-0.01em;line-height:1.35;color:rgb(var(--hel-text));white-space:pre-line;overflow-wrap:anywhere}.hk-error-landing__desc{position:relative;margin:var(--space-8, 0.5rem) 0 0;font-size:var(--text-sm, 0.8125rem);line-height:1.6;color:rgb(var(--hel-muted));white-space:pre-line;overflow-wrap:anywhere}.hk-error-landing__details{position:relative;margin-top:var(--space-16, 1rem);text-align:start}.hk-error-landing__details-label{display:flex;align-items:center;gap:var(--space-4, 4px);padding:0 var(--space-2, 2px);font-size:var(--text-xs, 0.75rem);font-weight:500;letter-spacing:.02em;color:rgb(var(--hel-muted));-webkit-user-select:none;user-select:none}.hk-error-landing__details-pane{position:relative;margin-top:var(--space-6, 0.375rem);height:max(9rem,20vh);height:max(9rem,20dvh);border:1px solid rgb(var(--hel-border)/70%);border-radius:var(--radius-md, 10px);background:rgb(var(--hel-muted)/4%)}.hk-error-landing__details-body{height:100%;overflow:auto;border-radius:inherit;padding:var(--space-4, 0.25rem);scrollbar-width:none}.hk-error-landing__details-body::-webkit-scrollbar{display:none}.hk-error-landing .hk-error-landing__details-body .s-tool-json-tree{max-height:none;height:auto;overflow:visible;margin:0;padding:0;border:none;border-radius:0;background:rgba(0,0,0,0)}.hk-error-landing__actions{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--space-8, 0.5rem);margin-top:var(--space-16, 1rem)}.hk-error-landing.is-inline{min-height:100%;padding:0 var(--space-12, 0.75rem);align-items:flex-start;background:rgba(0,0,0,0)}.hk-error-landing.is-inline.is-fill{flex:1 1 auto;min-height:0}.hk-error-landing.is-inline .hk-error-landing__card{margin:auto;padding:var(--space-16, 1rem) var(--space-28, 1.75rem) var(--space-24, 1.5rem);display:flex;flex-direction:column;max-height:100%;min-height:min-content}.hk-error-landing.is-inline .hk-error-landing__details{display:flex;flex-direction:column;min-height:0}.hk-error-landing.is-inline .hk-error-landing__details-pane{height:auto;flex-grow:0;flex-shrink:1;flex-basis:max(9rem,20vh);flex-basis:max(9rem,20dvh);min-height:6rem}@media(max-width: 480px){.hk-error-landing__card{padding:var(--space-28, 1.75rem) 1.125rem var(--space-24, 1.5rem)}}:root{--c-primary-faint: rgb(var(--color-primary, 122 162 247) / 5%);--c-primary-subtle: rgb(var(--color-primary, 122 162 247) / 8%);--c-primary-dim: rgb(var(--color-primary, 122 162 247) / 10%);--c-primary-light: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-overlay: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-medium: rgb(var(--color-primary, 122 162 247) / 25%);--c-primary-strong: rgb(var(--color-primary, 122 162 247) / 40%);--c-primary-intense: rgb(var(--color-primary, 122 162 247) / 40%);--c-error-dim: rgb(var(--color-error, 247 118 142) / 10%);--c-error-overlay: rgb(var(--color-error, 247 118 142) / 20%);--c-error-strong: rgb(var(--color-error, 247 118 142) / 50%);--shadow-elevated: 0 8px 32px rgb(0 0 0 / 15%);--shadow-dropdown: 0 4px 24px rgb(0 0 0 / 12%), 0 0 0 1px rgb(255 255 255 / 4%);--shadow-button: 0 4px 14px rgb(var(--color-primary, 122 162 247) / 35%);--shadow-button-danger: 0 4px 14px rgb(var(--color-error, 247 118 142) / 35%);--shadow-focus: 0 0 0 3px rgb(var(--color-primary, 122 162 247) / 12%);--shadow-focus-error: 0 0 0 3px rgb(var(--color-error, 247 118 142) / 12%);--border-input: rgb(var(--color-border, 100 100 100) / 15%);--border-faint: rgb(var(--color-border, 100 100 100) / 10%);--border-subtle: rgb(var(--color-border, 100 100 100) / 12%);--hi-icon-color: rgb(var(--color-text, 30 30 30));--hi-icon-size-xs: 0.75rem;--hi-icon-size-sm: 1rem;--hi-icon-size-md: 1.25rem;--hi-icon-size-lg: 1.5rem;--hi-duration-instant: var(--duration-instant, 0.1s);--hi-border-color-focus: rgb(var(--color-primary, 122 162 247));--hi-glow-color: transparent;--hi-bg-surface-dark: rgb(var(--color-muted, 108 108 108) / 12%);--hi-color-primary-dark: color-mix(in srgb, rgb(var(--color-primary, 122 162 247)) 82%, #000);--hi-color-secondary-dark: color-mix(in srgb, rgb(var(--color-secondary, 81 154 115)) 82%, #000);--hi-color-danger-dark: color-mix(in srgb, rgb(var(--color-error, 247 118 142)) 82%, #000);--hi-color-success-dark: color-mix(in srgb, rgb(var(--color-success, 14 184 64)) 82%, #000);--hi-color-text-on-secondary: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-danger: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-success: rgb(var(--color-on-solid, 255 255 255))}:root{--color-primary: 122 162 247;--color-on-primary: 255 255 255;--color-on-solid: 255 255 255;--color-on-solid-text: 255 255 255;--color-on-solid-icon: 255 255 255;--color-secondary: 81 154 115;--color-accent: 247 181 0;--color-text: 30 30 30;--color-text-secondary: 102 102 102;--color-text-tertiary: 140 140 140;--color-muted: 108 108 108;--color-border: 100 100 100;--color-focused-border: 122 162 247;--color-background: 255 255 255;--color-surface: 255 255 255;--color-selected-bg: 191 218 242;--color-selected-text: 30 30 30;--color-status-bar-bg: 240 244 248;--color-success: 14 184 64;--color-error: 247 118 142;--color-warning: 238 214 119;--color-info: 106 190 214;--hi-color-primary: rgb(var(--color-primary));--hi-color-secondary: rgb(var(--color-secondary));--hi-color-surface: rgb(var(--color-surface));--hi-color-background: rgb(var(--color-background));--hi-color-border: rgb(var(--color-border) / 15%);--hi-color-text-primary: rgb(var(--color-text));--hi-color-text-secondary: rgb(var(--color-text-secondary));--hi-color-muted: rgb(var(--color-muted));--hi-color-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-solid: rgb(var(--color-on-solid-text, 255 255 255));--hi-color-icon-on-solid: rgb(var(--color-on-solid-icon, 255 255 255));--hi-color-success: rgb(var(--color-success));--hi-color-error: rgb(var(--color-error));--hi-color-warning: rgb(var(--color-warning));--hi-color-info: rgb(var(--color-info));--hi-color-bg-subtle: rgb(var(--color-surface) / 0.5);--hi-color-bg-elevated: rgb(var(--color-surface));--hi-color-bg-canvas: rgb(var(--color-background));--hi-secondary-bg: rgb(var(--color-surface) / 0.5);--hi-color-accent: rgb(var(--color-accent));--hi-primary: var(--hi-color-primary);--hi-surface: var(--hi-color-surface);--hi-card-bg: var(--hi-color-surface)}.hk-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-6);font-family:inherit;font-weight:600;line-height:1;border:1px solid rgba(0,0,0,0);border-radius:var(--radius-sm);cursor:pointer;transition-property:background-color,border-color,color,box-shadow,opacity,transform,filter;transition-duration:var(--duration-normal);transition-timing-function:var(--ease-standard);white-space:nowrap;-webkit-user-select:none;user-select:none;outline:none;position:relative}.hk-btn:focus-visible{box-shadow:0 0 0 2px rgb(var(--color-surface)),0 0 0 4px rgb(var(--color-primary))}.hk-btn:disabled{opacity:.5;cursor:not-allowed}.hk-btn-xs{padding:var(--space-4) var(--space-8);font-size:var(--text-xs);min-height:1.375rem}.hk-btn-sm{padding:var(--space-6) var(--space-12);font-size:var(--text-xs);min-height:1.75rem}.hk-btn-md{padding:var(--space-8) var(--space-16);font-size:var(--text-base);min-height:2.5rem}.hk-btn-lg{padding:var(--space-10) var(--space-24);font-size:var(--text-base);min-height:2.75rem}.hk-btn-xs .hk-btn-icon,.hk-btn-xs .hk-btn-suffix{inline-size:.8125rem;block-size:.8125rem}.hk-btn-xs .hk-btn-icon .hk-icon,.hk-btn-xs .hk-btn-suffix .hk-icon{inline-size:100%;block-size:100%}.hk-btn-icon,.hk-btn-suffix{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:1rem;block-size:1rem;line-height:0}.hk-btn-icon svg,.hk-btn-suffix svg{display:block}.hk-btn>svg{flex:none}.hk-btn-icon-only{padding:0;box-sizing:border-box}.hk-btn-xs.hk-btn-icon-only{width:1.375rem}.hk-btn-sm.hk-btn-icon-only{width:1.75rem}.hk-btn-md.hk-btn-icon-only{width:2.5rem}.hk-btn-lg.hk-btn-icon-only{width:2.75rem}.hk-btn-block.hk-btn-icon-only{width:100%}.hk-btn-label{display:inline-flex;align-items:center;min-width:0}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}.hk-btn-primary{background:rgb(var(--color-primary));color:rgb(var(--color-on-solid))}.hk-btn-primary svg{color:rgb(var(--color-on-solid-icon, 255 255 255))}.hk-btn-primary:hover:not(:disabled):not(:focus-visible){filter:brightness(1.1);box-shadow:var(--shadow-button)}.hk-btn-primary:active:not(:disabled){filter:brightness(0.95)}.hk-btn-secondary{background:rgb(var(--color-surface));color:rgb(var(--color-text));border-color:rgb(var(--color-border)/20%);-webkit-backdrop-filter:blur(var(--blur-sm));backdrop-filter:blur(var(--blur-sm))}.hk-btn-secondary:hover:not(:disabled){border-color:var(--c-primary-strong)}.hk-btn-secondary:active:not(:disabled){filter:brightness(0.95)}.hk-btn-outline{background:rgba(0,0,0,0);color:rgb(var(--color-primary));border-color:var(--c-primary-intense)}.hk-btn-outline:hover:not(:disabled){background:var(--c-primary-light);border-color:rgb(var(--color-primary))}.hk-btn-outline:active:not(:disabled){filter:brightness(0.95)}.hk-btn-ghost{background:rgba(0,0,0,0);color:rgb(var(--color-text));border-color:rgba(0,0,0,0)}.hk-btn-ghost:hover:not(:disabled){background:var(--c-primary-light)}.hk-btn-ghost:active:not(:disabled){filter:brightness(0.95)}.hk-btn-danger{background:rgb(var(--color-error));color:rgb(var(--color-on-solid))}.hk-btn-danger:hover:not(:disabled):not(:focus-visible){filter:brightness(1.1);box-shadow:var(--shadow-button-danger)}.hk-btn-danger:active:not(:disabled){filter:brightness(0.95)}.hk-btn-block{width:100%}.hk-btn-loading{pointer-events:none}@media(prefers-color-scheme: dark){:root{--color-primary: 136 192 208;--color-on-solid: 46 52 64;--color-on-solid-text: 46 52 64;--color-on-solid-icon: 46 52 64;--color-text: 247 249 252;--color-muted: 180 180 180;--color-border: 128 128 128;--color-background: 22 27 38;--color-surface: 34 40 54;--color-error: 191 97 106}}";
|
|
14
|
+
export const BOOT_LANDING_CSS = ":root{--c-primary-faint: rgb(var(--color-primary, 122 162 247) / 5%);--c-primary-subtle: rgb(var(--color-primary, 122 162 247) / 8%);--c-primary-dim: rgb(var(--color-primary, 122 162 247) / 10%);--c-primary-light: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-overlay: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-medium: rgb(var(--color-primary, 122 162 247) / 25%);--c-primary-strong: rgb(var(--color-primary, 122 162 247) / 40%);--c-primary-intense: rgb(var(--color-primary, 122 162 247) / 40%);--c-error-dim: rgb(var(--color-error, 247 118 142) / 10%);--c-error-overlay: rgb(var(--color-error, 247 118 142) / 20%);--c-error-strong: rgb(var(--color-error, 247 118 142) / 50%);--shadow-elevated: 0 8px 32px rgb(0 0 0 / 15%);--shadow-dropdown: 0 4px 24px rgb(0 0 0 / 12%), 0 0 0 1px rgb(255 255 255 / 4%);--shadow-button: 0 4px 14px rgb(var(--color-primary, 122 162 247) / 35%);--shadow-button-danger: 0 4px 14px rgb(var(--color-error, 247 118 142) / 35%);--shadow-focus: 0 0 0 3px rgb(var(--color-primary, 122 162 247) / 12%);--shadow-focus-error: 0 0 0 3px rgb(var(--color-error, 247 118 142) / 12%);--border-input: rgb(var(--color-border, 100 100 100) / 15%);--border-faint: rgb(var(--color-border, 100 100 100) / 10%);--border-subtle: rgb(var(--color-border, 100 100 100) / 12%);--hi-icon-color: rgb(var(--color-text, 30 30 30));--hi-icon-size-xs: 0.75rem;--hi-icon-size-sm: 1rem;--hi-icon-size-md: 1.25rem;--hi-icon-size-lg: 1.5rem;--hi-duration-instant: var(--duration-instant, 0.1s);--hi-border-color-focus: rgb(var(--color-primary, 122 162 247));--hi-glow-color: transparent;--hi-bg-surface-dark: rgb(var(--color-muted, 108 108 108) / 12%);--hi-color-primary-dark: color-mix(in srgb, rgb(var(--color-primary, 122 162 247)) 82%, #000);--hi-color-secondary-dark: color-mix(in srgb, rgb(var(--color-secondary, 81 154 115)) 82%, #000);--hi-color-danger-dark: color-mix(in srgb, rgb(var(--color-error, 247 118 142)) 82%, #000);--hi-color-success-dark: color-mix(in srgb, rgb(var(--color-success, 14 184 64)) 82%, #000);--hi-color-text-on-secondary: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-danger: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-success: rgb(var(--color-on-solid, 255 255 255))}:root{--color-primary: 122 162 247;--color-on-primary: 255 255 255;--color-on-solid: 255 255 255;--color-on-solid-text: 255 255 255;--color-on-solid-icon: 255 255 255;--color-secondary: 81 154 115;--color-accent: 247 181 0;--color-text: 30 30 30;--color-text-secondary: 102 102 102;--color-text-tertiary: 140 140 140;--color-muted: 108 108 108;--color-border: 100 100 100;--color-focused-border: 122 162 247;--color-background: 255 255 255;--color-surface: 255 255 255;--color-selected-bg: 191 218 242;--color-selected-text: 30 30 30;--color-status-bar-bg: 240 244 248;--color-success: 14 184 64;--color-error: 247 118 142;--color-warning: 238 214 119;--color-info: 106 190 214;--hi-color-primary: rgb(var(--color-primary));--hi-color-secondary: rgb(var(--color-secondary));--hi-color-surface: rgb(var(--color-surface));--hi-color-background: rgb(var(--color-background));--hi-color-border: rgb(var(--color-border) / 15%);--hi-color-text-primary: rgb(var(--color-text));--hi-color-text-secondary: rgb(var(--color-text-secondary));--hi-color-muted: rgb(var(--color-muted));--hi-color-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-solid: rgb(var(--color-on-solid-text, 255 255 255));--hi-color-icon-on-solid: rgb(var(--color-on-solid-icon, 255 255 255));--hi-color-success: rgb(var(--color-success));--hi-color-error: rgb(var(--color-error));--hi-color-warning: rgb(var(--color-warning));--hi-color-info: rgb(var(--color-info));--hi-color-bg-subtle: rgb(var(--color-surface) / 0.5);--hi-color-bg-elevated: rgb(var(--color-surface));--hi-color-bg-canvas: rgb(var(--color-background));--hi-secondary-bg: rgb(var(--color-surface) / 0.5);--hi-color-accent: rgb(var(--color-accent));--hi-primary: var(--hi-color-primary);--hi-surface: var(--hi-color-surface);--hi-card-bg: var(--hi-color-surface)}:root{--space-1: 0.0625rem;--space-2: 0.125rem;--space-4: 0.25rem;--space-6: 0.375rem;--space-8: 0.5rem;--space-10: 0.625rem;--space-12: 0.75rem;--space-14: 0.875rem;--space-16: 1rem;--space-20: 1.25rem;--space-24: 1.5rem;--space-28: 1.75rem;--space-32: 2rem;--space-40: 2.5rem;--text-3xs: 0.5rem;--text-2xs: 0.625rem;--text-xs: 0.75rem;--text-sm: 0.8125rem;--text-base: 0.875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--text-2xl: 1.5rem;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--radius-full: 9999px;--blur-xs: 4px;--blur-sm: 8px;--blur-md: 16px;--blur-lg: 24px;--blur-xl: 24px;--opacity-half: 0.92;--duration-instant: 0.1s;--duration-short: 0.15s;--duration-fast: 0.15s;--duration-normal: 0.3s;--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);--ease-in: cubic-bezier(0.4, 0, 1, 1);--ease-elastic: cubic-bezier(0.16, 1, 0.3, 1);--hi-duration-fast: 0.15s;--hi-ease-default: cubic-bezier(0.4, 0, 0.2, 1);--hk-z-base: 0;--hk-z-above: 1;--hk-z-scrollbar: 10;--hk-z-fab: 40;--hk-z-toolbar: 50;--hk-z-header: 100;--hk-z-sidebar: 900;--hk-z-drag: 950;--hk-z-breadcrumb: 2500;--hk-z-float: 3000;--hk-z-toast: 4000;--hk-z-veil: 4100;--hk-z-modal: 5000;--hk-z-tooltip: 6000;--hk-z-overlay-root: 90000;--hk-z-hud: 90050;--hk-z-error: 95000;--hk-z-titlebar: 100001;--viewport-gutter: 16px;--font-sans: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif;--font-reading: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif;--font-mono: ui-monospace, \"SF Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", \"PingFang SC\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", monospace}@media(max-width: 767px){:root{--viewport-gutter: 8px}}.hk-error-landing{--hel-background: var(--color-background, 246 247 249);--hel-surface: var(--color-surface, 255 255 255);--hel-text: var(--color-text, 30 32 36);--hel-muted: var(--color-muted, 118 122 128);--hel-border: var(--color-border, 222 224 228);--hel-primary: var(--color-primary, 58 118 236);--hel-error: var(--color-error, 226 84 84);--hel-warning: var(--color-warning, 222 168 62);width:100%;min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center;padding:var(--space-20, 1.25rem);background:rgb(var(--hel-background));color:rgb(var(--hel-text));font-family:var(--font-sans, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif)}.hk-error-landing__card{position:relative;overflow:hidden;width:min(440px,100%);background:rgb(var(--hel-surface));border:1px solid rgb(var(--hel-border)/70%);border-radius:var(--radius-xl, 20px);box-shadow:var(--shadow-lg, 0 18px 48px rgba(0, 0, 0, 0.08));padding:2.25rem var(--space-28, 1.75rem) var(--space-28, 1.75rem);text-align:center}.hk-error-landing__card::before{content:\"\";position:absolute;inset:0 0 auto;height:150px;background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-error)/8%), transparent 72%);pointer-events:none}.hk-error-landing.is-warning .hk-error-landing__card::before{background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-warning)/10%), transparent 72%)}.hk-error-landing.is-info .hk-error-landing__card::before{background:radial-gradient(58% 100% at 50% 0%, rgb(var(--hel-primary)/7%), transparent 72%)}.hk-error-landing__brand{position:relative;display:flex;justify-content:center;margin-bottom:var(--space-16, 1rem)}.hk-error-landing__brand:empty{display:none}.hk-error-landing__icon{position:relative;width:64px;height:64px;margin:0 auto var(--space-14, 0.875rem);display:flex;align-items:center;justify-content:center;border-radius:50%}.hk-error-landing__icon,.hk-error-landing__icon svg{color:rgb(var(--hel-error))}.hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-error)/18%), rgb(var(--hel-error)/6%));border:1px solid rgb(var(--hel-error)/26%);box-shadow:0 0 0 8px rgb(var(--hel-error)/5%),0 10px 28px -12px rgb(var(--hel-error)/45%)}.hk-error-landing.is-warning .hk-error-landing__icon,.hk-error-landing.is-warning .hk-error-landing__icon svg{color:rgb(var(--hel-warning))}.hk-error-landing.is-warning .hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-warning)/20%), rgb(var(--hel-warning)/7%));border-color:rgb(var(--hel-warning)/28%);box-shadow:0 0 0 8px rgb(var(--hel-warning)/6%),0 10px 28px -12px rgb(var(--hel-warning)/45%)}.hk-error-landing.is-info .hk-error-landing__icon,.hk-error-landing.is-info .hk-error-landing__icon svg{color:rgb(var(--hel-primary))}.hk-error-landing.is-info .hk-error-landing__icon{background:linear-gradient(145deg, rgb(var(--hel-primary)/16%), rgb(var(--hel-primary)/5%));border-color:rgb(var(--hel-primary)/24%);box-shadow:0 0 0 8px rgb(var(--hel-primary)/5%),0 10px 28px -12px rgb(var(--hel-primary)/40%)}.hk-error-landing__meta{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--space-6, 0.375rem);margin-bottom:var(--space-8, 0.5rem)}.hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-error));--hk-badge-bg: rgb(var(--hel-error) / 10%);--hk-badge-border: rgb(var(--hel-error) / 22%)}.hk-error-landing.is-warning .hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-warning));--hk-badge-bg: rgb(var(--hel-warning) / 12%);--hk-badge-border: rgb(var(--hel-warning) / 24%)}.hk-error-landing.is-info .hk-error-landing__meta .hk-badge:not(.hk-badge-muted){--hk-badge-text: rgb(var(--hel-primary));--hk-badge-bg: rgb(var(--hel-primary) / 10%);--hk-badge-border: rgb(var(--hel-primary) / 20%)}.hk-error-landing__meta .hk-badge-muted{--hk-badge-text: rgb(var(--hel-muted));--hk-badge-bg: rgb(var(--hel-muted) / 8%);--hk-badge-border: rgb(var(--hel-border) / 80%)}.hk-error-landing__title{position:relative;margin:0;font-size:var(--text-xl, 1.25rem);font-weight:650;letter-spacing:-0.01em;line-height:1.35;color:rgb(var(--hel-text));white-space:pre-line;overflow-wrap:anywhere}.hk-error-landing__desc{position:relative;margin:var(--space-8, 0.5rem) 0 0;font-size:var(--text-sm, 0.8125rem);line-height:1.6;color:rgb(var(--hel-muted));white-space:pre-line;overflow-wrap:anywhere}.hk-error-landing__details{position:relative;margin-top:var(--space-16, 1rem);text-align:start}.hk-error-landing__details-label{display:flex;align-items:center;gap:var(--space-4, 4px);padding:0 var(--space-2, 2px);font-size:var(--text-xs, 0.75rem);font-weight:500;letter-spacing:.02em;color:rgb(var(--hel-muted));-webkit-user-select:none;user-select:none}.hk-error-landing__details-pane{position:relative;margin-top:var(--space-6, 0.375rem);height:max(9rem,20vh);height:max(9rem,20dvh);border:1px solid rgb(var(--hel-border)/70%);border-radius:var(--radius-md, 10px);background:rgb(var(--hel-muted)/4%)}.hk-error-landing__details-body{height:100%;overflow:auto;border-radius:inherit;padding:var(--space-4, 0.25rem);scrollbar-width:none}.hk-error-landing__details-body::-webkit-scrollbar{display:none}.hk-error-landing .hk-error-landing__details-body .s-tool-json-tree{max-height:none;height:auto;overflow:visible;margin:0;padding:0;border:none;border-radius:0;background:rgba(0,0,0,0)}.hk-error-landing__actions{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--space-8, 0.5rem);margin-top:var(--space-16, 1rem)}.hk-error-landing.is-inline{min-height:100%;padding:0 var(--space-12, 0.75rem);align-items:flex-start;background:rgba(0,0,0,0)}.hk-error-landing.is-inline.is-fill{flex:1 1 auto;min-height:0}.hk-error-landing.is-inline .hk-error-landing__card{margin:auto;padding:var(--space-16, 1rem) var(--space-28, 1.75rem) var(--space-24, 1.5rem);display:flex;flex-direction:column;max-height:100%;min-height:min-content}.hk-error-landing.is-inline .hk-error-landing__details{display:flex;flex-direction:column;min-height:0}.hk-error-landing.is-inline .hk-error-landing__details-pane{height:auto;flex-grow:0;flex-shrink:1;flex-basis:max(9rem,20vh);flex-basis:max(9rem,20dvh);min-height:6rem}@media(max-width: 480px){.hk-error-landing__card{padding:var(--space-28, 1.75rem) 1.125rem var(--space-24, 1.5rem)}}:root{--c-primary-faint: rgb(var(--color-primary, 122 162 247) / 5%);--c-primary-subtle: rgb(var(--color-primary, 122 162 247) / 8%);--c-primary-dim: rgb(var(--color-primary, 122 162 247) / 10%);--c-primary-light: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-overlay: rgb(var(--color-primary, 122 162 247) / 15%);--c-primary-medium: rgb(var(--color-primary, 122 162 247) / 25%);--c-primary-strong: rgb(var(--color-primary, 122 162 247) / 40%);--c-primary-intense: rgb(var(--color-primary, 122 162 247) / 40%);--c-error-dim: rgb(var(--color-error, 247 118 142) / 10%);--c-error-overlay: rgb(var(--color-error, 247 118 142) / 20%);--c-error-strong: rgb(var(--color-error, 247 118 142) / 50%);--shadow-elevated: 0 8px 32px rgb(0 0 0 / 15%);--shadow-dropdown: 0 4px 24px rgb(0 0 0 / 12%), 0 0 0 1px rgb(255 255 255 / 4%);--shadow-button: 0 4px 14px rgb(var(--color-primary, 122 162 247) / 35%);--shadow-button-danger: 0 4px 14px rgb(var(--color-error, 247 118 142) / 35%);--shadow-focus: 0 0 0 3px rgb(var(--color-primary, 122 162 247) / 12%);--shadow-focus-error: 0 0 0 3px rgb(var(--color-error, 247 118 142) / 12%);--border-input: rgb(var(--color-border, 100 100 100) / 15%);--border-faint: rgb(var(--color-border, 100 100 100) / 10%);--border-subtle: rgb(var(--color-border, 100 100 100) / 12%);--hi-icon-color: rgb(var(--color-text, 30 30 30));--hi-icon-size-xs: 0.75rem;--hi-icon-size-sm: 1rem;--hi-icon-size-md: 1.25rem;--hi-icon-size-lg: 1.5rem;--hi-duration-instant: var(--duration-instant, 0.1s);--hi-border-color-focus: rgb(var(--color-primary, 122 162 247));--hi-glow-color: transparent;--hi-bg-surface-dark: rgb(var(--color-muted, 108 108 108) / 12%);--hi-color-primary-dark: color-mix(in srgb, rgb(var(--color-primary, 122 162 247)) 82%, #000);--hi-color-secondary-dark: color-mix(in srgb, rgb(var(--color-secondary, 81 154 115)) 82%, #000);--hi-color-danger-dark: color-mix(in srgb, rgb(var(--color-error, 247 118 142)) 82%, #000);--hi-color-success-dark: color-mix(in srgb, rgb(var(--color-success, 14 184 64)) 82%, #000);--hi-color-text-on-secondary: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-danger: rgb(var(--color-on-solid, 255 255 255));--hi-color-text-on-success: rgb(var(--color-on-solid, 255 255 255))}:root{--color-primary: 122 162 247;--color-on-primary: 255 255 255;--color-on-solid: 255 255 255;--color-on-solid-text: 255 255 255;--color-on-solid-icon: 255 255 255;--color-secondary: 81 154 115;--color-accent: 247 181 0;--color-text: 30 30 30;--color-text-secondary: 102 102 102;--color-text-tertiary: 140 140 140;--color-muted: 108 108 108;--color-border: 100 100 100;--color-focused-border: 122 162 247;--color-background: 255 255 255;--color-surface: 255 255 255;--color-selected-bg: 191 218 242;--color-selected-text: 30 30 30;--color-status-bar-bg: 240 244 248;--color-success: 14 184 64;--color-error: 247 118 142;--color-warning: 238 214 119;--color-info: 106 190 214;--hi-color-primary: rgb(var(--color-primary));--hi-color-secondary: rgb(var(--color-secondary));--hi-color-surface: rgb(var(--color-surface));--hi-color-background: rgb(var(--color-background));--hi-color-border: rgb(var(--color-border) / 15%);--hi-color-text-primary: rgb(var(--color-text));--hi-color-text-secondary: rgb(var(--color-text-secondary));--hi-color-muted: rgb(var(--color-muted));--hi-color-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-primary: rgb(var(--color-on-primary));--hi-color-text-on-solid: rgb(var(--color-on-solid-text, 255 255 255));--hi-color-icon-on-solid: rgb(var(--color-on-solid-icon, 255 255 255));--hi-color-success: rgb(var(--color-success));--hi-color-error: rgb(var(--color-error));--hi-color-warning: rgb(var(--color-warning));--hi-color-info: rgb(var(--color-info));--hi-color-bg-subtle: rgb(var(--color-surface) / 0.5);--hi-color-bg-elevated: rgb(var(--color-surface));--hi-color-bg-canvas: rgb(var(--color-background));--hi-secondary-bg: rgb(var(--color-surface) / 0.5);--hi-color-accent: rgb(var(--color-accent));--hi-primary: var(--hi-color-primary);--hi-surface: var(--hi-color-surface);--hi-card-bg: var(--hi-color-surface)}.hk-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-6);font-family:inherit;font-weight:600;line-height:1;border:1px solid rgba(0,0,0,0);border-radius:var(--radius-sm);cursor:pointer;transition-property:background-color,border-color,color,box-shadow,opacity,transform,filter;transition-duration:var(--duration-normal);transition-timing-function:var(--ease-standard);white-space:nowrap;-webkit-user-select:none;user-select:none;outline:none;position:relative}.hk-btn:focus-visible{box-shadow:0 0 0 2px rgb(var(--color-surface)),0 0 0 4px rgb(var(--color-primary))}.hk-btn:disabled{opacity:.5;cursor:not-allowed}.hk-btn-xs{padding:var(--space-4) var(--space-8);font-size:var(--text-xs);min-height:1.375rem}.hk-btn-sm{padding:var(--space-6) var(--space-12);font-size:var(--text-xs);min-height:1.75rem}.hk-btn-md{padding:var(--space-8) var(--space-16);font-size:var(--text-base);min-height:2.5rem}.hk-btn-lg{padding:var(--space-10) var(--space-24);font-size:var(--text-base);min-height:2.75rem}.hk-btn-xs .hk-btn-icon,.hk-btn-xs .hk-btn-suffix{inline-size:.8125rem;block-size:.8125rem}.hk-btn-xs .hk-btn-icon .hk-icon,.hk-btn-xs .hk-btn-suffix .hk-icon{inline-size:100%;block-size:100%}.hk-btn-icon,.hk-btn-suffix{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:1rem;block-size:1rem;line-height:0}.hk-btn-icon svg,.hk-btn-suffix svg{display:block}.hk-btn>svg{flex:none}.hk-btn-icon-only{padding:0;box-sizing:border-box}.hk-btn-xs.hk-btn-icon-only{width:1.375rem}.hk-btn-sm.hk-btn-icon-only{width:1.75rem}.hk-btn-md.hk-btn-icon-only{width:2.5rem}.hk-btn-lg.hk-btn-icon-only{width:2.75rem}.hk-btn-block.hk-btn-icon-only{width:100%}.hk-btn-label{display:inline-flex;align-items:center;min-width:0}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 639.98px){.hk-btn-label-collapse-sm.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-sm.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 767.98px){.hk-btn-label-collapse-md.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-md.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 1023.98px){.hk-btn-label-collapse-lg.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-lg.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-xs .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-xs{padding:0;box-sizing:border-box;width:1.375rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-sm .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-sm{padding:0;box-sizing:border-box;width:1.75rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-md .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-md{padding:0;box-sizing:border-box;width:2.5rem}}@media(max-width: 1279.98px){.hk-btn-label-collapse-xl.hk-btn-lg .hk-btn-label{display:none}.hk-btn-label-collapse-xl.hk-btn-lg{padding:0;box-sizing:border-box;width:2.75rem}}.hk-btn-primary{background:rgb(var(--color-primary));color:rgb(var(--color-on-solid))}.hk-btn-primary svg{color:rgb(var(--color-on-solid-icon, 255 255 255))}.hk-btn-primary:hover:not(:disabled):not(:focus-visible){filter:brightness(1.1);box-shadow:var(--shadow-button)}.hk-btn-primary:active:not(:disabled){filter:brightness(0.95)}.hk-btn-secondary{background:rgb(var(--color-surface));color:rgb(var(--color-text));border-color:rgb(var(--color-border)/20%);-webkit-backdrop-filter:blur(var(--blur-sm));backdrop-filter:blur(var(--blur-sm))}.hk-btn-secondary:hover:not(:disabled){border-color:var(--c-primary-strong)}.hk-btn-secondary:active:not(:disabled){filter:brightness(0.95)}.hk-btn-outline{background:rgba(0,0,0,0);color:rgb(var(--color-primary));border-color:var(--c-primary-intense)}.hk-btn-outline:hover:not(:disabled){background:var(--c-primary-light);border-color:rgb(var(--color-primary))}.hk-btn-outline:active:not(:disabled){filter:brightness(0.95)}.hk-btn-ghost{background:rgba(0,0,0,0);color:rgb(var(--color-text));border-color:rgba(0,0,0,0)}.hk-btn-ghost:hover:not(:disabled){background:var(--c-primary-light)}.hk-btn-ghost:active:not(:disabled){filter:brightness(0.95)}.hk-btn-danger{background:rgb(var(--color-error));color:rgb(var(--color-on-solid))}.hk-btn-danger:hover:not(:disabled):not(:focus-visible){filter:brightness(1.1);box-shadow:var(--shadow-button-danger)}.hk-btn-danger:active:not(:disabled){filter:brightness(0.95)}.hk-btn-block{width:100%}.hk-btn-loading{pointer-events:none}@media(prefers-color-scheme: dark){:root{--color-primary: 136 192 208;--color-on-solid: 46 52 64;--color-on-solid-text: 46 52 64;--color-on-solid-icon: 46 52 64;--color-text: 247 249 252;--color-muted: 180 180 180;--color-border: 128 128 128;--color-background: 22 27 38;--color-surface: 34 40 54;--color-error: 191 97 106}}";
|
|
@@ -27,6 +27,13 @@ import { BOOT_LANDING_CSS } from "./bootLandingCss.mjs";
|
|
|
27
27
|
* "reuse the component" a mechanical property instead of a promise (edit the
|
|
28
28
|
* component sheet and the panel follows through one regeneration).
|
|
29
29
|
*
|
|
30
|
+
* Scope note (2026-11-07 mutation round): the entry @uses the VENDORED
|
|
31
|
+
* `styles/theme/scale.scss`, not the canonical `src/scale.scss` — drifting
|
|
32
|
+
* the canonical copy alone turns fontStacks.contract.test.ts red (three
|
|
33
|
+
* arms) but is invisible here. Token edits: change src/scale.scss, re-copy
|
|
34
|
+
* to the vendored path, regenerate this artifact — all three guards then
|
|
35
|
+
* see the same bytes.
|
|
36
|
+
*
|
|
30
37
|
* Regenerate with (from packages/vue): pnpm run gen:boot-landing-css
|
|
31
38
|
*/
|
|
32
39
|
|
package/src/scale.scss
CHANGED
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
/* Fonts — keep in sync with packages/vue/src/theme/fontContext.ts */
|
|
118
118
|
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
119
119
|
--font-reading: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
120
|
-
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", "PingFang SC", "Microsoft YaHei", monospace;
|
|
120
|
+
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", monospace;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/* Mobile viewports halve the floating-layer gutter. The breakpoint mirrors
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
/* Fonts — keep in sync with packages/vue/src/theme/fontContext.ts */
|
|
120
120
|
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
121
121
|
--font-reading: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
122
|
-
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", "PingFang SC", "Microsoft YaHei", monospace;
|
|
122
|
+
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", monospace;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/* Mobile viewports halve the floating-layer gutter. The breakpoint mirrors
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { HIKARI_FONT_MONO, HIKARI_FONT_READING, HIKARI_FONT_SANS } from "./fontContext";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Font-stack sync contract (2026-11-07): the static SCSS tokens and the
|
|
11
|
+
* runtime fontContext constants are copies of one truth, and they
|
|
12
|
+
* drifted once — `--font-sans` carried the Linux CJK face
|
|
13
|
+
* ("Noto Sans CJK SC") in both sheet copies while `--font-mono` carried
|
|
14
|
+
* it only in fontContext.ts, so any surface rendering the STATIC sheet
|
|
15
|
+
* (pre-boot paint, or a downstream utility emitting its own literal
|
|
16
|
+
* stack) dropped CJK text into a random platform mono on Linux. All
|
|
17
|
+
* FOUR copies are pinned here — both scale.scss sheets, the fontContext
|
|
18
|
+
* constants, and the Sass-variable fallbacks in
|
|
19
|
+
* `components/_hikari-vars.scss` — so the next face can only be added
|
|
20
|
+
* in one place by failing here.
|
|
21
|
+
*
|
|
22
|
+
* Known-positive anchor: the mono stack must keep a CJK UI face AFTER the
|
|
23
|
+
* Latin mono faces and BEFORE the generic `monospace` catcher — that
|
|
24
|
+
* ordering IS the CJK-safety contract (Latin glyphs mono, CJK glyphs
|
|
25
|
+
* sans), not an accident of copy order.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
|
|
30
|
+
const SCSS_SOURCES = [
|
|
31
|
+
resolve(here, "../scale.scss"),
|
|
32
|
+
// Vendored byte-identical copy for npm installs (see its header).
|
|
33
|
+
resolve(here, "../styles/theme/scale.scss"),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
function tokenFromScss(path: string, token: string): string {
|
|
37
|
+
const css = readFileSync(path, "utf-8");
|
|
38
|
+
const match = css.match(new RegExp(`--${token}:\\s*([^;]+);`));
|
|
39
|
+
expect(match, `${path} must declare --${token}`).toBeTruthy();
|
|
40
|
+
return match![1]!.trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("font stack sync (scale.scss ↔ fontContext.ts)", () => {
|
|
44
|
+
it("keeps every vendored scale.scss copy byte-identical on the font block", () => {
|
|
45
|
+
const blocks = SCSS_SOURCES.map((path) => {
|
|
46
|
+
const css = readFileSync(path, "utf-8");
|
|
47
|
+
const match = css.match(/--font-sans:[^;]+;\s*--font-reading:[^;]+;\s*--font-mono:[^;]+;/);
|
|
48
|
+
expect(match, `${path} must declare the font token block`).toBeTruthy();
|
|
49
|
+
return match![0];
|
|
50
|
+
});
|
|
51
|
+
for (const block of blocks.slice(1)) {
|
|
52
|
+
expect(block).toBe(blocks[0]);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("keeps --font-sans/--font-reading/--font-mono identical to the fontContext constants", () => {
|
|
57
|
+
for (const path of SCSS_SOURCES) {
|
|
58
|
+
expect(tokenFromScss(path, "font-sans")).toBe(HIKARI_FONT_SANS);
|
|
59
|
+
expect(tokenFromScss(path, "font-reading")).toBe(HIKARI_FONT_READING);
|
|
60
|
+
expect(tokenFromScss(path, "font-mono")).toBe(HIKARI_FONT_MONO);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("keeps the Sass-variable fallback copies identical to the constants too", () => {
|
|
65
|
+
// _hikari-vars.scss mirrors both stacks as Sass variables for the
|
|
66
|
+
// `var(--font-*, …)` fallbacks component sheets use; normalize its
|
|
67
|
+
// single-quoted faces before comparing.
|
|
68
|
+
const normalize = (stack: string) => stack.replace(/'/g, '"').trim();
|
|
69
|
+
const vars = readFileSync(resolve(here, "../components/_hikari-vars.scss"), "utf-8");
|
|
70
|
+
const sassSans = vars.match(/\$hikari-font-family-sans:\s*([^;]+);/);
|
|
71
|
+
const sassMono = vars.match(/\$hikari-font-family-mono:\s*([^;]+);/);
|
|
72
|
+
expect(sassSans, "_hikari-vars.scss must declare the sans fallback").toBeTruthy();
|
|
73
|
+
expect(sassMono, "_hikari-vars.scss must declare the mono fallback").toBeTruthy();
|
|
74
|
+
expect(normalize(sassSans![1]!)).toBe(HIKARI_FONT_SANS);
|
|
75
|
+
expect(normalize(sassMono![1]!)).toBe(HIKARI_FONT_MONO);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("orders every mono stack CJK-safe: the Linux CJK face lands before the generic monospace catcher", () => {
|
|
79
|
+
// The generic catcher is where Linux fontconfig roulette lives — the
|
|
80
|
+
// Linux-reachable CJK face must always be resolved before the stack
|
|
81
|
+
// reaches it. Known positive: this exact predicate FAILS on the
|
|
82
|
+
// pre-2026-11-07 stack (`…"PingFang SC", "Microsoft YaHei",
|
|
83
|
+
// monospace`) — "PingFang SC" / "Microsoft YaHei" don't exist on
|
|
84
|
+
// Linux builds, so a predicate accepting ANY CJK face there would
|
|
85
|
+
// have passed the stack that caused the incident. Only the
|
|
86
|
+
// Linux-coverable face satisfies it.
|
|
87
|
+
for (const stack of [HIKARI_FONT_MONO, ...SCSS_SOURCES.map((p) => tokenFromScss(p, "font-mono")),]) {
|
|
88
|
+
const faces = stack.split(",").map((f) => f.trim().replace(/^"|"$/g, ""));
|
|
89
|
+
const catcher = faces.indexOf("monospace");
|
|
90
|
+
expect(catcher, "stack must end in the generic monospace catcher").toBe(faces.length - 1);
|
|
91
|
+
const beforeCatcher = faces.slice(0, catcher);
|
|
92
|
+
expect(
|
|
93
|
+
beforeCatcher.some((f) => /Noto Sans CJK/.test(f)),
|
|
94
|
+
`Linux CJK face must precede the monospace catcher: ${stack}`,
|
|
95
|
+
).toBe(true);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|