@celestia-island/hikari 0.43.16 → 0.43.18
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 +3 -17
- package/src/components/HkAboutModal.scss +54 -0
- package/src/components/HkAboutModal.test.tsx +181 -0
- package/src/components/HkAboutModal.tsx +127 -51
- package/src/i18n/locales/ar/platform.json +2 -0
- package/src/i18n/locales/de/platform.json +2 -0
- package/src/i18n/locales/en/platform.json +2 -0
- package/src/i18n/locales/es/platform.json +2 -0
- package/src/i18n/locales/fr/platform.json +2 -0
- package/src/i18n/locales/ja/platform.json +2 -0
- package/src/i18n/locales/ko/platform.json +2 -0
- package/src/i18n/locales/pt/platform.json +2 -0
- package/src/i18n/locales/ru/platform.json +2 -0
- package/src/i18n/locales/zh-Hans/platform.json +2 -0
- package/src/i18n/locales/zh-Hant/platform.json +2 -0
- package/src/styles/admin-tokens.scss +0 -1
- package/src/styles/tokenInventory.test.ts +37 -72
- package/src/styles/vendoredSync.test.ts +5 -28
- package/src/styles/theme/_glass.scss +0 -34
- package/src/styles/theme/_layout.scss +0 -124
- package/src/styles/theme/_scrollbar.scss +0 -74
- package/src/styles/theme/_tokens.scss +0 -70
- package/src/styles/theme/base.scss +0 -632
- package/src/styles/theme/foundation.scss +0 -220
- package/src/styles/theme/mixins.scss +0 -794
- package/src/styles/theme/themes.scss +0 -26
- package/src/styles/theme/variables.scss +0 -126
package/package.json
CHANGED
|
@@ -103,20 +103,6 @@ registerCssAnimation("s-voice-wave-bounce", { infinite: true });
|
|
|
103
103
|
// components/HkPlaceholderMarquee.scss — overflowing placeholder sweep
|
|
104
104
|
// (loops forever; pure CSS, loop geometry on inline custom properties).
|
|
105
105
|
registerCssAnimation("hk-placeholder-marquee-scroll", { infinite: true });
|
|
106
|
-
// styles/theme/mixins.scss
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// its keyframes — the parity test caught the gap 6 days later.
|
|
110
|
-
registerCssAnimation("pulse", { infinite: true });
|
|
111
|
-
registerCssAnimation("ambient-move", { infinite: true });
|
|
112
|
-
registerCssAnimation("pulse-glow-animation", { infinite: true });
|
|
113
|
-
registerCssAnimation("expand-horizontal");
|
|
114
|
-
registerCssAnimation("collapse-vertical");
|
|
115
|
-
registerCssAnimation("slide-in-left");
|
|
116
|
-
registerCssAnimation("slide-in-right");
|
|
117
|
-
registerCssAnimation("slide-in-top");
|
|
118
|
-
registerCssAnimation("slide-in-bottom");
|
|
119
|
-
registerCssAnimation("fade-in-scale");
|
|
120
|
-
registerCssAnimation("fade-out-scale");
|
|
121
|
-
registerCssAnimation("blur-in");
|
|
122
|
-
registerCssAnimation("bounce-in");
|
|
106
|
+
// The styles/theme/mixins.scss ambient/glow/entrance keyframes were
|
|
107
|
+
// retired with the legacy Gen-1 sheet vendoring (hikari #476) — their
|
|
108
|
+
// registrar entries went with them.
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
@use "./hikari-vars" as vars;
|
|
3
3
|
|
|
4
4
|
.s-about-modal {
|
|
5
|
+
position: relative;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Clipped, pointer-inert decorative layer (canvas or any subtree handed in
|
|
9
|
+
// through the `backdrop` prop). Sits under `.s-about-modal-body`; sizing,
|
|
10
|
+
// background and animation belong to the consumer's subtree.
|
|
11
|
+
.s-about-modal-backdrop {
|
|
12
|
+
position: absolute;
|
|
13
|
+
inset: 0;
|
|
14
|
+
z-index: 0;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
border-radius: var(--radius-lg, 12px);
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
|
|
19
|
+
> * {
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.s-about-modal-body {
|
|
26
|
+
position: relative;
|
|
27
|
+
z-index: 1;
|
|
5
28
|
display: flex;
|
|
6
29
|
flex-direction: column;
|
|
7
30
|
gap: var(--space-16, 1rem);
|
|
@@ -13,6 +36,15 @@
|
|
|
13
36
|
gap: var(--space-12, 0.75rem);
|
|
14
37
|
}
|
|
15
38
|
|
|
39
|
+
.s-about-modal-logo-img {
|
|
40
|
+
width: 3rem;
|
|
41
|
+
height: 3rem;
|
|
42
|
+
flex-shrink: 0;
|
|
43
|
+
border-radius: var(--radius-lg, 12px);
|
|
44
|
+
object-fit: contain;
|
|
45
|
+
user-select: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
16
48
|
.s-about-modal-logo {
|
|
17
49
|
display: flex;
|
|
18
50
|
align-items: center;
|
|
@@ -40,6 +72,19 @@
|
|
|
40
72
|
color: rgb(var(--color-muted));
|
|
41
73
|
}
|
|
42
74
|
|
|
75
|
+
.s-about-modal-tagline {
|
|
76
|
+
margin: var(--space-2, 0.125rem) 0 0;
|
|
77
|
+
font-size: var(--text-xs, 0.75rem);
|
|
78
|
+
color: rgb(var(--color-muted));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.s-about-modal-description {
|
|
82
|
+
margin: 0;
|
|
83
|
+
font-size: var(--text-sm, 0.8125rem);
|
|
84
|
+
line-height: 1.55;
|
|
85
|
+
color: rgb(var(--color-muted));
|
|
86
|
+
}
|
|
87
|
+
|
|
43
88
|
.s-about-modal-rows {
|
|
44
89
|
display: flex;
|
|
45
90
|
flex-direction: column;
|
|
@@ -64,6 +109,15 @@
|
|
|
64
109
|
font-family: var(--font-mono, vars.$hikari-font-family-mono);
|
|
65
110
|
}
|
|
66
111
|
|
|
112
|
+
.s-about-modal-row-link {
|
|
113
|
+
color: rgb(var(--color-primary));
|
|
114
|
+
text-decoration: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.s-about-modal-row-link:hover {
|
|
118
|
+
text-decoration: underline;
|
|
119
|
+
}
|
|
120
|
+
|
|
67
121
|
.s-about-modal-links {
|
|
68
122
|
display: flex;
|
|
69
123
|
flex-direction: column;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createApp, defineComponent, h, nextTick, ref } from "vue";
|
|
3
|
+
|
|
4
|
+
import { HkAboutModal } from "./HkAboutModal";
|
|
5
|
+
import { setLocale } from "../i18n/context";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* HkAboutModal contract tests for the branding wave:
|
|
9
|
+
* - the first-letter tile is the default identity and `logoSrc` swaps it
|
|
10
|
+
* for an image
|
|
11
|
+
* - tagline / description / author / license / copyright render the given
|
|
12
|
+
* strings, authorHref turns the author value into an external link
|
|
13
|
+
* - the backdrop factory renders inside the clipped backdrop layer
|
|
14
|
+
* - links render as external chips
|
|
15
|
+
* - author / license labels resolve through the i18n bundles
|
|
16
|
+
*
|
|
17
|
+
* (Repo test convention: raw createApp + document queries, no
|
|
18
|
+
* @vue/test-utils dependency.)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const mounts: Array<{ app: ReturnType<typeof createApp>; container: HTMLElement }> = [];
|
|
22
|
+
|
|
23
|
+
interface AboutHarness {
|
|
24
|
+
open: ReturnType<typeof ref<boolean>>;
|
|
25
|
+
unmount: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function mountAbout(props: Record<string, unknown> = {}): AboutHarness {
|
|
29
|
+
const container = document.createElement("div");
|
|
30
|
+
document.body.appendChild(container);
|
|
31
|
+
|
|
32
|
+
const open = ref(true);
|
|
33
|
+
const Wrapper = defineComponent({
|
|
34
|
+
setup() {
|
|
35
|
+
return () =>
|
|
36
|
+
h(HkAboutModal, {
|
|
37
|
+
appName: "Test App",
|
|
38
|
+
version: "1.2.3",
|
|
39
|
+
...props,
|
|
40
|
+
modelValue: open.value,
|
|
41
|
+
"onUpdate:modelValue": (v: boolean) => {
|
|
42
|
+
open.value = v;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const app = createApp(Wrapper);
|
|
48
|
+
app.mount(container);
|
|
49
|
+
mounts.push({ app, container });
|
|
50
|
+
return { open, unmount: () => app.unmount() };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function query<T extends Element>(selector: string): T {
|
|
54
|
+
const el = document.body.querySelector<T>(selector);
|
|
55
|
+
expect(el, `${selector} renders`).toBeTruthy();
|
|
56
|
+
return el!;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function flushModal() {
|
|
60
|
+
await nextTick();
|
|
61
|
+
await nextTick();
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
for (const { app, container } of mounts.splice(0)) {
|
|
67
|
+
app.unmount();
|
|
68
|
+
container.remove();
|
|
69
|
+
}
|
|
70
|
+
setLocale("en");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("HkAboutModal branding", () => {
|
|
74
|
+
it("renders the first-letter tile when no logoSrc is given", async () => {
|
|
75
|
+
mountAbout();
|
|
76
|
+
await flushModal();
|
|
77
|
+
const tile = query<HTMLElement>(".s-about-modal-logo");
|
|
78
|
+
expect(tile.textContent?.trim()).toBe("T");
|
|
79
|
+
expect(document.body.querySelector(".s-about-modal-logo-img")).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("renders the logo image when logoSrc is given", async () => {
|
|
83
|
+
mountAbout({ logoSrc: "data:image/webp;base64,AAA" });
|
|
84
|
+
await flushModal();
|
|
85
|
+
const img = query<HTMLImageElement>("img.s-about-modal-logo-img");
|
|
86
|
+
expect(img.getAttribute("src")).toBe("data:image/webp;base64,AAA");
|
|
87
|
+
expect(document.body.querySelector(".s-about-modal-logo")).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("renders tagline and description strings", async () => {
|
|
91
|
+
mountAbout({ tagline: "Chat with your agents", description: "Save the world." });
|
|
92
|
+
await flushModal();
|
|
93
|
+
expect(query<HTMLElement>(".s-about-modal-tagline").textContent).toBe("Chat with your agents");
|
|
94
|
+
expect(query<HTMLElement>(".s-about-modal-description").textContent).toBe("Save the world.");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("renders author and license rows with an linked author", async () => {
|
|
98
|
+
mountAbout({
|
|
99
|
+
author: "Celestia Island",
|
|
100
|
+
authorHref: "https://github.com/celestia-island",
|
|
101
|
+
license: "BUSL-1.1",
|
|
102
|
+
});
|
|
103
|
+
await flushModal();
|
|
104
|
+
const link = query<HTMLAnchorElement>(".s-about-modal-row-link");
|
|
105
|
+
expect(link.textContent).toBe("Celestia Island");
|
|
106
|
+
expect(link.getAttribute("href")).toBe("https://github.com/celestia-island");
|
|
107
|
+
expect(link.getAttribute("target")).toBe("_blank");
|
|
108
|
+
const rows = [...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row")];
|
|
109
|
+
const licenseRow = rows.find((row) => row.textContent?.includes("BUSL-1.1"));
|
|
110
|
+
expect(licenseRow, "license row renders").toBeTruthy();
|
|
111
|
+
expect(licenseRow!.textContent).toContain("License");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("renders the author row as plain text without authorHref", async () => {
|
|
115
|
+
mountAbout({ author: "Celestia Island" });
|
|
116
|
+
await flushModal();
|
|
117
|
+
expect(query<HTMLElement>(".s-about-modal-row").textContent).toContain("Celestia Island");
|
|
118
|
+
expect(document.body.querySelector(".s-about-modal-row-link")).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("renders the backdrop factory inside the clipped layer", async () => {
|
|
122
|
+
mountAbout({ backdrop: () => h("div", { class: "test-backdrop-marker" }) });
|
|
123
|
+
await flushModal();
|
|
124
|
+
const layer = query<HTMLElement>(".s-about-modal-backdrop");
|
|
125
|
+
expect(layer.getAttribute("aria-hidden")).toBe("true");
|
|
126
|
+
expect(layer.querySelector(".test-backdrop-marker")).toBeTruthy();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("renders links as external chips", async () => {
|
|
130
|
+
mountAbout({
|
|
131
|
+
links: [
|
|
132
|
+
{ label: "celestia.world", href: "https://celestia.world" },
|
|
133
|
+
{ label: "celestia.ac.cn", href: "https://celestia.ac.cn" },
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
await flushModal();
|
|
137
|
+
const chips = [
|
|
138
|
+
...document.body.querySelectorAll<HTMLAnchorElement>(".s-about-modal-link"),
|
|
139
|
+
];
|
|
140
|
+
expect(chips.map((chip) => chip.textContent)).toEqual(["celestia.world", "celestia.ac.cn"]);
|
|
141
|
+
for (const chip of chips) {
|
|
142
|
+
expect(chip.getAttribute("target")).toBe("_blank");
|
|
143
|
+
expect(chip.getAttribute("rel")).toContain("noopener");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("honors the copyright override and defaults to the app name", async () => {
|
|
148
|
+
const year = String(new Date().getFullYear());
|
|
149
|
+
const harness = mountAbout({ copyright: "Celestia Island" });
|
|
150
|
+
await flushModal();
|
|
151
|
+
let badge = query<HTMLElement>(".s-about-modal-footer");
|
|
152
|
+
expect(badge.textContent).toContain(`© ${year} Celestia Island`);
|
|
153
|
+
harness.unmount();
|
|
154
|
+
await flushModal();
|
|
155
|
+
|
|
156
|
+
mountAbout();
|
|
157
|
+
await flushModal();
|
|
158
|
+
badge = query<HTMLElement>(".s-about-modal-footer");
|
|
159
|
+
expect(badge.textContent).toContain(`© ${year} Test App`);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("localizes the author and license labels", async () => {
|
|
163
|
+
mountAbout({ author: "Celestia Island", license: "BUSL-1.1" });
|
|
164
|
+
await flushModal();
|
|
165
|
+
const rows = () => [
|
|
166
|
+
...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row-label"),
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
setLocale("zh-Hans");
|
|
170
|
+
await flushModal();
|
|
171
|
+
let text = rows().map((el) => el.textContent);
|
|
172
|
+
expect(text.some((t) => t === "作者")).toBe(true);
|
|
173
|
+
expect(text.some((t) => t === "许可证")).toBe(true);
|
|
174
|
+
|
|
175
|
+
setLocale("en");
|
|
176
|
+
await flushModal();
|
|
177
|
+
text = rows().map((el) => el.textContent);
|
|
178
|
+
expect(text.some((t) => t === "Author")).toBe(true);
|
|
179
|
+
expect(text.some((t) => t === "License")).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -14,9 +14,13 @@ export interface HAboutLink {
|
|
|
14
14
|
* HkAboutModal — version / about dialog.
|
|
15
15
|
* (Upstreamed from shittim-chest's plana-legacy layer.)
|
|
16
16
|
*
|
|
17
|
-
* Shows the app identity and version metadata plus optional
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Shows the app identity and version metadata plus optional branding: a
|
|
18
|
+
* logo image, tagline, organization blurb, author / license rows, a
|
|
19
|
+
* decorative backdrop layer (canvas or anything else, rendered behind the
|
|
20
|
+
* content and pointer-inert) and external links. Version/build hashes
|
|
21
|
+
* render as short hashes when longer than 12 chars (full value in `title`
|
|
22
|
+
* tooltip). Every branding prop is optional — the modal degrades to the
|
|
23
|
+
* plain identity + version card when none are given.
|
|
20
24
|
*/
|
|
21
25
|
export const HkAboutModal = defineComponent({
|
|
22
26
|
name: "HkAboutModal",
|
|
@@ -32,8 +36,28 @@ export const HkAboutModal = defineComponent({
|
|
|
32
36
|
engineVersion: { type: String, default: undefined },
|
|
33
37
|
/** Optional engine build hash / commit. */
|
|
34
38
|
engineBuildHash: { type: String, default: undefined },
|
|
39
|
+
/** Optional logo image URL — replaces the first-letter tile. */
|
|
40
|
+
logoSrc: { type: String, default: undefined },
|
|
41
|
+
/** Optional one-liner under the version (e.g. the app tagline). */
|
|
42
|
+
tagline: { type: String, default: undefined },
|
|
43
|
+
/** Optional paragraph below the header (vision / organization blurb). */
|
|
44
|
+
description: { type: String, default: undefined },
|
|
45
|
+
/** Optional author / organization row. */
|
|
46
|
+
author: { type: String, default: undefined },
|
|
47
|
+
/** Optional link applied to the author value. */
|
|
48
|
+
authorHref: { type: String, default: undefined },
|
|
49
|
+
/** Optional license identifier row (e.g. "BUSL-1.1"). */
|
|
50
|
+
license: { type: String, default: undefined },
|
|
51
|
+
/** Optional copyright holder in the footer (defaults to the app name). */
|
|
52
|
+
copyright: { type: String, default: undefined },
|
|
35
53
|
/** Optional external links (e.g. GitHub, docs). */
|
|
36
54
|
links: { type: Array as PropType<HAboutLink[]>, default: () => [] },
|
|
55
|
+
/**
|
|
56
|
+
* Optional decorative backdrop factory, rendered behind the content
|
|
57
|
+
* inside a clipped, pointer-inert layer. The modal owns only the layer —
|
|
58
|
+
* sizing, theming and the render loop belong to the returned subtree.
|
|
59
|
+
*/
|
|
60
|
+
backdrop: { type: Function as PropType<() => unknown>, default: undefined },
|
|
37
61
|
title: { type: String, default: undefined },
|
|
38
62
|
},
|
|
39
63
|
emits: {
|
|
@@ -46,6 +70,20 @@ export const HkAboutModal = defineComponent({
|
|
|
46
70
|
return hash.length > 12 ? `${hash.slice(0, 12)}…` : hash;
|
|
47
71
|
}
|
|
48
72
|
|
|
73
|
+
const renderAuthorValue = () => {
|
|
74
|
+
if (!props.authorHref) return props.author;
|
|
75
|
+
return (
|
|
76
|
+
<a
|
|
77
|
+
class="s-about-modal-row-link"
|
|
78
|
+
href={props.authorHref}
|
|
79
|
+
target="_blank"
|
|
80
|
+
rel="noopener noreferrer"
|
|
81
|
+
>
|
|
82
|
+
{props.author}
|
|
83
|
+
</a>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
49
87
|
return () => (
|
|
50
88
|
<HModal
|
|
51
89
|
modelValue={props.modelValue}
|
|
@@ -54,59 +92,97 @@ export const HkAboutModal = defineComponent({
|
|
|
54
92
|
width="30rem"
|
|
55
93
|
>
|
|
56
94
|
<div class="s-about-modal">
|
|
57
|
-
|
|
58
|
-
<div class="s-about-modal-
|
|
59
|
-
|
|
60
|
-
<h2 class="s-about-modal-name">{props.appName}</h2>
|
|
61
|
-
<p class="s-about-modal-version">
|
|
62
|
-
{t("hikari::about.version", "Version")} {props.version}
|
|
63
|
-
</p>
|
|
95
|
+
{props.backdrop && (
|
|
96
|
+
<div class="s-about-modal-backdrop" aria-hidden="true">
|
|
97
|
+
{props.backdrop()}
|
|
64
98
|
</div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
<span class="s-about-modal-row-value">{props.engineVersion}</span>
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
{props.engineBuildHash && (
|
|
83
|
-
<div class="s-about-modal-row">
|
|
84
|
-
<span class="s-about-modal-row-label">{t("hikari::about.engineBuildHash", "Engine build")}</span>
|
|
85
|
-
<span class="s-about-modal-row-value" title={props.engineBuildHash}>
|
|
86
|
-
{shortHash(props.engineBuildHash)}
|
|
87
|
-
</span>
|
|
99
|
+
)}
|
|
100
|
+
<div class="s-about-modal-body">
|
|
101
|
+
<header class="s-about-modal-header">
|
|
102
|
+
{props.logoSrc ? (
|
|
103
|
+
<img class="s-about-modal-logo-img" src={props.logoSrc} alt="" draggable={false} />
|
|
104
|
+
) : (
|
|
105
|
+
<div class="s-about-modal-logo">{props.appName.slice(0, 1).toUpperCase()}</div>
|
|
106
|
+
)}
|
|
107
|
+
<div>
|
|
108
|
+
<h2 class="s-about-modal-name">{props.appName}</h2>
|
|
109
|
+
<p class="s-about-modal-version">
|
|
110
|
+
{t("hikari::about.version", "Version")} {props.version}
|
|
111
|
+
</p>
|
|
112
|
+
{props.tagline && <p class="s-about-modal-tagline">{props.tagline}</p>}
|
|
88
113
|
</div>
|
|
89
|
-
|
|
90
|
-
</div>
|
|
114
|
+
</header>
|
|
91
115
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
{props.description && <p class="s-about-modal-description">{props.description}</p>}
|
|
117
|
+
|
|
118
|
+
<div class="s-about-modal-rows">
|
|
119
|
+
{props.author && (
|
|
120
|
+
<div class="s-about-modal-row">
|
|
121
|
+
<span class="s-about-modal-row-label">{t("hikari::about.author", "Author")}</span>
|
|
122
|
+
<span class="s-about-modal-row-value">{renderAuthorValue()}</span>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
{props.license && (
|
|
126
|
+
<div class="s-about-modal-row">
|
|
127
|
+
<span class="s-about-modal-row-label">
|
|
128
|
+
{t("hikari::about.license", "License")}
|
|
129
|
+
</span>
|
|
130
|
+
<span class="s-about-modal-row-value">{props.license}</span>
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
{props.buildHash && (
|
|
134
|
+
<div class="s-about-modal-row">
|
|
135
|
+
<span class="s-about-modal-row-label">{t("hikari::about.buildHash", "Build")}</span>
|
|
136
|
+
<span class="s-about-modal-row-value" title={props.buildHash}>
|
|
137
|
+
{shortHash(props.buildHash)}
|
|
138
|
+
</span>
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
{props.engineVersion && (
|
|
142
|
+
<div class="s-about-modal-row">
|
|
143
|
+
<span class="s-about-modal-row-label">
|
|
144
|
+
{t("hikari::about.engineVersion", "Engine version")}
|
|
145
|
+
</span>
|
|
146
|
+
<span class="s-about-modal-row-value">{props.engineVersion}</span>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
{props.engineBuildHash && (
|
|
150
|
+
<div class="s-about-modal-row">
|
|
151
|
+
<span class="s-about-modal-row-label">
|
|
152
|
+
{t("hikari::about.engineBuildHash", "Engine build")}
|
|
153
|
+
</span>
|
|
154
|
+
<span class="s-about-modal-row-value" title={props.engineBuildHash}>
|
|
155
|
+
{shortHash(props.engineBuildHash)}
|
|
156
|
+
</span>
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
102
159
|
</div>
|
|
103
|
-
)}
|
|
104
160
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
{props.links.length > 0 && (
|
|
162
|
+
<div class="s-about-modal-links">
|
|
163
|
+
<span class="s-about-modal-row-label">{t("hikari::about.links", "Links")}</span>
|
|
164
|
+
<div class="s-about-modal-links-list">
|
|
165
|
+
{props.links.map((link) => (
|
|
166
|
+
<a
|
|
167
|
+
key={link.href}
|
|
168
|
+
href={link.href}
|
|
169
|
+
target="_blank"
|
|
170
|
+
rel="noopener noreferrer"
|
|
171
|
+
class="s-about-modal-link"
|
|
172
|
+
>
|
|
173
|
+
{link.label}
|
|
174
|
+
</a>
|
|
175
|
+
))}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
|
|
180
|
+
<footer class="s-about-modal-footer">
|
|
181
|
+
<HBadge variant="muted">
|
|
182
|
+
© {new Date().getFullYear()} {props.copyright ?? props.appName}
|
|
183
|
+
</HBadge>
|
|
184
|
+
</footer>
|
|
185
|
+
</div>
|
|
110
186
|
</div>
|
|
111
187
|
</HModal>
|
|
112
188
|
);
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "إصدار المحرك",
|
|
19
19
|
"hikari::about.engineBuildHash": "بناء المحرك",
|
|
20
20
|
"hikari::about.links": "روابط",
|
|
21
|
+
"hikari::about.author": "المؤلف",
|
|
22
|
+
"hikari::about.license": "الترخيص",
|
|
21
23
|
"hikari::theme.mode": "وضع السمة",
|
|
22
24
|
"hikari::theme.modeLight": "فاتح",
|
|
23
25
|
"hikari::theme.modeDark": "داكن",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Engine-Version",
|
|
19
19
|
"hikari::about.engineBuildHash": "Engine-Build",
|
|
20
20
|
"hikari::about.links": "Links",
|
|
21
|
+
"hikari::about.author": "Autor",
|
|
22
|
+
"hikari::about.license": "Lizenz",
|
|
21
23
|
"hikari::theme.mode": "Design-Modus",
|
|
22
24
|
"hikari::theme.modeLight": "Hell",
|
|
23
25
|
"hikari::theme.modeDark": "Dunkel",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Engine version",
|
|
19
19
|
"hikari::about.engineBuildHash": "Engine build",
|
|
20
20
|
"hikari::about.links": "Links",
|
|
21
|
+
"hikari::about.author": "Author",
|
|
22
|
+
"hikari::about.license": "License",
|
|
21
23
|
"hikari::theme.mode": "Theme mode",
|
|
22
24
|
"hikari::theme.modeLight": "Light",
|
|
23
25
|
"hikari::theme.modeDark": "Dark",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Versión del motor",
|
|
19
19
|
"hikari::about.engineBuildHash": "Compilación del motor",
|
|
20
20
|
"hikari::about.links": "Enlaces",
|
|
21
|
+
"hikari::about.author": "Autor",
|
|
22
|
+
"hikari::about.license": "Licencia",
|
|
21
23
|
"hikari::theme.mode": "Modo de tema",
|
|
22
24
|
"hikari::theme.modeLight": "Claro",
|
|
23
25
|
"hikari::theme.modeDark": "Oscuro",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Version du moteur",
|
|
19
19
|
"hikari::about.engineBuildHash": "Build du moteur",
|
|
20
20
|
"hikari::about.links": "Liens",
|
|
21
|
+
"hikari::about.author": "Auteur",
|
|
22
|
+
"hikari::about.license": "Licence",
|
|
21
23
|
"hikari::theme.mode": "Mode du thème",
|
|
22
24
|
"hikari::theme.modeLight": "Clair",
|
|
23
25
|
"hikari::theme.modeDark": "Sombre",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "エンジンバージョン",
|
|
19
19
|
"hikari::about.engineBuildHash": "エンジンビルド",
|
|
20
20
|
"hikari::about.links": "リンク",
|
|
21
|
+
"hikari::about.author": "作者",
|
|
22
|
+
"hikari::about.license": "ライセンス",
|
|
21
23
|
"hikari::theme.mode": "テーマモード",
|
|
22
24
|
"hikari::theme.modeLight": "ライト",
|
|
23
25
|
"hikari::theme.modeDark": "ダーク",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "엔진 버전",
|
|
19
19
|
"hikari::about.engineBuildHash": "엔진 빌드",
|
|
20
20
|
"hikari::about.links": "링크",
|
|
21
|
+
"hikari::about.author": "제작자",
|
|
22
|
+
"hikari::about.license": "라이선스",
|
|
21
23
|
"hikari::theme.mode": "테마 모드",
|
|
22
24
|
"hikari::theme.modeLight": "라이트",
|
|
23
25
|
"hikari::theme.modeDark": "다크",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Versão do motor",
|
|
19
19
|
"hikari::about.engineBuildHash": "Build do motor",
|
|
20
20
|
"hikari::about.links": "Links",
|
|
21
|
+
"hikari::about.author": "Autor",
|
|
22
|
+
"hikari::about.license": "Licença",
|
|
21
23
|
"hikari::theme.mode": "Modo do tema",
|
|
22
24
|
"hikari::theme.modeLight": "Claro",
|
|
23
25
|
"hikari::theme.modeDark": "Escuro",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "Версия движка",
|
|
19
19
|
"hikari::about.engineBuildHash": "Сборка движка",
|
|
20
20
|
"hikari::about.links": "Ссылки",
|
|
21
|
+
"hikari::about.author": "Автор",
|
|
22
|
+
"hikari::about.license": "Лицензия",
|
|
21
23
|
"hikari::theme.mode": "Режим темы",
|
|
22
24
|
"hikari::theme.modeLight": "Светлая",
|
|
23
25
|
"hikari::theme.modeDark": "Тёмная",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "引擎版本",
|
|
19
19
|
"hikari::about.engineBuildHash": "引擎构建",
|
|
20
20
|
"hikari::about.links": "链接",
|
|
21
|
+
"hikari::about.author": "作者",
|
|
22
|
+
"hikari::about.license": "许可证",
|
|
21
23
|
"hikari::theme.mode": "主题模式",
|
|
22
24
|
"hikari::theme.modeLight": "浅色",
|
|
23
25
|
"hikari::theme.modeDark": "深色",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"hikari::about.engineVersion": "引擎版本",
|
|
19
19
|
"hikari::about.engineBuildHash": "引擎建置",
|
|
20
20
|
"hikari::about.links": "連結",
|
|
21
|
+
"hikari::about.author": "作者",
|
|
22
|
+
"hikari::about.license": "授權條款",
|
|
21
23
|
"hikari::theme.mode": "主題模式",
|
|
22
24
|
"hikari::theme.modeLight": "淺色",
|
|
23
25
|
"hikari::theme.modeDark": "深色",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
--c-primary-light: rgb(var(--color-primary, 255 107 157) / 15%);
|
|
20
20
|
--c-primary-subtle: rgb(var(--color-primary, 255 107 157) / 8%);
|
|
21
21
|
--c-primary-overlay: rgb(var(--color-primary, 255 107 157) / 15%);
|
|
22
|
-
--border-faint: rgb(var(--color-border, 100 100 100) / 10%);
|
|
23
22
|
|
|
24
23
|
/* ── Hikari color bridge ─ */
|
|
25
24
|
--hi-color-primary: rgb(var(--color-primary, 255 107 157));
|