create-tauri-ui 1.0.6 → 1.0.7

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/README.md CHANGED
@@ -56,21 +56,25 @@ Options:
56
56
  ## Examples
57
57
 
58
58
  **Vite app with all defaults:**
59
+
59
60
  ```bash
60
61
  npm create tauri-ui@latest my-app -- --template vite --yes
61
62
  ```
62
63
 
63
64
  **Next.js app, no dashboard, no workflow:**
65
+
64
66
  ```bash
65
67
  npm create tauri-ui@latest my-app -- --template next --yes --no-starter --no-workflow
66
68
  ```
67
69
 
68
70
  **Astro app with a custom bundle identifier:**
71
+
69
72
  ```bash
70
73
  bun create tauri-ui my-app --template astro --identifier com.example.astroapp --yes
71
74
  ```
72
75
 
73
76
  **Vite app with size-optimized release binary:**
77
+
74
78
  ```bash
75
79
  bun create tauri-ui my-app --template vite --size-optimize --yes
76
80
  ```
@@ -93,21 +97,21 @@ Every generated project gets:
93
97
 
94
98
  **Optional** (prompted during scaffolding, or passed as flags):
95
99
 
96
- | Battery | Flag | Notes |
97
- |---|---|---|
98
- | Starter dashboard | `--starter` / `--no-starter` | based on `dashboard-01` |
99
- | Rust invoke example | `--invoke-example` / `--no-invoke-example` | |
100
- | Size optimization | `--size-optimize` / `--no-size-optimize` | ~65% smaller release binary (9 MB → 3.1 MB) |
101
- | GitHub release workflow | `--workflow` / `--no-workflow` | |
100
+ | Battery | Flag | Notes |
101
+ | ----------------------- | ------------------------------------------ | ------------------------------------------- |
102
+ | Starter dashboard | `--starter` / `--no-starter` | based on `dashboard-01` |
103
+ | Rust invoke example | `--invoke-example` / `--no-invoke-example` | |
104
+ | Size optimization | `--size-optimize` / `--no-size-optimize` | ~65% smaller release binary (9 MB → 3.1 MB) |
105
+ | GitHub release workflow | `--workflow` / `--no-workflow` | |
102
106
 
103
107
  ## Template status
104
108
 
105
- | Template | Status |
106
- |---|---|
107
- | `vite` | smoke-tested end to end |
108
- | `next` | stable |
109
- | `react-router` | stable |
110
- | `astro` | stable |
109
+ | Template | Status |
110
+ | ------------------ | ----------------------------------------------- |
111
+ | `vite` | smoke-tested end to end |
112
+ | `next` | stable |
113
+ | `react-router` | stable |
114
+ | `astro` | stable |
111
115
  | `start` (TanStack) | experimental — validate carefully after changes |
112
116
 
113
117
  Scaffolding into `.` (current directory) is not supported yet.
@@ -145,12 +149,14 @@ bun run --cwd packages/create-tauri-ui start -- --help
145
149
  ## Testing
146
150
 
147
151
  **Type check + build:**
152
+
148
153
  ```bash
149
154
  bun run --cwd packages/create-tauri-ui check-types
150
155
  bun run --cwd packages/create-tauri-ui build
151
156
  ```
152
157
 
153
158
  **Single template smoke test:**
159
+
154
160
  ```bash
155
161
  rm -rf /tmp/ctui-vite
156
162
  node packages/create-tauri-ui/index.js /tmp/ctui-vite --template vite --yes --no-workflow
@@ -158,6 +164,7 @@ cd /tmp/ctui-vite && bun install && bun run build
158
164
  ```
159
165
 
160
166
  **All templates:**
