@celestia-island/hikari 0.45.4 → 0.45.5

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.45.4",
3
+ "version": "0.45.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -32,8 +32,16 @@
32
32
 
33
33
  .s-about-modal-header {
34
34
  display: flex;
35
+ flex-direction: column;
36
+ align-items: center;
37
+ gap: var(--space-8, 0.5rem);
38
+ text-align: center;
39
+ }
40
+
41
+ .s-about-modal-header > div {
42
+ display: flex;
43
+ flex-direction: column;
35
44
  align-items: center;
36
- gap: var(--space-12, 0.75rem);
37
45
  }
38
46
 
39
47
  .s-about-modal-logo-img {
@@ -80,6 +88,13 @@
80
88
  color: rgb(var(--color-text));
81
89
  }
82
90
 
91
+ .s-about-modal-made-by {
92
+ margin: 0;
93
+ font-size: var(--text-sm, 0.8125rem);
94
+ text-align: center;
95
+ color: rgb(var(--color-text));
96
+ }
97
+
83
98
  .s-about-modal-tagline {
84
99
  margin: var(--space-2, 0.125rem) 0 0;
85
100
  font-size: var(--text-xs, 0.75rem);
@@ -94,40 +94,62 @@ describe("HkAboutModal branding", () => {
94
94
  expect(query<HTMLElement>(".s-about-modal-description").textContent).toBe("Save the world.");
95
95
  });
96
96
 
97
- it("renders the made-by row with a linked origin", async () => {
97
+ it("renders the made-by sentence with a linked author name", async () => {
98
98
  mountAbout({
99
- author: "由 伊欧 主创",
100
- origin: "来自 Celestia Island",
101
- originHref: "https://github.com/celestia-island",
102
- license: "BUSL-1.1",
99
+ madeByPrefix: "由",
100
+ madeByName: "伊欧",
101
+ madeByNameHref: "https://github.com/langyo",
102
+ madeBySuffix: " 主创,来自 Celestia Island",
103
103
  });
104
104
  await flushModal();
105
- const row = query<HTMLElement>(".s-about-modal-row");
106
- expect(row.querySelector(".s-about-modal-row-label")?.textContent).toBe("由 伊欧 主创");
105
+ const line = query<HTMLElement>(".s-about-modal-made-by");
106
+ expect(line.textContent).toBe("由伊欧 主创,来自 Celestia Island");
107
107
  const link = query<HTMLAnchorElement>(".s-about-modal-row-link");
108
- expect(link.textContent).toBe("来自 Celestia Island");
109
- expect(link.getAttribute("href")).toBe("https://github.com/celestia-island");
108
+ expect(link.textContent).toBe("伊欧");
109
+ expect(link.getAttribute("href")).toBe("https://github.com/langyo");
110
110
  expect(link.getAttribute("target")).toBe("_blank");
111
- const rows = [...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row")];
112
- const licenseRow = rows.find((row) => row.textContent?.includes("BUSL-1.1"));
113
- expect(licenseRow, "license row renders").toBeTruthy();
114
- expect(licenseRow!.textContent).toContain("License");
115
111
  });
116
112
 
117
- it("renders the made-by row as plain text without originHref", async () => {
118
- mountAbout({ author: "由 伊欧 主创", origin: "来自 Celestia Island" });
113
+ it("renders the made-by sentence without a link when no href", async () => {
114
+ mountAbout({ madeByPrefix: "由", madeByName: "伊欧", madeBySuffix: " 主创" });
119
115
  await flushModal();
120
- expect(query<HTMLElement>(".s-about-modal-row").textContent).toContain("来自 Celestia Island");
116
+ expect(query<HTMLElement>(".s-about-modal-made-by").textContent).toBe("由伊欧 主创");
121
117
  expect(document.body.querySelector(".s-about-modal-row-link")).toBeNull();
122
118
  });
123
119
 
124
- it("renders the centered slogan and centered legal footer links", async () => {
120
+ it("renders software-component version rows", async () => {
121
+ mountAbout({
122
+ componentVersions: [
123
+ { label: "WebUI 版本", value: "0.1.138 REW2HF 生产" },
124
+ { label: "计算引擎版本", value: "0.1.0" },
125
+ ],
126
+ });
127
+ await flushModal();
128
+ const rows = [...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row")];
129
+ expect(rows.length).toBe(2);
130
+ expect(rows[0]!.textContent).toContain("WebUI 版本");
131
+ expect(rows[0]!.textContent).toContain("0.1.138 REW2HF 生产");
132
+ expect(rows[1]!.textContent).toContain("计算引擎版本");
133
+ });
134
+
135
+ it("renders centered license chips", async () => {
136
+ mountAbout({
137
+ licenses: [
138
+ { label: "SySL-1.0", href: "https://github.com/celestia-island/hikari/blob/master/LICENSE" },
139
+ { label: "BUSL-1.1", href: "https://github.com/celestia-island/shittim-chest/blob/master/LICENSE" },
140
+ ],
141
+ });
142
+ await flushModal();
143
+ const chips = [...document.body.querySelectorAll<HTMLAnchorElement>(".s-about-modal-link")];
144
+ expect(chips.map((chip) => chip.textContent)).toEqual(["SySL-1.0", "BUSL-1.1"]);
145
+ for (const chip of chips) expect(chip.getAttribute("target")).toBe("_blank");
146
+ });
147
+
148
+ it("renders centered legal footer links", async () => {
125
149
  mountAbout({
126
- slogan: "技术宅拯救世界。",
127
150
  footerLinks: [{ label: "京ICP备2026xxxx号", href: "https://beian.miit.gov.cn/" }],
128
151
  });
129
152
  await flushModal();
130
- expect(query<HTMLElement>(".s-about-modal-slogan").textContent).toBe("技术宅拯救世界。");
131
153
  const links = [
132
154
  ...document.body.querySelectorAll<HTMLAnchorElement>(".s-about-modal-footer-link"),
133
155
  ];
@@ -177,19 +199,11 @@ describe("HkAboutModal branding", () => {
177
199
  expect(badge.textContent).toContain(`© ${year} Test App`);
178
200
  });
179
201
 
180
- it("localizes the license label", async () => {
181
- mountAbout({ license: "BUSL-1.1" });
182
- await flushModal();
183
- const labels = () => [
184
- ...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row-label"),
185
- ];
186
-
187
- setLocale("zh-Hans");
188
- await flushModal();
189
- expect(labels().some((el) => el.textContent === "许可证")).toBe(true);
190
-
191
- setLocale("en");
202
+ it("renders no branding rows when no branding props are given", async () => {
203
+ mountAbout();
192
204
  await flushModal();
193
- expect(labels().some((el) => el.textContent === "License")).toBe(true);
205
+ expect(document.body.querySelectorAll(".s-about-modal-row").length).toBe(0);
206
+ expect(document.body.querySelector(".s-about-modal-made-by")).toBeNull();
207
+ expect(document.body.querySelector(".s-about-modal-links")).toBeNull();
194
208
  });
195
209
  });
@@ -10,18 +10,23 @@ export interface HAboutLink {
10
10
  href: string;
11
11
  }
12
12
 
13
+ /** One software-component version row (label left, composed value right). */
14
+ export interface HAboutComponentVersion {
15
+ label: string;
16
+ value: string;
17
+ }
18
+
13
19
  /**
14
20
  * HkAboutModal — version / about dialog.
15
21
  * (Upstreamed from shittim-chest's plana-legacy layer.)
16
22
  *
17
- * Shows the app identity and version metadata plus optional branding: a
18
- * logo image, tagline, centered slogan + organization line, a made-by /
19
- * origin row, license row, a decorative backdrop layer (canvas or anything
20
- * else, rendered behind the content and pointer-inert), external link
21
- * chips and centered legal footer links (e.g. ICP filings). Version/build
22
- * hashes render as short hashes when longer than 12 chars (full value in
23
- * `title` tooltip). Every branding prop is optional — the modal degrades
24
- * to the plain identity + version card when none are given.
23
+ * Shows a centered identity block (logo above the name above the tagline)
24
+ * plus optional branding: a made-by sentence with a linked author name, a
25
+ * centered organization line, software-component version rows, license and
26
+ * external link chips, a decorative backdrop layer (canvas or anything
27
+ * else, rendered behind the content and pointer-inert) and centered legal
28
+ * footer links (e.g. ICP filings). Every branding prop is optional — the
29
+ * modal degrades to the plain identity card when none are given.
25
30
  */
26
31
  export const HkAboutModal = defineComponent({
27
32
  name: "HkAboutModal",
@@ -31,28 +36,27 @@ export const HkAboutModal = defineComponent({
31
36
  appName: { type: String, required: true },
32
37
  /** Application version (e.g. "0.1.4"). */
33
38
  version: { type: String, required: true },
34
- /** Optional app build hash / commit. */
35
- buildHash: { type: String, default: undefined },
36
- /** Optional engine version (backend), e.g. "0.2.1". */
37
- engineVersion: { type: String, default: undefined },
38
- /** Optional engine build hash / commit. */
39
- engineBuildHash: { type: String, default: undefined },
40
39
  /** Optional logo image URL — replaces the first-letter tile. */
41
40
  logoSrc: { type: String, default: undefined },
42
41
  /** Optional one-liner under the version (e.g. the app tagline). */
43
42
  tagline: { type: String, default: undefined },
44
- /** Optional centered slogan below the header (the app's one-liner). */
45
- slogan: { type: String, default: undefined },
46
- /** Optional centered small line under the slogan (organization blurb). */
43
+ /** Optional centered small line under the header (organization blurb). */
47
44
  description: { type: String, default: undefined },
48
- /** Optional made-by row: left label (e.g. "由 伊欧 主创"). */
49
- author: { type: String, default: undefined },
50
- /** Optional right value of the made-by row (e.g. "来自 Celestia Island"). */
51
- origin: { type: String, default: undefined },
52
- /** Optional link applied to the origin value. */
53
- originHref: { type: String, default: undefined },
54
- /** Optional license identifier row (e.g. "BUSL-1.1"). */
55
- license: { type: String, default: undefined },
45
+ /** Made-by sentence: text before the linked author name (e.g. "由"). */
46
+ madeByPrefix: { type: String, default: undefined },
47
+ /** Made-by sentence: the linked author name (e.g. "伊欧"). */
48
+ madeByName: { type: String, default: undefined },
49
+ /** Link applied to the author name (e.g. the GitHub profile). */
50
+ madeByNameHref: { type: String, default: undefined },
51
+ /** Made-by sentence: text after the name (e.g. " 主创,来自 …"). */
52
+ madeBySuffix: { type: String, default: undefined },
53
+ /** License chips (e.g. SySL-1.0 / BUSL-1.1), rendered centered. */
54
+ licenses: { type: Array as PropType<HAboutLink[]>, default: () => [] },
55
+ /** Software-component version rows (WebUI / engines), label + value. */
56
+ componentVersions: {
57
+ type: Array as PropType<HAboutComponentVersion[]>,
58
+ default: () => [],
59
+ },
56
60
  /** Optional copyright holder in the footer (defaults to the app name). */
57
61
  copyright: { type: String, default: undefined },
58
62
  /** Optional external links (e.g. GitHub, docs). */
@@ -73,21 +77,26 @@ export const HkAboutModal = defineComponent({
73
77
  setup(props, { emit }) {
74
78
  const { t } = useI18n();
75
79
 
76
- function shortHash(hash: string): string {
77
- return hash.length > 12 ? `${hash.slice(0, 12)}…` : hash;
78
- }
79
-
80
- const renderOriginValue = () => {
81
- if (!props.originHref) return props.origin;
82
- return (
80
+ const renderMadeBy = () => {
81
+ if (!props.madeByName) return null;
82
+ const name = props.madeByNameHref ? (
83
83
  <a
84
84
  class="s-about-modal-row-link"
85
- href={props.originHref}
85
+ href={props.madeByNameHref}
86
86
  target="_blank"
87
87
  rel="noopener noreferrer"
88
88
  >
89
- {props.origin}
89
+ {props.madeByName}
90
90
  </a>
91
+ ) : (
92
+ <span>{props.madeByName}</span>
93
+ );
94
+ return (
95
+ <p class="s-about-modal-made-by">
96
+ {props.madeByPrefix}
97
+ {name}
98
+ {props.madeBySuffix}
99
+ </p>
91
100
  );
92
101
  };
93
102
 
@@ -120,51 +129,37 @@ export const HkAboutModal = defineComponent({
120
129
  </div>
121
130
  </header>
122
131
 
123
- {props.slogan && <p class="s-about-modal-slogan">{props.slogan}</p>}
132
+ {renderMadeBy()}
124
133
  {props.description && <p class="s-about-modal-description">{props.description}</p>}
125
134
 
126
- <div class="s-about-modal-rows">
127
- {(props.author || props.origin) && (
128
- <div class="s-about-modal-row">
129
- <span class="s-about-modal-row-label">{props.author}</span>
130
- <span class="s-about-modal-row-value">{renderOriginValue()}</span>
131
- </div>
132
- )}
133
- {props.license && (
134
- <div class="s-about-modal-row">
135
- <span class="s-about-modal-row-label">
136
- {t("hikari::about.license", "License")}
137
- </span>
138
- <span class="s-about-modal-row-value">{props.license}</span>
139
- </div>
140
- )}
141
- {props.buildHash && (
142
- <div class="s-about-modal-row">
143
- <span class="s-about-modal-row-label">{t("hikari::about.buildHash", "Build")}</span>
144
- <span class="s-about-modal-row-value" title={props.buildHash}>
145
- {shortHash(props.buildHash)}
146
- </span>
147
- </div>
148
- )}
149
- {props.engineVersion && (
150
- <div class="s-about-modal-row">
151
- <span class="s-about-modal-row-label">
152
- {t("hikari::about.engineVersion", "Engine version")}
153
- </span>
154
- <span class="s-about-modal-row-value">{props.engineVersion}</span>
155
- </div>
156
- )}
157
- {props.engineBuildHash && (
158
- <div class="s-about-modal-row">
159
- <span class="s-about-modal-row-label">
160
- {t("hikari::about.engineBuildHash", "Engine build")}
161
- </span>
162
- <span class="s-about-modal-row-value" title={props.engineBuildHash}>
163
- {shortHash(props.engineBuildHash)}
164
- </span>
135
+ {props.componentVersions.length > 0 && (
136
+ <div class="s-about-modal-rows">
137
+ {props.componentVersions.map((component) => (
138
+ <div class="s-about-modal-row" key={component.label}>
139
+ <span class="s-about-modal-row-label">{component.label}</span>
140
+ <span class="s-about-modal-row-value">{component.value}</span>
141
+ </div>
142
+ ))}
143
+ </div>
144
+ )}
145
+
146
+ {props.licenses.length > 0 && (
147
+ <div class="s-about-modal-links">
148
+ <div class="s-about-modal-links-list">
149
+ {props.licenses.map((license) => (
150
+ <a
151
+ key={license.href}
152
+ href={license.href}
153
+ target="_blank"
154
+ rel="noopener noreferrer"
155
+ class="s-about-modal-link"
156
+ >
157
+ {license.label}
158
+ </a>
159
+ ))}
165
160
  </div>
166
- )}
167
- </div>
161
+ </div>
162
+ )}
168
163
 
169
164
  {props.links.length > 0 && (
170
165
  <div class="s-about-modal-links">
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "فشل النسخ",
15
15
  "hikari::about.title": "حول",
16
16
  "hikari::about.version": "الإصدار",
17
- "hikari::about.buildHash": "البناء",
18
- "hikari::about.engineVersion": "إصدار المحرك",
19
- "hikari::about.engineBuildHash": "بناء المحرك",
20
- "hikari::about.license": "الترخيص",
21
17
  "hikari::theme.mode": "وضع السمة",
22
18
  "hikari::theme.modeLight": "فاتح",
23
19
  "hikari::theme.modeDark": "داكن",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Kopieren fehlgeschlagen",
15
15
  "hikari::about.title": "Über",
16
16
  "hikari::about.version": "Version",
17
- "hikari::about.buildHash": "Build",
18
- "hikari::about.engineVersion": "Engine-Version",
19
- "hikari::about.engineBuildHash": "Engine-Build",
20
- "hikari::about.license": "Lizenz",
21
17
  "hikari::theme.mode": "Design-Modus",
22
18
  "hikari::theme.modeLight": "Hell",
23
19
  "hikari::theme.modeDark": "Dunkel",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Copy failed",
15
15
  "hikari::about.title": "About",
16
16
  "hikari::about.version": "Version",
17
- "hikari::about.buildHash": "Build",
18
- "hikari::about.engineVersion": "Engine version",
19
- "hikari::about.engineBuildHash": "Engine build",
20
- "hikari::about.license": "License",
21
17
  "hikari::theme.mode": "Theme mode",
22
18
  "hikari::theme.modeLight": "Light",
23
19
  "hikari::theme.modeDark": "Dark",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Error al copiar",
15
15
  "hikari::about.title": "Acerca de",
16
16
  "hikari::about.version": "Versión",
17
- "hikari::about.buildHash": "Compilación",
18
- "hikari::about.engineVersion": "Versión del motor",
19
- "hikari::about.engineBuildHash": "Compilación del motor",
20
- "hikari::about.license": "Licencia",
21
17
  "hikari::theme.mode": "Modo de tema",
22
18
  "hikari::theme.modeLight": "Claro",
23
19
  "hikari::theme.modeDark": "Oscuro",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Échec de la copie",
15
15
  "hikari::about.title": "À propos",
16
16
  "hikari::about.version": "Version",
17
- "hikari::about.buildHash": "Build",
18
- "hikari::about.engineVersion": "Version du moteur",
19
- "hikari::about.engineBuildHash": "Build du moteur",
20
- "hikari::about.license": "Licence",
21
17
  "hikari::theme.mode": "Mode du thème",
22
18
  "hikari::theme.modeLight": "Clair",
23
19
  "hikari::theme.modeDark": "Sombre",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "コピーに失敗しました",
15
15
  "hikari::about.title": "このアプリについて",
16
16
  "hikari::about.version": "バージョン",
17
- "hikari::about.buildHash": "ビルド",
18
- "hikari::about.engineVersion": "エンジンバージョン",
19
- "hikari::about.engineBuildHash": "エンジンビルド",
20
- "hikari::about.license": "ライセンス",
21
17
  "hikari::theme.mode": "テーマモード",
22
18
  "hikari::theme.modeLight": "ライト",
23
19
  "hikari::theme.modeDark": "ダーク",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "복사 실패",
15
15
  "hikari::about.title": "정보",
16
16
  "hikari::about.version": "버전",
17
- "hikari::about.buildHash": "빌드",
18
- "hikari::about.engineVersion": "엔진 버전",
19
- "hikari::about.engineBuildHash": "엔진 빌드",
20
- "hikari::about.license": "라이선스",
21
17
  "hikari::theme.mode": "테마 모드",
22
18
  "hikari::theme.modeLight": "라이트",
23
19
  "hikari::theme.modeDark": "다크",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Falha ao copiar",
15
15
  "hikari::about.title": "Sobre",
16
16
  "hikari::about.version": "Versão",
17
- "hikari::about.buildHash": "Build",
18
- "hikari::about.engineVersion": "Versão do motor",
19
- "hikari::about.engineBuildHash": "Build do motor",
20
- "hikari::about.license": "Licença",
21
17
  "hikari::theme.mode": "Modo do tema",
22
18
  "hikari::theme.modeLight": "Claro",
23
19
  "hikari::theme.modeDark": "Escuro",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "Не удалось скопировать",
15
15
  "hikari::about.title": "О программе",
16
16
  "hikari::about.version": "Версия",
17
- "hikari::about.buildHash": "Сборка",
18
- "hikari::about.engineVersion": "Версия движка",
19
- "hikari::about.engineBuildHash": "Сборка движка",
20
- "hikari::about.license": "Лицензия",
21
17
  "hikari::theme.mode": "Режим темы",
22
18
  "hikari::theme.modeLight": "Светлая",
23
19
  "hikari::theme.modeDark": "Тёмная",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "复制失败",
15
15
  "hikari::about.title": "关于",
16
16
  "hikari::about.version": "版本",
17
- "hikari::about.buildHash": "构建",
18
- "hikari::about.engineVersion": "引擎版本",
19
- "hikari::about.engineBuildHash": "引擎构建",
20
- "hikari::about.license": "许可证",
21
17
  "hikari::theme.mode": "主题模式",
22
18
  "hikari::theme.modeLight": "浅色",
23
19
  "hikari::theme.modeDark": "深色",
@@ -14,10 +14,6 @@
14
14
  "hikari::clipboard.copyFailed": "複製失敗",
15
15
  "hikari::about.title": "關於",
16
16
  "hikari::about.version": "版本",
17
- "hikari::about.buildHash": "建置",
18
- "hikari::about.engineVersion": "引擎版本",
19
- "hikari::about.engineBuildHash": "引擎建置",
20
- "hikari::about.license": "授權條款",
21
17
  "hikari::theme.mode": "主題模式",
22
18
  "hikari::theme.modeLight": "淺色",
23
19
  "hikari::theme.modeDark": "深色",