@svadmin/ui 0.38.2 → 0.38.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.
Files changed (144) hide show
  1. package/dist/app.css +8 -4
  2. package/dist/app.theme.css +8 -4
  3. package/dist/components/AICommandBar.svelte +16 -15
  4. package/dist/components/AboutSettings.svelte +12 -11
  5. package/dist/components/AdminApp.svelte +144 -70
  6. package/dist/components/AdminApp.svelte.d.ts +6 -3
  7. package/dist/components/ApiSettings.svelte +19 -16
  8. package/dist/components/AppearanceSettings.svelte +18 -16
  9. package/dist/components/AuditLogDrawer.svelte +6 -3
  10. package/dist/components/AuditLogViewer.svelte +18 -15
  11. package/dist/components/Authenticated.svelte +4 -4
  12. package/dist/components/AutoForm.svelte +19 -16
  13. package/dist/components/AutoSaveIndicator.svelte +7 -5
  14. package/dist/components/AutoTable.svelte +85 -71
  15. package/dist/components/AutoTable.svelte.d.ts +1 -1
  16. package/dist/components/Breadcrumbs.svelte +17 -13
  17. package/dist/components/CancelTaskButton.svelte +5 -2
  18. package/dist/components/CatchAllNavigate.svelte +3 -3
  19. package/dist/components/ChatDialog.svelte +22 -19
  20. package/dist/components/ComboboxField.svelte +6 -4
  21. package/dist/components/CommandPalette.svelte +20 -20
  22. package/dist/components/ConfigErrorScreen.svelte +12 -10
  23. package/dist/components/ConfirmDialog.svelte +6 -4
  24. package/dist/components/CreatePage.svelte +4 -2
  25. package/dist/components/DevTools.svelte +11 -9
  26. package/dist/components/DrawerForm.svelte +5 -2
  27. package/dist/components/EditPage.svelte +8 -5
  28. package/dist/components/EmptyState.svelte +5 -3
  29. package/dist/components/ErrorBoundary.svelte +5 -3
  30. package/dist/components/ErrorComponent.svelte +6 -4
  31. package/dist/components/ErrorPage.svelte +12 -10
  32. package/dist/components/FieldRenderer.svelte +17 -15
  33. package/dist/components/ForgotPasswordPage.svelte +22 -19
  34. package/dist/components/Header.svelte +7 -4
  35. package/dist/components/InfiniteList.svelte +6 -3
  36. package/dist/components/IntegrationsSettings.svelte +9 -6
  37. package/dist/components/KeyboardShortcuts.svelte +4 -2
  38. package/dist/components/Layout.svelte +12 -13
  39. package/dist/components/LazyPage.svelte +47 -0
  40. package/dist/components/LazyPage.svelte.d.ts +14 -0
  41. package/dist/components/ListPage.svelte +8 -5
  42. package/dist/components/LoginPage.svelte +24 -22
  43. package/dist/components/MarkdownRenderer.svelte +89 -35
  44. package/dist/components/ModalForm.svelte +4 -2
  45. package/dist/components/NavigateToResource.svelte +3 -3
  46. package/dist/components/NotificationsSettings.svelte +26 -24
  47. package/dist/components/PasswordInput.svelte +4 -2
  48. package/dist/components/PermissionMatrix.svelte +15 -12
  49. package/dist/components/ProfilePage.svelte +23 -21
  50. package/dist/components/RegisterPage.svelte +21 -19
  51. package/dist/components/ResourceOperationsPage.svelte +3 -2
  52. package/dist/components/RetryTaskButton.svelte +5 -2
  53. package/dist/components/RolesSettings.svelte +10 -8
  54. package/dist/components/SecuritySettings.svelte +26 -23
  55. package/dist/components/SettingsPage.svelte +14 -11
  56. package/dist/components/ShowPage.svelte +9 -7
  57. package/dist/components/Sidebar.svelte +36 -28
  58. package/dist/components/SidebarItem.svelte +4 -4
  59. package/dist/components/SidebarItem.svelte.d.ts +1 -1
  60. package/dist/components/StepsForm.svelte +11 -8
  61. package/dist/components/TaskDetails.svelte +14 -11
  62. package/dist/components/TaskList.svelte +14 -11
  63. package/dist/components/TaskQueueDrawer.svelte +34 -32
  64. package/dist/components/TaskStatusBadge.svelte +5 -2
  65. package/dist/components/Toast.svelte +18 -4
  66. package/dist/components/TooltipButton.svelte +25 -3
  67. package/dist/components/TwoFactorAuthPage.svelte +26 -24
  68. package/dist/components/UndoableNotification.svelte +5 -2
  69. package/dist/components/UpdatePasswordPage.svelte +18 -15
  70. package/dist/components/VirtualTable.svelte +0 -4
  71. package/dist/components/account/CompanyProfilePage.svelte +13 -11
  72. package/dist/components/account/GetStartedPage.svelte +15 -13
  73. package/dist/components/account/ImportMembersPage.svelte +14 -12
  74. package/dist/components/account/MembersStarterPage.svelte +10 -8
  75. package/dist/components/account/SecurityLogPage.svelte +24 -22
  76. package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
  77. package/dist/components/account/SettingsPlainPage.svelte +14 -12
  78. package/dist/components/account/SettingsSidebarPage.svelte +11 -9
  79. package/dist/components/account/TeamMembersPage.svelte +10 -8
  80. package/dist/components/buttons/CloneButton.svelte +4 -2
  81. package/dist/components/buttons/CreateButton.svelte +4 -2
  82. package/dist/components/buttons/DeleteButton.svelte +6 -4
  83. package/dist/components/buttons/EditButton.svelte +4 -2
  84. package/dist/components/buttons/ExportButton.svelte +4 -2
  85. package/dist/components/buttons/ImportButton.svelte +4 -2
  86. package/dist/components/buttons/RefreshButton.svelte +4 -2
  87. package/dist/components/buttons/SaveButton.svelte +4 -2
  88. package/dist/components/buttons/ShowButton.svelte +4 -2
  89. package/dist/components/fieldComponentMap.js +0 -3
  90. package/dist/components/fields/ArrayField.svelte +6 -4
  91. package/dist/components/fields/JsonField.svelte +5 -3
  92. package/dist/components/fields/RelationField.svelte +3 -3
  93. package/dist/components/fields/RichTextField.svelte +4 -2
  94. package/dist/components/network/TeamCrewTablePage.svelte +18 -16
  95. package/dist/components/network/UserCardsNFTPage.svelte +11 -9
  96. package/dist/components/profile/ActivityTimeline.svelte +10 -8
  97. package/dist/components/profile/ProfileCard.svelte +13 -11
  98. package/dist/components/profile/ProjectsGrid.svelte +7 -5
  99. package/dist/components/profile/PublicProfilePage.svelte +18 -16
  100. package/dist/components/profile/TeamsShowcase.svelte +7 -5
  101. package/dist/components/toast-host.svelte.d.ts +5 -0
  102. package/dist/components/toast-host.svelte.js +21 -0
  103. package/dist/components/ui/command/command-dialog.svelte +29 -13
  104. package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
  105. package/dist/components/ui/command/command-empty.svelte +18 -0
  106. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  107. package/dist/components/ui/command/command-group.svelte +41 -0
  108. package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
  109. package/dist/components/ui/command/command-input.svelte +23 -0
  110. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  111. package/dist/components/ui/command/command-item.svelte +43 -0
  112. package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
  113. package/dist/components/ui/command/command-list.svelte +18 -0
  114. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  115. package/dist/components/ui/command/command-loading.svelte +12 -0
  116. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  117. package/dist/components/ui/command/command-separator.svelte +23 -0
  118. package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
  119. package/dist/components/ui/command/command.svelte +26 -0
  120. package/dist/components/ui/command/command.svelte.d.ts +4 -0
  121. package/dist/components/ui/command/index.d.ts +55 -2
  122. package/dist/components/ui/command/index.js +21 -2
  123. package/dist/components/ui/sheet/sheet-content.svelte +1 -4
  124. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
  125. package/dist/components/ui/tabs/tabs-content.svelte +6 -2
  126. package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
  127. package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
  128. package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
  129. package/dist/components/ui/tabs/tabs.svelte +12 -1
  130. package/dist/components/ui/tooltip/index.d.ts +4 -1
  131. package/dist/components/ui/tooltip/index.js +3 -1
  132. package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
  133. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
  134. package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
  135. package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
  136. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
  137. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
  138. package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
  139. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
  140. package/dist/components/ui/tooltip/tooltip.svelte +63 -3
  141. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
  142. package/dist/router-state.svelte.d.ts +13 -0
  143. package/dist/router-state.svelte.js +72 -37
  144. package/package.json +13 -11