167
+
161
168
  ```bash
162
169
  for template in vite next start react-router astro; do
163
170
  dir="/tmp/create-tauri-ui-$template"
@@ -36,6 +36,12 @@ import {
36
36
  DropdownMenuTrigger,
37
37
  } from "__ALIAS_PREFIX__components/ui/dropdown-menu"
38
38
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "__ALIAS_PREFIX__components/ui/tabs"
39
+ import {
40
+ Tooltip,
41
+ TooltipContent,
42
+ TooltipProvider,
43
+ TooltipTrigger,
44
+ } from "__ALIAS_PREFIX__components/ui/tooltip"
39
45
  import {
40
46
  DEBUG_EVENT_NAME,
41
47
  type DebugEvent,
@@ -86,6 +92,90 @@ type PathDiagnostics = {
86
92
  resourceDir: string | null
87
93
  }
88
94
 
95
+ type LocaleDefaultsDiagnostics = {
96
+ localeChain: string[]
97
+ navigatorLanguage: string
98
+ intlLocale: string
99
+ region: string | null
100
+ timeZone: string
101
+ calendar: string
102
+ numberingSystem: string
103
+ hourCycle: string
104
+ hour12: boolean | null
105
+ firstDayOfWeek: string
106
+ documentLang: string
107
+ documentDir: string
108
+ }
109
+
110
+ type AccessibilityDefaultsDiagnostics = {
111
+ colorScheme: "dark" | "light"
112
+ reducedMotion: "reduce" | "no-preference"
113
+ contrast: "more" | "less" | "custom" | "no-preference"
114
+ forcedColors: "active" | "none"
115
+ invertedColors: "inverted" | "none"
116
+ reducedTransparency: "reduce" | "no-preference"
117
+ }
118
+
119
+ type InputDefaultsDiagnostics = {
120
+ platform: string
121
+ userAgent: string
122
+ maxTouchPoints: number
123
+ touchCapable: boolean
124
+ pointer: "fine" | "coarse" | "none"
125
+ anyPointer: "fine" | "coarse" | "none"
126
+ hover: boolean
127
+ anyHover: boolean
128
+ hardwareConcurrency: number | null
129
+ deviceMemory: number | null
130
+ }
131
+
132
+ type DisplayDefaultsDiagnostics = {
133
+ viewport: string
134
+ screen: string
135
+ availableScreen: string
136
+ devicePixelRatio: number
137
+ orientation: string
138
+ colorGamut: "rec2020" | "p3" | "srgb" | "unknown"
139
+ dynamicRange: "high" | "standard"
140
+ monochrome: boolean
141
+ }
142
+
143
+ type NetworkDefaultsDiagnostics = {
144
+ online: boolean
145
+ effectiveType: string
146
+ saveData: boolean | null
147
+ downlink: number | null
148
+ rtt: number | null
149
+ }
150
+
151
+ type NetworkInformationLike = EventTarget & {
152
+ effectiveType?: string
153
+ saveData?: boolean
154
+ downlink?: number
155
+ rtt?: number
156
+ }
157
+
158
+ type ExtendedNavigator = Navigator & {
159
+ connection?: NetworkInformationLike
160
+ mozConnection?: NetworkInformationLike
161
+ webkitConnection?: NetworkInformationLike
162
+ deviceMemory?: number
163
+ userAgentData?: {
164
+ platform?: string
165
+ }
166
+ }
167
+
168
+ type SourceOrigin = "web" | "tauri"
169
+
170
+ type GridEntryMeta = {
171
+ code: string
172
+ origin: SourceOrigin
173
+ }
174
+
175
+ type GridEntry =
176
+ | [label: string, value: React.ReactNode]
177
+ | [label: string, value: React.ReactNode, meta: GridEntryMeta]
178
+
89
179
  type ErrorLog = {
90
180
  id: string
91
181
  timestamp: string
@@ -242,6 +332,81 @@ function getLogLevelClasses(level: string) {
242
332
  }
243
333
  }
244
334
 
335
+ function createMediaQueryList(query: string) {
336
+ try {
337
+ return window.matchMedia(query)
338
+ } catch {
339
+ return null
340
+ }
341
+ }
342
+
343
+ function matchesMediaQuery(query: string) {
344
+ return createMediaQueryList(query)?.matches ?? false
345
+ }
346
+
347
+ function getLocaleChain() {
348
+ const candidates = [...navigator.languages]
349
+
350
+ if (navigator.language) {
351
+ candidates.push(navigator.language)
352
+ }
353
+
354
+ return [...new Set(candidates.filter(Boolean))]
355
+ }
356
+
357
+ function getFirstDayOfWeek(locale: string) {
358
+ try {
359
+ const intlLocale = new Intl.Locale(locale) as Intl.Locale & {
360
+ weekInfo?: {
361
+ firstDay: number
362
+ }
363
+ }
364
+
365
+ return intlLocale.weekInfo?.firstDay?.toString() ?? "Unavailable"
366
+ } catch {
367
+ return "Unavailable"
368
+ }
369
+ }
370
+
371
+ function getContrastPreference() {
372
+ if (matchesMediaQuery("(prefers-contrast: more)")) return "more"
373
+ if (matchesMediaQuery("(prefers-contrast: less)")) return "less"
374
+ if (matchesMediaQuery("(prefers-contrast: custom)")) return "custom"
375
+ return "no-preference"
376
+ }
377
+
378
+ function getColorGamut() {
379
+ if (matchesMediaQuery("(color-gamut: rec2020)")) return "rec2020"
380
+ if (matchesMediaQuery("(color-gamut: p3)")) return "p3"
381
+ if (matchesMediaQuery("(color-gamut: srgb)")) return "srgb"
382
+ return "unknown"
383
+ }
384
+
385
+ function getPointerPrecision(query: "(pointer: fine)" | "(pointer: coarse)") {
386
+ if (matchesMediaQuery(query)) {
387
+ return query.includes("fine") ? "fine" : "coarse"
388
+ }
389
+
390
+ return "none"
391
+ }
392
+
393
+ function getAnyPointerPrecision() {
394
+ if (matchesMediaQuery("(any-pointer: fine)")) return "fine"
395
+ if (matchesMediaQuery("(any-pointer: coarse)")) return "coarse"
396
+ return "none"
397
+ }
398
+
399
+ function getConnectionInfo() {
400
+ const extendedNavigator = navigator as ExtendedNavigator
401
+
402
+ return (
403
+ extendedNavigator.connection ??
404
+ extendedNavigator.mozConnection ??
405
+ extendedNavigator.webkitConnection ??
406
+ null
407
+ )
408
+ }
409
+
245
410
  function pushRecent<T>(items: T[], nextItem: T) {
246
411
  return [nextItem, ...items].slice(0, MAX_LOG_ITEMS)
247
412
  }
@@ -288,25 +453,119 @@ function DebugSection({
288
453
  )
289
454
  }
290
455
 
456
+ const DebugPanelThemeContext = React.createContext<React.CSSProperties | undefined>(
457
+ undefined
458
+ )
459
+
460
+ function SourceOriginChip({ origin }: { origin: SourceOrigin }) {
461
+ return (
462
+ <span
463
+ className={cn(
464
+ "inline-flex h-3.5 items-center rounded-sm border px-1 text-[9px] font-semibold uppercase tracking-wide",
465
+ origin === "tauri"
466
+ ? "border-amber-500/30 bg-amber-500/15 text-amber-600 dark:text-amber-400"
467
+ : "border-sky-500/30 bg-sky-500/15 text-sky-600 dark:text-sky-400"
468
+ )}
469
+ >
470
+ {origin} API
471
+ </span>
472
+ )
473
+ }
474
+
475
+ function GridLabel({ label, meta }: { label: string; meta?: GridEntryMeta }) {
476
+ const themeStyle = React.useContext(DebugPanelThemeContext)
477
+ const [copied, setCopied] = React.useState(false)
478
+
479
+ React.useEffect(() => {
480
+ if (!copied) {
481
+ return
482
+ }
483
+
484
+ const timeoutId = window.setTimeout(() => {
485
+ setCopied(false)
486
+ }, 1200)
487
+
488
+ return () => {
489
+ window.clearTimeout(timeoutId)
490
+ }
491
+ }, [copied])
492
+
493
+ if (!meta) {
494
+ return <span>{label}</span>
495
+ }
496
+
497
+ const handleCopySnippet = async () => {
498
+ try {
499
+ await navigator.clipboard.writeText(meta.code)
500
+ setCopied(true)
501
+ } catch {
502
+ setCopied(false)
503
+ }
504
+ }
505
+
506
+ return (
507
+ <Tooltip>
508
+ <TooltipTrigger asChild>
509
+ <span className="cursor-help underline decoration-dotted decoration-muted-foreground/40 underline-offset-4">
510
+ {label}
511
+ </span>
512
+ </TooltipTrigger>
513
+ <TooltipContent
514
+ side="right"
515
+ align="start"
516
+ style={themeStyle}
517
+ className="max-w-[320px] border border-border/40 bg-popover p-2 text-popover-foreground shadow-md [&>:last-child]:hidden"
518
+ >
519
+ <div className="flex items-center gap-2">
520
+ <SourceOriginChip origin={meta.origin} />
521
+ <button
522
+ type="button"
523
+ onClick={handleCopySnippet}
524
+ title={copied ? "Copied!" : "Click to copy"}
525
+ className="group relative flex-1 overflow-hidden rounded-sm bg-muted px-1.5 py-1 text-left font-mono text-[10px] leading-4 text-foreground transition-colors hover:bg-muted/70 active:bg-muted/50"
526
+ >
527
+ <pre className="whitespace-pre-wrap break-all">{meta.code}</pre>
528
+ <span
529
+ className={cn(
530
+ "pointer-events-none absolute inset-0 flex items-center justify-center rounded-sm bg-popover/95 text-[10px] font-semibold text-foreground transition-opacity",
531
+ copied ? "opacity-100" : "opacity-0"
532
+ )}
533
+ >
534
+ Copied!
535
+ </span>
536
+ </button>
537
+ </div>
538
+ </TooltipContent>
539
+ </Tooltip>
540
+ )
541
+ }
542
+
291
543
  function KeyValueGrid({
292
544
  entries,
293
545
  }: {
294
- entries: Array<[label: string, value: React.ReactNode]>
546
+ entries: ReadonlyArray<GridEntry>
295
547
  }) {
296
548
  return (
297
549
  <dl className="grid text-[11px] leading-5">
298
- {entries.map(([label, value], index) => (
299
- <div
300
- key={label}
301
- className={cn(
302
- "grid grid-cols-[92px_minmax(0,1fr)] items-start gap-3 py-1",
303
- index > 0 && "border-t border-border/20"
304
- )}
305
- >
306
- <dt className="text-muted-foreground/90">{label}</dt>
307
- <dd className="font-mono break-all text-foreground">{value}</dd>
308
- </div>
309
- ))}
550
+ {entries.map((entry, index) => {
551
+ const [label, value] = entry
552
+ const meta = entry.length === 3 ? entry[2] : undefined
553
+
554
+ return (
555
+ <div
556
+ key={label}
557
+ className={cn(
558
+ "grid grid-cols-[92px_minmax(0,1fr)] items-start gap-3 py-1",
559
+ index > 0 && "border-t border-border/20"
560
+ )}
561
+ >
562
+ <dt className="text-muted-foreground/90">
563
+ <GridLabel label={label} meta={meta} />
564
+ </dt>
565
+ <dd className="font-mono break-all text-foreground">{value}</dd>
566
+ </div>
567
+ )
568
+ })}
310
569
  </dl>
311
570
  )
312
571
  }
@@ -372,6 +631,58 @@ export function DebugPanel() {
372
631
  appConfigDir: null,
373
632
  resourceDir: null,
374
633
  })
634
+ const [localeDefaults, setLocaleDefaults] = React.useState<LocaleDefaultsDiagnostics>({
635
+ localeChain: [],
636
+ navigatorLanguage: "",
637
+ intlLocale: "Unavailable",
638
+ region: null,
639
+ timeZone: "Unavailable",
640
+ calendar: "Unavailable",
641
+ numberingSystem: "Unavailable",
642
+ hourCycle: "Unavailable",
643
+ hour12: null,
644
+ firstDayOfWeek: "Unavailable",
645
+ documentLang: "",
646
+ documentDir: "",
647
+ })
648
+ const [accessibilityDefaults, setAccessibilityDefaults] =
649
+ React.useState<AccessibilityDefaultsDiagnostics>({
650
+ colorScheme: "light",
651
+ reducedMotion: "no-preference",
652
+ contrast: "no-preference",
653
+ forcedColors: "none",
654
+ invertedColors: "none",
655
+ reducedTransparency: "no-preference",
656
+ })
657
+ const [inputDefaults, setInputDefaults] = React.useState<InputDefaultsDiagnostics>({
658
+ platform: "Unavailable",
659
+ userAgent: "",
660
+ maxTouchPoints: 0,
661
+ touchCapable: false,
662
+ pointer: "none",
663
+ anyPointer: "none",
664
+ hover: false,
665
+ anyHover: false,
666
+ hardwareConcurrency: null,
667
+ deviceMemory: null,
668
+ })
669
+ const [displayDefaults, setDisplayDefaults] = React.useState<DisplayDefaultsDiagnostics>({
670
+ viewport: "Unavailable",
671
+ screen: "Unavailable",
672
+ availableScreen: "Unavailable",
673
+ devicePixelRatio: 1,
674
+ orientation: "Unavailable",
675
+ colorGamut: "unknown",
676
+ dynamicRange: "standard",
677
+ monochrome: false,
678
+ })
679
+ const [networkDefaults, setNetworkDefaults] = React.useState<NetworkDefaultsDiagnostics>({
680
+ online: true,
681
+ effectiveType: "Unavailable",
682
+ saveData: null,
683
+ downlink: null,
684
+ rtt: null,
685
+ })
375
686
  const [externalLinks, setExternalLinks] = React.useState<string[]>([])
376
687
  const [invokeLogs, setInvokeLogs] = React.useState<DebugEvent[]>([])
377
688
  const [runtimeEvents, setRuntimeEvents] = React.useState<RuntimeEventDebugEvent[]>([])
@@ -524,6 +835,159 @@ export function DebugPanel() {
524
835
  void loadPaths()
525
836
  }, [tauriReady])
526
837
 
838
+ React.useEffect(() => {
839
+ function refreshSystemDefaults() {
840
+ const resolved = new Intl.DateTimeFormat().resolvedOptions()
841
+ const localeChain = getLocaleChain()
842
+ let region: string | null = null
843
+
844
+ try {
845
+ region = new Intl.Locale(resolved.locale).region ?? null
846
+ } catch {
847
+ region = null
848
+ }
849
+
850
+ setLocaleDefaults({
851
+ localeChain,
852
+ navigatorLanguage: navigator.language || "Unavailable",
853
+ intlLocale: resolved.locale,
854
+ region,
855
+ timeZone: resolved.timeZone ?? "Unavailable",
856
+ calendar: resolved.calendar ?? "Unavailable",
857
+ numberingSystem: resolved.numberingSystem ?? "Unavailable",
858
+ hourCycle: resolved.hourCycle ?? "Unavailable",
859
+ hour12: resolved.hour12 ?? null,
860
+ firstDayOfWeek: getFirstDayOfWeek(resolved.locale),
861
+ documentLang: document.documentElement.lang || "(unset)",
862
+ documentDir: document.documentElement.dir || "(unset)",
863
+ })
864
+
865
+ const extendedNavigator = navigator as ExtendedNavigator
866
+
867
+ setAccessibilityDefaults({
868
+ colorScheme: matchesMediaQuery(COLOR_SCHEME_QUERY) ? "dark" : "light",
869
+ reducedMotion: matchesMediaQuery("(prefers-reduced-motion: reduce)")
870
+ ? "reduce"
871
+ : "no-preference",
872
+ contrast: getContrastPreference(),
873
+ forcedColors: matchesMediaQuery("(forced-colors: active)") ? "active" : "none",
874
+ invertedColors: matchesMediaQuery("(inverted-colors: inverted)")
875
+ ? "inverted"
876
+ : "none",
877
+ reducedTransparency: matchesMediaQuery("(prefers-reduced-transparency: reduce)")
878
+ ? "reduce"
879
+ : "no-preference",
880
+ })
881
+
882
+ setInputDefaults({
883
+ platform:
884
+ extendedNavigator.userAgentData?.platform ??
885
+ navigator.platform ??
886
+ "Unavailable",
887
+ userAgent: navigator.userAgent || "Unavailable",
888
+ maxTouchPoints: navigator.maxTouchPoints ?? 0,
889
+ touchCapable:
890
+ navigator.maxTouchPoints > 0 ||
891
+ matchesMediaQuery("(pointer: coarse)") ||
892
+ "ontouchstart" in window,
893
+ pointer:
894
+ getPointerPrecision("(pointer: fine)") === "fine"
895
+ ? "fine"
896
+ : getPointerPrecision("(pointer: coarse)"),
897
+ anyPointer: getAnyPointerPrecision(),
898
+ hover: matchesMediaQuery("(hover: hover)"),
899
+ anyHover: matchesMediaQuery("(any-hover: hover)"),
900
+ hardwareConcurrency: navigator.hardwareConcurrency ?? null,
901
+ deviceMemory: extendedNavigator.deviceMemory ?? null,
902
+ })
903
+
904
+ setDisplayDefaults({
905
+ viewport: `${window.innerWidth} × ${window.innerHeight}`,
906
+ screen: `${window.screen.width} × ${window.screen.height}`,
907
+ availableScreen: `${window.screen.availWidth} × ${window.screen.availHeight}`,
908
+ devicePixelRatio: window.devicePixelRatio,
909
+ orientation:
910
+ window.screen.orientation?.type ??
911
+ (window.innerWidth >= window.innerHeight ? "landscape" : "portrait"),
912
+ colorGamut: getColorGamut(),
913
+ dynamicRange: matchesMediaQuery("(dynamic-range: high)") ? "high" : "standard",
914
+ monochrome: matchesMediaQuery("(monochrome)"),
915
+ })
916
+
917
+ const connection = getConnectionInfo()
918
+
919
+ setNetworkDefaults({
920
+ online: navigator.onLine,
921
+ effectiveType: connection?.effectiveType ?? "Unavailable",
922
+ saveData: connection?.saveData ?? null,
923
+ downlink: connection?.downlink ?? null,
924
+ rtt: connection?.rtt ?? null,
925
+ })
926
+ }
927
+
928
+ refreshSystemDefaults()
929
+
930
+ const mediaQueries = [
931
+ COLOR_SCHEME_QUERY,
932
+ "(prefers-reduced-motion: reduce)",
933
+ "(prefers-contrast: more)",
934
+ "(prefers-contrast: less)",
935
+ "(prefers-contrast: custom)",
936
+ "(forced-colors: active)",
937
+ "(inverted-colors: inverted)",
938
+ "(prefers-reduced-transparency: reduce)",
939
+ "(pointer: fine)",
940
+ "(pointer: coarse)",
941
+ "(any-pointer: fine)",
942
+ "(any-pointer: coarse)",
943
+ "(hover: hover)",
944
+ "(any-hover: hover)",
945
+ "(color-gamut: rec2020)",
946
+ "(color-gamut: p3)",
947
+ "(color-gamut: srgb)",
948
+ "(dynamic-range: high)",
949
+ "(monochrome)",
950
+ ]
951
+ .map((query) => createMediaQueryList(query))
952
+ .filter((query): query is MediaQueryList => query !== null)
953
+
954
+ const handleChange = () => {
955
+ refreshSystemDefaults()
956
+ }
957
+
958
+ for (const mediaQuery of mediaQueries) {
959
+ mediaQuery.addEventListener("change", handleChange)
960
+ }
961
+
962
+ window.addEventListener("resize", handleChange)
963
+ window.addEventListener("online", handleChange)
964
+ window.addEventListener("offline", handleChange)
965
+ window.screen.orientation?.addEventListener?.("change", handleChange)
966
+ document.documentElement.addEventListener(
967
+ "languagechange",
968
+ handleChange as EventListener
969
+ )
970
+
971
+ const connection = getConnectionInfo()
972
+ connection?.addEventListener?.("change", handleChange)
973
+
974
+ return () => {
975
+ for (const mediaQuery of mediaQueries) {
976
+ mediaQuery.removeEventListener("change", handleChange)
977
+ }
978
+
979
+ window.removeEventListener("resize", handleChange)
980
+ window.removeEventListener("online", handleChange)
981
+ window.removeEventListener("offline", handleChange)
982
+ window.screen.orientation?.removeEventListener?.("change", handleChange)
983
+ document.documentElement.removeEventListener(
984
+ "languagechange",
985
+ handleChange as EventListener
986
+ )
987
+ connection?.removeEventListener?.("change", handleChange)
988
+ }
989
+ }, [])
990
+
527
991
  React.useEffect(() => {
528
992
  if (!tauriReady || !locationState.pathname) {
529
993
  return
@@ -1059,19 +1523,20 @@ export function DebugPanel() {
1059
1523
  <div className={overviewLayoutClassName}>
1060
1524
  <DebugSection
1061
1525
  title="App"
1062
- description="Static application metadata from Tauri v2."
1526
+ description="Static application metadata and current route."
1063
1527
  >
1064
1528
  <KeyValueGrid
1065
1529
  entries={[
1066
- ["Route", locationState.pathname || "/"],
1067
- ["URL", locationState.href || "Unavailable"],
1068
- ["Search", locationState.search || "(none)"],
1069
- ["Hash", locationState.hash || "(none)"],
1070
- ["Bridge", tauriReady ? "Tauri" : "Web"],
1071
- ["Name", appInfo.name ?? "Unavailable"],
1072
- ["Version", appInfo.version ?? "Unavailable"],
1073
- ["Identifier", appInfo.identifier ?? "Unavailable"],
1074
- ["Tauri", appInfo.tauriVersion ?? "Unavailable"],
1530
+ ["Route", locationState.pathname || "/", { code: "window.location.pathname", origin: "web" }],
1531
+ ["URL", locationState.href || "Unavailable", { code: "window.location.href", origin: "web" }],
1532
+ ["Search", locationState.search || "(none)", { code: "window.location.search", origin: "web" }],
1533
+ ["Hash", locationState.hash || "(none)", { code: "window.location.hash", origin: "web" }],
1534
+ ["Bridge", tauriReady ? "Tauri" : "Web", { code: "isTauri()", origin: "tauri" }],
1535
+ ["Name", appInfo.name ?? "Unavailable", { code: "await getName()", origin: "tauri" }],
1536
+ ["Version", appInfo.version ?? "Unavailable", { code: "await getVersion()", origin: "tauri" }],
1537
+ ["Identifier", appInfo.identifier ?? "Unavailable", { code: "await getIdentifier()", origin: "tauri" }],
1538
+ ["Tauri", appInfo.tauriVersion ?? "Unavailable", { code: "await getTauriVersion()", origin: "tauri" }],
1539
+ ["Webview", windowInfo.label, { code: "getCurrentWebviewWindow().label", origin: "tauri" }],
1075
1540
  ["Shortcut", "Cmd/Ctrl + D"],
1076
1541
  ]}
1077
1542
  />
@@ -1083,57 +1548,68 @@ export function DebugPanel() {
1083
1548
  >
1084
1549
  <KeyValueGrid
1085
1550
  entries={[
1086
- ["Label", windowInfo.label],
1087
- ["Title", windowInfo.title ?? "Unavailable"],
1551
+ ["Label", windowInfo.label, { code: "getCurrentWindow().label", origin: "tauri" }],
1552
+ ["Title", windowInfo.title ?? "Unavailable", { code: "await getCurrentWindow().title()", origin: "tauri" }],
1088
1553
  [
1089
1554
  "Viewport",
1090
1555
  windowInfo.viewportSize
1091
1556
  ? `${windowInfo.viewportSize.width} × ${windowInfo.viewportSize.height}`
1092
1557
  : "Unavailable",
1558
+ { code: "window.innerWidth × window.innerHeight", origin: "web" },
1093
1559
  ],
1094
1560
  [
1095
1561
  "Tauri inner",
1096
1562
  windowInfo.tauriInnerSize
1097
1563
  ? `${windowInfo.tauriInnerSize.width} × ${windowInfo.tauriInnerSize.height}`
1098
1564
  : "Unavailable",
1565
+ { code: "await getCurrentWindow().innerSize()", origin: "tauri" },
1099
1566
  ],
1100
1567
  [
1101
1568
  "Window outer",
1102
1569
  windowInfo.outerSize
1103
1570
  ? `${windowInfo.outerSize.width} × ${windowInfo.outerSize.height}`
1104
1571
  : "Unavailable",
1572
+ { code: "await getCurrentWindow().outerSize()", origin: "tauri" },
1105
1573
  ],
1106
1574
  [
1107
1575
  "Position",
1108
1576
  windowInfo.outerPosition
1109
1577
  ? `${windowInfo.outerPosition.x}, ${windowInfo.outerPosition.y}`
1110
1578
  : "Unavailable",
1579
+ { code: "await getCurrentWindow().outerPosition()", origin: "tauri" },
1111
1580
  ],
1112
- ["Scale", windowInfo.scaleFactor ?? "Unavailable"],
1113
- ["Focused", String(windowInfo.focused)],
1114
- ["Visible", String(windowInfo.visible)],
1115
- ["Maximized", String(windowInfo.maximized)],
1116
- ["Fullscreen", String(windowInfo.fullscreen)],
1117
- ["Decorated", String(windowInfo.decorated)],
1581
+ ["Scale", windowInfo.scaleFactor ?? "Unavailable", { code: "await getCurrentWindow().scaleFactor()", origin: "tauri" }],
1582
+ ["Focused", String(windowInfo.focused), { code: "await getCurrentWindow().isFocused()", origin: "tauri" }],
1583
+ ["Visible", String(windowInfo.visible), { code: "await getCurrentWindow().isVisible()", origin: "tauri" }],
1584
+ ["Maximized", String(windowInfo.maximized), { code: "await getCurrentWindow().isMaximized()", origin: "tauri" }],
1585
+ ["Fullscreen", String(windowInfo.fullscreen), { code: "await getCurrentWindow().isFullscreen()", origin: "tauri" }],
1586
+ ["Decorated", String(windowInfo.decorated), { code: "await getCurrentWindow().isDecorated()", origin: "tauri" }],
1118
1587
  [
1119
1588
  "Monitor",
1120
1589
  windowInfo.monitor
1121
1590
  ? `${windowInfo.monitor.name ?? "Unknown"} (${windowInfo.monitor.size.width} × ${windowInfo.monitor.size.height})`
1122
1591
  : "Unavailable",
1592
+ { code: "await currentMonitor()", origin: "tauri" },
1123
1593
  ],
1124
1594
  ]}
1125
1595
  />
1126
1596
  </DebugSection>
1127
1597
 
1128
1598
  <DebugSection
1129
- title="Theme"
1130
- description="Theme state from the root HTML element and system media query."
1599
+ title="Theme & A11y"
1600
+ description="Theme state plus accessibility preferences from media queries."
1131
1601
  >
1132
1602
  <KeyValueGrid
1133
1603
  entries={[
1134
- ["Current", themeInfo.current],
1135
- ["System", themeInfo.system],
1136
- ["HTML class", themeInfo.htmlClass || "(none)"],
1604
+ ["Current", themeInfo.current, { code: 'document.documentElement.classList.contains("dark")', origin: "web" }],
1605
+ ["System", themeInfo.system, { code: 'matchMedia("(prefers-color-scheme: dark)")', origin: "web" }],
1606
+ ["HTML class", themeInfo.htmlClass || "(none)", { code: "document.documentElement.className", origin: "web" }],
1607
+ ["Color scheme", accessibilityDefaults.colorScheme, { code: 'matchMedia("(prefers-color-scheme: dark)")', origin: "web" }],
1608
+ ["Reduced motion", accessibilityDefaults.reducedMotion, { code: 'matchMedia("(prefers-reduced-motion: reduce)")', origin: "web" }],
1609
+ ["Contrast", accessibilityDefaults.contrast, { code: 'matchMedia("(prefers-contrast: more | less | custom)")', origin: "web" }],
1610
+ ["Forced colors", accessibilityDefaults.forcedColors, { code: 'matchMedia("(forced-colors: active)")', origin: "web" }],
1611
+ ["Inverted colors", accessibilityDefaults.invertedColors, { code: 'matchMedia("(inverted-colors: inverted)")', origin: "web" }],
1612
+ ["Transparency", accessibilityDefaults.reducedTransparency, { code: 'matchMedia("(prefers-reduced-transparency: reduce)")', origin: "web" }],
1137
1613
  ]}
1138
1614
  />
1139
1615
  </DebugSection>
@@ -1346,16 +1822,18 @@ export function DebugPanel() {
1346
1822
  description="Resolved Tauri application directories."
1347
1823
  >
1348
1824
  <div className="space-y-1.5">
1349
- {[
1350
- ["App data", pathInfo.appDataDir],
1351
- ["App config", pathInfo.appConfigDir],
1352
- ["Resources", pathInfo.resourceDir],
1353
- ].map(([label, value]) => (
1825
+ {([
1826
+ ["App data", pathInfo.appDataDir, "await appDataDir()"],
1827
+ ["App config", pathInfo.appConfigDir, "await appConfigDir()"],
1828
+ ["Resources", pathInfo.resourceDir, "await resourceDir()"],
1829
+ ] as const).map(([label, value, code]) => (
1354
1830
  <div
1355
1831
  key={label}
1356
1832
  className="grid grid-cols-[92px_minmax(0,1fr)_auto] items-start gap-3 border-t border-border/20 py-1 text-[11px] first:border-t-0 first:pt-0"
1357
1833
  >
1358
- <div className="text-muted-foreground">{label}</div>
1834
+ <div className="text-muted-foreground">
1835
+ <GridLabel label={label} meta={{ code, origin: "tauri" }} />
1836
+ </div>
1359
1837
  <div className="font-mono break-all text-foreground">
1360
1838
  {value ?? "Unavailable"}
1361
1839
  </div>
@@ -1369,10 +1847,118 @@ export function DebugPanel() {
1369
1847
  </DebugSection>
1370
1848
 
1371
1849
  <DebugSection
1372
- title="Webview"
1373
- description="Current webview identity."
1850
+ title="Display"
1851
+ description="Viewport, screen, and media capability signals."
1852
+ >
1853
+ <KeyValueGrid
1854
+ entries={[
1855
+ ["Viewport", displayDefaults.viewport, { code: "window.innerWidth × window.innerHeight", origin: "web" }],
1856
+ ["Screen", displayDefaults.screen, { code: "window.screen.width × window.screen.height", origin: "web" }],
1857
+ ["Available", displayDefaults.availableScreen, { code: "window.screen.availWidth × window.screen.availHeight", origin: "web" }],
1858
+ ["DPR", displayDefaults.devicePixelRatio, { code: "window.devicePixelRatio", origin: "web" }],
1859
+ ["Orientation", displayDefaults.orientation, { code: "window.screen.orientation?.type", origin: "web" }],
1860
+ ["Color gamut", displayDefaults.colorGamut, { code: 'matchMedia("(color-gamut: rec2020 | p3 | srgb)")', origin: "web" }],
1861
+ ["Dynamic range", displayDefaults.dynamicRange, { code: 'matchMedia("(dynamic-range: high)")', origin: "web" }],
1862
+ ["Monochrome", String(displayDefaults.monochrome), { code: 'matchMedia("(monochrome)")', origin: "web" }],
1863
+ ]}
1864
+ />
1865
+ </DebugSection>
1866
+
1867
+ <DebugSection
1868
+ title="Locale"
1869
+ description="Host locale, timezone, calendar, and Intl formatting defaults."
1374
1870
  >
1375
- <KeyValueGrid entries={[["Current label", windowInfo.label]]} />
1871
+ <KeyValueGrid
1872
+ entries={[
1873
+ [
1874
+ "Locale chain",
1875
+ localeDefaults.localeChain.length
1876
+ ? localeDefaults.localeChain.join(", ")
1877
+ : "Unavailable",
1878
+ { code: "[...navigator.languages, navigator.language]", origin: "web" },
1879
+ ],
1880
+ ["Navigator", localeDefaults.navigatorLanguage, { code: "navigator.language", origin: "web" }],
1881
+ ["Intl locale", localeDefaults.intlLocale, { code: "new Intl.DateTimeFormat().resolvedOptions().locale", origin: "web" }],
1882
+ ["Region", localeDefaults.region ?? "Unavailable", { code: "new Intl.Locale(locale).region", origin: "web" }],
1883
+ ["Time zone", localeDefaults.timeZone, { code: "new Intl.DateTimeFormat().resolvedOptions().timeZone", origin: "web" }],
1884
+ ["Calendar", localeDefaults.calendar, { code: "new Intl.DateTimeFormat().resolvedOptions().calendar", origin: "web" }],
1885
+ ["Numbering", localeDefaults.numberingSystem, { code: "new Intl.DateTimeFormat().resolvedOptions().numberingSystem", origin: "web" }],
1886
+ ["Hour cycle", localeDefaults.hourCycle, { code: "new Intl.DateTimeFormat().resolvedOptions().hourCycle", origin: "web" }],
1887
+ [
1888
+ "12-hour",
1889
+ localeDefaults.hour12 === null
1890
+ ? "Unavailable"
1891
+ : String(localeDefaults.hour12),
1892
+ { code: "new Intl.DateTimeFormat().resolvedOptions().hour12", origin: "web" },
1893
+ ],
1894
+ ["First day", localeDefaults.firstDayOfWeek, { code: "new Intl.Locale(locale).weekInfo?.firstDay", origin: "web" }],
1895
+ ["HTML lang", localeDefaults.documentLang, { code: "document.documentElement.lang", origin: "web" }],
1896
+ ["HTML dir", localeDefaults.documentDir, { code: "document.documentElement.dir", origin: "web" }],
1897
+ ]}
1898
+ />
1899
+ </DebugSection>
1900
+
1901
+ <DebugSection
1902
+ title="Input"
1903
+ description="Pointer, touch, hover, and hardware defaults."
1904
+ >
1905
+ <KeyValueGrid
1906
+ entries={[
1907
+ ["Platform", inputDefaults.platform, { code: "navigator.userAgentData?.platform ?? navigator.platform", origin: "web" }],
1908
+ ["Pointer", inputDefaults.pointer, { code: 'matchMedia("(pointer: fine | coarse)")', origin: "web" }],
1909
+ ["Any pointer", inputDefaults.anyPointer, { code: 'matchMedia("(any-pointer: fine | coarse)")', origin: "web" }],
1910
+ ["Hover", String(inputDefaults.hover), { code: 'matchMedia("(hover: hover)")', origin: "web" }],
1911
+ ["Any hover", String(inputDefaults.anyHover), { code: 'matchMedia("(any-hover: hover)")', origin: "web" }],
1912
+ ["Touch points", inputDefaults.maxTouchPoints, { code: "navigator.maxTouchPoints", origin: "web" }],
1913
+ ["Touch capable", String(inputDefaults.touchCapable), { code: 'maxTouchPoints > 0 || matchMedia("(pointer: coarse)") || "ontouchstart" in window', origin: "web" }],
1914
+ [
1915
+ "CPU threads",
1916
+ inputDefaults.hardwareConcurrency ?? "Unavailable",
1917
+ { code: "navigator.hardwareConcurrency", origin: "web" },
1918
+ ],
1919
+ [
1920
+ "Device memory",
1921
+ inputDefaults.deviceMemory === null
1922
+ ? "Unavailable"
1923
+ : `${inputDefaults.deviceMemory} GB`,
1924
+ { code: "navigator.deviceMemory", origin: "web" },
1925
+ ],
1926
+ ["User agent", inputDefaults.userAgent, { code: "navigator.userAgent", origin: "web" }],
1927
+ ]}
1928
+ />
1929
+ </DebugSection>
1930
+
1931
+ <DebugSection
1932
+ title="Network"
1933
+ description="Online state and connection quality hints from the browser."
1934
+ >
1935
+ <KeyValueGrid
1936
+ entries={[
1937
+ ["Online", String(networkDefaults.online), { code: "navigator.onLine", origin: "web" }],
1938
+ ["Effective type", networkDefaults.effectiveType, { code: "navigator.connection?.effectiveType", origin: "web" }],
1939
+ [
1940
+ "Save-Data",
1941
+ networkDefaults.saveData === null
1942
+ ? "Unavailable"
1943
+ : String(networkDefaults.saveData),
1944
+ { code: "navigator.connection?.saveData", origin: "web" },
1945
+ ],
1946
+ [
1947
+ "Downlink",
1948
+ networkDefaults.downlink === null
1949
+ ? "Unavailable"
1950
+ : `${networkDefaults.downlink} Mbps`,
1951
+ { code: "navigator.connection?.downlink", origin: "web" },
1952
+ ],
1953
+ [
1954
+ "RTT",
1955
+ networkDefaults.rtt === null
1956
+ ? "Unavailable"
1957
+ : `${networkDefaults.rtt} ms`,
1958
+ { code: "navigator.connection?.rtt", origin: "web" },
1959
+ ],
1960
+ ]}
1961
+ />
1376
1962
  </DebugSection>
1377
1963
  </div>
1378
1964
  </TabsContent>
@@ -1426,19 +2012,21 @@ export function DebugPanel() {
1426
2012
  )
1427
2013
 
1428
2014
  return (
1429
- <>
1430
- {open ? (
1431
- <div
1432
- className={panelFrameClassName}
1433
- style={
1434
- panelSide === "bottom"
1435
- ? { ...debugPanelThemeStyle, height: `${BOTTOM_PANEL_HEIGHT}px` }
1436
- : { ...debugPanelThemeStyle, width: `${SIDE_PANEL_WIDTH}px` }
1437
- }
1438
- >
1439
- {panelContent}
1440
- </div>
1441
- ) : null}
1442
- </>
2015
+ <TooltipProvider delayDuration={200}>
2016
+ <DebugPanelThemeContext.Provider value={debugPanelThemeStyle}>
2017
+ {open ? (
2018
+ <div
2019
+ className={panelFrameClassName}
2020
+ style={
2021
+ panelSide === "bottom"
2022
+ ? { ...debugPanelThemeStyle, height: `${BOTTOM_PANEL_HEIGHT}px` }
2023
+ : { ...debugPanelThemeStyle, width: `${SIDE_PANEL_WIDTH}px` }
2024
+ }
2025
+ >
2026
+ {panelContent}
2027
+ </div>
2028
+ ) : null}
2029
+ </DebugPanelThemeContext.Provider>
2030
+ </TooltipProvider>
1443
2031
  )
1444
2032
  }
package/dist/index.mjs CHANGED
@@ -98,7 +98,7 @@ import { DashboardShell } from "@/components/dashboard-shell"
98
98
  ])
99
99
  .build(),
100
100
  )
101
- `;function Ze(){const e=a.dirname(R(import.meta.url)),t=[a.resolve(e,"../assets/debug-panel"),a.resolve(e,"../../assets/debug-panel")];return t.find(r=>s.existsSync(r))??t[0]}const Qe=Ze();function et(e){return e==="react-router"?"~/":"@/"}function oe(e,t){switch(t.template){case"next":return a.join(e,"components");case"vite":case"start":case"astro":return a.join(e,"src/components");case"react-router":return a.join(e,"app/components")}}function tt(e,t){switch(t.template){case"next":return a.join(e,"lib");case"vite":case"start":case"astro":return a.join(e,"src/lib");case"react-router":return a.join(e,"app/lib")}}function rt(e,t){return a.join(oe(e,t),"ui")}function y(e){switch(e.template){case"next":return"@/components/debug-panel";case"vite":return"./components/debug-panel.tsx";case"start":return"../components/debug-panel";case"react-router":return"./components/debug-panel";case"astro":return"@/components/debug-panel"}}async function nt(e,t){for(const r of["button","badge","dropdown-menu","tabs"]){const n=a.join(rt(e,t),`${r}.tsx`);s.existsSync(n)||await re(e,r)}}function F(e){return s.readFileSync(a.join(Qe,e),"utf-8")}function it(e,t){const r=a.join(oe(e,t),"debug-panel.tsx"),n=tt(e,t);s.mkdirSync(a.dirname(r),{recursive:!0}),s.mkdirSync(n,{recursive:!0}),s.writeFileSync(r,F("debug-panel.tsx.tmpl").split("__ALIAS_PREFIX__").join(et(t.template)),"utf-8"),s.writeFileSync(a.join(n,"debug-events.ts"),F("debug-events.ts.tmpl"),"utf-8"),s.writeFileSync(a.join(n,"tauri.ts"),F("tauri.ts.tmpl"),"utf-8")}function at(e){f(a.join(e,"package.json"),t=>(t.dependencies=t.dependencies||{},t.dependencies["@tauri-apps/plugin-log"]||(t.dependencies["@tauri-apps/plugin-log"]="^2"),t))}function se(e,t){if(e.includes(t))return e;const r=/\[dependencies\]\r?\n/;if(!r.test(e))throw new Error(`Could not find [dependencies] while inserting ${t}.`);return e.replace(r,`[dependencies]
101
+ `;function Ze(){const e=a.dirname(R(import.meta.url)),t=[a.resolve(e,"../assets/debug-panel"),a.resolve(e,"../../assets/debug-panel")];return t.find(r=>s.existsSync(r))??t[0]}const Qe=Ze();function et(e){return e==="react-router"?"~/":"@/"}function oe(e,t){switch(t.template){case"next":return a.join(e,"components");case"vite":case"start":case"astro":return a.join(e,"src/components");case"react-router":return a.join(e,"app/components")}}function tt(e,t){switch(t.template){case"next":return a.join(e,"lib");case"vite":case"start":case"astro":return a.join(e,"src/lib");case"react-router":return a.join(e,"app/lib")}}function rt(e,t){return a.join(oe(e,t),"ui")}function y(e){switch(e.template){case"next":return"@/components/debug-panel";case"vite":return"./components/debug-panel.tsx";case"start":return"../components/debug-panel";case"react-router":return"./components/debug-panel";case"astro":return"@/components/debug-panel"}}async function nt(e,t){for(const r of["button","badge","dropdown-menu","tabs","tooltip"]){const n=a.join(rt(e,t),`${r}.tsx`);s.existsSync(n)||await re(e,r)}}function F(e){return s.readFileSync(a.join(Qe,e),"utf-8")}function it(e,t){const r=a.join(oe(e,t),"debug-panel.tsx"),n=tt(e,t);s.mkdirSync(a.dirname(r),{recursive:!0}),s.mkdirSync(n,{recursive:!0}),s.writeFileSync(r,F("debug-panel.tsx.tmpl").split("__ALIAS_PREFIX__").join(et(t.template)),"utf-8"),s.writeFileSync(a.join(n,"debug-events.ts"),F("debug-events.ts.tmpl"),"utf-8"),s.writeFileSync(a.join(n,"tauri.ts"),F("tauri.ts.tmpl"),"utf-8")}function at(e){f(a.join(e,"package.json"),t=>(t.dependencies=t.dependencies||{},t.dependencies["@tauri-apps/plugin-log"]||(t.dependencies["@tauri-apps/plugin-log"]="^2"),t))}function se(e,t){if(e.includes(t))return e;const r=/\[dependencies\]\r?\n/;if(!r.test(e))throw new Error(`Could not find [dependencies] while inserting ${t}.`);return e.replace(r,`[dependencies]
102
102
  ${t}
103
103
  `)}function ot(e){const t=a.join(e,"src-tauri/Cargo.toml"),r=a.join(e,"src-tauri/src/lib.rs");c(t,n=>{let i=n;return i=se(i,'tauri-plugin-log = "2"'),i=se(i,'log = "0.4"'),i}),f(a.join(e,"src-tauri/capabilities/default.json"),n=>(n.permissions=Array.isArray(n.permissions)?n.permissions:[],n.permissions.includes("log:default")||n.permissions.push("log:default"),n)),c(r,n=>{let i=n;if(i.includes(G)||(i.includes(`use tauri_plugin_opener::OpenerExt;
104
104
  `)?i=i.replace(`use tauri_plugin_opener::OpenerExt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tauri-ui",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "🦀 Create modern Tauri desktop apps in just a few simple steps.",
5
5
  "homepage": "https://github.com/agmmnn/tauri-ui",
6
6
  "bugs": {
@@ -39,15 +39,15 @@
39
39
  "updatesub": "git submodule update --init --recursive --remote"
40
40
  },
41
41
  "dependencies": {
42
- "@clack/prompts": "^1.1.0",
42
+ "@clack/prompts": "^1.2.0",
43
43
  "picocolors": "^1.1.1"
44
44
  },
45
45
  "devDependencies": {
46
- "@types/node": "^25.5.0",
47
- "@typescript/native-preview": "7.0.0-dev.20260329.1",
48
- "oxfmt": "^0.42.0",
49
- "oxlint": "^1.57.0",
50
- "typescript": "^6.0.2",
46
+ "@types/node": "^25.6.0",
47
+ "@typescript/native-preview": "7.0.0-dev.20260420.1",
48
+ "oxfmt": "^0.45.0",
49
+ "oxlint": "^1.60.0",
50
+ "typescript": "^6.0.3",
51
51
  "unbuild": "^3.6.1"
52
52
  }
53
53
  }