@otmc/ui 0.1.2 → 0.1.4

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/branding/Footer.tsx
2
2
  import { Stack, Group, Text, Anchor, Tooltip, ActionIcon } from "@mantine/core";
3
+ import { Copyright } from "lucide-react";
3
4
  import {
4
- IconCopyright,
5
5
  IconHeart,
6
6
  IconBrandFacebook,
7
7
  IconBrandGithub,
@@ -56,7 +56,7 @@ function Footer({
56
56
  "Copyright",
57
57
  " "
58
58
  ] }),
59
- /* @__PURE__ */ jsx(IconCopyright, { size: 15 }),
59
+ /* @__PURE__ */ jsx(Copyright, { size: 15, color: "var(--mantine-color-dimmed)" }),
60
60
  /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
61
61
  (/* @__PURE__ */ new Date()).getFullYear(),
62
62
  " ",
@@ -100,35 +100,376 @@ function Footer({
100
100
  ] });
101
101
  }
102
102
 
103
- // src/modals/Information.tsx
104
- import { useState } from "react";
103
+ // src/components/ThemeMenu.tsx
105
104
  import {
106
- Modal,
105
+ ActionIcon as ActionIcon2,
106
+ Menu,
107
+ Tooltip as Tooltip2
108
+ } from "@mantine/core";
109
+ import { Sun, Moon, Monitor, Clock } from "lucide-react";
110
+
111
+ // src/contexts/ThemeProvider.tsx
112
+ import { createContext, useContext, useEffect, useState } from "react";
113
+ import {
114
+ useMantineColorScheme
115
+ } from "@mantine/core";
116
+ import { jsx as jsx2 } from "react/jsx-runtime";
117
+ var ThemeContext = createContext(null);
118
+ var useTheme = () => {
119
+ const ctx = useContext(ThemeContext);
120
+ if (!ctx) {
121
+ throw new Error("useTheme must be used inside ThemeProvider");
122
+ }
123
+ return ctx;
124
+ };
125
+ var resolveColorScheme = (colorScheme) => {
126
+ if (colorScheme === "auto") {
127
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
128
+ }
129
+ return colorScheme;
130
+ };
131
+ var resolveTimeBasedTheme = (settings) => {
132
+ const now = /* @__PURE__ */ new Date();
133
+ const minutes = now.getHours() * 60 + now.getMinutes();
134
+ const [lh, lm] = settings.lightStartTime.split(":").map(Number);
135
+ const [dh, dm] = settings.darkStartTime.split(":").map(Number);
136
+ const light = lh * 60 + lm;
137
+ const dark = dh * 60 + dm;
138
+ return light < dark ? minutes >= light && minutes < dark ? "light" : "dark" : minutes >= light || minutes < dark ? "light" : "dark";
139
+ };
140
+ var resolveAutoScheme = () => window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
141
+ var ThemeProvider = ({
142
+ children,
143
+ defaultTheme = "auto",
144
+ defaultTimeBasedSettings = {
145
+ lightStartTime: "07:00",
146
+ darkStartTime: "19:00"
147
+ },
148
+ storagePrefix = ""
149
+ }) => {
150
+ const themeStorageKey = `${storagePrefix}theme`;
151
+ const timeStorageKey = `${storagePrefix}time-theme`;
152
+ const { colorScheme, setColorScheme } = useMantineColorScheme();
153
+ const [theme, setThemeState] = useState(
154
+ () => localStorage.getItem(themeStorageKey) ?? defaultTheme
155
+ );
156
+ const [timeBasedSettings, setTimeBasedSettings] = useState(
157
+ () => JSON.parse(localStorage.getItem(timeStorageKey) || "null") ?? defaultTimeBasedSettings
158
+ );
159
+ const handleSetTimeBasedSettings = (settings) => {
160
+ setTimeBasedSettings(settings);
161
+ localStorage.setItem(timeStorageKey, JSON.stringify(settings));
162
+ if (theme === "time-based") {
163
+ applyTheme("time-based");
164
+ }
165
+ };
166
+ const applyTheme = (mode) => {
167
+ let scheme;
168
+ switch (mode) {
169
+ case "light":
170
+ case "dark":
171
+ case "auto":
172
+ scheme = mode;
173
+ break;
174
+ case "time-based":
175
+ scheme = resolveTimeBasedTheme(timeBasedSettings);
176
+ break;
177
+ default:
178
+ scheme = "auto";
179
+ }
180
+ setColorScheme(scheme);
181
+ };
182
+ useEffect(() => {
183
+ applyTheme(theme);
184
+ }, [theme, timeBasedSettings]);
185
+ const setTheme = (mode) => {
186
+ setThemeState(mode);
187
+ localStorage.setItem(themeStorageKey, mode);
188
+ applyTheme(mode);
189
+ };
190
+ const toggleTheme = () => {
191
+ setTheme(colorScheme === "dark" ? "light" : "dark");
192
+ };
193
+ const currentTheme = theme === "time-based" ? resolveTimeBasedTheme(timeBasedSettings) : colorScheme === "auto" ? resolveAutoScheme() : colorScheme;
194
+ return /* @__PURE__ */ jsx2(
195
+ ThemeContext.Provider,
196
+ {
197
+ value: {
198
+ theme,
199
+ currentTheme,
200
+ setTheme,
201
+ toggleTheme,
202
+ timeBasedSettings,
203
+ setTimeBasedSettings: handleSetTimeBasedSettings
204
+ },
205
+ children
206
+ }
207
+ );
208
+ };
209
+
210
+ // src/components/ThemeMenu.tsx
211
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
212
+ var DEFAULT_LABELS = {
213
+ tooltip: "Ch\u1EBF \u0111\u1ED9 giao di\u1EC7n",
214
+ title: "Ch\u1EBF \u0111\u1ED9 giao di\u1EC7n",
215
+ light: "S\xE1ng",
216
+ dark: "T\u1ED1i",
217
+ auto: "Theo h\u1EC7 th\u1ED1ng",
218
+ timeBased: "Theo th\u1EDDi gian"
219
+ };
220
+ var MODE_ICONS = {
221
+ light: /* @__PURE__ */ jsx3(Sun, { size: 16 }),
222
+ dark: /* @__PURE__ */ jsx3(Moon, { size: 16 }),
223
+ auto: /* @__PURE__ */ jsx3(Monitor, { size: 16 }),
224
+ "time-based": /* @__PURE__ */ jsx3(Clock, { size: 16 })
225
+ };
226
+ function ThemeMenu({
227
+ modes = ["light", "dark", "auto", "time-based"],
228
+ labels = {},
229
+ iconSize = 20,
230
+ size = "lg",
231
+ menuProps = {}
232
+ }) {
233
+ const { theme, currentTheme, setTheme } = useTheme();
234
+ const text = { ...DEFAULT_LABELS, ...labels };
235
+ const isDark = currentTheme === "dark";
236
+ const activeColor = isDark ? "yellow.8" : "blue.8";
237
+ const triggerIcon = theme === "light" ? /* @__PURE__ */ jsx3(Sun, { size: iconSize, strokeWidth: 2 }) : theme === "dark" ? /* @__PURE__ */ jsx3(Moon, { size: iconSize, strokeWidth: 2 }) : theme === "auto" ? /* @__PURE__ */ jsx3(Monitor, { size: iconSize, strokeWidth: 2 }) : /* @__PURE__ */ jsx3(Clock, { size: iconSize, strokeWidth: 2 });
238
+ return /* @__PURE__ */ jsxs2(Menu, { shadow: "md", width: 200, position: "bottom-end", ...menuProps, children: [
239
+ /* @__PURE__ */ jsx3(Menu.Target, { children: /* @__PURE__ */ jsx3(Tooltip2, { label: text.tooltip, position: "bottom", withArrow: true, children: /* @__PURE__ */ jsx3(
240
+ ActionIcon2,
241
+ {
242
+ variant: "subtle",
243
+ color: activeColor,
244
+ size,
245
+ radius: "lg",
246
+ style: {
247
+ boxShadow: isDark ? "0 0 15px rgba(250, 176, 5, 0.2)" : "0 0 15px rgba(34, 139, 230, 0.2)"
248
+ },
249
+ children: triggerIcon
250
+ }
251
+ ) }) }),
252
+ /* @__PURE__ */ jsxs2(Menu.Dropdown, { style: { borderRadius: "11px" }, children: [
253
+ /* @__PURE__ */ jsx3(Menu.Label, { children: text.title }),
254
+ modes.map((mode) => /* @__PURE__ */ jsx3(
255
+ Menu.Item,
256
+ {
257
+ leftSection: MODE_ICONS[mode],
258
+ onClick: () => setTheme(mode),
259
+ color: theme === mode ? activeColor : void 0,
260
+ fw: theme === mode ? 700 : 400,
261
+ children: text[mode === "time-based" ? "timeBased" : mode]
262
+ },
263
+ mode
264
+ ))
265
+ ] })
266
+ ] });
267
+ }
268
+
269
+ // src/components/ThemeSetting.tsx
270
+ import {
271
+ Box,
272
+ Card,
273
+ Divider,
274
+ Group as Group2,
275
+ SegmentedControl,
107
276
  Stack as Stack2,
108
- Title,
109
277
  Text as Text2,
110
- Group as Group2,
111
- Divider,
112
- Badge,
113
278
  ThemeIcon,
279
+ Title
280
+ } from "@mantine/core";
281
+ import { TimeInput } from "@mantine/dates";
282
+ import {
283
+ IconClock,
284
+ IconDeviceLaptop,
285
+ IconMoon,
286
+ IconPalette,
287
+ IconSun
288
+ } from "@tabler/icons-react";
289
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
290
+ var DEFAULT_LABELS2 = {
291
+ title: "Appearance",
292
+ subtitle: "Control how the application looks and feels",
293
+ themeMode: "Theme mode",
294
+ themeModeHint: "Auto will switch between light and dark based on time of day",
295
+ currentlyActive: "Currently active:",
296
+ timeBasedSettings: "Time-based settings",
297
+ lightStartTime: "Light theme start time",
298
+ darkStartTime: "Dark theme start time",
299
+ light: "Light",
300
+ dark: "Dark",
301
+ system: "System",
302
+ timeBased: "Time-based"
303
+ };
304
+ var MODE_ICONS2 = {
305
+ light: /* @__PURE__ */ jsx4(IconSun, { size: 16 }),
306
+ dark: /* @__PURE__ */ jsx4(IconMoon, { size: 16 }),
307
+ auto: /* @__PURE__ */ jsx4(IconDeviceLaptop, { size: 16 }),
308
+ "time-based": /* @__PURE__ */ jsx4(IconClock, { size: 16 })
309
+ };
310
+ var MODE_ORDER = ["light", "dark", "auto", "time-based"];
311
+ function ThemeSetting({
312
+ modes = MODE_ORDER,
313
+ labels = {},
314
+ cardProps = {}
315
+ }) {
316
+ const {
317
+ theme,
318
+ timeBasedSettings,
319
+ setTheme,
320
+ setTimeBasedSettings,
321
+ currentTheme
322
+ } = useTheme();
323
+ const text = { ...DEFAULT_LABELS2, ...labels };
324
+ const isTimeBased = theme === "time-based";
325
+ const isDark = currentTheme === "dark";
326
+ const showTimeBased = modes.includes("time-based");
327
+ return /* @__PURE__ */ jsx4(
328
+ Card,
329
+ {
330
+ withBorder: true,
331
+ radius: "lg",
332
+ p: "25px",
333
+ bg: isDark ? "dark.8" : "gray.0",
334
+ style: { flex: 1, overflow: "auto", display: "flex", flexDirection: "column" },
335
+ ...cardProps,
336
+ children: /* @__PURE__ */ jsxs3(Stack2, { gap: "lg", style: { flex: 1 }, children: [
337
+ /* @__PURE__ */ jsxs3(Box, { children: [
338
+ /* @__PURE__ */ jsxs3(Group2, { gap: "xs", children: [
339
+ /* @__PURE__ */ jsx4(ThemeIcon, { variant: "subtle", color: "blue.8", size: "md", radius: "md", children: /* @__PURE__ */ jsx4(IconPalette, { size: 25 }) }),
340
+ /* @__PURE__ */ jsx4(Title, { order: 4, children: text.title })
341
+ ] }),
342
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: text.subtitle })
343
+ ] }),
344
+ /* @__PURE__ */ jsx4(Divider, {}),
345
+ /* @__PURE__ */ jsxs3(Box, { children: [
346
+ /* @__PURE__ */ jsx4(Text2, { fw: 500, mb: 6, children: text.themeMode }),
347
+ /* @__PURE__ */ jsx4(
348
+ SegmentedControl,
349
+ {
350
+ fullWidth: true,
351
+ value: theme,
352
+ radius: "lg",
353
+ color: "teal.8",
354
+ bg: isDark ? "dark.6" : "gray.3",
355
+ onChange: (value) => setTheme(value),
356
+ data: MODE_ORDER.filter((mode) => modes.includes(mode)).map(
357
+ (mode) => ({
358
+ value: mode,
359
+ label: /* @__PURE__ */ jsxs3(Group2, { gap: 6, children: [
360
+ MODE_ICONS2[mode],
361
+ /* @__PURE__ */ jsx4("span", { children: mode === "light" ? text.light : mode === "dark" ? text.dark : mode === "auto" ? text.system : text.timeBased })
362
+ ] })
363
+ })
364
+ )
365
+ }
366
+ ),
367
+ /* @__PURE__ */ jsx4(Text2, { size: "xs", c: "dimmed", mt: "xs", children: text.themeModeHint }),
368
+ /* @__PURE__ */ jsxs3(Text2, { size: "xs", mt: 4, children: [
369
+ text.currentlyActive,
370
+ " ",
371
+ /* @__PURE__ */ jsx4("b", { children: isDark ? text.dark : text.light })
372
+ ] })
373
+ ] }),
374
+ showTimeBased && /* @__PURE__ */ jsxs3(Fragment, { children: [
375
+ /* @__PURE__ */ jsx4(Divider, {}),
376
+ /* @__PURE__ */ jsxs3(
377
+ Box,
378
+ {
379
+ style: {
380
+ opacity: isTimeBased ? 1 : 0.45,
381
+ pointerEvents: isTimeBased ? "auto" : "none",
382
+ transition: "opacity 150ms ease"
383
+ },
384
+ children: [
385
+ /* @__PURE__ */ jsxs3(Group2, { gap: 6, mb: 6, children: [
386
+ /* @__PURE__ */ jsx4(IconClock, { size: 16 }),
387
+ /* @__PURE__ */ jsx4(Text2, { fw: 500, children: text.timeBasedSettings })
388
+ ] }),
389
+ /* @__PURE__ */ jsxs3(Group2, { grow: true, children: [
390
+ /* @__PURE__ */ jsxs3(Box, { children: [
391
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", mb: 4, children: text.lightStartTime }),
392
+ /* @__PURE__ */ jsx4(
393
+ TimeInput,
394
+ {
395
+ withSeconds: false,
396
+ radius: "13px",
397
+ value: timeBasedSettings.lightStartTime,
398
+ onChange: (event) => setTimeBasedSettings({
399
+ ...timeBasedSettings,
400
+ lightStartTime: event.currentTarget.value
401
+ })
402
+ }
403
+ )
404
+ ] }),
405
+ /* @__PURE__ */ jsxs3(Box, { children: [
406
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", mb: 4, children: text.darkStartTime }),
407
+ /* @__PURE__ */ jsx4(
408
+ TimeInput,
409
+ {
410
+ withSeconds: false,
411
+ radius: "13px",
412
+ value: timeBasedSettings.darkStartTime,
413
+ onChange: (event) => setTimeBasedSettings({
414
+ ...timeBasedSettings,
415
+ darkStartTime: event.currentTarget.value
416
+ })
417
+ }
418
+ )
419
+ ] })
420
+ ] }),
421
+ /* @__PURE__ */ jsxs3(Text2, { size: "xs", c: "dimmed", mt: "xs", children: [
422
+ text.light,
423
+ ": ",
424
+ timeBasedSettings.lightStartTime,
425
+ " \u2192",
426
+ " ",
427
+ timeBasedSettings.darkStartTime,
428
+ /* @__PURE__ */ jsx4("br", {}),
429
+ text.dark,
430
+ ": ",
431
+ timeBasedSettings.darkStartTime,
432
+ " \u2192",
433
+ " ",
434
+ timeBasedSettings.lightStartTime
435
+ ] })
436
+ ]
437
+ }
438
+ )
439
+ ] })
440
+ ] })
441
+ }
442
+ );
443
+ }
444
+
445
+ // src/modals/Information.tsx
446
+ import { useState as useState2 } from "react";
447
+ import {
448
+ Modal,
449
+ Stack as Stack3,
450
+ Title as Title2,
451
+ Text as Text3,
452
+ Group as Group3,
453
+ Divider as Divider2,
454
+ Badge,
455
+ ThemeIcon as ThemeIcon2,
114
456
  SimpleGrid,
115
457
  Paper,
116
- ActionIcon as ActionIcon2,
117
- Tooltip as Tooltip2,
458
+ ActionIcon as ActionIcon3,
459
+ Tooltip as Tooltip3,
118
460
  useComputedColorScheme
119
461
  } from "@mantine/core";
