@celestia-island/hikari 0.45.7 → 0.45.8
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
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
color: rgb(var(--color-muted));
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
/* ── Credits:
|
|
129
|
+
/* ── Credits: credits sentence + organization blurb ────────────────────── */
|
|
130
130
|
|
|
131
131
|
.s-about-modal-credits {
|
|
132
132
|
display: flex;
|
|
@@ -136,24 +136,19 @@
|
|
|
136
136
|
margin-top: calc(-1 * var(--space-4, 0.25rem));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
// Inline flow (not flex): the sentence is assembled from text runs and
|
|
140
|
+
// chips, and the runs carry their own spaces around the chips.
|
|
141
|
+
.s-about-modal-credits-line {
|
|
140
142
|
margin: 0;
|
|
141
143
|
font-size: var(--text-sm, 0.8125rem);
|
|
144
|
+
line-height: 2;
|
|
142
145
|
text-align: center;
|
|
146
|
+
text-wrap: pretty;
|
|
143
147
|
color: rgb(var(--color-text));
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
.s-about-modal-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.s-about-modal-row-link {
|
|
151
|
-
color: rgb(var(--color-primary));
|
|
152
|
-
text-decoration: none;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.s-about-modal-row-link:hover {
|
|
156
|
-
text-decoration: underline;
|
|
150
|
+
.s-about-modal-credit-name {
|
|
151
|
+
color: rgb(var(--color-text));
|
|
157
152
|
}
|
|
158
153
|
|
|
159
154
|
.s-about-modal-description {
|
|
@@ -257,8 +252,8 @@
|
|
|
257
252
|
gap: var(--space-6, 0.375rem);
|
|
258
253
|
}
|
|
259
254
|
|
|
260
|
-
// Ghost pills:
|
|
261
|
-
//
|
|
255
|
+
// Ghost pills: every link in this dialog is a tag, so they share one face —
|
|
256
|
+
// credits names, licenses, external links and the legal filings.
|
|
262
257
|
.s-about-modal-link {
|
|
263
258
|
padding: var(--space-2, 0.125rem) var(--space-10, 0.625rem);
|
|
264
259
|
border: 1px solid var(--border-subtle, rgb(100 100 100 / 12%));
|
|
@@ -273,6 +268,14 @@
|
|
|
273
268
|
background var(--duration-short, 0.15s) ease;
|
|
274
269
|
}
|
|
275
270
|
|
|
271
|
+
// Chips inside the credits sentence sit in a text run: keep them on the
|
|
272
|
+
// baseline and let the surrounding spaces do the spacing.
|
|
273
|
+
.s-about-modal-credit-link {
|
|
274
|
+
display: inline-block;
|
|
275
|
+
vertical-align: middle;
|
|
276
|
+
margin: 0 0.1em;
|
|
277
|
+
}
|
|
278
|
+
|
|
276
279
|
.s-about-modal-link:hover {
|
|
277
280
|
border-color: var(--c-primary-strong, rgb(var(--color-primary) / 40%));
|
|
278
281
|
background: var(--c-primary-faint, rgb(var(--color-primary) / 5%));
|
|
@@ -292,25 +295,20 @@
|
|
|
292
295
|
|
|
293
296
|
/* ── Legal footer: filing links + copyright ───────────────────────────── */
|
|
294
297
|
|
|
298
|
+
// Filing numbers are long, so the legal chips render a notch smaller than
|
|
299
|
+
// the license / link rows.
|
|
300
|
+
.s-about-modal-footer-link {
|
|
301
|
+
font-size: var(--text-2xs, 0.625rem);
|
|
302
|
+
}
|
|
303
|
+
|
|
295
304
|
.s-about-modal-footer-links {
|
|
296
305
|
display: flex;
|
|
297
306
|
flex-wrap: wrap;
|
|
298
307
|
justify-content: center;
|
|
299
|
-
gap: var(--space-
|
|
308
|
+
gap: var(--space-6, 0.375rem);
|
|
300
309
|
margin-top: calc(-1 * var(--space-4, 0.25rem));
|
|
301
310
|
}
|
|
302
311
|
|
|
303
|
-
.s-about-modal-footer-link {
|
|
304
|
-
font-size: var(--text-xs, 0.75rem);
|
|
305
|
-
color: rgb(var(--color-muted));
|
|
306
|
-
text-decoration: none;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.s-about-modal-footer-link:hover {
|
|
310
|
-
color: rgb(var(--color-primary));
|
|
311
|
-
text-decoration: underline;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
312
|
.s-about-modal-footer {
|
|
315
313
|
display: flex;
|
|
316
314
|
justify-content: center;
|
|
@@ -9,8 +9,9 @@ import { setLocale } from "../i18n/context";
|
|
|
9
9
|
* - the first-letter tile is the default identity and `logoSrc` swaps it
|
|
10
10
|
* for an image inside the identity frame
|
|
11
11
|
* - version and tagline share one subtitle line
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
12
|
+
* - the credits sentence assembles text runs and linked name chips, and a
|
|
13
|
+
* name without an href stays plain text
|
|
14
|
+
* - description / license / copyright render the given strings
|
|
14
15
|
* - component rows split the machine value from an optional meta pill
|
|
15
16
|
* - the backdrop factory renders inside the clipped backdrop layer
|
|
16
17
|
* - licenses and links render as chips under one centered block
|
|
@@ -96,27 +97,44 @@ describe("HkAboutModal branding", () => {
|
|
|
96
97
|
expect(query<HTMLElement>(".s-about-modal-description").textContent).toBe("Save the world.");
|
|
97
98
|
});
|
|
98
99
|
|
|
99
|
-
it("renders the
|
|
100
|
+
it("renders the credits sentence with linked name chips", async () => {
|
|
100
101
|
mountAbout({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
credits: [
|
|
103
|
+
{ text: "来自 " },
|
|
104
|
+
{ name: "Celestia Island", href: "https://github.com/celestia-island" },
|
|
105
|
+
{ text: ",由 " },
|
|
106
|
+
{ name: "伊欧", href: "https://github.com/langyo" },
|
|
107
|
+
{ text: " 倾力设计" },
|
|
108
|
+
],
|
|
105
109
|
});
|
|
106
110
|
await flushModal();
|
|
107
|
-
const line = query<HTMLElement>(".s-about-modal-
|
|
108
|
-
expect(line.textContent).toBe("
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(
|
|
111
|
+
const line = query<HTMLElement>(".s-about-modal-credits-line");
|
|
112
|
+
expect(line.textContent).toBe("来自 Celestia Island,由 伊欧 倾力设计");
|
|
113
|
+
const chips = [
|
|
114
|
+
...document.body.querySelectorAll<HTMLAnchorElement>(".s-about-modal-credit-link"),
|
|
115
|
+
];
|
|
116
|
+
expect(chips.map((chip) => chip.textContent)).toEqual(["Celestia Island", "伊欧"]);
|
|
117
|
+
// Every link in the dialog is a tag and opens in a new tab.
|
|
118
|
+
for (const chip of chips) {
|
|
119
|
+
expect(chip.classList.contains("s-about-modal-link")).toBe(true);
|
|
120
|
+
expect(chip.getAttribute("target")).toBe("_blank");
|
|
121
|
+
expect(chip.getAttribute("rel")).toContain("noopener");
|
|
122
|
+
}
|
|
123
|
+
expect(chips[0]!.getAttribute("href")).toBe("https://github.com/celestia-island");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("keeps a credit name as plain text when no href is given", async () => {
|
|
127
|
+
mountAbout({ credits: [{ text: "由 " }, { name: "伊欧" }, { text: " 主创" }] });
|
|
128
|
+
await flushModal();
|
|
129
|
+
expect(query<HTMLElement>(".s-about-modal-credits-line").textContent).toBe("由 伊欧 主创");
|
|
130
|
+
expect(query<HTMLElement>(".s-about-modal-credit-name").textContent).toBe("伊欧");
|
|
131
|
+
expect(document.body.querySelector(".s-about-modal-credit-link")).toBeNull();
|
|
113
132
|
});
|
|
114
133
|
|
|
115
|
-
it("
|
|
116
|
-
mountAbout({
|
|
134
|
+
it("skips empty credit parts", async () => {
|
|
135
|
+
mountAbout({ credits: [{ text: "" }, { name: "" }, { text: "仅此一句。" }] });
|
|
117
136
|
await flushModal();
|
|
118
|
-
expect(query<HTMLElement>(".s-about-modal-
|
|
119
|
-
expect(document.body.querySelector(".s-about-modal-row-link")).toBeNull();
|
|
137
|
+
expect(query<HTMLElement>(".s-about-modal-credits-line").textContent).toBe("仅此一句。");
|
|
120
138
|
});
|
|
121
139
|
|
|
122
140
|
it("renders software-component version rows with a meta pill and tone", async () => {
|
|
@@ -234,7 +252,7 @@ describe("HkAboutModal branding", () => {
|
|
|
234
252
|
mountAbout();
|
|
235
253
|
await flushModal();
|
|
236
254
|
expect(document.body.querySelectorAll(".s-about-modal-row").length).toBe(0);
|
|
237
|
-
expect(document.body.querySelector(".s-about-modal-
|
|
255
|
+
expect(document.body.querySelector(".s-about-modal-credits-line")).toBeNull();
|
|
238
256
|
expect(document.body.querySelector(".s-about-modal-links")).toBeNull();
|
|
239
257
|
expect(document.body.querySelector(".s-about-modal-chips")).toBeNull();
|
|
240
258
|
expect(document.body.querySelector(".s-about-modal-credits")).toBeNull();
|
|
@@ -10,6 +10,24 @@ export interface HAboutLink {
|
|
|
10
10
|
href: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* One piece of the credits line.
|
|
15
|
+
*
|
|
16
|
+
* The line is a sentence assembled from parts so a host can phrase (and
|
|
17
|
+
* order) it freely per locale: literal `text` runs sit between linked
|
|
18
|
+
* `name` chips (organization first, author second, …). The names render as
|
|
19
|
+
* the same chips as the license / link rows — every link in this dialog is
|
|
20
|
+
* a tag.
|
|
21
|
+
*/
|
|
22
|
+
export interface HAboutCredit {
|
|
23
|
+
/** Literal sentence fragment (mutually exclusive with `name`). */
|
|
24
|
+
text?: string;
|
|
25
|
+
/** Linked chip label (mutually exclusive with `text`). */
|
|
26
|
+
name?: string;
|
|
27
|
+
/** Target for a `name` chip; opens in a new tab. */
|
|
28
|
+
href?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
/**
|
|
14
32
|
* One software-component version row.
|
|
15
33
|
*
|
|
@@ -33,10 +51,11 @@ export interface HAboutComponentVersion {
|
|
|
33
51
|
*
|
|
34
52
|
* Layout (2026-09 redesign): a centered identity hero (haloed logo, name,
|
|
35
53
|
* then version + tagline on one compact line), an optional credits block
|
|
36
|
-
* (
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
54
|
+
* (a sentence built from text runs and linked name chips, plus the
|
|
55
|
+
* organization blurb), a
|
|
56
|
+
* bordered spec card holding the software-component versions, and the
|
|
57
|
+
* link rows — licenses, external links, legal filings and the credits
|
|
58
|
+
* names all render as the same ghost tag. Every one opens in a new tab. Every branding prop is optional — the modal degrades
|
|
40
59
|
* to the plain identity card when none are given.
|
|
41
60
|
*/
|
|
42
61
|
export const HkAboutModal = defineComponent({
|
|
@@ -53,14 +72,12 @@ export const HkAboutModal = defineComponent({
|
|
|
53
72
|
tagline: { type: String, default: undefined },
|
|
54
73
|
/** Optional centered small line under the credits (organization blurb). */
|
|
55
74
|
description: { type: String, default: undefined },
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/** Made-by sentence: text after the name (e.g. " 主创,来自 …"). */
|
|
63
|
-
madeBySuffix: { type: String, default: undefined },
|
|
75
|
+
/**
|
|
76
|
+
* Credits sentence, assembled from text runs and linked name chips
|
|
77
|
+
* (e.g. 来自 <Celestia Island>,由 <伊欧> 倾力设计). Names render as the
|
|
78
|
+
* same chips as the license / link rows.
|
|
79
|
+
*/
|
|
80
|
+
credits: { type: Array as PropType<HAboutCredit[]>, default: () => [] },
|
|
64
81
|
/** License chips (e.g. SySL-1.0 / BUSL-1.1), rendered centered. */
|
|
65
82
|
licenses: { type: Array as PropType<HAboutLink[]>, default: () => [] },
|
|
66
83
|
/** Software-component version rows (WebUI / engines), label + value. */
|
|
@@ -88,25 +105,38 @@ export const HkAboutModal = defineComponent({
|
|
|
88
105
|
setup(props, { emit }) {
|
|
89
106
|
const { t } = useI18n();
|
|
90
107
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
// One chip renderer for every link in the dialog: credits names,
|
|
109
|
+
// licenses, external links and the legal filings all render as tags.
|
|
110
|
+
const renderChip = (label: string, href: string, extraClass: string) => (
|
|
111
|
+
<a
|
|
112
|
+
key={`${extraClass}:${href}`}
|
|
113
|
+
class={`s-about-modal-link ${extraClass}`}
|
|
114
|
+
href={href}
|
|
115
|
+
target="_blank"
|
|
116
|
+
rel="noopener noreferrer"
|
|
117
|
+
>
|
|
118
|
+
{label}
|
|
119
|
+
</a>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const renderCredits = () => {
|
|
123
|
+
const parts = props.credits.filter((part) => part.text || part.name);
|
|
124
|
+
if (parts.length === 0) return null;
|
|
105
125
|
return (
|
|
106
|
-
<p class="s-about-modal-
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
<p class="s-about-modal-credits-line">
|
|
127
|
+
{parts.map((part, index) =>
|
|
128
|
+
part.name ? (
|
|
129
|
+
part.href ? (
|
|
130
|
+
renderChip(part.name, part.href, "s-about-modal-credit-link")
|
|
131
|
+
) : (
|
|
132
|
+
<span key={`name:${index}`} class="s-about-modal-credit-name">
|
|
133
|
+
{part.name}
|
|
134
|
+
</span>
|
|
135
|
+
)
|
|
136
|
+
) : (
|
|
137
|
+
<span key={`text:${index}`}>{part.text}</span>
|
|
138
|
+
),
|
|
139
|
+
)}
|
|
110
140
|
</p>
|
|
111
141
|
);
|
|
112
142
|
};
|
|
@@ -116,17 +146,7 @@ export const HkAboutModal = defineComponent({
|
|
|
116
146
|
return (
|
|
117
147
|
<div class="s-about-modal-links" data-slot={slot}>
|
|
118
148
|
<div class="s-about-modal-links-list">
|
|
119
|
-
{items.map((item) => (
|
|
120
|
-
<a
|
|
121
|
-
key={item.href}
|
|
122
|
-
href={item.href}
|
|
123
|
-
target="_blank"
|
|
124
|
-
rel="noopener noreferrer"
|
|
125
|
-
class="s-about-modal-link"
|
|
126
|
-
>
|
|
127
|
-
{item.label}
|
|
128
|
-
</a>
|
|
129
|
-
))}
|
|
149
|
+
{items.map((item) => renderChip(item.label, item.href, ""))}
|
|
130
150
|
</div>
|
|
131
151
|
</div>
|
|
132
152
|
);
|
|
@@ -177,9 +197,9 @@ export const HkAboutModal = defineComponent({
|
|
|
177
197
|
</div>
|
|
178
198
|
</header>
|
|
179
199
|
|
|
180
|
-
{(props.
|
|
200
|
+
{(props.credits.length > 0 || props.description) && (
|
|
181
201
|
<div class="s-about-modal-credits">
|
|
182
|
-
{
|
|
202
|
+
{renderCredits()}
|
|
183
203
|
{props.description && (
|
|
184
204
|
<p class="s-about-modal-description">{props.description}</p>
|
|
185
205
|
)}
|
|
@@ -216,17 +236,9 @@ export const HkAboutModal = defineComponent({
|
|
|
216
236
|
|
|
217
237
|
{props.footerLinks.length > 0 && (
|
|
218
238
|
<div class="s-about-modal-footer-links">
|
|
219
|
-
{props.footerLinks.map((link) =>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
href={link.href}
|
|
223
|
-
target="_blank"
|
|
224
|
-
rel="noopener noreferrer"
|
|
225
|
-
class="s-about-modal-footer-link"
|
|
226
|
-
>
|
|
227
|
-
{link.label}
|
|
228
|
-
</a>
|
|
229
|
-
))}
|
|
239
|
+
{props.footerLinks.map((link) =>
|
|
240
|
+
renderChip(link.label, link.href, "s-about-modal-footer-link"),
|
|
241
|
+
)}
|
|
230
242
|
</div>
|
|
231
243
|
)}
|
|
232
244
|
|
package/src/index.ts
CHANGED
|
@@ -356,6 +356,7 @@ export { HkProtocolModal as HProtocolModal } from "./components/HkProtocolModal"
|
|
|
356
356
|
export {
|
|
357
357
|
HkAboutModal as HAboutModal,
|
|
358
358
|
type HAboutComponentVersion,
|
|
359
|
+
type HAboutCredit,
|
|
359
360
|
type HAboutLink,
|
|
360
361
|
} from "./components/HkAboutModal";
|
|
361
362
|
export { HkLogWindow as HLogWindow, type HLogTab } from "./components/HkLogWindow";
|