@@ -2,13 +2,16 @@
2
2
  /* eslint-disable no-undef, svelte/no-at-html-tags */
3
3
  import { getChatProvider, getChatContext, setChatContext, getAgentProvider, registerApproval, resolveApproval } from '@svadmin/core';
4
4
  import type { ChatMessage, ChatAction } from '@svadmin/core';
5
- import { t } from '@svadmin/core/i18n';
5
+ import { useTranslation } from '@svadmin/core/i18n';
6
+
6
7
  import { useParsed } from '@svadmin/core';
7
8
  import { fly, fade, scale } from 'svelte/transition';
8
9
  import { Button } from './ui/button/index.js';
9
10
  import TooltipButton from './TooltipButton.svelte';
10
11
  import { MessageCircle, X, Minus, Send, Loader2, Bot, Trash2 } from '@lucide/svelte';
11
12
 
13
+ const i18n = useTranslation();
14
+
12
15
  interface Props {
13
16
  /** localStorage key for chat history persistence. Set to '' to disable. */
14
17
  persistKey?: string;
@@ -254,18 +257,18 @@
254
257
 
255
258
  collectedActions.push(
256
259
  {
257
- label: `✅ ${t('common.confirm') || 'Approve'}: ${event.description}`,
260
+ label: `✅ ${i18n.t('common.confirm') || 'Approve'}: ${event.description}`,
258
261
  variant: 'default',
259
262
  payload: { approvalId: reqId, approved: true },
260
263
  },
261
264
  {
262
- label: `❌ ${t('common.cancel') || 'Reject'}`,
265
+ label: `❌ ${i18n.t('common.cancel') || 'Reject'}`,
263
266
  variant: 'destructive',
264
267
  payload: { approvalId: reqId, approved: false },
265
268
  },
266
269
  );
267
270
 
268
- assistantMsg.content += `\n⚠️ **${t('chat.approvalRequired') || 'Approval required'}**: ${event.description}`;
271
+ assistantMsg.content += `\n⚠️ **${i18n.t('chat.approvalRequired') || 'Approval required'}**: ${event.description}`;
269
272
  assistantMsg.actions = [...collectedActions];
270
273
  messages = [...messages.slice(0, -1), { ...assistantMsg }];
271
274
  scrollToBottom();
@@ -305,7 +308,7 @@
305
308
  }
