create-tauri-ui 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ import { attachConsole } from "@tauri-apps/plugin-log"
14
14
  import {
15
15
  AlertTriangleIcon,
16
16
  CopyIcon,
17
+ EllipsisIcon,
17
18
  ExternalLinkIcon,
18
19
  PinIcon,
19
20
  RefreshCwIcon,
@@ -24,13 +25,17 @@ import {
24
25
  import { Badge } from "__ALIAS_PREFIX__components/ui/badge"
25
26
  import { Button } from "__ALIAS_PREFIX__components/ui/button"
26
27
  import {
27
- Sheet,
28
- SheetContent,
29
- SheetHeader,
30
- SheetTitle,
31
- } from "__ALIAS_PREFIX__components/ui/sheet"
28
+ DropdownMenu,
29
+ DropdownMenuContent,
30
+ DropdownMenuItem,
31
+ DropdownMenuLabel,
32
+ DropdownMenuRadioGroup,
33
+ DropdownMenuRadioItem,
34
+ DropdownMenuSeparator,
35
+ DropdownMenuShortcut,
36
+ DropdownMenuTrigger,
37
+ } from "__ALIAS_PREFIX__components/ui/dropdown-menu"
32
38
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "__ALIAS_PREFIX__components/ui/tabs"
33
- import { ToggleGroup, ToggleGroupItem } from "__ALIAS_PREFIX__components/ui/toggle-group"
34
39
  import {
35
40
  DEBUG_EVENT_NAME,
36
41
  type DebugEvent,
@@ -104,7 +109,54 @@ const DEBUG_PANEL_SIDE_KEY = "ctui-debug-panel-side"
104
109
  const DEBUG_PANEL_TAB_KEY = "ctui-debug-panel-tab"
105
110
  const DEBUG_PANEL_ATTACH_KEY = "ctui-debug-panel-attach"
106
111
  const SIDE_PANEL_WIDTH = 480
107
- const BOTTOM_PANEL_HEIGHT = 320
112
+ const BOTTOM_PANEL_HEIGHT = 360
113
+ const DEBUG_PANEL_THEME_STYLE_LIGHT = {
114
+ colorScheme: "light",
115
+ "--background": "oklch(0.955 0.006 250)",
116
+ "--foreground": "oklch(0.255 0.015 250)",
117
+ "--card": "oklch(0.985 0.004 250)",
118
+ "--card-foreground": "oklch(0.255 0.015 250)",
119
+ "--popover": "oklch(0.955 0.006 250)",
120
+ "--popover-foreground": "oklch(0.255 0.015 250)",
121
+ "--primary": "oklch(0.56 0.1 235)",
122
+ "--primary-foreground": "oklch(0.985 0 0)",
123
+ "--secondary": "oklch(0.925 0.008 250)",
124
+ "--secondary-foreground": "oklch(0.31 0.015 250)",
125
+ "--muted": "oklch(0.935 0.006 250)",
126
+ "--muted-foreground": "oklch(0.52 0.014 250)",
127
+ "--accent": "oklch(0.925 0.01 250)",
128
+ "--accent-foreground": "oklch(0.255 0.015 250)",
129
+ "--destructive": "oklch(0.63 0.20 24)",
130
+ "--destructive-foreground": "oklch(0.985 0 0)",
131
+ "--border": "oklch(0.86 0.008 250)",
132
+ "--input": "oklch(0.86 0.008 250)",
133
+ "--ring": "oklch(0.62 0.08 235)",
134
+ "--radius": "0.5rem",
135
+ } as React.CSSProperties
136
+
137
+ const DEBUG_PANEL_THEME_STYLE_DARK = {
138
+ colorScheme: "dark",
139
+ "--background": "oklch(0.205 0.012 250)",
140
+ "--foreground": "oklch(0.92 0.008 250)",
141
+ "--card": "oklch(0.235 0.012 250)",
142
+ "--card-foreground": "oklch(0.92 0.008 250)",
143
+ "--popover": "oklch(0.205 0.012 250)",
144
+ "--popover-foreground": "oklch(0.92 0.008 250)",
145
+ "--primary": "oklch(0.74 0.085 230)",
146
+ "--primary-foreground": "oklch(0.19 0.01 250)",
147
+ "--secondary": "oklch(0.28 0.012 250)",
148
+ "--secondary-foreground": "oklch(0.9 0.008 250)",
149
+ "--muted": "oklch(0.255 0.012 250)",
150
+ "--muted-foreground": "oklch(0.7 0.012 250)",
151
+ "--accent": "oklch(0.28 0.012 250)",
152
+ "--accent-foreground": "oklch(0.92 0.008 250)",
153
+ "--destructive": "oklch(0.66 0.19 24)",
154
+ "--destructive-foreground": "oklch(0.98 0 0)",
155
+ "--border": "oklch(0.36 0.01 250)",
156
+ "--input": "oklch(0.36 0.01 250)",
157
+ "--ring": "oklch(0.68 0.07 230)",
158
+ "--radius": "0.5rem",
159
+ } as React.CSSProperties
108
160
 
109
161
  function isDebugPanelSide(value: string): value is DebugPanelSide {
110
162
  return value === "left" || value === "right" || value === "bottom"
@@ -176,17 +228,17 @@ function parsePluginLogMessage(message: string) {
176
228
  function getLogLevelClasses(level: string) {
177
229
  switch (level) {
178
230
  case "error":
179
- return "border-destructive/40 bg-destructive/10 text-destructive"
231
+ return "border-destructive/25 bg-destructive/15 text-destructive"
180
232
  case "warn":
181
- return "border-amber-500/40 bg-amber-500/10 text-amber-600 dark:text-amber-400"
233
+ return "border-amber-500/25 bg-amber-500/15 text-amber-600 dark:text-amber-400"
182
234
  case "info":
183
- return "border-emerald-500/40 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
235
+ return "border-emerald-500/25 bg-emerald-500/15 text-emerald-600 dark:text-emerald-400"
184
236
  case "debug":
185
- return "border-sky-500/40 bg-sky-500/10 text-sky-600 dark:text-sky-400"
237
+ return "border-sky-500/25 bg-sky-500/15 text-sky-600 dark:text-sky-400"
186
238
  case "trace":
187
- return "border-border bg-muted/60 text-muted-foreground"
239
+ return "border-border/25 bg-muted text-muted-foreground"
188
240
  default:
189
- return "border-border bg-muted/60 text-foreground"
241
+ return "border-border/25 bg-muted text-foreground"
190
242
  }
191
243
  }
192
244
 
@@ -205,20 +257,33 @@ function DebugSection({
205
257
  title,
206
258
  description,
207
259
  children,
260
+ className,
261
+ bodyClassName,
208
262
  }: {
209
263
  title: string
210
264
  description?: string
211
265
  children: React.ReactNode
266
+ className?: string
267
+ bodyClassName?: string
212
268
  }) {
213
269
  return (
214
- <section className="space-y-3 rounded-xl border bg-card/60 p-4">
215
- <div className="space-y-1">
216
- <h3 className="text-sm font-medium">{title}</h3>
270
+ <section
271
+ className={cn(
272
+ "min-h-0 overflow-hidden rounded-lg border border-border/25 bg-card",
273
+ className
274
+ )}
275
+ >
276
+ <div className="border-b border-border/20 px-3 py-2">
277
+ <h3 className="text-[12px] font-semibold tracking-tight text-foreground">
278
+ {title}
279
+ </h3>
217
280
  {description ? (
218
- <p className="text-xs text-muted-foreground">{description}</p>
281
+ <p className="mt-0.5 text-[11px] leading-4 text-muted-foreground">
282
+ {description}
283
+ </p>
219
284
  ) : null}
220
285
  </div>
221
- {children}
286
+ <div className={cn("min-h-0 p-3", bodyClassName)}>{children}</div>
222
287
  </section>
223
288
  )
224
289
  }
@@ -229,13 +294,16 @@ function KeyValueGrid({
229
294
  entries: Array<[label: string, value: React.ReactNode]>
230
295
  }) {
231
296
  return (
232
- <dl className="grid gap-2 text-xs">
233
- {entries.map(([label, value]) => (
297
+ <dl className="grid text-[11px] leading-5">
298
+ {entries.map(([label, value], index) => (
234
299
  <div
235
300
  key={label}
236
- className="grid grid-cols-[110px_minmax(0,1fr)] items-start gap-3"
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
+ )}
237
305
  >
238
- <dt className="text-muted-foreground">{label}</dt>
306
+ <dt className="text-muted-foreground/90">{label}</dt>
239
307
  <dd className="font-mono break-all text-foreground">{value}</dd>
240
308
  </div>
241
309
  ))}
@@ -391,14 +459,22 @@ export function DebugPanel() {
391
459
  updateThemeInfo()
392
460
 
393
461
  const mediaQuery = window.matchMedia(COLOR_SCHEME_QUERY)
462
+ const observer = new MutationObserver(() => {
463
+ updateThemeInfo()
464
+ })
394
465
  const handleMediaChange = () => {
395
466
  updateThemeInfo()
396
467
  }
397
468
 
398
469
  mediaQuery.addEventListener("change", handleMediaChange)
470
+ observer.observe(document.documentElement, {
471
+ attributes: true,
472
+ attributeFilter: ["class"],
473
+ })
399
474
 
400
475
  return () => {
401
476
  mediaQuery.removeEventListener("change", handleMediaChange)
477
+ observer.disconnect()
402
478
  }
403
479
  }, [])
404
480
 
@@ -794,7 +870,7 @@ export function DebugPanel() {
794
870
  }, [])
795
871
 
796
872
  React.useEffect(() => {
797
- const mainElement = document.querySelector("main")
873
+ const mainElement = document.querySelector("[data-ui-scroll-container]")
798
874
 
799
875
  if (!(mainElement instanceof HTMLElement)) {
800
876
  return
@@ -854,89 +930,93 @@ export function DebugPanel() {
854
930
  }, 1200)
855
931
  }
856
932
 
933
+ const isBottomDock = panelSide === "bottom"
934
+ const runtimeCount =
935
+ invokeLogs.length + externalLinks.length + runtimeEvents.length + logEntries.length
936
+ const overviewLayoutClassName = isBottomDock
937
+ ? "grid min-h-full content-start gap-3 pr-1 xl:grid-cols-[minmax(0,1fr)_minmax(0,1.2fr)_minmax(0,0.9fr)]"
938
+ : "h-full space-y-3 overflow-y-auto pr-1"
939
+ const runtimeLayoutClassName = isBottomDock
940
+ ? "grid min-h-full content-start gap-3 pr-1 xl:grid-cols-2"
941
+ : "h-full space-y-3 overflow-y-auto pr-1"
942
+ const systemLayoutClassName = isBottomDock
943
+ ? "grid min-h-full content-start gap-3 pr-1 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]"
944
+ : "h-full space-y-3 overflow-y-auto pr-1"
945
+ const debugPanelThemeStyle =
946
+ themeInfo.current === "light"
947
+ ? DEBUG_PANEL_THEME_STYLE_LIGHT
948
+ : DEBUG_PANEL_THEME_STYLE_DARK
949
+
857
950
  const panelContent = (
858
951
  <>
859
- <SheetHeader className="gap-3 border-b p-4">
860
- <div className="flex items-start justify-between gap-3">
861
- <div className="space-y-1">
862
- {!attached && panelSide !== "bottom" ? (
863
- <SheetTitle className="sr-only">Development Debug Panel</SheetTitle>
864
- ) : null}
865
- <h2 className="flex items-center gap-2 text-base font-medium text-foreground">
866
- <TerminalSquareIcon className="size-4" />
952
+ <div className="border-b px-3 py-2">
953
+ <div className="flex items-center justify-between gap-3">
954
+ <div className="min-w-0">
955
+ <h2 className="flex items-center gap-2 text-[13px] font-semibold tracking-tight text-foreground">
956
+ <TerminalSquareIcon className="size-3.5" />
867
957
  Development Debug Panel
868
958
  </h2>
869
959
  </div>
870
- <div className="flex items-center gap-2">
871
- <div className="inline-flex items-center rounded-md border bg-muted/40 px-2 py-1 text-xs">
872
- <span
873
- className={cn(
874
- "font-medium transition-colors",
875
- tauriReady ? "text-foreground" : "text-muted-foreground"
876
- )}
877
- >
878
- Tauri
879
- </span>
880
- <span className="px-1 text-muted-foreground">/</span>
881
- <span
882
- className={cn(
883
- "font-medium transition-colors",
884
- tauriReady ? "text-muted-foreground" : "text-foreground"
885
- )}
886
- >
887
- Web
888
- </span>
889
- </div>
890
- <Button
891
- variant="ghost"
892
- size="icon-sm"
893
- onClick={() => setOpen(false)}
894
- aria-label="Close debug panel"
895
- >
896
- <XIcon className="size-4" />
897
- </Button>
898
- </div>
899
- </div>
900
- <div className="flex flex-wrap items-center justify-between gap-3 text-xs text-muted-foreground">
901
- <div className="flex items-center gap-2">
902
- <span className="rounded-md border px-1.5 py-0.5 font-mono">Cmd/Ctrl + D</span>
903
- <span>toggle panel</span>
904
- </div>
905
- <div className="flex items-center gap-2">
960
+ <div className="flex items-center gap-1.5">
906
961
  <Button
907
962
  variant={attached ? "secondary" : "outline"}
908
963
  size="icon-sm"
964
+ className="size-7"
909
965
  onClick={() => setAttached((current) => !current)}
910
966
  aria-pressed={attached}
911
967
  aria-label={attached ? "Detach debug panel" : "Attach debug panel"}
912
968
  title={attached ? "Detach debug panel" : "Attach debug panel"}
913
969
  >
914
- <PinIcon className="size-4" />
970
+ <PinIcon className="size-3.5" />
915
971
  </Button>
916
- <ToggleGroup
917
- type="single"
918
- value={panelSide}
919
- onValueChange={(value) => {
920
- if (isDebugPanelSide(value)) {
921
- setPanelSide(value)
922
- }
923
- }}
924
- variant="outline"
925
- size="sm"
972
+ <DropdownMenu>
973
+ <DropdownMenuTrigger asChild>
974
+ <Button variant="ghost" size="icon-sm" className="size-7" aria-label="Debug panel options">
975
+ <EllipsisIcon className="size-3.5" />
976
+ </Button>
977
+ </DropdownMenuTrigger>
978
+ <DropdownMenuContent align="end" className="w-52">
979
+ <DropdownMenuItem onClick={() => void handleCopySnapshot()}>
980
+ <CopyIcon className="size-3.5" />
981
+ {copied ? "Copied snapshot" : "Copy snapshot"}
982
+ </DropdownMenuItem>
983
+ <DropdownMenuItem onClick={() => window.location.reload()}>
984
+ <RefreshCwIcon className="size-3.5" />
985
+ Reload app
986
+ </DropdownMenuItem>
987
+ <DropdownMenuSeparator />
988
+ <DropdownMenuLabel>Dock</DropdownMenuLabel>
989
+ <DropdownMenuRadioGroup
990
+ value={panelSide}
991
+ onValueChange={(value) => {
992
+ if (isDebugPanelSide(value)) {
993
+ setPanelSide(value)
994
+ }
995
+ }}
996
+ >
997
+ <DropdownMenuRadioItem value="left">Left</DropdownMenuRadioItem>
998
+ <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
999
+ <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
1000
+ </DropdownMenuRadioGroup>
1001
+ <DropdownMenuSeparator />
1002
+ <DropdownMenuItem disabled>
1003
+ Toggle panel
1004
+ <DropdownMenuShortcut>⌘/Ctrl+D</DropdownMenuShortcut>
1005
+ </DropdownMenuItem>
1006
+ </DropdownMenuContent>
1007
+ </DropdownMenu>
1008
+ <Button
1009
+ variant="ghost"
1010
+ size="icon-sm"
1011
+ className="size-7"
1012
+ onClick={() => setOpen(false)}
1013
+ aria-label="Close debug panel"
926
1014
  >
927
- <ToggleGroupItem value="left" aria-label="Dock panel left">
928
- Left
929
- </ToggleGroupItem>
930
- <ToggleGroupItem value="right" aria-label="Dock panel right">
931
- Right
932
- </ToggleGroupItem>
933
- <ToggleGroupItem value="bottom" aria-label="Dock panel bottom">
934
- Bottom
935
- </ToggleGroupItem>
936
- </ToggleGroup>
1015
+ <XIcon className="size-3.5" />
1016
+ </Button>
937
1017
  </div>
938
1018
  </div>
939
- </SheetHeader>
1019
+ </div>
940
1020
 
941
1021
  <Tabs
942
1022
  value={activeTab}
@@ -945,31 +1025,38 @@ export function DebugPanel() {
945
1025
  setActiveTab(value)
946
1026
  }
947
1027
  }}
948
- className="min-h-0 flex-1 gap-0 px-4"
1028
+ className="min-h-0 flex-1 gap-0 px-3"
949
1029
  >
950
- <TabsList className="w-full justify-start">
951
- <TabsTrigger value="overview">Overview</TabsTrigger>
952
- <TabsTrigger value="runtime">
1030
+ <TabsList className="-mx-3 grid h-9 w-[calc(100%+1.5rem)] grid-cols-4 justify-start border-y border-border/20 bg-muted p-0">
1031
+ <TabsTrigger value="overview" className="h-9 rounded-none border-r border-border/20 px-2 text-[12px] font-medium">
1032
+ Overview
1033
+ </TabsTrigger>
1034
+ <TabsTrigger value="runtime" className="h-9 rounded-none border-r border-border/20 px-2 text-[12px] font-medium">
953
1035
  Runtime
954
- {invokeLogs.length || externalLinks.length || runtimeEvents.length || logEntries.length ? (
955
- <Badge variant="secondary" className="ml-1 h-5 min-w-5 px-1.5">
956
- {invokeLogs.length + externalLinks.length + runtimeEvents.length + logEntries.length}
1036
+ {runtimeCount ? (
1037
+ <Badge variant="secondary" className="ml-1 h-4 min-w-4 rounded-sm px-1 text-[10px]">
1038
+ {runtimeCount}
957
1039
  </Badge>
958
1040
  ) : null}
959
1041
  </TabsTrigger>
960
- <TabsTrigger value="system">System</TabsTrigger>
961
- <TabsTrigger value="errors">
1042
+ <TabsTrigger value="system" className="h-9 rounded-none border-r border-border/20 px-2 text-[12px] font-medium">
1043
+ System
1044
+ </TabsTrigger>
1045
+ <TabsTrigger value="errors" className="h-9 rounded-none px-2 text-[12px] font-medium">
962
1046
  Errors
963
1047
  {errors.length ? (
964
- <Badge variant="destructive" className="ml-1 h-5 min-w-5 px-1.5">
1048
+ <Badge variant="destructive" className="ml-1 h-4 min-w-4 rounded-sm px-1 text-[10px]">
965
1049
  {errors.length}
966
1050
  </Badge>
967
1051
  ) : null}
968
1052
  </TabsTrigger>
969
1053
  </TabsList>
970
1054
 
971
- <TabsContent value="overview" className="min-h-0 flex-1 overflow-hidden pt-2">
972
- <div className="h-full space-y-4 overflow-y-auto pr-1">
1055
+ <TabsContent
1056
+ value="overview"
1057
+ className={cn("min-h-0 flex-1 pt-2", isBottomDock ? "overflow-y-auto" : "overflow-hidden")}
1058
+ >
1059
+ <div className={overviewLayoutClassName}>
973
1060
  <DebugSection
974
1061
  title="App"
975
1062
  description="Static application metadata from Tauri v2."
@@ -980,10 +1067,12 @@ export function DebugPanel() {
980
1067
  ["URL", locationState.href || "Unavailable"],
981
1068
  ["Search", locationState.search || "(none)"],
982
1069
  ["Hash", locationState.hash || "(none)"],
1070
+ ["Bridge", tauriReady ? "Tauri" : "Web"],
983
1071
  ["Name", appInfo.name ?? "Unavailable"],
984
1072
  ["Version", appInfo.version ?? "Unavailable"],
985
1073
  ["Identifier", appInfo.identifier ?? "Unavailable"],
986
1074
  ["Tauri", appInfo.tauriVersion ?? "Unavailable"],
1075
+ ["Shortcut", "Cmd/Ctrl + D"],
987
1076
  ]}
988
1077
  />
989
1078
  </DebugSection>
@@ -1051,35 +1140,45 @@ export function DebugPanel() {
1051
1140
  </div>
1052
1141
  </TabsContent>
1053
1142
 
1054
- <TabsContent value="runtime" className="min-h-0 flex-1 overflow-hidden pt-2">
1055
- <div className="h-full space-y-4 overflow-y-auto pr-1">
1143
+ <TabsContent
1144
+ value="runtime"
1145
+ className={cn("min-h-0 flex-1 pt-2", isBottomDock ? "overflow-y-auto" : "overflow-hidden")}
1146
+ >
1147
+ <div className={runtimeLayoutClassName}>
1056
1148
  <DebugSection
1057
1149
  title="Recent invoke() calls"
1058
1150
  description="Calls captured through the tracked Tauri helper."
1151
+ bodyClassName="min-h-0"
1059
1152
  >
1060
1153
  {invokeLogs.length ? (
1061
- <div className="max-h-72 space-y-2 overflow-y-auto pr-1">
1154
+ <div
1155
+ className={cn(
1156
+ "space-y-1.5 overflow-y-auto pr-1",
1157
+ isBottomDock ? "h-full" : "max-h-72"
1158
+ )}
1159
+ >
1062
1160
  {invokeLogs.map((entry) => {
1063
1161
  if (entry.kind !== "invoke") {
1064
1162
  return null
1065
1163
  }
1066
1164
 
1067
1165
  return (
1068
- <div key={entry.id} className="rounded-lg border bg-background/70 p-3">
1069
- <div className="mb-2 flex items-center justify-between gap-3">
1070
- <div className="font-mono text-xs">{entry.command}</div>
1166
+ <div key={entry.id} className="rounded-md border border-border/20 bg-card p-2.5">
1167
+ <div className="mb-1.5 flex items-center justify-between gap-3">
1168
+ <div className="font-mono text-[11px]">{entry.command}</div>
1071
1169
  <div className="flex items-center gap-2">
1072
1170
  <Badge
1073
1171
  variant={entry.status === "success" ? "secondary" : "destructive"}
1172
+ className="h-4 rounded-sm px-1 text-[10px]"
1074
1173
  >
1075
1174
  {entry.status}
1076
1175
  </Badge>
1077
- <span className="text-[11px] text-muted-foreground">
1176
+ <span className="text-[10px] text-muted-foreground">
1078
1177
  {entry.durationMs} ms
1079
1178
  </span>
1080
1179
  </div>
1081
1180
  </div>
1082
- <pre className="overflow-x-auto rounded-md bg-muted/60 p-2 text-[11px] leading-relaxed break-words whitespace-pre-wrap">
1181
+ <pre className="overflow-x-auto rounded-md bg-muted p-2 text-[10px] leading-4 break-words whitespace-pre-wrap">
1083
1182
  {JSON.stringify(
1084
1183
  {
1085
1184
  args: entry.args,
@@ -1101,44 +1200,32 @@ export function DebugPanel() {
1101
1200
  )}
1102
1201
  </DebugSection>
1103
1202
 
1104
- <DebugSection
1105
- title="External links"
1106
- description="URLs intercepted by the external-link guard."
1107
- >
1108
- {externalLinks.length ? (
1109
- <ul className="max-h-56 space-y-2 overflow-y-auto pr-1 text-xs">
1110
- {externalLinks.map((href, index) => (
1111
- <li key={`${href}-${index}`} className="flex gap-2">
1112
- <ExternalLinkIcon className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
1113
- <span className="font-mono break-all">{href}</span>
1114
- </li>
1115
- ))}
1116
- </ul>
1117
- ) : (
1118
- <p className="text-xs text-muted-foreground">
1119
- No external links opened yet.
1120
- </p>
1121
- )}
1122
- </DebugSection>
1123
-
1124
1203
  <DebugSection
1125
1204
  title="Runtime events"
1126
1205
  description="Recent Tauri window and emitted event activity."
1206
+ bodyClassName="min-h-0"
1127
1207
  >
1128
1208
  {runtimeEvents.length ? (
1129
- <ul className="max-h-72 space-y-2 overflow-y-auto pr-1 text-xs">
1209
+ <ul
1210
+ className={cn(
1211
+ "space-y-1.5 overflow-y-auto pr-1 text-[11px]",
1212
+ isBottomDock ? "h-full" : "max-h-72"
1213
+ )}
1214
+ >
1130
1215
  {newestFirst(runtimeEvents).map((entry) => (
1131
- <li key={entry.id} className="relative rounded-lg border bg-background/70 p-3">
1216
+ <li key={entry.id} className="relative rounded-md border border-border/20 bg-card p-2.5">
1132
1217
  {highlightedRuntimeEventIds.includes(entry.id) ? <EntryHighlight /> : null}
1133
1218
  <div className="mb-1 flex items-center justify-between gap-3">
1134
1219
  <span className="font-mono">{entry.name}</span>
1135
- <Badge variant="outline">{entry.source}</Badge>
1220
+ <Badge variant="outline" className="h-4 rounded-sm px-1 text-[10px]">
1221
+ {entry.source}
1222
+ </Badge>
1136
1223
  </div>
1137
- <div className="mb-2 text-[11px] text-muted-foreground">
1224
+ <div className="mb-1.5 text-[10px] text-muted-foreground">
1138
1225
  {formatTimestamp(entry.timestamp)}
1139
1226
  </div>
1140
1227
  {entry.payload !== undefined ? (
1141
- <pre className="overflow-x-auto rounded-md bg-muted/60 p-2 text-[11px] leading-relaxed break-words whitespace-pre-wrap">
1228
+ <pre className="overflow-x-auto rounded-md bg-muted p-2 text-[10px] leading-4 break-words whitespace-pre-wrap">
1142
1229
  {JSON.stringify(entry.payload, null, 2)}
1143
1230
  </pre>
1144
1231
  ) : null}
@@ -1155,14 +1242,20 @@ export function DebugPanel() {
1155
1242
  <DebugSection
1156
1243
  title="Plugin logs"
1157
1244
  description="Recent logs surfaced through the browser console and Tauri log plugin."
1245
+ bodyClassName="min-h-0"
1158
1246
  >
1159
1247
  {logEntries.length ? (
1160
- <ul className="max-h-72 space-y-2 overflow-y-auto pr-1 text-xs">
1248
+ <ul
1249
+ className={cn(
1250
+ "space-y-1.5 overflow-y-auto pr-1 text-[11px]",
1251
+ isBottomDock ? "h-full" : "max-h-72"
1252
+ )}
1253
+ >
1161
1254
  {newestFirst(logEntries).map((entry) => {
1162
1255
  const parsed = parsePluginLogMessage(entry.message)
1163
1256
 
1164
1257
  return (
1165
- <li key={entry.id} className="relative rounded-lg border bg-background/70 p-3">
1258
+ <li key={entry.id} className="relative rounded-md border border-border/20 bg-card p-2.5">
1166
1259
  {highlightedLogIds.includes(entry.id) ? <EntryHighlight /> : null}
1167
1260
  {parsed ? (
1168
1261
  <div className="space-y-2">
@@ -1171,33 +1264,35 @@ export function DebugPanel() {
1171
1264
  <div className="flex min-w-0 items-center gap-2">
1172
1265
  <span
1173
1266
  className={cn(
1174
- "inline-flex rounded-md border px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide",
1267
+ "inline-flex rounded-sm border px-1 py-0.5 text-[10px] font-medium uppercase tracking-wide",
1175
1268
  getLogLevelClasses(parsed.level)
1176
1269
  )}
1177
1270
  >
1178
1271
  {parsed.level}
1179
1272
  </span>
1180
- <span className="truncate font-mono text-[11px] text-muted-foreground">
1273
+ <span className="truncate font-mono text-[10px] text-muted-foreground">
1181
1274
  {parsed.target}
1182
1275
  </span>
1183
1276
  </div>
1184
1277
  </div>
1185
- <div className="shrink-0 text-right text-[11px] text-muted-foreground">
1278
+ <div className="shrink-0 text-right text-[10px] text-muted-foreground">
1186
1279
  <div>{parsed.time}</div>
1187
1280
  <div>{parsed.date}</div>
1188
1281
  </div>
1189
1282
  </div>
1190
- <p className="font-mono break-words text-foreground">{parsed.text}</p>
1283
+ <p className="font-mono break-words text-[11px] text-foreground">{parsed.text}</p>
1191
1284
  </div>
1192
1285
  ) : (
1193
1286
  <>
1194
1287
  <div className="mb-1 flex items-center justify-between gap-3">
1195
- <Badge variant="outline">{entry.level}</Badge>
1196
- <span className="text-[11px] text-muted-foreground">
1288
+ <Badge variant="outline" className="h-4 rounded-sm px-1 text-[10px]">
1289
+ {entry.level}
1290
+ </Badge>
1291
+ <span className="text-[10px] text-muted-foreground">
1197
1292
  {formatTimestamp(entry.timestamp)}
1198
1293
  </span>
1199
1294
  </div>
1200
- <p className="font-mono break-words text-foreground">
1295
+ <p className="font-mono break-words text-[11px] text-foreground">
1201
1296
  {entry.message}
1202
1297
  </p>
1203
1298
  </>
@@ -1212,16 +1307,45 @@ export function DebugPanel() {
1212
1307
  </p>
1213
1308
  )}
1214
1309
  </DebugSection>
1310
+
1311
+ <DebugSection
1312
+ title="External links"
1313
+ description="URLs intercepted by the external-link guard."
1314
+ bodyClassName="min-h-0"
1315
+ >
1316
+ {externalLinks.length ? (
1317
+ <ul
1318
+ className={cn(
1319
+ "space-y-1.5 overflow-y-auto pr-1 text-[11px]",
1320
+ isBottomDock ? "h-full" : "max-h-56"
1321
+ )}
1322
+ >
1323
+ {externalLinks.map((href, index) => (
1324
+ <li key={`${href}-${index}`} className="flex gap-2 rounded-md border border-border/20 bg-card p-2">
1325
+ <ExternalLinkIcon className="mt-0.5 size-3 shrink-0 text-muted-foreground" />
1326
+ <span className="font-mono break-all">{href}</span>
1327
+ </li>
1328
+ ))}
1329
+ </ul>
1330
+ ) : (
1331
+ <p className="text-xs text-muted-foreground">
1332
+ No external links opened yet.
1333
+ </p>
1334
+ )}
1335
+ </DebugSection>
1215
1336
  </div>
1216
1337
  </TabsContent>
1217
1338
 
1218
- <TabsContent value="system" className="min-h-0 flex-1 overflow-hidden pt-2">
1219
- <div className="h-full space-y-4 overflow-y-auto pr-1">
1339
+ <TabsContent
1340
+ value="system"
1341
+ className={cn("min-h-0 flex-1 pt-2", isBottomDock ? "overflow-y-auto" : "overflow-hidden")}
1342
+ >
1343
+ <div className={systemLayoutClassName}>
1220
1344
  <DebugSection
1221
1345
  title="Paths"
1222
1346
  description="Resolved Tauri application directories."
1223
1347
  >
1224
- <div className="space-y-2">
1348
+ <div className="space-y-1.5">
1225
1349
  {[
1226
1350
  ["App data", pathInfo.appDataDir],
1227
1351
  ["App config", pathInfo.appConfigDir],
@@ -1229,18 +1353,13 @@ export function DebugPanel() {
1229
1353
  ].map(([label, value]) => (
1230
1354
  <div
1231
1355
  key={label}
1232
- className="grid grid-cols-[110px_minmax(0,1fr)_auto] items-start gap-3 text-xs"
1356
+ 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"
1233
1357
  >
1234
1358
  <div className="text-muted-foreground">{label}</div>
1235
1359
  <div className="font-mono break-all text-foreground">
1236
1360
  {value ?? "Unavailable"}
1237
1361
  </div>
1238
- <Button
1239
- variant="ghost"
1240
- size="sm"
1241
- disabled={!value}
1242
- onClick={() => value && void handleCopyText(value)}
1243
- >
1362
+ <Button variant="ghost" size="sm" disabled={!value} onClick={() => value && void handleCopyText(value)}>
1244
1363
  <CopyIcon className="size-3.5" />
1245
1364
  Copy
1246
1365
  </Button>
@@ -1249,24 +1368,27 @@ export function DebugPanel() {
1249
1368
  </div>
1250
1369
  </DebugSection>
1251
1370
 
1252
- <DebugSection title="Webview" description="Current webview identity.">
1371
+ <DebugSection
1372
+ title="Webview"
1373
+ description="Current webview identity."
1374
+ >
1253
1375
  <KeyValueGrid entries={[["Current label", windowInfo.label]]} />
1254
1376
  </DebugSection>
1255
1377
  </div>
1256
1378
  </TabsContent>
1257
1379
 
1258
1380
  <TabsContent value="errors" className="min-h-0 flex-1 overflow-hidden pt-2">
1259
- <div className="h-full space-y-4 overflow-y-auto pr-1">
1381
+ <div className="h-full overflow-y-auto pr-1">
1260
1382
  <DebugSection
1261
1383
  title="Recent errors"
1262
1384
  description="Uncaught runtime errors and unhandled promise rejections."
1263
1385
  >
1264
1386
  {errors.length ? (
1265
- <ul className="space-y-2 text-xs">
1387
+ <ul className="space-y-1.5 text-[11px]">
1266
1388
  {errors.map((entry) => (
1267
1389
  <li
1268
1390
  key={entry.id}
1269
- className="rounded-lg border bg-background/70 p-3"
1391
+ className="rounded-md border border-border/20 bg-card p-2.5"
1270
1392
  >
1271
1393
  <div className="mb-1 flex items-center gap-2">
1272
1394
  <AlertTriangleIcon className="size-3.5 text-destructive" />
@@ -1290,22 +1412,12 @@ export function DebugPanel() {
1290
1412
  </div>
1291
1413
  </TabsContent>
1292
1414
 
1293
- <div className="mt-4 flex shrink-0 flex-wrap gap-2 border-t py-3">
1294
- <Button onClick={handleCopySnapshot} variant="outline" size="sm">
1295
- <CopyIcon className="size-4" />
1296
- {copied ? "Copied" : "Copy snapshot"}
1297
- </Button>
1298
- <Button onClick={() => window.location.reload()} variant="outline" size="sm">
1299
- <RefreshCwIcon className="size-4" />
1300
- Reload app
1301
- </Button>
1302
- </div>
1303
1415
  </Tabs>
1304
1416
  </>
1305
1417
  )
1306
1418
 
1307
- const attachedPanelClassName = cn(
1308
- "ui-selectable fixed z-50 flex overflow-hidden bg-popover text-sm text-popover-foreground shadow-lg",
1419
+ const panelFrameClassName = cn(
1420
+ "ui-selectable fixed z-50 flex overflow-hidden border border-border/25 bg-background text-[12px] text-foreground",
1309
1421
  panelSide === "bottom"
1310
1422
  ? "inset-x-0 bottom-0 flex-col border-t"
1311
1423
  : panelSide === "left"
@@ -1315,40 +1427,18 @@ export function DebugPanel() {
1315
1427
 
1316
1428
  return (
1317
1429
  <>
1318
- {attached ? (
1319
- open ? (
1320
- <div
1321
- className={attachedPanelClassName}
1322
- style={
1323
- panelSide === "bottom"
1324
- ? { height: `${BOTTOM_PANEL_HEIGHT}px` }
1325
- : { width: `${SIDE_PANEL_WIDTH}px` }
1326
- }
1327
- >
1328
- {panelContent}
1329
- </div>
1330
- ) : null
1331
- ) : panelSide === "bottom" ? (
1332
- open ? (
1333
- <div
1334
- className="ui-selectable fixed inset-x-0 bottom-0 z-50 flex flex-col overflow-hidden border-t bg-popover text-sm text-popover-foreground shadow-lg"
1335
- style={{ height: `${BOTTOM_PANEL_HEIGHT}px` }}
1336
- >
1337
- {panelContent}
1338
- </div>
1339
- ) : null
1340
- ) : (
1341
- <Sheet open={open} onOpenChange={setOpen}>
1342
- <SheetContent
1343
- side={panelSide}
1344
- showCloseButton={false}
1345
- className="ui-selectable flex h-full max-w-[92vw] flex-col gap-0 overflow-hidden p-0"
1346
- style={{ width: `${SIDE_PANEL_WIDTH}px` }}
1347
- >
1348
- {panelContent}
1349
- </SheetContent>
1350
- </Sheet>
1351
- )}
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}
1352
1442
  </>
1353
1443
  )
1354
1444
  }