Skip to content

Commit 07058b8

Browse files
committed
perf(frontend): optimize startup bundle and unify typography
1 parent 09e17ef commit 07058b8

9 files changed

Lines changed: 297 additions & 84 deletions

File tree

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dev": "vite",
88
"build:dev": "tsc --noEmit && vite build --minify false --mode development",
99
"build": "tsc --noEmit && vite build --mode production",
10+
"build:analyze": "tsc --noEmit && vite build --mode analyze",
1011
"preview": "vite preview",
1112
"type-check": "tsc --noEmit"
1213
},

frontend/src/hero.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// hero.ts
2-
import { heroui } from "@heroui/react";
2+
import { heroui } from "@heroui/theme";
33

44
export default heroui({
55
themes: {

frontend/src/i18n.ts

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,72 @@
11
import i18n from "i18next";
2+
import type { BackendModule } from "i18next";
23
import { initReactI18next } from "react-i18next";
34
import LanguageDetector from "i18next-browser-languagedetector";
4-
import en from "@/assets/locales/en_US.json";
5-
import zh from "@/assets/locales/zh_CN.json";
6-
import ru from "@/assets/locales/ru_RU.json";
7-
import ja from "@/assets/locales/ja_JP.json";
8-
import zhhk from "@/assets/locales/zh_HK.json";
5+
import { normalizeLanguage } from "@/utils/i18nUtils";
96

7+
const localeLoaders = {
8+
en_US: () => import("@/assets/locales/en_US.json"),
9+
zh_CN: () => import("@/assets/locales/zh_CN.json"),
10+
ru_RU: () => import("@/assets/locales/ru_RU.json"),
11+
ja_JP: () => import("@/assets/locales/ja_JP.json"),
12+
zh_HK: () => import("@/assets/locales/zh_HK.json"),
13+
} as const;
1014

15+
type SupportedLocale = keyof typeof localeLoaders;
1116

12-
export const resources = {
13-
en_US: { translation: en },
14-
"en-US": { translation: en },
15-
en: { translation: en },
16-
zh_CN: { translation: zh },
17-
"zh-CN": { translation: zh },
18-
zh: { translation: zh },
19-
ru_RU: { translation: ru },
20-
"ru-RU": { translation: ru },
21-
ru: { translation: ru },
22-
ja_JP: { translation: ja },
23-
"ja-JP": { translation: ja },
24-
ja: { translation: ja },
25-
zh_HK: { translation: zhhk },
26-
"zh-HK": { translation: zhhk },
27-
zhhk: { translation: zhhk },
17+
const supportedLngs = [
18+
"en_US",
19+
"en-US",
20+
"en",
21+
"zh_CN",
22+
"zh-CN",
23+
"zh",
24+
"ru_RU",
25+
"ru-RU",
26+
"ru",
27+
"ja_JP",
28+
"ja-JP",
29+
"ja",
30+
"zh_HK",
31+
"zh-HK",
32+
"zhhk",
33+
] as const;
34+
35+
const resolveLocale = (language: string): SupportedLocale => {
36+
const normalized = normalizeLanguage(language);
37+
38+
if (normalized in localeLoaders) {
39+
return normalized as SupportedLocale;
40+
}
41+
42+
return "en_US";
43+
};
44+
45+
const localeBackend: BackendModule = {
46+
type: "backend",
47+
init: () => {},
48+
read(language, _namespace, callback) {
49+
const locale = resolveLocale(language);
50+
51+
localeLoaders[locale]()
52+
.then((module) => {
53+
callback(null, module.default);
54+
})
55+
.catch((error) => {
56+
callback(error, null);
57+
});
58+
},
2859
};
2960

30-
i18n
61+
export const i18nReady = i18n
3162
.use(LanguageDetector)
63+
.use(localeBackend)
3264
.use(initReactI18next)
3365
.init({
34-
resources,
3566
load: "currentOnly",
67+
partialBundledLanguages: true,
3668
fallbackLng: "en_US",
37-
supportedLngs: [
38-
"en_US",
39-
"en-US",
40-
"en",
41-
"zh_CN",
42-
"zh-CN",
43-
"zh",
44-
"ru_RU",
45-
"ru-RU",
46-
"ru",
47-
"ja_JP",
48-
"ja-JP",
49-
"ja",
50-
"zh_HK",
51-
"zh-HK",
52-
"zhhk",
53-
],
69+
supportedLngs,
5470
lowerCaseLng: false,
5571
nonExplicitSupportedLngs: true,
5672
detection: {

frontend/src/main.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@ const StartupShell = ({ errorMessage = "", visible = true }) => (
170170
background:
171171
"linear-gradient(180deg, rgba(248,250,252,1) 0%, rgba(241,245,249,1) 100%)",
172172
color: "#0f172a",
173-
fontFamily:
174-
'"MiSans","Segoe UI","PingFang SC","Microsoft YaHei",sans-serif',
173+
fontFamily: 'var(--font-sans, sans-serif)',
175174
opacity: visible ? 1 : 0,
176175
visibility: visible ? "visible" : "hidden",
177176
transition: "opacity 220ms ease, visibility 220ms ease",
@@ -235,7 +234,7 @@ const bootstrapApp = async () => {
235234
try {
236235
const [
237236
{ default: App },
238-
{ default: i18n },
237+
{ default: i18n, i18nReady },
239238
{ I18nextProvider },
240239
{ ThemeProvider: NextThemesProvider },
241240
{ HeroUIProvider },
@@ -260,6 +259,8 @@ const bootstrapApp = async () => {
260259
"ll-startup-app-bundle-loaded",
261260
);
262261

262+
await i18nReady;
263+
263264
const router = createHashRouter(
264265
createRoutesFromElements(<Route path="/*" element={<App />} />),
265266
);

frontend/src/pages/SettingsPage.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ export const SettingsPage: React.FC = () => {
279279
description={t("settings.header.content")}
280280
/>
281281
<Tabs
282-
aria-label={t("settings.header.title")}
282+
aria-label={t("settings.header.title")}
283283
selectedKey={selectedTab}
284284
onSelectionChange={(k) => setSelectedTab(k as string)}
285285
classNames={{
@@ -483,7 +483,7 @@ export const SettingsPage: React.FC = () => {
483483
</Button>
484484
</DropdownTrigger>
485485
<DropdownMenu
486-
aria-label={t("settings.body.language.button")}
486+
aria-label={t("settings.body.language.button")}
487487
variant="flat"
488488
disallowEmptySelection
489489
selectionMode="single"
@@ -1031,7 +1031,9 @@ export const SettingsPage: React.FC = () => {
10311031
</p>
10321032
<Select
10331033
size="sm"
1034-
aria-label={t("settings.appearance.background_fit_mode")}
1034+
aria-label={t(
1035+
"settings.appearance.background_fit_mode",
1036+
)}
10351037
disallowEmptySelection
10361038
classNames={COMPONENT_STYLES.select}
10371039
selectedKeys={new Set([backgroundFitMode])}
@@ -1131,7 +1133,9 @@ export const SettingsPage: React.FC = () => {
11311133
</p>
11321134
<Select
11331135
size="sm"
1134-
aria-label={t("settings.appearance.background_play_order")}
1136+
aria-label={t(
1137+
"settings.appearance.background_play_order",
1138+
)}
11351139
disallowEmptySelection
11361140
classNames={COMPONENT_STYLES.select}
11371141
selectedKeys={
@@ -1218,7 +1222,9 @@ export const SettingsPage: React.FC = () => {
12181222
step={1}
12191223
maxValue={50}
12201224
minValue={0}
1221-
aria-label={t("settings.appearance.background_blur")}
1225+
aria-label={t(
1226+
"settings.appearance.background_blur",
1227+
)}
12221228
value={backgroundBlur}
12231229
classNames={{
12241230
filler: "bg-primary-500",
@@ -1281,7 +1287,9 @@ export const SettingsPage: React.FC = () => {
12811287
step={1}
12821288
maxValue={100}
12831289
minValue={20}
1284-
aria-label={t("settings.appearance.background_brightness")}
1290+
aria-label={t(
1291+
"settings.appearance.background_brightness",
1292+
)}
12851293
value={backgroundBrightness}
12861294
classNames={{
12871295
filler: "bg-primary-500",
@@ -1346,7 +1354,9 @@ export const SettingsPage: React.FC = () => {
13461354
step={1}
13471355
maxValue={100}
13481356
minValue={0}
1349-
aria-label={t("settings.appearance.background_opacity")}
1357+
aria-label={t(
1358+
"settings.appearance.background_opacity",
1359+
)}
13501360
value={backgroundOpacity}
13511361
classNames={{
13521362
filler: "bg-primary-500",
@@ -1771,7 +1781,9 @@ export const SettingsPage: React.FC = () => {
17711781
step={1}
17721782
maxValue={100}
17731783
minValue={0}
1774-
aria-label={t("settings.appearance.background_base_opacity")}
1784+
aria-label={t(
1785+
"settings.appearance.background_base_opacity",
1786+
)}
17751787
value={
17761788
themeSettingMode === "light"
17771789
? lightBackgroundBaseOpacity
@@ -2497,10 +2509,14 @@ export const SettingsPage: React.FC = () => {
24972509
hideCloseButton
24982510
isDismissable={false}
24992511
type={gdkDlError ? "error" : "info"}
2500-
title={gdkDlError ? t("common.error") : t("settings.gdk.download.title")}
2512+
title={
2513+
gdkDlError ? t("common.error") : t("settings.gdk.download.title")
2514+
}
25012515
icon={gdkDlError ? undefined : <FaDownload className="w-6 h-6" />}
25022516
confirmText={gdkDlError ? t("common.close") : undefined}
2503-
onConfirm={gdkDlError ? () => gdkProgressDisclosure.onClose() : undefined}
2517+
onConfirm={
2518+
gdkDlError ? () => gdkProgressDisclosure.onClose() : undefined
2519+
}
25042520
footer={
25052521
gdkDlError ? undefined : (
25062522
<>

frontend/src/shims/heroui.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
export { Avatar } from "@heroui/avatar";
2+
export type { AvatarProps } from "@heroui/avatar";
3+
4+
export { Button, ButtonGroup } from "@heroui/button";
5+
export type { ButtonGroupProps, ButtonProps } from "@heroui/button";
6+
7+
export { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
8+
export type {
9+
CardFooterProps,
10+
CardProps,
11+
} from "@heroui/card";
12+
13+
export { Checkbox } from "@heroui/checkbox";
14+
export type { CheckboxProps } from "@heroui/checkbox";
15+
16+
export { Chip } from "@heroui/chip";
17+
export type { ChipProps } from "@heroui/chip";
18+
19+
export { Divider } from "@heroui/divider";
20+
export type { DividerProps } from "@heroui/divider";
21+
22+
export {
23+
Dropdown,
24+
DropdownItem,
25+
DropdownMenu,
26+
DropdownTrigger,
27+
} from "@heroui/dropdown";
28+
export type {
29+
DropdownItemProps,
30+
DropdownMenuProps,
31+
DropdownProps,
32+
DropdownTriggerProps,
33+
} from "@heroui/dropdown";
34+
35+
export { HeroUIProvider } from "@heroui/system";
36+
export type { HeroUIProviderProps } from "@heroui/system";
37+
38+
export { Image } from "@heroui/image";
39+
export type { ImageProps } from "@heroui/image";
40+
41+
export { Input, Textarea } from "@heroui/input";
42+
export type { InputProps, TextAreaProps } from "@heroui/input";
43+
44+
export { Link } from "@heroui/link";
45+
export type { LinkProps } from "@heroui/link";
46+
47+
export {
48+
Modal,
49+
ModalBody,
50+
ModalContent,
51+
ModalFooter,
52+
ModalHeader,
53+
} from "@heroui/modal";
54+
export type {
55+
ModalBodyProps,
56+
ModalContentProps,
57+
ModalFooterProps,
58+
ModalHeaderProps,
59+
ModalProps,
60+
} from "@heroui/modal";
61+
62+
export { Pagination } from "@heroui/pagination";
63+
export type { PaginationProps } from "@heroui/pagination";
64+
65+
export { Popover, PopoverContent, PopoverTrigger } from "@heroui/popover";
66+
export type {
67+
PopoverContentProps,
68+
PopoverProps,
69+
PopoverTriggerProps,
70+
} from "@heroui/popover";
71+
72+
export { Progress } from "@heroui/progress";
73+
export type { ProgressProps } from "@heroui/progress";
74+
75+
export { Radio, RadioGroup } from "@heroui/radio";
76+
export type { RadioGroupProps, RadioProps } from "@heroui/radio";
77+
78+
export { ScrollShadow } from "@heroui/scroll-shadow";
79+
export type { ScrollShadowProps } from "@heroui/scroll-shadow";
80+
81+
export { Select, SelectItem } from "@heroui/select";
82+
export type { SelectItemProps, SelectProps } from "@heroui/select";
83+
84+
export { Skeleton } from "@heroui/skeleton";
85+
export type { SkeletonProps } from "@heroui/skeleton";
86+
87+
export { Slider } from "@heroui/slider";
88+
export type { SliderProps } from "@heroui/slider";
89+
90+
export { Spinner } from "@heroui/spinner";
91+
export type { SpinnerProps } from "@heroui/spinner";
92+
93+
export { Switch } from "@heroui/switch";
94+
export type { SwitchProps } from "@heroui/switch";
95+
96+
export { Tab, Tabs } from "@heroui/tabs";
97+
export type { TabItemProps, TabsProps } from "@heroui/tabs";
98+
99+
export {
100+
Table,
101+
TableBody,
102+
TableCell,
103+
TableColumn,
104+
TableHeader,
105+
TableRow,
106+
} from "@heroui/table";
107+
export type {
108+
TableBodyProps,
109+
TableCellProps,
110+
TableColumnProps,
111+
TableHeaderProps,
112+
TableProps,
113+
TableRowProps,
114+
} from "@heroui/table";
115+
116+
export { ToastProvider, addToast } from "@heroui/toast";
117+
export type { ToastProps } from "@heroui/toast";
118+
119+
export { Tooltip } from "@heroui/tooltip";
120+
export type { TooltipProps } from "@heroui/tooltip";
121+
122+
export { User } from "@heroui/user";
123+
export type { UserProps } from "@heroui/user";
124+
125+
export { useDisclosure } from "@heroui/use-disclosure";
126+
export type {
127+
UseDisclosureProps,
128+
UseDisclosureReturn,
129+
} from "@heroui/use-disclosure";

0 commit comments

Comments
 (0)