@celestia-island/hikari 0.55.198 → 0.55.200
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
|
@@ -7,15 +7,12 @@
|
|
|
7
7
|
.s-scheme-mode-switch {
|
|
8
8
|
align-self: center;
|
|
9
9
|
|
|
10
|
-
/* The
|
|
11
|
-
|
|
12
|
-
2026-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
padding: var(--space-2, 2px);
|
|
17
|
-
border-radius: var(--hi-radius-sm, 4px);
|
|
18
|
-
}
|
|
10
|
+
/* The editor renders variant="segmented" — the DEFAULT segmented
|
|
11
|
+
chrome (hikari's own track) is exactly what the user asked for
|
|
12
|
+
(2026-10-23: the bespoke tinted track read as a different button
|
|
13
|
+
family from every other segmented group). The 2026-09-16 custom
|
|
14
|
+
track predates the editor's move to the segmented variant and is
|
|
15
|
+
retired with it. */
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
/* Why-this-strip note: the tabs switch which palette you are EDITING;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// HkStatusRow — the standard status-card anatomy (rail + chip + body +
|
|
2
|
+
// action). Tones map the palette channels; the absent variant is the
|
|
3
|
+
// quiet dashed edge for missing entries.
|
|
4
|
+
|
|
5
|
+
.hk-status-row {
|
|
6
|
+
position: relative;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 0.75rem;
|
|
10
|
+
padding: 0.875rem 1rem 0.875rem 1.125rem;
|
|
11
|
+
border-radius: 0.625rem;
|
|
12
|
+
border: 1px solid rgb(var(--color-border, 60 60 60) / 18%);
|
|
13
|
+
background: rgb(var(--color-surface, 255 255 255) / 70%);
|
|
14
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
15
|
+
|
|
16
|
+
&::before {
|
|
17
|
+
content: "";
|
|
18
|
+
position: absolute;
|
|
19
|
+
inset-block: 0.625rem;
|
|
20
|
+
left: 0;
|
|
21
|
+
width: 3px;
|
|
22
|
+
border-radius: 999px;
|
|
23
|
+
background: rgb(var(--color-muted, 108 108 108) / 45%);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&--info {
|
|
27
|
+
border-color: rgb(var(--color-primary, 0 107 255) / 35%);
|
|
28
|
+
&::before { background: rgb(var(--color-primary, 0 107 255)); }
|
|
29
|
+
&:hover { border-color: rgb(var(--color-primary, 0 107 255) / 60%); }
|
|
30
|
+
}
|
|
31
|
+
&--success {
|
|
32
|
+
border-color: rgb(var(--color-success, 0 128 68) / 30%);
|
|
33
|
+
&::before { background: rgb(var(--color-success, 0 128 68)); }
|
|
34
|
+
}
|
|
35
|
+
&--warning {
|
|
36
|
+
border-color: rgb(var(--color-warning, 200 140 30) / 35%);
|
|
37
|
+
&::before { background: rgb(var(--color-warning, 200 140 30)); }
|
|
38
|
+
}
|
|
39
|
+
&--error {
|
|
40
|
+
border-color: rgb(var(--color-error, 226 58 69) / 35%);
|
|
41
|
+
&::before { background: rgb(var(--color-error, 226 58 69)); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&--absent {
|
|
45
|
+
border-style: dashed;
|
|
46
|
+
opacity: 0.75;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.hk-status-row__chip {
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
width: 2.25rem;
|
|
55
|
+
height: 2.25rem;
|
|
56
|
+
flex-shrink: 0;
|
|
57
|
+
border-radius: 0.5rem;
|
|
58
|
+
background: rgb(var(--color-muted, 108 108 108) / 10%);
|
|
59
|
+
color: rgb(var(--color-muted, 108 108 108));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.hk-status-row--info .hk-status-row__chip {
|
|
63
|
+
background: rgb(var(--color-primary, 0 107 255) / 12%);
|
|
64
|
+
color: rgb(var(--color-primary, 0 107 255));
|
|
65
|
+
}
|
|
66
|
+
.hk-status-row--success .hk-status-row__chip {
|
|
67
|
+
background: rgb(var(--color-success, 0 128 68) / 12%);
|
|
68
|
+
color: rgb(var(--color-success, 0 128 68));
|
|
69
|
+
}
|
|
70
|
+
.hk-status-row--warning .hk-status-row__chip {
|
|
71
|
+
background: rgb(var(--color-warning, 200 140 30) / 14%);
|
|
72
|
+
color: rgb(var(--color-warning, 200 140 30));
|
|
73
|
+
}
|
|
74
|
+
.hk-status-row--error .hk-status-row__chip {
|
|
75
|
+
background: rgb(var(--color-error, 226 58 69) / 12%);
|
|
76
|
+
color: rgb(var(--color-error, 226 58 69));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.hk-status-row__body {
|
|
80
|
+
flex: 1;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
gap: 0.125rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.hk-status-row__title {
|
|
88
|
+
font-size: 0.875rem;
|
|
89
|
+
font-weight: 600;
|
|
90
|
+
line-height: 1.35;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.hk-status-row__status {
|
|
94
|
+
font-size: 0.8125rem;
|
|
95
|
+
line-height: 1.45;
|
|
96
|
+
color: var(--hk-text-muted, rgb(var(--color-muted, 108 108 108)));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.hk-status-row__action {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createApp, defineComponent, h, type App } from "vue";
|
|
3
|
+
|
|
4
|
+
import HkStatusRow from "./HkStatusRow";
|
|
5
|
+
|
|
6
|
+
const containers: HTMLElement[] = [];
|
|
7
|
+
|
|
8
|
+
function mountRow(props: Record<string, unknown>, slots: Record<string, () => unknown> = {}): {
|
|
9
|
+
app: App;
|
|
10
|
+
container: HTMLElement;
|
|
11
|
+
} {
|
|
12
|
+
const container = document.createElement("div");
|
|
13
|
+
document.body.appendChild(container);
|
|
14
|
+
containers.push(container);
|
|
15
|
+
const Wrapper = defineComponent({
|
|
16
|
+
render: () =>
|
|
17
|
+
h(HkStatusRow as never, props, slots),
|
|
18
|
+
});
|
|
19
|
+
const app = createApp(Wrapper);
|
|
20
|
+
app.mount(container);
|
|
21
|
+
return { app, container };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
for (const c of containers.splice(0)) c.remove();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("HkStatusRow (the standard status-card anatomy)", () => {
|
|
29
|
+
it("renders title, status, chip and action slots", () => {
|
|
30
|
+
const t = mountRow(
|
|
31
|
+
{ title: "evernight", status: "detected" },
|
|
32
|
+
{ icon: () => h("i"), action: () => h("button", "register") },
|
|
33
|
+
);
|
|
34
|
+
expect(t.container.querySelector(".hk-status-row__title")?.textContent).toBe("evernight");
|
|
35
|
+
expect(t.container.querySelector(".hk-status-row__status")?.textContent).toBe("detected");
|
|
36
|
+
expect(t.container.querySelector(".hk-status-row__chip")).not.toBeNull();
|
|
37
|
+
expect(t.container.querySelector(".hk-status-row__action button")?.textContent).toBe("register");
|
|
38
|
+
t.app.unmount();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("maps the tone onto the modifier class", () => {
|
|
42
|
+
const t = mountRow({ title: "x", tone: "warning" });
|
|
43
|
+
expect(t.container.querySelector(".hk-status-row--warning")).not.toBeNull();
|
|
44
|
+
t.app.unmount();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("defaults to neutral", () => {
|
|
48
|
+
const t = mountRow({ title: "x" });
|
|
49
|
+
expect(t.container.querySelector(".hk-status-row--neutral")).not.toBeNull();
|
|
50
|
+
t.app.unmount();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("absent renders the dashed quiet variant", () => {
|
|
54
|
+
const t = mountRow({ title: "x", absent: true });
|
|
55
|
+
expect(t.container.querySelector(".hk-status-row--absent")).not.toBeNull();
|
|
56
|
+
t.app.unmount();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("omits the status line when empty and the action zone without a slot", () => {
|
|
60
|
+
const t = mountRow({ title: "x" });
|
|
61
|
+
expect(t.container.querySelector(".hk-status-row__status")).toBeNull();
|
|
62
|
+
expect(t.container.querySelector(".hk-status-row__action")).toBeNull();
|
|
63
|
+
t.app.unmount();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { defineComponent, type PropType, type VNode } from "vue";
|
|
2
|
+
|
|
3
|
+
import "./HkStatusRow.scss";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* HkStatusRow — the family's standard status-card row (task-3 D,
|
|
7
|
+
* user ruling 2026-10-23): one anatomy for every "a thing and its
|
|
8
|
+
* state" listing — the registration wizard's members, the desktop
|
|
9
|
+
* services panel, future install checklists.
|
|
10
|
+
*
|
|
11
|
+
* Anatomy (deliberately fixed):
|
|
12
|
+
* - a 3px status RAIL on the card's left edge — the state at a glance;
|
|
13
|
+
* - an icon CHIP (rounded square, status-tinted wash, default slot);
|
|
14
|
+
* - TITLE + STATUS LINE stacked in the body;
|
|
15
|
+
* - an optional trailing ACTION slot (a button, a copy affordance).
|
|
16
|
+
*
|
|
17
|
+
* The `tone` prop is the single state axis: neutral | info (primary) |
|
|
18
|
+
* success | warning | error. Hosts map their domain states onto tones
|
|
19
|
+
* — the row never guesses.
|
|
20
|
+
*/
|
|
21
|
+
export type HkStatusRowTone = "neutral" | "info" | "success" | "warning" | "error";
|
|
22
|
+
|
|
23
|
+
export const HkStatusRow = defineComponent({
|
|
24
|
+
name: "HkStatusRow",
|
|
25
|
+
props: {
|
|
26
|
+
/** The state axis — hosts map domain states onto tones. */
|
|
27
|
+
tone: {
|
|
28
|
+
type: String as PropType<HkStatusRowTone>,
|
|
29
|
+
default: "neutral",
|
|
30
|
+
validator: (v: unknown) =>
|
|
31
|
+
v === "neutral" || v === "info" || v === "success" || v === "warning" || v === "error",
|
|
32
|
+
},
|
|
33
|
+
/** Heading line (the member/service name). */
|
|
34
|
+
title: { type: String, required: true },
|
|
35
|
+
/** Status line under the title (state prose). */
|
|
36
|
+
status: { type: String, default: "" },
|
|
37
|
+
/** Quiet variant: dashed edge + reduced opacity for absent rows. */
|
|
38
|
+
absent: { type: Boolean, default: false },
|
|
39
|
+
},
|
|
40
|
+
setup(props, { slots }) {
|
|
41
|
+
return () => (
|
|
42
|
+
<div
|
|
43
|
+
class={{
|
|
44
|
+
"hk-status-row": true,
|
|
45
|
+
[`hk-status-row--${props.tone}`]: true,
|
|
46
|
+
"hk-status-row--absent": props.absent,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
<span class="hk-status-row__chip">{slots.icon?.()}</span>
|
|
50
|
+
<div class="hk-status-row__body">
|
|
51
|
+
<span class="hk-status-row__title">{props.title}</span>
|
|
52
|
+
{props.status ? <span class="hk-status-row__status">{props.status}</span> : null}
|
|
53
|
+
{slots.extra?.()}
|
|
54
|
+
</div>
|
|
55
|
+
{slots.action?.() ? <div class="hk-status-row__action">{slots.action?.()}</div> : null}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default HkStatusRow;
|
package/src/index.ts
CHANGED
|
@@ -615,6 +615,8 @@ export { default as HkConfirmDialog } from "./components/HkConfirmDialog";
|
|
|
615
615
|
export { default as HkCrossfade } from "./components/HkCrossfade";
|
|
616
616
|
export { default as HkTransitionContainer } from "./components/HkTransitionContainer";
|
|
617
617
|
export { default as HkWindowControls, isApplePlatform } from "./components/HkWindowControls";
|
|
618
|
+
export { default as HkStatusRow } from "./components/HkStatusRow";
|
|
619
|
+
export type { HkStatusRowTone } from "./components/HkStatusRow";
|
|
618
620
|
export type { WindowControlsVariant } from "./components/HkWindowControls";
|
|
619
621
|
export { default as HkDivider } from "./components/HkDivider";
|
|
620
622
|
export { default as HkDiffViewer } from "./components/HkDiffViewer";
|