rune-lab 0.0.19 → 0.0.20

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 (80) hide show
  1. package/README.md +28 -6
  2. package/dist/actions/portal.js +6 -1
  3. package/dist/components/Icon.svelte +13 -2
  4. package/dist/components/RuneProvider.svelte +78 -0
  5. package/dist/components/RuneProvider.svelte.d.ts +9 -0
  6. package/dist/composables/useRuneLab.d.ts +17 -0
  7. package/dist/composables/useRuneLab.js +19 -0
  8. package/dist/config.d.ts +4 -85
  9. package/dist/config.js +6 -32
  10. package/dist/context.d.ts +11 -0
  11. package/dist/context.js +12 -0
  12. package/dist/devtools/API_Monitor.svelte +5 -2
  13. package/dist/devtools/Toaster.svelte +13 -8
  14. package/dist/devtools/createConfigStore.svelte.d.ts +11 -1
  15. package/dist/devtools/createConfigStore.svelte.js +13 -11
  16. package/dist/features/command-palette/CommandPalette.svelte +12 -4
  17. package/dist/features/config/components/AppSettingSelector.svelte +85 -3
  18. package/dist/features/config/components/AppSettingSelector.svelte.d.ts +2 -0
  19. package/dist/features/config/components/CurrencySelector.svelte +27 -9
  20. package/dist/features/config/components/CurrencySelector.svelte.d.ts +3 -1
  21. package/dist/features/config/components/LanguageSelector.svelte +21 -6
  22. package/dist/features/config/components/LanguageSelector.svelte.d.ts +3 -1
  23. package/dist/features/config/components/ThemeSelector.svelte +23 -8
  24. package/dist/features/config/components/ThemeSelector.svelte.d.ts +3 -1
  25. package/dist/features/detail-panels/DashboardPanel.svelte +34 -17
  26. package/dist/features/detail-panels/ShortcutsPanel.svelte +185 -88
  27. package/dist/features/detail-panels/ShowcasePanel.svelte +20 -6
  28. package/dist/features/shortcuts/ShortcutPalette.svelte +14 -6
  29. package/dist/index.d.ts +7 -1
  30. package/dist/index.js +9 -6
  31. package/dist/layout/NavigationPanel.svelte +63 -117
  32. package/dist/layout/WorkspaceLayout.svelte +74 -30
  33. package/dist/layout/WorkspaceLayout.svelte.d.ts +2 -2
  34. package/dist/layout/WorkspaceStrip.svelte +11 -3
  35. package/dist/layout/index.d.ts +1 -1
  36. package/dist/layout/index.js +1 -1
  37. package/dist/paraglide/runtime.d.ts +43 -2
  38. package/dist/paraglide/runtime.js +143 -23
  39. package/dist/paraglide/server.js +37 -14
  40. package/dist/persistence/drivers.d.ts +10 -0
  41. package/dist/persistence/drivers.js +71 -0
  42. package/dist/persistence/types.d.ts +17 -0
  43. package/dist/persistence/types.js +2 -0
  44. package/dist/showcase/AppStateInspector.svelte +26 -11
  45. package/dist/showcase/Showcase.svelte +4 -1
  46. package/dist/state/api.svelte.d.ts +3 -3
  47. package/dist/state/api.svelte.js +9 -2
  48. package/dist/state/app.svelte.d.ts +3 -3
  49. package/dist/state/app.svelte.js +14 -4
  50. package/dist/state/commands.svelte.d.ts +21 -4
  51. package/dist/state/commands.svelte.js +92 -90
  52. package/dist/state/currency.svelte.d.ts +4 -1
  53. package/dist/state/currency.svelte.js +16 -8
  54. package/dist/state/index.d.ts +9 -9
  55. package/dist/state/index.js +10 -9
  56. package/dist/state/language.svelte.d.ts +4 -1
  57. package/dist/state/language.svelte.js +27 -8
  58. package/dist/state/layout.svelte.d.ts +10 -3
  59. package/dist/state/layout.svelte.js +56 -14
  60. package/dist/state/shortcuts.svelte.d.ts +56 -6
  61. package/dist/state/shortcuts.svelte.js +21 -69
  62. package/dist/state/theme.svelte.d.ts +4 -1
  63. package/dist/state/theme.svelte.js +16 -8
  64. package/dist/state/toast.svelte.d.ts +3 -3
  65. package/dist/state/toast.svelte.js +9 -2
  66. package/package.json +9 -9
  67. package/dist/features/command-palette/commands.svelte.d.ts +0 -8
  68. package/dist/features/command-palette/commands.svelte.js +0 -5
  69. package/dist/features/config/stores/api.svelte.d.ts +0 -13
  70. package/dist/features/config/stores/api.svelte.js +0 -5
  71. package/dist/features/config/stores/app.svelte.d.ts +0 -13
  72. package/dist/features/config/stores/app.svelte.js +0 -5
  73. package/dist/features/config/stores/currency.svelte.d.ts +0 -8
  74. package/dist/features/config/stores/currency.svelte.js +0 -5
  75. package/dist/features/config/stores/language.svelte.d.ts +0 -8
  76. package/dist/features/config/stores/language.svelte.js +0 -5
  77. package/dist/features/config/stores/theme.svelte.d.ts +0 -8
  78. package/dist/features/config/stores/theme.svelte.js +0 -5
  79. package/dist/features/config/stores/toast.svelte.d.ts +0 -9
  80. package/dist/features/config/stores/toast.svelte.js +0 -5