120
462
  import {
121
- IconAward,
122
- IconCalendar,
123
- IconCheck,
124
- IconCopy,
125
- IconCpu,
126
- IconHash,
127
- IconLicense,
128
- IconMapPin,
129
- IconTag,
130
- IconUser
131
- } from "@tabler/icons-react";
463
+ Award,
464
+ Calendar,
465
+ Check,
466
+ Copy,
467
+ Cpu,
468
+ Hash,
469
+ MapPin,
470
+ Tag,
471
+ User
472
+ } from "lucide-react";
132
473
 
133
474
  // src/utils/commonUtils.ts
134
475
  var copyToClipboard = async (text) => {
@@ -268,10 +609,10 @@ function formatHumanDate(date, options) {
268
609
  }
269
610
 
270
611
  // src/modals/Information.tsx
271
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
272
- var DEFAULT_TITLE = /* @__PURE__ */ jsxs2(Group2, { gap: "xs", wrap: "nowrap", children: [
273
- /* @__PURE__ */ jsx2(IconCpu, { size: 20 }),
274
- /* @__PURE__ */ jsx2("span", { children: "System Information" })
612
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
613
+ var DEFAULT_TITLE = /* @__PURE__ */ jsxs4(Group3, { gap: "xs", wrap: "nowrap", children: [
614
+ /* @__PURE__ */ jsx5(Cpu, { size: 20 }),
615
+ /* @__PURE__ */ jsx5("span", { children: "System Information" })
275
616
  ] });
276
617
  function InformationContent({ info }) {
277
618
  const colorScheme = useComputedColorScheme();
@@ -279,46 +620,46 @@ function InformationContent({ info }) {
279
620
  const versionType = info.versionType ?? DEFAULT_VERSION_TYPE;
280
621
  const cards = [
281
622
  {
282
- icon: /* @__PURE__ */ jsx2(IconUser, { size: 18 }),
623
+ icon: /* @__PURE__ */ jsx5(User, { size: 18 }),
283
624
  label: "Author",
284
625
  value: author,
285
626
  color: "blue"
286
627
  },
287
628
  {
288
- icon: /* @__PURE__ */ jsx2(IconLicense, { size: 18 }),
629
+ icon: /* @__PURE__ */ jsx5(Award, { size: 18 }),
289
630
  label: "License",
290
631
  value: info.license ?? DEFAULT_LICENSE,
291
632
  color: "orange"
292
633
  },
293
634
  {
294
- icon: /* @__PURE__ */ jsx2(IconMapPin, { size: 18 }),
635
+ icon: /* @__PURE__ */ jsx5(MapPin, { size: 18 }),
295
636
  label: "Location",
296
637
  value: info.location ?? DEFAULT_LOCATION,
297
638
  color: "teal"
298
639
  },
299
640
  {
300
- icon: /* @__PURE__ */ jsx2(IconTag, { size: 18 }),
641
+ icon: /* @__PURE__ */ jsx5(Tag, { size: 18 }),
301
642
  label: "Version Type",
302
643
  value: versionType,
303
644
  color: "red"
304
645
  },
305
646
  {
306
- icon: /* @__PURE__ */ jsx2(IconCalendar, { size: 18 }),
647
+ icon: /* @__PURE__ */ jsx5(Calendar, { size: 18 }),
307
648
  label: "Build Time",
308
649
  value: info.buildTime !== void 0 ? formatAbsoluteAndRelativeTime(info.buildTime) : UNKNOWN,
309
650
  color: "green"
310
651
  },
311
652
  ...info.metadata ?? []
312
653
  ];
313
- return /* @__PURE__ */ jsxs2(Stack2, { gap: "lg", children: [
314
- /* @__PURE__ */ jsxs2(Stack2, { gap: "xs", align: "center", children: [
654
+ return /* @__PURE__ */ jsxs4(Stack3, { gap: "lg", children: [
655
+ /* @__PURE__ */ jsxs4(Stack3, { gap: "xs", align: "center", children: [
315
656
  info.logo,
316
- /* @__PURE__ */ jsxs2(Stack2, { gap: 2, align: "center", children: [
317
- /* @__PURE__ */ jsx2(Title, { order: 2, children: info.name }),
318
- info.tagline && /* @__PURE__ */ jsx2(Text2, { size: "sm", c: "dimmed", w: 600, ta: "center", children: info.tagline })
657
+ /* @__PURE__ */ jsxs4(Stack3, { gap: 2, align: "center", children: [
658
+ /* @__PURE__ */ jsx5(Title2, { order: 2, children: info.name }),
659
+ info.tagline && /* @__PURE__ */ jsx5(Text3, { size: "sm", c: "dimmed", w: 600, ta: "center", children: info.tagline })
319
660
  ] }),
320
- /* @__PURE__ */ jsxs2(Group2, { gap: "xs", children: [
321
- info.version && /* @__PURE__ */ jsxs2(
661
+ /* @__PURE__ */ jsxs4(Group3, { gap: "xs", children: [
662
+ info.version && /* @__PURE__ */ jsxs4(
322
663
  Badge,
323
664
  {
324
665
  size: "lg",
@@ -333,7 +674,7 @@ function InformationContent({ info }) {
333
674
  ]
334
675
  }
335
676
  ),
336
- /* @__PURE__ */ jsxs2(
677
+ /* @__PURE__ */ jsxs4(
337
678
  Badge,
338
679
  {
339
680
  size: "lg",
@@ -350,39 +691,39 @@ function InformationContent({ info }) {
350
691
  )
351
692
  ] })
352
693
  ] }),
353
- /* @__PURE__ */ jsx2(Divider, { label: "System Information", labelPosition: "center" }),
354
- /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
355
- cards.map((card) => /* @__PURE__ */ jsx2(
694
+ /* @__PURE__ */ jsx5(Divider2, { label: "System Information", labelPosition: "center" }),
695
+ /* @__PURE__ */ jsxs4(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
696
+ cards.map((card) => /* @__PURE__ */ jsx5(
356
697
  InfoCard,
357
698
  {
358
- icon: card.icon ?? /* @__PURE__ */ jsx2(IconAward, { size: 18 }),
699
+ icon: card.icon ?? /* @__PURE__ */ jsx5(Award, { size: 18 }),
359
700
  label: card.label,
360
701
  value: card.value,
361
702
  color: card.color ?? "gray"
362
703
  },
363
704
  card.label
364
705
  )),
365
- /* @__PURE__ */ jsx2(
706
+ /* @__PURE__ */ jsx5(
366
707
  Paper,
367
708
  {
368
709
  withBorder: true,
369
710
  p: "sm",
370
711
  radius: "18px",
371
712
  bg: colorScheme === "dark" ? "gray.9" : "gray.0",
372
- children: /* @__PURE__ */ jsxs2(Group2, { justify: "space-between", wrap: "nowrap", children: [
373
- /* @__PURE__ */ jsxs2(Group2, { gap: "sm", flex: 1, children: [
374
- /* @__PURE__ */ jsx2(ThemeIcon, { variant: "light", color: "yellow", size: "lg", radius: "14px", children: /* @__PURE__ */ jsx2(IconHash, { size: 18 }) }),
375
- /* @__PURE__ */ jsxs2(Stack2, { gap: 2, children: [
376
- /* @__PURE__ */ jsx2(Text2, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Build ID" }),
377
- /* @__PURE__ */ jsx2(Text2, { size: "sm", fw: 500, truncate: true, children: info.buildId ?? UNKNOWN })
713
+ children: /* @__PURE__ */ jsxs4(Group3, { justify: "space-between", wrap: "nowrap", children: [
714
+ /* @__PURE__ */ jsxs4(Group3, { gap: "sm", flex: 1, children: [
715
+ /* @__PURE__ */ jsx5(ThemeIcon2, { variant: "light", color: "yellow", size: "lg", radius: "14px", children: /* @__PURE__ */ jsx5(Hash, { size: 18 }) }),
716
+ /* @__PURE__ */ jsxs4(Stack3, { gap: 2, children: [
717
+ /* @__PURE__ */ jsx5(Text3, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Build ID" }),
718
+ /* @__PURE__ */ jsx5(Text3, { size: "sm", fw: 500, truncate: true, children: info.buildId ?? UNKNOWN })
378
719
  ] })
379
720
  ] }),
380
- info.buildId && /* @__PURE__ */ jsx2(CopyBuildIdButton, { buildId: info.buildId })
721
+ info.buildId && /* @__PURE__ */ jsx5(CopyBuildIdButton, { buildId: info.buildId })
381
722
  ] })
382
723
  }
383
724
  )
384
725
  ] }),
385
- /* @__PURE__ */ jsx2(
726
+ /* @__PURE__ */ jsx5(
386
727
  Footer,
387
728
  {
388
729
  copyright: info.copyright,
@@ -399,7 +740,7 @@ function InformationModal({
399
740
  info,
400
741
  title = DEFAULT_TITLE
401
742
  }) {
402
- return /* @__PURE__ */ jsx2(
743
+ return /* @__PURE__ */ jsx5(
403
744
  Modal,
404
745
  {
405
746
  opened,
@@ -419,12 +760,12 @@ function InformationModal({
419
760
  }
420
761
  },
421
762
  title,
422
- children: /* @__PURE__ */ jsx2(InformationContent, { info })
763
+ children: /* @__PURE__ */ jsx5(InformationContent, { info })
423
764
  }
424
765
  );
425
766
  }
426
767
  function CopyBuildIdButton({ buildId }) {
427
- const [copied, setCopied] = useState(false);
768
+ const [copied, setCopied] = useState2(false);
428
769
  const handleCopy = async () => {
429
770
  const success = await copyToClipboard(buildId);
430
771
  if (success) {
@@ -432,13 +773,13 @@ function CopyBuildIdButton({ buildId }) {
432
773
  setTimeout(() => setCopied(false), 2e3);
433
774
  }
434
775
  };
435
- return /* @__PURE__ */ jsx2(Tooltip2, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx2(
436
- ActionIcon2,
776
+ return /* @__PURE__ */ jsx5(Tooltip3, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx5(
777
+ ActionIcon3,
437
778
  {
438
779
  variant: "subtle",
439
780
  color: copied ? "teal" : "gray",
440
781
  onClick: handleCopy,
441
- children: copied ? /* @__PURE__ */ jsx2(IconCheck, { size: 16 }) : /* @__PURE__ */ jsx2(IconCopy, { size: 16 })
782
+ children: copied ? /* @__PURE__ */ jsx5(Check, { size: 16 }) : /* @__PURE__ */ jsx5(Copy, { size: 16 })
442
783
  }
443
784
  ) });
444
785
  }
@@ -449,14 +790,115 @@ function InfoCard({
449
790
  color
450
791
  }) {
451
792
  const colorScheme = useComputedColorScheme();
452
- return /* @__PURE__ */ jsx2(Paper, { withBorder: true, p: "md", radius: "18px", h: "100%", bg: colorScheme === "dark" ? "gray.9" : "gray.0", children: /* @__PURE__ */ jsxs2(Group2, { gap: "sm", align: "flex-start", children: [
453
- /* @__PURE__ */ jsx2(ThemeIcon, { color, variant: "light", size: "lg", radius: "14px", children: icon }),
454
- /* @__PURE__ */ jsxs2(Stack2, { gap: 2, flex: 1, children: [
455
- /* @__PURE__ */ jsx2(Text2, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: label }),
456
- /* @__PURE__ */ jsx2(Text2, { size: "sm", fw: 500, lineClamp: 1, children: value })
793
+ return /* @__PURE__ */ jsx5(Paper, { withBorder: true, p: "md", radius: "18px", h: "100%", bg: colorScheme === "dark" ? "gray.9" : "gray.0", children: /* @__PURE__ */ jsxs4(Group3, { gap: "sm", align: "flex-start", children: [
794
+ /* @__PURE__ */ jsx5(ThemeIcon2, { color, variant: "light", size: "lg", radius: "14px", children: icon }),
795
+ /* @__PURE__ */ jsxs4(Stack3, { gap: 2, flex: 1, children: [
796
+ /* @__PURE__ */ jsx5(Text3, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: label }),
797
+ /* @__PURE__ */ jsx5(Text3, { size: "sm", fw: 500, lineClamp: 1, children: value })
457
798
  ] })
458
799
  ] }) });
459
800
  }
801
+
802
+ // src/pages/NotFound.tsx
803
+ import {
804
+ Box as Box2,
805
+ Button,
806
+ Container,
807
+ Group as Group4,
808
+ Paper as Paper2,
809
+ Text as Text4,
810
+ Title as Title3,
811
+ Code,
812
+ Badge as Badge2
813
+ } from "@mantine/core";
814
+ import {
815
+ IconArrowLeft,
816
+ IconHome,
817
+ IconSearch,
818
+ IconFileOff
819
+ } from "@tabler/icons-react";
820
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
821
+ function NotFound({
822
+ path,
823
+ onSearchClick,
824
+ onHomeClick
825
+ }) {
826
+ const currentPath = path || (typeof window !== "undefined" ? window.location.pathname : "/404");
827
+ return /* @__PURE__ */ jsx6(
828
+ Box2,
829
+ {
830
+ style: {
831
+ minHeight: "100vh",
832
+ display: "flex",
833
+ alignItems: "center",
834
+ background: "radial-gradient(circle at top left, rgba(34, 139, 230, 0.15), transparent 45%), radial-gradient(circle at bottom right, rgba(34, 211, 238, 0.12), transparent 45%), var(--mantine-color-body)"
835
+ },
836
+ children: /* @__PURE__ */ jsx6(Container, { size: "xl", py: 80, children: /* @__PURE__ */ jsxs5(Paper2, { radius: "xl", p: 48, withBorder: true, style: { borderColor: "var(--mantine-color-default-border)", textAlign: "center" }, children: [
837
+ /* @__PURE__ */ jsx6(
838
+ Box2,
839
+ {
840
+ mx: "auto",
841
+ mb: "xl",
842
+ w: 96,
843
+ h: 96,
844
+ style: {
845
+ borderRadius: "50%",
846
+ display: "flex",
847
+ alignItems: "center",
848
+ justifyContent: "center",
849
+ background: "linear-gradient(135deg, var(--mantine-color-blue-6), var(--mantine-color-cyan-6))",
850
+ boxShadow: "0 12px 32px rgba(34, 139, 230, 0.35)"
851
+ },
852
+ children: /* @__PURE__ */ jsx6(IconFileOff, { size: 44, stroke: 1.5, color: "white" })
853
+ }
854
+ ),
855
+ /* @__PURE__ */ jsx6(
856
+ Text4,
857
+ {
858
+ fz: { base: 72, sm: 100 },
859
+ fw: 900,
860
+ lh: 1,
861
+ variant: "gradient",
862
+ gradient: { from: "blue.7", to: "cyan.5", deg: 45 },
863
+ children: "404"
864
+ }
865
+ ),
866
+ /* @__PURE__ */ jsx6(Badge2, { variant: "light", color: "blue", size: "lg", mb: "xs", children: "NOT FOUND" }),
867
+ /* @__PURE__ */ jsx6(Title3, { order: 2, mt: "sm", mb: "xs", children: "Trang kh\xF4ng t\u1ED3n t\u1EA1i" }),
868
+ /* @__PURE__ */ jsx6(Text4, { c: "dimmed", size: "sm", maw: 440, mx: "auto", children: "Trang ho\u1EB7c \u0111\u01B0\u1EDDng d\u1EABn b\u1EA1n truy c\u1EADp kh\xF4ng t\u1ED3n t\u1EA1i ho\u1EB7c \u0111\xE3 \u0111\u01B0\u1EE3c di chuy\u1EC3n. Vui l\xF2ng ki\u1EC3m tra l\u1EA1i \u0111\u01B0\u1EDDng d\u1EABn ho\u1EB7c quay v\u1EC1 trang ch\u1EE7." }),
869
+ /* @__PURE__ */ jsx6(Box2, { mt: "lg", children: /* @__PURE__ */ jsx6(Code, { color: "blue", variant: "light", px: "md", py: 6, children: currentPath }) }),
870
+ /* @__PURE__ */ jsxs5(Group4, { gap: "sm", mt: "xl", justify: "center", children: [
871
+ /* @__PURE__ */ jsx6(
872
+ Button,
873
+ {
874
+ variant: "light",
875
+ leftSection: /* @__PURE__ */ jsx6(IconArrowLeft, { size: 16 }),
876
+ onClick: () => window.history.back(),
877
+ children: "Quay l\u1EA1i"
878
+ }
879
+ ),
880
+ onSearchClick && /* @__PURE__ */ jsx6(
881
+ Button,
882
+ {
883
+ variant: "outline",
884
+ leftSection: /* @__PURE__ */ jsx6(IconSearch, { size: 16 }),
885
+ onClick: onSearchClick,
886
+ children: "T\xECm Docs"
887
+ }
888
+ ),
889
+ /* @__PURE__ */ jsx6(
890
+ Button,
891
+ {
892
+ leftSection: /* @__PURE__ */ jsx6(IconHome, { size: 16 }),
893
+ onClick: onHomeClick || (() => window.location.href = "/"),
894
+ children: "Trang ch\u1EE7"
895
+ }
896
+ )
897
+ ] })
898
+ ] }) })
899
+ }
900
+ );
901
+ }
460
902
  export {
461
903
  DEFAULT_AUTHOR,
462
904
  DEFAULT_AUTHOR_HREF,
@@ -467,6 +909,10 @@ export {
467
909
  Footer,
468
910
  InformationContent,
469
911
  InformationModal,
912
+ NotFound,
913
+ ThemeMenu,
914
+ ThemeProvider,
915
+ ThemeSetting,
470
916
  UNKNOWN,
471
917
  calculateRemainingTime,
472
918
  copyToClipboard,
@@ -475,6 +921,8 @@ export {
475
921
  formatHumanDate,
476
922
  formatRelativeTime,
477
923
  normalizeDate,
478
- pad
924
+ pad,
925
+ resolveColorScheme,
926
+ useTheme
479
927
  };
480
928
  //# sourceMappingURL=index.js.map