306
309
  } catch (err: unknown) {
307
310
  if (err instanceof Error && err.name === 'AbortError') return;
308
- assistantMsg.content = t('chat.error') || 'Sorry, something went wrong. Please try again.';
311
+ assistantMsg.content = i18n.t('chat.error') || 'Sorry, something went wrong. Please try again.';
309
312
  messages = [...messages.slice(0, -1), { ...assistantMsg }];
310
313
  } finally {
311
314
  isStreaming = false;
@@ -401,9 +404,9 @@
401
404
  }
402
405
 
403
406
  const suggestions = $derived([
404
- t('chat.suggestion1') || 'How do I create a new resource?',
405
- t('chat.suggestion2') || 'Explain the data model',
406
- t('chat.suggestion3') || 'Help me write a filter query',
407
+ i18n.t('chat.suggestion1') || 'How do I create a new resource?',
408
+ i18n.t('chat.suggestion2') || 'Explain the data model',
409
+ i18n.t('chat.suggestion3') || 'Help me write a filter query',
407
410
  ]);
408
411
  </script>
409
412
 
@@ -414,7 +417,7 @@
414
417
  {#if !open}
415
418
  <div transition:scale={{ duration: 200 }}>
416
419
  <TooltipButton
417
- tooltip={t('chat.title') || 'AI Assistant'}
420
+ tooltip={i18n.t('chat.title') || 'AI Assistant'}
418
421
  variant="default"
419
422
  size="icon"
420
423
  class="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-[9998] h-12 w-12 rounded-full shadow-xl hover:shadow-2xl hover:scale-110 transition-all bg-primary text-primary-foreground"
@@ -450,17 +453,17 @@
450
453
  </div>
451
454
  <div>
452
455
  <h3 class="text-sm font-semibold leading-none">
453
- {t('chat.title') || 'AI Assistant'}
456
+ {i18n.t('chat.title') || 'AI Assistant'}
454
457
  </h3>
455
458
  {#if isStreaming}
456
- <p class="text-[10px] opacity-80 mt-0.5">{t('chat.typing') || 'Typing...'}</p>
459
+ <p class="text-[10px] opacity-80 mt-0.5">{i18n.t('chat.typing') || 'Typing...'}</p>
457
460
  {/if}
458
461
  </div>
459
462
  </div>
460
463
  <div class="flex items-center gap-0.5">
461
464
  {#if messages.length > 0}
462
465
  <TooltipButton
463
- tooltip={t('chat.clear') || 'Clear'}
466
+ tooltip={i18n.t('chat.clear') || 'Clear'}
464
467
  variant="ghost"
465
468
  size="icon"
466
469
  class="h-7 w-7 text-primary-foreground/70 hover:text-primary-foreground hover:bg-primary-foreground/10"
@@ -470,7 +473,7 @@
470
473
  </TooltipButton>
471
474
  {/if}
472
475
  <TooltipButton
473
- tooltip={minimized ? (t('common.expand') || 'Expand') : (t('common.collapse') || 'Minimize')}
476
+ tooltip={minimized ? (i18n.t('common.expand') || 'Expand') : (i18n.t('common.collapse') || 'Minimize')}
474
477
  variant="ghost"
475
478
  size="icon"
476
479
  class="h-7 w-7 text-primary-foreground/70 hover:text-primary-foreground hover:bg-primary-foreground/10"
@@ -479,7 +482,7 @@
479
482
  <Minus class="h-3.5 w-3.5" />
480
483
  </TooltipButton>
481
484
  <TooltipButton
482
- tooltip={t('common.close') || 'Close'}
485
+ tooltip={i18n.t('common.close') || 'Close'}
483
486
  variant="ghost"
484
487
  size="icon"
485
488
  class="h-7 w-7 text-primary-foreground/70 hover:text-primary-foreground hover:bg-primary-foreground/10"
@@ -503,10 +506,10 @@
503
506
  <Bot class="h-6 w-6 text-primary" />
504
507
  </div>
505
508
  <h4 class="text-sm font-semibold text-foreground mb-1">
506
- {t('chat.welcome') || 'How can I help?'}
509
+ {i18n.t('chat.welcome') || 'How can I help?'}
507
510
  </h4>
508
511
  <p class="text-xs text-muted-foreground mb-4">
509
- {t('chat.welcomeDesc') || 'Ask me anything about your admin panel.'}
512
+ {i18n.t('chat.welcomeDesc') || 'Ask me anything about your admin panel.'}
510
513
  </p>
511
514
  <div class="flex flex-col gap-2 w-full max-w-[240px]">
512
515
  {#each suggestions as suggestion, _i (_i)}
@@ -551,7 +554,7 @@
551
554
  {:else}
552
555
  <div class="flex items-center gap-1.5 text-muted-foreground">
553
556
  <Loader2 class="h-3.5 w-3.5 animate-spin" />
554
- <span class="text-xs">{t('chat.thinking') || 'Thinking...'}</span>
557
+ <span class="text-xs">{i18n.t('chat.thinking') || 'Thinking...'}</span>
555
558
  </div>
556
559
  {/if}
557
560
  {:else}
@@ -569,7 +572,7 @@
569
572
  <textarea
570
573
  bind:value={inputValue}
571
574
  onkeydown={handleKeydown}
572
- placeholder={t('chat.placeholder') || 'Type a message...'}
575
+ placeholder={i18n.t('chat.placeholder') || 'Type a message...'}
573
576
  rows={1}
574
577
  disabled={isStreaming}
575
578
  class="flex-1 resize-none rounded-xl border border-input bg-background px-3 py-2.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 disabled:opacity-50 max-h-[100px] min-h-[40px]"
@@ -597,7 +600,7 @@
597
600
  </div>
598
601
  <p class="text-[10px] text-muted-foreground mt-1.5 text-center">
599
602
  <kbd class="px-1 py-0.5 rounded border border-border bg-muted text-[9px] font-mono">Ctrl+Shift+L</kbd>
600
- {t('chat.shortcutHint') || 'to toggle'}
603
+ {i18n.t('chat.shortcutHint') || 'to toggle'}
601
604
  </p>
602
605
  </div>
603
606
  {/if}
@@ -1,14 +1,16 @@
1
1
  <script lang="ts">
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
- import { t } from '@svadmin/core/i18n';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
4
  import { useSelect } from '@svadmin/core';
5
5
  import type { Filter } from '@svadmin/core';
6
6
  import { Button } from './ui/button/index.js';
7
7
  import TooltipButton from './TooltipButton.svelte';
8
- import { Command } from 'cmdk-sv';
8
+ import { Command } from './ui/command/index.js';
9
9
  import { Skeleton } from './ui/skeleton/index.js';
10
10
  import { ChevronsUpDown, Check, X, Search } from '@lucide/svelte';
11
11
 
12
+ const i18n = useTranslation();
13
+
12
14
  function clickOutside(node: HTMLElement) {
13
15
  const handler = (e: MouseEvent) => {
14
16
  if (!node.contains(e.target as Node)) open = false;
@@ -89,7 +91,7 @@
89
91
  >
90
92
  {#if selectedLabel}
91
93
  <span class="truncate">{selectedLabel}</span>
92
- <TooltipButton tooltip={t('common.clear')} size="icon-sm" class="h-5 w-5 ml-1" onclick={handleClear}>
94
+ <TooltipButton tooltip={i18n.t('common.clear')} size="icon-sm" class="h-5 w-5 ml-1" onclick={handleClear}>
93
95
  <X class="h-3 w-3" />
94
96
  </TooltipButton>
95
97
  {:else}
@@ -125,7 +127,7 @@
125
127
  <Command.Item
126
128
  value={opt.label}
127
129
  onSelect={() => handleSelect(opt.value)}
128
- class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground"
130
+ class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected]:bg-accent data-[selected]:text-accent-foreground"
129
131
  >
130
132
  <Check class="mr-2 h-3.5 w-3.5 {value === opt.value ? 'opacity-100' : 'opacity-0'}" />
131
133
  {opt.label}
@@ -1,12 +1,12 @@
1
1
  <script lang="ts">
2
- import { getResources, getAgentProvider, getChatProvider } from '@svadmin/core';
3
- import { navigate } from '@svadmin/core/router';
4
- import { t } from '@svadmin/core/i18n';
5
- import { toggleTheme } from '@svadmin/core';
6
- import { Command } from 'cmdk-sv';
2
+ import { captureAdminContext, getResources, getAgentProvider, getChatProvider, toggleTheme } from '@svadmin/core';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+ import { Command } from './ui/command/index.js';
7
5
  import * as Dialog from './ui/dialog/index.js';
8
6
  import { Search, LayoutDashboard, Plus, Sun, FileText, Sparkles } from '@lucide/svelte';
9
7
 
8
+ const i18n = useTranslation();
9
+
10
10
  interface Props {
11
11
  open?: boolean;
12
12
  /** Callback fired when user selects "Ask AI" with a query string */
@@ -14,6 +14,7 @@
14
14
  }
15
15
 
16
16
  let { open = $bindable(false), onAskAI }: Props = $props();
17
+ const adminContext = captureAdminContext();
17
18
  let searchValue = $state('');
18
19
 
19
20
  const resources = $derived(getResources());
@@ -37,17 +38,17 @@
37
38
  close();
38
39
  }
39
40
 
40
- const itemClass = 'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50';
41
+ const itemClass = 'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected]:bg-accent data-[selected]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50';
41
42
  </script>
42
43
 
43
44
  <Dialog.Dialog bind:open>
44
45
  <Dialog.DialogContent class="overflow-hidden p-0 sm:max-w-[560px]">
45
- <Command.Root class="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-semibold [&_[data-cmdk-group-heading]]:uppercase [&_[data-cmdk-group-heading]]:tracking-wider">
46
+ <Command.Root class="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[data-command-group-heading]]:text-muted-foreground [&_[data-command-group-heading]]:px-2 [&_[data-command-group-heading]]:py-1.5 [&_[data-command-group-heading]]:text-xs [&_[data-command-group-heading]]:font-semibold [&_[data-command-group-heading]]:uppercase [&_[data-command-group-heading]]:tracking-wider">
46
47
  <div class="flex items-center border-b px-3" data-cmdk-input-wrapper="">
47
48
  <Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
48
49
  <Command.Input
49
50
  bind:value={searchValue}
50
- placeholder={hasAI ? (t('commandPalette.searchOrAsk') || 'Search or ask AI...') : t('common.search')}
51
+ placeholder={hasAI ? (i18n.t('commandPalette.searchOrAsk') || 'Search or ask AI...') : i18n.t('common.search')}
51
52
  class="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
52
53
  />
53
54
  <kbd class="ml-2 text-[10px] font-mono font-medium text-muted-foreground bg-muted px-1.5 py-0.5 rounded border border-border">ESC</kbd>
@@ -60,10 +61,10 @@
60
61
  onclick={askAI}
61
62
  >
62
63
  <Sparkles class="h-3.5 w-3.5" />
63
- {t('commandPalette.askAI') || 'Ask AI'}: "{searchValue.trim()}"
64
+ {i18n.t('commandPalette.askAI') || 'Ask AI'}: "{searchValue.trim()}"
64
65
  </button>
65
66
  {:else}
66
- {t('common.noData')}
67
+ {i18n.t('common.noData')}
67
68
  {/if}
68
69
  </Command.Empty>
69
70
 
@@ -76,7 +77,7 @@
76
77
  class={itemClass}
77
78
  >
78
79
  <Sparkles class="h-4 w-4 text-primary" />
79
- <span class="text-primary font-medium">{t('commandPalette.askAI') || 'Ask AI'}</span>
80
+ <span class="text-primary font-medium">{i18n.t('commandPalette.askAI') || 'Ask AI'}</span>
80
81
  <span class="text-muted-foreground truncate ml-1">"{searchValue.trim()}"</span>
81
82
  </Command.Item>
82
83
  </Command.Group>
@@ -84,19 +85,19 @@
84
85
  {/if}
85
86
 
86
87
  <!-- Navigation -->
87
- <Command.Group heading={t('common.home')}>
88
+ <Command.Group heading={i18n.t('common.home')}>
88
89
  <Command.Item
89
90
  value="home"
90
- onSelect={() => act(() => navigate('/'))}
91
+ onSelect={() => act(() => adminContext.navigate('/'))}
91
92
  class={itemClass}
92
93
  >
93
94
  <LayoutDashboard class="h-4 w-4 text-muted-foreground" />
94
- {t('common.home')}
95
+ {i18n.t('common.home')}
95
96
  </Command.Item>
96
97
  {#each resources as r, _i (_i)}
97
98
  <Command.Item
98
99
  value={r.name}
99
- onSelect={() => act(() => navigate(`/${r.name}`))}
100
+ onSelect={() => act(() => adminContext.navigate(`/${r.name}`))}
100
101
  class={itemClass}
101
102
  >
102
103
  <FileText class="h-4 w-4 text-muted-foreground" />
@@ -108,15 +109,15 @@
108
109
  <Command.Separator class="mx-1 my-1 h-px bg-border" />
109
110
 
110
111
  <!-- Actions -->
111
- <Command.Group heading={t('common.actions')}>
112
+ <Command.Group heading={i18n.t('common.actions')}>
112
113
  {#each resources as r, _i (_i)}
113
114
  <Command.Item
114
115
  value={"create-" + r.name}
115
- onSelect={() => act(() => navigate(`/${r.name}/create`))}
116
+ onSelect={() => act(() => adminContext.navigate(`/${r.name}/create`))}
116
117
  class={itemClass}
117
118
  >
118
119
  <Plus class="h-4 w-4 text-muted-foreground" />
119
- {t('common.create')} {r.label}
120
+ {i18n.t('common.create')} {r.label}
120
121
  </Command.Item>
121
122
  {/each}
122
123
  <Command.Item
@@ -125,11 +126,10 @@
125
126
  class={itemClass}
126
127
  >
127
128
  <Sun class="h-4 w-4 text-muted-foreground" />
128
- {t('common.toggleTheme')}
129
+ {i18n.t('common.toggleTheme')}
129
130
  </Command.Item>
130
131
  </Command.Group>
131
132
  </Command.List>
132
133
  </Command.Root>
133
134
  </Dialog.DialogContent>
134
135
  </Dialog.Dialog>
135
-
@@ -1,17 +1,19 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import { AlertTriangle, Copy, CheckCircle } from '@lucide/svelte';
4
4
  import { Button } from './ui/button/index.js';
5
5
  import TooltipButton from './TooltipButton.svelte';
6
6
  import * as Card from './ui/card/index.js';
7
7
 
8
+ const i18n = useTranslation();
9
+
8
10
  let { title, missingVars = [], envTemplate = '' } = $props<{
9
11
  title?: string;
10
12
  missingVars?: { key: string; description?: string }[];
11
13
  envTemplate?: string;
12
14
  }>();
13
15
 
14
- const displayTitle = $derived(title ?? t('config.missingEnvTitle'));
16
+ const displayTitle = $derived(title ?? i18n.t('config.missingEnvTitle'));
15
17
 
16
18
  let copied = $state<Record<string, boolean>>({});
17
19
  let copyTimers = $state<Record<string, ReturnType<typeof setTimeout>>>({});
@@ -57,7 +59,7 @@
57
59
  </div>
58
60
  <Card.CardTitle class="text-xl">{displayTitle}</Card.CardTitle>
59
61
  <p class="text-sm text-muted-foreground">
60
- {t('config.missingEnvDescription')}
62
+ {i18n.t('config.missingEnvDescription')}
61
63
  </p>
62
64
  </Card.CardHeader>
63
65
  <Card.CardContent class="space-y-4">
@@ -72,7 +74,7 @@
72
74
  {/if}
73
75
  </div>
74
76
  <TooltipButton
75
- tooltip={t('common.copy')}
77
+ tooltip={i18n.t('common.copy')}
76
78
  variant="ghost"
77
79
  size="icon"
78
80
  class="h-7 w-7 shrink-0"
@@ -92,9 +94,9 @@
92
94
  {#if envTemplate}
93
95
  <div class="rounded-lg border overflow-hidden">
94
96
  <div class="flex items-center justify-between px-3 py-2 bg-muted/50 border-b border-border/50">
95
- <span class="text-xs font-medium text-muted-foreground">{t('config.envFilePath')}</span>
97
+ <span class="text-xs font-medium text-muted-foreground">{i18n.t('config.envFilePath')}</span>
96
98
  <TooltipButton
97
- tooltip={t('common.copyAll')}
99
+ tooltip={i18n.t('common.copyAll')}
98
100
  variant="ghost"
99
101
  size="sm"
100
102
  class="h-7 gap-1"
@@ -102,10 +104,10 @@
102
104
  >
103
105
  {#if copied['__all__']}
104
106
  <CheckCircle class="h-3.5 w-3.5 text-green-500" />
105
- <span class="text-xs">{t('common.copied')}</span>
107
+ <span class="text-xs">{i18n.t('common.copied')}</span>
106
108
  {:else}
107
109
  <Copy class="h-3.5 w-3.5" />
108
- <span class="text-xs">{t('common.copyAll')}</span>
110
+ <span class="text-xs">{i18n.t('common.copyAll')}</span>
109
111
  {/if}
110
112
  </TooltipButton>
111
113
  </div>
@@ -114,11 +116,11 @@
114
116
  {/if}
115
117
 
116
118
  <p class="text-xs text-muted-foreground text-center mt-4">
117
- {t('config.reload')}
119
+ {i18n.t('config.reload')}
118
120
  </p>
119
121
 
120
122
  <Button variant="outline" class="w-full" onclick={() => window.location.reload()}>
121
- {t('config.reloadButton')}
123
+ {i18n.t('config.reloadButton')}
122
124
  </Button>
123
125
  </Card.CardContent>
124
126
  </Card.Card>
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
2
  import * as AlertDialog from './ui/alert-dialog/index.js';
3
3
  import { Button } from './ui/button/index.js';
4
- import { t } from '@svadmin/core/i18n';
4
+ import { useTranslation } from '@svadmin/core/i18n';
5
+
6
+ const i18n = useTranslation();
5
7
 
6
8
  let { open = $bindable(false), title, message = '',
7
9
  confirmText, cancelText, variant = 'danger',
@@ -22,9 +24,9 @@
22
24
  info: 'default' as const,
23
25
  };
24
26
 
25
- const resolvedTitle = $derived(title ?? t('common.confirmAction'));
26
- const resolvedConfirmText = $derived(confirmText ?? t('common.confirm'));
27
- const resolvedCancelText = $derived(cancelText ?? t('common.cancel'));
27
+ const resolvedTitle = $derived(title ?? i18n.t('common.confirmAction'));
28
+ const resolvedConfirmText = $derived(confirmText ?? i18n.t('common.confirm'));
29
+ const resolvedCancelText = $derived(cancelText ?? i18n.t('common.cancel'));
28
30
 
29
31
  let confirmed = $state(false);
30
32
 
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { getResource } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
4
  import type { Snippet } from 'svelte';
5
5
  import PageHeader from './PageHeader.svelte';
6
6
  import AutoForm from './AutoForm.svelte';
7
7
  import ListButton from './buttons/ListButton.svelte';
8
8
 
9
+ const i18n = useTranslation();
10
+
9
11
  interface Props {
10
12
  resourceName: string;
11
13
  title?: string;
@@ -21,7 +23,7 @@
21
23
  }: Props = $props();
22
24
 
23
25
  const resource = $derived(getResource(resourceName));
24
- const pageTitle = $derived(title ?? `${t('common.create')}${resource.label}`);
26
+ const pageTitle = $derived(title ?? `${i18n.t('common.create')}${resource.label}`);
25
27
  </script>
26
28
 
27
29
  <div class="space-y-6 {className}">
@@ -1,10 +1,9 @@
1
1
  <script lang="ts">
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  import { fly } from 'svelte/transition';
4
- import { getResources } from '@svadmin/core';
5
- import { getTheme, getColorTheme } from '@svadmin/core';
6
- import { getLocale, t } from '@svadmin/core/i18n';
7
- import { currentPath } from '@svadmin/core/router';
4
+ import { captureAdminContext, getResources, getTheme, getColorTheme } from '@svadmin/core';
5
+ import { useTranslation } from '@svadmin/core/i18n';
6
+
8
7
  import TooltipButton from './TooltipButton.svelte';
9
8
  import * as Tabs from './ui/tabs/index.js';
10
9
  import { Badge } from './ui/badge/index.js';
@@ -13,6 +12,9 @@
13
12
  import { X, Bug, ChevronDown, ChevronUp, Wand2 } from '@lucide/svelte';
14
13
  import InferencerPanel from './InferencerPanel.svelte';
15
14
 
15
+ const i18n = useTranslation();
16
+
17
+ const adminContext = captureAdminContext();
16
18
  let visible = $state(false);
17
19
  let collapsed = $state(false);
18
20
 
@@ -28,10 +30,10 @@
28
30
  }
29
31
 
30
32
  const resources = $derived.by(() => { try { return getResources(); } catch { return []; } });
31
- const path = $derived(currentPath());
33
+ const path = $derived(adminContext.currentPath());
32
34
  const theme = $derived(getTheme());
33
35
  const colorTheme = $derived(getColorTheme());
34
- const locale = $derived(getLocale());
36
+ const locale = $derived(i18n.locale);
35
37
 
36
38
  // Auto-trace state changes in dev mode via Svelte 5 $inspect
37
39
  if ((import.meta as any).env?.DEV) {
@@ -56,14 +58,14 @@
56
58
  <span>svadmin DevTools</span>
57
59
  </div>
58
60
  <div class="flex gap-1">
59
- <TooltipButton tooltip={collapsed ? t('common.expand') : t('common.collapse')} variant="ghost" size="icon" class="h-6 w-6" onclick={() => collapsed = !collapsed}>
61
+ <TooltipButton tooltip={collapsed ? i18n.t('common.expand') : i18n.t('common.collapse')} variant="ghost" size="icon" class="h-6 w-6" onclick={() => collapsed = !collapsed}>
60
62
  {#if collapsed}
61
63
  <ChevronUp class="h-3.5 w-3.5" />
62
64
  {:else}
63
65
  <ChevronDown class="h-3.5 w-3.5" />
64
66
  {/if}
65
67
  </TooltipButton>
66
- <TooltipButton tooltip={t('common.close')} variant="ghost" size="icon" class="h-6 w-6" onclick={toggle}>
68
+ <TooltipButton tooltip={i18n.t('common.close')} variant="ghost" size="icon" class="h-6 w-6" onclick={toggle}>
67
69
  <X class="h-3.5 w-3.5" />
68
70
  </TooltipButton>
69
71
  </div>
@@ -138,7 +140,7 @@
138
140
  </div>
139
141
  {:else}
140
142
  <TooltipButton
141
- tooltip={t('devtools.title')}
143
+ tooltip={i18n.t('devtools.title')}
142
144
  variant="default"
143
145
  size="icon"
144
146
  class="fixed bottom-4 right-4 z-[9999] h-9 w-9 rounded-full shadow-lg opacity-60 hover:opacity-100 hover:scale-110 transition-all"
@@ -1,9 +1,12 @@
1
1
  <script lang="ts">
2
2
  import { getResource } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+
4
5
  import * as Sheet from './ui/sheet/index.js';
5
6
  import AutoForm from './AutoForm.svelte';
6
7
 
8
+ const i18n = useTranslation();
9
+
7
10
  let { resourceName, mode = 'create', id, open = $bindable(false), side = 'right', onSuccess } = $props<{
8
11
  resourceName: string;
9
12
  mode?: 'create' | 'edit';
@@ -25,7 +28,7 @@
25
28
  <Sheet.Content>
26
29
  <Sheet.Header>
27
30
  <Sheet.Title>
28
- {mode === 'create' ? `${t('common.create')}${resource.label}` : `${t('common.edit')}${resource.label}`}
31
+ {mode === 'create' ? `${i18n.t('common.create')}${resource.label}` : `${i18n.t('common.edit')}${resource.label}`}
29
32
  </Sheet.Title>
30
33
  </Sheet.Header>
31
34
  <div class="flex-1 overflow-y-auto">
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- import { getResource } from '@svadmin/core';
3
- import { navigate } from '@svadmin/core/router';
4
- import { t } from '@svadmin/core/i18n';
2
+ import { captureAdminContext, getResource } from '@svadmin/core';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+
5
5
  import type { Snippet } from 'svelte';
6
6
  import PageHeader from './PageHeader.svelte';
7
7
  import AutoForm from './AutoForm.svelte';
@@ -10,6 +10,8 @@
10
10
  import RefreshButton from './buttons/RefreshButton.svelte';
11
11
  import DeleteButton from './buttons/DeleteButton.svelte';
12
12
 
13
+ const i18n = useTranslation();
14
+
13
15
  interface Props {
14
16
  resourceName: string;
15
17
  id: string | number;
@@ -27,9 +29,10 @@
27
29
  headerActions,
28
30
  class: className = '',
29
31
  }: Props = $props();
32
+ const adminContext = captureAdminContext();
30
33
 
31
34
  const resource = $derived(getResource(resourceName));
32
- const pageTitle = $derived(title ?? `${t('common.edit')} ${resource.label} #${id}`);
35
+ const pageTitle = $derived(title ?? `${i18n.t('common.edit')} ${resource.label} #${id}`);
33
36
  const showDelete = $derived(canDelete ?? resource.canDelete !== false);
34
37
  </script>
35
38
 
@@ -40,7 +43,7 @@
40
43
  <ShowButton resource={resourceName} recordItemId={id} hideText />
41
44
  <RefreshButton resource={resourceName} hideText />
42
45
  {#if showDelete !== false}
43
- <DeleteButton resource={resourceName} recordItemId={id} hideText onSuccess={() => navigate(`/${resourceName}`)} />
46
+ <DeleteButton resource={resourceName} recordItemId={id} hideText onSuccess={() => adminContext.navigate(`/${resourceName}`)} />
44
47
  {/if}
45
48
  {#if headerActions}
46
49
  {@render headerActions()}
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { scale } from 'svelte/transition';
3
3
  import { PackageOpen } from '@lucide/svelte';
4
- import { t } from '@svadmin/core';
4
+ import { useTranslation } from '@svadmin/core';
5
+
6
+ const i18n = useTranslation();
5
7
 
6
8
  let {
7
- title = t('empty.title'),
8
- description = t('empty.description'),
9
+ title = i18n.t('empty.title'),
10
+ description = i18n.t('empty.description'),
9
11
  children,
10
12
  } = $props<{
11
13
  title?: string;
@@ -3,7 +3,9 @@
3
3
  import { AlertTriangle } from '@lucide/svelte';
4
4
  import { Button } from './ui/button/index.js';
5
5
  import * as Alert from './ui/alert/index.js';
6
- import { t } from '@svadmin/core/i18n';
6
+ import { useTranslation } from '@svadmin/core/i18n';
7
+
8
+ const i18n = useTranslation();
7
9
 
8
10
  let {
9
11
  children,
@@ -32,14 +34,14 @@
32
34
  <div class="flex items-start gap-3">
33
35
  <AlertTriangle class="h-5 w-5 mt-0.5 shrink-0" />
34
36
  <div class="space-y-2">
35
- <Alert.Title>{t('common.error')}</Alert.Title>
37
+ <Alert.Title>{i18n.t('common.error')}</Alert.Title>
36
38
  <Alert.Description class="text-sm">
37
39
  {error instanceof Error ? error.message : String(error)}
38
40
  </Alert.Description>
39
41
  </div>
40
42
  </div>
41
43
  <Button variant="outline" size="sm" onclick={reset} class="w-full">
42
- {t('common.retry')}
44
+ {i18n.t('common.retry')}
43
45
  </Button>
44
46
  </Alert.Root>
45
47
  </div>
@@ -1,13 +1,15 @@
1
1
  <script lang="ts">
2
2
  import { AlertTriangle, RefreshCw } from '@lucide/svelte';
3
- import { t } from '@svadmin/core';
3
+ import { useTranslation } from '@svadmin/core';
4
4
  import { Button } from './ui/button';
5
5
  import * as Alert from './ui/alert/index.js';
6
6
 
7
+ const i18n = useTranslation();
8
+
7
9
  let {
8
10
  error,
9
- title = t('error.title'),
10
- description = t('error.description'),
11
+ title = i18n.t('error.title'),
12
+ description = i18n.t('error.description'),
11
13
  retry,
12
14
  } = $props<{
13
15
  error?: Error | null;
@@ -28,7 +30,7 @@
28
30
  <div class="mt-4">
29
31
  <Button variant="outline" size="sm" onclick={retry}>
30
32
  <RefreshCw class="h-3 w-3" data-icon="inline-start" />
31
- {t('buttons.retry')}
33
+ {i18n.t('buttons.retry')}
32
34
  </Button>
33
35
  </div>
34
36
  {/if}