package/README.md CHANGED
@@ -60,16 +60,18 @@ bun install rune-lab
60
60
 
61
61
  ## Project Configuration
62
62
 
63
- ### Deno / SSR projects
63
+ After installing, two configuration steps are required to ensure components are
64
+ compiled and styled correctly in your consuming project.
64
65
 
65
- Because Deno's Vite SSR module runner externalizes `node_modules` by default,
66
- `.svelte` files from this package would be loaded as raw ES modules, bypassing
67
- the Svelte compiler. To prevent this, you need to tell Vite to process
68
- `rune-lab` through its plugin pipeline.
66
+ ### Step 1 Vite: process `rune-lab` through the Svelte compiler
69
67
 
70
- Add the following to your consuming project's `vite.config.ts`:
68
+ Vite's SSR module runner externalizes `node_modules` by default, which means
69
+ `.svelte` files from this package would be loaded as raw ES modules, bypassing
70
+ the Svelte compiler entirely. Add the following to your `vite.config.ts` to
71
+ force Vite to process `rune-lab` through its plugin pipeline:
71
72
 
72
73
  ```ts
74
+ // vite.config.ts
73
75
  export default defineConfig({
74
76
  plugins: [sveltekit()],
75
77
  ssr: {
@@ -81,6 +83,26 @@ export default defineConfig({
81
83
  This ensures the Svelte plugin transforms the components correctly during SSR,
82
84
  just as it would for your own source files.
83
85
 
86
+ ### Step 2 — Tailwind CSS: scan `rune-lab` for utility classes
87
+
88
+ Tailwind only generates CSS for the classes it can find by scanning your source
89
+ files. Because `rune-lab` lives in `node_modules`, its DaisyUI classes are not
90
+ scanned by default and the components will appear unstyled.
91
+
92
+ Add a `@source` directive to your project's main CSS file to tell Tailwind to
93
+ also scan the `rune-lab` dist output:
94
+
95
+ ```css
96
+ /* app.css / layout.css / global.css — wherever you import Tailwind */
97
+ @import "tailwindcss";
98
+ @source "../node_modules/rune-lab/dist"; /* 👈 add this */
99
+ ```
100
+
101
+ > **Note:** Adjust the relative path to `node_modules` if your CSS file lives at
102
+ > a different depth in your project tree. With both steps in place, all DaisyUI
103
+ > component classes used by `rune-lab` will be included in your build and theme
104
+ > switching will work across library components and your own code alike.
105
+
84
106
  ## License
85
107
 
86
108
  MIT License - See [LICENSE](LICENSE) for details.
@@ -3,22 +3,27 @@
3
3
  */
4
4
  export function portal(node, target = "body") {
5
5
  let targetNode;
6
+ let isTeleported = false;
6
7
  function update(newTarget) {
7
8
  if (typeof newTarget === "string") {
8
9
  targetNode = document.querySelector(newTarget);
10
+ if (!targetNode && import.meta.env?.DEV) {
11
+ console.warn(`portal target selector "${newTarget}" not found in document.`);
12
+ }
9
13
  }
10
14
  else {
11
15
  targetNode = newTarget;
12
16
  }
13
17
  if (targetNode) {
14
18
  targetNode.appendChild(node);
19
+ isTeleported = true;
15
20
  }
16
21
  }
17
22
  update(target);
18
23
  return {
19
24
  update,
20
25
  destroy() {
21
- if (node.parentNode) {
26
+ if (isTeleported && node.parentNode) {
22
27
  node.parentNode.removeChild(node);
23
28
  }
24
29
  },
@@ -15,8 +15,19 @@
15
15
  info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
16
16
  shortcut: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
17
17
  close: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
18
- external: '<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>'
18
+ external: '<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>',
19
+ unknown: '<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/>'
19
20
  };
21
+
22
+ const path = $derived.by(() => {
23
+ if (!paths[name]) {
24
+ if (import.meta.env?.DEV) {
25
+ console.warn(`[Icon] Unknown icon name: "${name}"`);
26
+ }
27
+ return paths.unknown;
28
+ }
29
+ return paths[name];
30
+ });
20
31
  </script>
21
32
 
22
33
  <svg
@@ -30,5 +41,5 @@
30
41
  class="{size} {className}"
31
42
  >
32
43
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
33
- {@html paths[name]}
44
+ {@html path}
34
45
  </svg>
@@ -0,0 +1,78 @@
1
+ <script lang="ts">
2
+ import { setContext, type Snippet } from "svelte";
3
+ import {
4
+ createAppStore,
5
+ createLayoutStore,
6
+ createCommandStore,
7
+ createApiStore,
8
+ createToastStore,
9
+ createThemeStore,
10
+ createLanguageStore,
11
+ createCurrencyStore,
12
+ createShortcutStore,
13
+ } from "../state/index";
14
+ import { Toaster, CommandPalette, ShortcutPalette } from "../index";
15
+
16
+ import type { PersistenceDriver } from "../persistence/types";
17
+ import { RUNE_LAB_CONTEXT } from "../context";
18
+
19
+ let { children, persistence } = $props<{
20
+ children: Snippet;
21
+ persistence?: PersistenceDriver;
22
+ }>();
23
+
24
+ // 1. Initialize Base Configuration Stores
25
+ const appStore = createAppStore();
26
+ const apiStore = createApiStore();
27
+ const toastStore = createToastStore();
28
+ // We use a closure approach (`() => persistence`) as supported by the updated config definitions,
29
+ // to strictly respect Svelte 5 state capturing validations without disabling them globally.
30
+ const themeStore = createThemeStore(() => persistence);
31
+ const languageStore = createLanguageStore(() => persistence);
32
+ const currencyStore = createCurrencyStore(() => persistence);
33
+ const shortcutStore = createShortcutStore();
34
+
35
+ // 2. Initialize Complex Stores (Dependency Injection)
36
+ const layoutStore = createLayoutStore(() => persistence);
37
+ const commandStore = createCommandStore({
38
+ appStore,
39
+ apiStore,
40
+ toastStore,
41
+ themeStore,
42
+ languageStore,
43
+ currencyStore,
44
+ });
45
+
46
+ // 3. Provide Contexts
47
+ setContext(RUNE_LAB_CONTEXT.app, appStore);
48
+ setContext(RUNE_LAB_CONTEXT.api, apiStore);
49
+ setContext(RUNE_LAB_CONTEXT.toast, toastStore);
50
+ setContext(RUNE_LAB_CONTEXT.theme, themeStore);
51
+ setContext(RUNE_LAB_CONTEXT.language, languageStore);
52
+ setContext(RUNE_LAB_CONTEXT.currency, currencyStore);
53
+ setContext(RUNE_LAB_CONTEXT.shortcut, shortcutStore);
54
+ setContext(RUNE_LAB_CONTEXT.layout, layoutStore);
55
+ setContext(RUNE_LAB_CONTEXT.commands, commandStore);
56
+
57
+ // Meta tags derived from app store state
58
+ const metaTags = $derived([
59
+ { name: "description", content: appStore.description },
60
+ { name: "author", content: appStore.author },
61
+ ]);
62
+ </script>
63
+
64
+ <svelte:head>
65
+ <title>{appStore.name}</title>
66
+ <link rel="icon" href={"/img/rune.png"} />
67
+ {#each metaTags as meta}
68
+ <meta name={meta.name} content={meta.content} />
69
+ {/each}
70
+ </svelte:head>
71
+
72
+ <!-- Global Overlays -->
73
+ <Toaster />
74
+ <CommandPalette />
75
+ <ShortcutPalette />
76
+
77
+ <!-- Render Children -->
78
+ {@render children()}
@@ -0,0 +1,9 @@
1
+ import { type Snippet } from "svelte";
2
+ import type { PersistenceDriver } from "../persistence/types";
3
+ type $$ComponentProps = {
4
+ children: Snippet;
5
+ persistence?: PersistenceDriver;
6
+ };
7
+ declare const RuneProvider: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type RuneProvider = ReturnType<typeof RuneProvider>;
9
+ export default RuneProvider;
@@ -0,0 +1,17 @@
1
+ import { getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore } from "../state/index";
2
+ export interface RuneLabContext {
3
+ app: ReturnType<typeof getAppStore>;
4
+ api: ReturnType<typeof getApiStore>;
5
+ toast: ReturnType<typeof getToastStore>;
6
+ theme: ReturnType<typeof getThemeStore>;
7
+ language: ReturnType<typeof getLanguageStore>;
8
+ currency: ReturnType<typeof getCurrencyStore>;
9
+ shortcut: ReturnType<typeof getShortcutStore>;
10
+ layout: ReturnType<typeof getLayoutStore>;
11
+ commands: ReturnType<typeof getCommandStore>;
12
+ }
13
+ /**
14
+ * Retrieves all Rune Lab stores from the Svelte context tree.
15
+ * Must be called during component initialization (in the `<script>` block).
16
+ */
17
+ export declare function useRuneLab(): RuneLabContext;
@@ -0,0 +1,19 @@
1
+ // src/lib/composables/useRuneLab.ts
2
+ import { getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore, } from "../state/index";
3
+ /**
4
+ * Retrieves all Rune Lab stores from the Svelte context tree.
5
+ * Must be called during component initialization (in the `<script>` block).
6
+ */
7
+ export function useRuneLab() {
8
+ return {
9
+ app: getAppStore(),
10
+ api: getApiStore(),
11
+ toast: getToastStore(),
12
+ theme: getThemeStore(),
13
+ language: getLanguageStore(),
14
+ currency: getCurrencyStore(),
15
+ shortcut: getShortcutStore(),
16
+ layout: getLayoutStore(),
17
+ commands: getCommandStore(),
18
+ };
19
+ }
package/dist/config.d.ts CHANGED
@@ -1,85 +1,4 @@
1
- import { apiStore, appStore, commandStore, currencyStore, languageStore, themeStore, toastStore } from "./state/index";
2
- /**
3
- * `appConfig` single entry-point to configure and control the entire app.
4
- *
5
- * @example
6
- * ```ts
7
- * import { appConfig } from 'rune-lab/config';
8
- *
9
- * appConfig.app.init({ name: 'My App', version: '1.0.0' });
10
- * appConfig.api.init('https://api.example.com', 'v1');
11
- * appConfig.toast.success('Ready!');
12
- * ```
13
- */
14
- export declare const appConfig: {
15
- /** Application metadata (name, version, description…) */
16
- readonly app: {
17
- name: string;
18
- version: string;
19
- description: string;
20
- author: string;
21
- repository: string;
22
- license: string;
23
- homepage: string;
24
- "__#private@#initialized": boolean;
25
- init(data: Partial<import(".").AppData>): void;
26
- get info(): import(".").AppData;
27
- };
28
- /** API connection state and base URL */
29
- readonly api: {
30
- url: string;
31
- version: string;
32
- connectionState: import("./state/api.svelte").ConnectionState;
33
- isConnected: boolean;
34
- isLoading: boolean;
35
- get URL(): string;
36
- get VERSION(): string;
37
- get IS_CONNECTED(): boolean;
38
- get IS_LOADING(): boolean;
39
- reconnect(): Promise<void>;
40
- init(url: string, version?: string): void;
41
- };
42
- /** Active theme + available themes */
43
- readonly theme: {
44
- current: string;
45
- available: import(".").Theme[];
46
- set(id: string): void;
47
- get(id: string): import(".").Theme | undefined;
48
- getProp<K extends keyof import(".").Theme>(prop: K, id?: string | undefined): import(".").Theme[K] | undefined;
49
- };
50
- /** Active language / locale */
51
- readonly language: {
52
- current: string | undefined;
53
- available: import(".").Language[];
54
- set(id: string | undefined): void;
55
- get(id: string | undefined): import(".").Language | undefined;
56
- getProp<K extends keyof import(".").Language>(prop: K, id?: string | undefined): import(".").Language[K] | undefined;
57
- };
58
- /** Active currency */
59
- readonly currency: {
60
- current: string | number;
61
- available: import(".").Currency[];
62
- set(id: string | number): void;
63
- get(id: string | number): import(".").Currency | undefined;
64
- getProp<K extends keyof import(".").Currency>(prop: K, id?: string | number | undefined): import(".").Currency[K] | undefined;
65
- };
66
- /** Toast notification queue */
67
- readonly toast: {
68
- toasts: import("./state/toast.svelte").Toast[];
69
- send(message: string, type?: import("./state/toast.svelte").ToastType, duration?: number): void;
70
- dismiss(id: string): void;
71
- success(msg: string): void;
72
- error(msg: string): void;
73
- warn(msg: string): void;
74
- info(msg: string): void;
75
- };
76
- /** Command palette registry */
77
- readonly commands: {
78
- commands: import(".").Command[];
79
- register(command: import(".").Command): void;
80
- unregister(id: string): void;
81
- search(query: string, parentId?: string): import(".").Command[];
82
- "__#private@#findById"(id: string, list: import(".").Command[]): import(".").Command | undefined;
83
- };
84
- };
85
- export { apiStore, appStore, commandStore, currencyStore, languageStore, themeStore, toastStore, };
1
+ export { AppStore, createAppStore, getAppStore } from "./state/app.svelte";
2
+ export { createLayoutStore, getLayoutStore, LayoutStore, } from "./state/layout.svelte";
3
+ export { type Command, CommandStore, createCommandStore, getCommandStore, } from "./state/commands.svelte";
4
+ export { createApiStore, createCurrencyStore, createLanguageStore, createShortcutStore, createThemeStore, createToastStore, getApiStore, getCurrencyStore, getLanguageStore, getShortcutStore, getThemeStore, getToastStore, } from "./state/index";
package/dist/config.js CHANGED
@@ -1,34 +1,8 @@
1
1
  // src/lib/config.ts
2
2
  // Unified facade over all app-level stores.
3
- // Import this instead of wiring each store individually.
4
- import { apiStore, appStore, commandStore, currencyStore, languageStore, themeStore, toastStore, } from "./state/index";
5
- /**
6
- * `appConfig` single entry-point to configure and control the entire app.
7
- *
8
- * @example
9
- * ```ts
10
- * import { appConfig } from 'rune-lab/config';
11
- *
12
- * appConfig.app.init({ name: 'My App', version: '1.0.0' });
13
- * appConfig.api.init('https://api.example.com', 'v1');
14
- * appConfig.toast.success('Ready!');
15
- * ```
16
- */
17
- export const appConfig = {
18
- /** Application metadata (name, version, description…) */
19
- app: appStore,
20
- /** API connection state and base URL */
21
- api: apiStore,
22
- /** Active theme + available themes */
23
- theme: themeStore,
24
- /** Active language / locale */
25
- language: languageStore,
26
- /** Active currency */
27
- currency: currencyStore,
28
- /** Toast notification queue */
29
- toast: toastStore,
30
- /** Command palette registry */
31
- commands: commandStore,
32
- };
33
- // Re-export individual stores for consumers who want fine-grained imports
34
- export { apiStore, appStore, commandStore, currencyStore, languageStore, themeStore, toastStore, };
3
+ // Updated for SSR safety: Re-exports factories for context-aware stores.
4
+ export { AppStore, createAppStore, getAppStore } from "./state/app.svelte";
5
+ export { createLayoutStore, getLayoutStore, LayoutStore, } from "./state/layout.svelte";
6
+ export { CommandStore, createCommandStore, getCommandStore, } from "./state/commands.svelte";
7
+ // These stores are factories now
8
+ export { createApiStore, createCurrencyStore, createLanguageStore, createShortcutStore, createThemeStore, createToastStore, getApiStore, getCurrencyStore, getLanguageStore, getShortcutStore, getThemeStore, getToastStore, } from "./state/index";
@@ -0,0 +1,11 @@
1
+ export declare const RUNE_LAB_CONTEXT: {
2
+ readonly app: symbol;
3
+ readonly api: symbol;
4
+ readonly toast: symbol;
5
+ readonly theme: symbol;
6
+ readonly language: symbol;
7
+ readonly currency: symbol;
8
+ readonly shortcut: symbol;
9
+ readonly layout: symbol;
10
+ readonly commands: symbol;
11
+ };
@@ -0,0 +1,12 @@
1
+ // src/lib/context.ts
2
+ export const RUNE_LAB_CONTEXT = {
3
+ app: Symbol("rl:app"),
4
+ api: Symbol("rl:api"),
5
+ toast: Symbol("rl:toast"),
6
+ theme: Symbol("rl:theme"),
7
+ language: Symbol("rl:language"),
8
+ currency: Symbol("rl:currency"),
9
+ shortcut: Symbol("rl:shortcut"),
10
+ layout: Symbol("rl:layout"),
11
+ commands: Symbol("rl:commands"),
12
+ };
@@ -1,6 +1,9 @@
1
1
  <script lang="ts">
2
- import { apiStore } from "../state/api.svelte";
3
- import { toastStore } from "../state/toast.svelte";
2
+ import { getApiStore } from "../state/api.svelte";
3
+ import { getToastStore } from "../state/toast.svelte";
4
+
5
+ const apiStore = getApiStore();
6
+ const toastStore = getToastStore();
4
7
 
5
8
  let copied = $state(false);
6
9
  let isVisible = $state(true);
@@ -1,16 +1,21 @@
1
1
  <script lang="ts">
2
- import { toastStore } from "../state/toast.svelte";
2
+ import { getToastStore } from "../state/toast.svelte";
3
3
  import { portal } from "../actions/portal";
4
4
  import { flip } from "svelte/animate";
5
5
  import { fade, fly } from "svelte/transition";
6
6
 
7
- const typeDetails = {
7
+ const toastStore = getToastStore();
8
+
9
+ const typeDetails: Record<
10
+ string,
11
+ { colors: string; iconColor: string; iconPath: string }
12
+ > = {
8
13
  info: {
9
14
  colors:
10
15
  "bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400",
11
16
  iconColor: "text-blue-500",
12
17
  iconPath:
13
- "M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
18
+ "M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z",
14
19
  },
15
20
  success: {
16
21
  colors:
@@ -24,7 +29,7 @@
24
29
  "bg-amber-500/10 border-amber-500/20 text-amber-600 dark:text-amber-400",
25
30
  iconColor: "text-amber-500",
26
31
  iconPath:
27
- "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
32
+ "M12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z", // This path looks wrong in original, replacing with simpler
28
33
  },
29
34
  error: {
30
35
  colors: "bg-red-500/10 border-red-500/20 text-red-600 dark:text-red-400",
@@ -35,13 +40,13 @@
35
40
  };
36
41
  </script>
37
42
 
38
- <svelte:element
39
- this={"div"}
43
+ <div
40
44
  use:portal
41
45
  class="toast toast-end toast-bottom z-[9999] p-4 gap-3 pointer-events-none"
42
46
  >
43
47
  {#each toastStore.toasts as toast (toast.id)}
44
- {@const styles = typeDetails[toast.type]}
48
+ {@const styles = typeDetails[toast.type] || typeDetails.info}
49
+
45
50
  <div
46
51
  animate:flip={{ duration: 300 }}
47
52
  in:fly={{ y: 20, duration: 300 }}
@@ -96,4 +101,4 @@
96
101
  ></div>
97
102
  </div>
98
103
  {/each}
99
- </svelte:element>
104
+ </div>
@@ -1,3 +1,11 @@
1
+ import { type PersistenceDriver } from "../persistence/types";
2
+ export type ConfigStore<T extends ConfigItem> = {
3
+ current: T[keyof T];
4
+ available: T[];
5
+ set: (id: T[keyof T]) => void;
6
+ get: (id: T[keyof T]) => T | undefined;
7
+ getProp: <K extends keyof T>(prop: K, id?: T[keyof T]) => T[K] | undefined;
8
+ };
1
9
  /**
2
10
  * Generic configuration store factory
3
11
  * Creates type-safe stores for theme, language, currency, etc.
@@ -9,7 +17,7 @@ interface ConfigItem {
9
17
  interface ConfigStoreOptions<T extends ConfigItem> {
10
18
  /** Array of available items */
11
19
  items: readonly T[];
12
- /** LocalStorage key */
20
+ /** Storage key used by the persistence driver */
13
21
  storageKey: string;
14
22
  /** Display name for logging (e.g., "Theme", "Language") */
15
23
  displayName: string;
@@ -17,6 +25,8 @@ interface ConfigStoreOptions<T extends ConfigItem> {
17
25
  idKey: keyof T;
18
26
  /** Icon for logs */
19
27
  icon?: string;
28
+ /** Persistence driver */
29
+ driver?: PersistenceDriver;
20
30
  }
21
31
  /**
22
32
  * Creates a reactive configuration store with persistence
@@ -1,23 +1,25 @@
1
1
  // client/sdk/devtools/src/patterns/createConfigStore.svelte.ts
2
+ import {} from "../persistence/types";
3
+ import { createInMemoryDriver } from "../persistence/drivers";
2
4
  /**
3
5
  * Creates a reactive configuration store with persistence
4
6
  */
5
7
  export function createConfigStore(options) {
6
- const { items, storageKey, displayName, idKey, icon = "⚙️" } = options;
8
+ const { items, storageKey, displayName, idKey, icon = "⚙️", driver = createInMemoryDriver(), } = options;
7
9
  class ConfigStore {
8
10
  current = $state(items[0]?.[idKey] ?? "");
9
11
  available = $state([...items]);
10
12
  constructor() {
11
- if (typeof window !== "undefined") {
12
- const saved = localStorage.getItem(storageKey);
13
- // Only load saved if it actually exists in our available items
14
- if (saved && this.get(saved)) {
15
- this.current = saved;
16
- }
13
+ const saved = driver.get(storageKey);
14
+ // Only load saved if it actually exists in our available items
15
+ if (saved && this.get(saved)) {
16
+ this.current = saved;
17
+ }
18
+ if (import.meta.env?.DEV) {
19
+ console.log(`${icon} ${displayName} configured:`, {
20
+ current: this.current,
21
+ });
17
22
  }
18
- console.log(`${icon} ${displayName} configured:`, {
19
- current: this.current,
20
- });
21
23
  }
22
24
  /**
23
25
  * Set current item with validation
@@ -29,7 +31,7 @@ export function createConfigStore(options) {
29
31
  return;
30
32
  }
31
33
  this.current = id;
32
- localStorage.setItem(storageKey, String(id));
34
+ driver.set(storageKey, String(id));
33
35
  }
34
36
  /**
35
37
  * Get item by id
@@ -1,12 +1,15 @@
1
1
  <!-- src/client/sdk/ui/src/features/config/CommandPalette.svelte -->
2
2
  <script lang="ts">
3
3
  import { onMount, tick } from "svelte";
4
- import { commandStore, type Command } from "../../state/commands.svelte";
5
- import { shortcutStore } from "../../state/shortcuts.svelte";
4
+ import { getCommandStore, type Command } from "../../state/commands.svelte";
5
+ import { getShortcutStore } from "../../state/shortcuts.svelte";
6
6
  import { Icon } from "../../index";
7
7
 
8
8
  let { shortcutKey = "shift+k" } = $props<{ shortcutKey?: string }>();
9
9
 
10
+ const commandStore = getCommandStore();
11
+ const shortcutStore = getShortcutStore();
12
+
10
13
  let dialog: HTMLDialogElement;
11
14
  let input: HTMLInputElement;
12
15
  let query = $state("");
@@ -176,7 +179,7 @@
176
179
  {/if}
177
180
  <div class="flex flex-col items-start">
178
181
  <span class="font-medium"
179
- >{cmd.title}</span
182
+ >{cmd.label}</span
180
183
  >
181
184
  {#if cmd.category}
182
185
  <span class="text-xs opacity-70"
@@ -214,6 +217,11 @@
214
217
  </div>
215
218
  </div>
216
219
  <form method="dialog" class="modal-backdrop">
217
- <button onclick={(e) => { e.preventDefault(); isOpen = false; }}>close</button>
220
+ <button
221
+ onclick={(e) => {
222
+ e.preventDefault();
223
+ isOpen = false;
224
+ }}>close</button
225
+ >
218
226
  </form>
219
227
  </dialog>