create-tauri-ui 1.0.5 → 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 +93 -102
- package/assets/debug-panel/debug-panel.tsx.tmpl +647 -59
- package/dist/index.mjs +80 -65
- package/package.json +7 -7
|
@@ -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:
|
|
546
|
+
entries: ReadonlyArray<GridEntry>
|
|
295
547
|
}) {
|
|
296
548
|
return (
|
|
297
549
|
<dl className="grid text-[11px] leading-5">
|
|
298
|
-
{entries.map((
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
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
|
|
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">
|
|
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="
|
|
1373
|
-
description="
|
|
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
|
|
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
|
-
{
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
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
|
}
|