rune-lab 0.0.21 → 0.1.1

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 (54) hide show
  1. package/README.md +107 -0
  2. package/dist/components/ApiMonitor.svelte.d.ts +3 -0
  3. package/dist/components/Icon.svelte +52 -12
  4. package/dist/components/Icon.svelte.d.ts +2 -1
  5. package/dist/components/RuneProvider.svelte +49 -10
  6. package/dist/components/RuneProvider.svelte.d.ts +11 -1
  7. package/dist/{devtools → components}/Toaster.svelte +2 -2
  8. package/dist/features/config/{components/AppSettingSelector.svelte → AppSettingSelector.svelte} +9 -3
  9. package/dist/features/config/{components/CurrencySelector.svelte → CurrencySelector.svelte} +6 -4
  10. package/dist/features/config/{components/LanguageSelector.svelte → LanguageSelector.svelte} +7 -7
  11. package/dist/features/config/{components/ThemeSelector.svelte → ThemeSelector.svelte} +6 -4
  12. package/dist/features/detail-panels/ShowcasePanel.svelte +4 -6
  13. package/dist/index.d.ts +12 -13
  14. package/dist/index.js +10 -13
  15. package/dist/layout/WorkspaceLayout.svelte +81 -40
  16. package/dist/layout/WorkspaceLayout.svelte.d.ts +18 -4
  17. package/dist/persistence/drivers.js +10 -12
  18. package/dist/server/index.d.ts +15 -0
  19. package/dist/server/index.js +20 -0
  20. package/dist/showcase/AppStateInspector.svelte +27 -9
  21. package/dist/showcase/Showcase.svelte +5 -4
  22. package/dist/showcase/state.svelte.d.ts +3 -0
  23. package/dist/showcase/state.svelte.js +3 -0
  24. package/dist/state/api.svelte.d.ts +2 -1
  25. package/dist/state/api.svelte.js +16 -4
  26. package/dist/state/app.svelte.d.ts +5 -0
  27. package/dist/state/app.svelte.js +7 -0
  28. package/dist/state/commands.svelte.d.ts +1 -1
  29. package/dist/state/currency.svelte.d.ts +1 -1
  30. package/dist/state/currency.svelte.js +1 -1
  31. package/dist/state/index.d.ts +3 -1
  32. package/dist/state/index.js +3 -1
  33. package/dist/state/language.svelte.d.ts +6 -2
  34. package/dist/state/language.svelte.js +12 -4
  35. package/dist/state/layout.svelte.d.ts +0 -2
  36. package/dist/state/layout.svelte.js +0 -4
  37. package/dist/state/shortcuts.svelte.d.ts +14 -14
  38. package/dist/state/shortcuts.svelte.js +0 -10
  39. package/dist/state/theme.svelte.d.ts +1 -1
  40. package/dist/state/theme.svelte.js +1 -1
  41. package/dist/state/toast-bridge.d.ts +29 -0
  42. package/dist/state/toast-bridge.js +43 -0
  43. package/package.json +10 -6
  44. package/dist/devtools/API_Monitor.svelte.d.ts +0 -3
  45. /package/dist/{devtools/API_Monitor.svelte → components/ApiMonitor.svelte} +0 -0
  46. /package/dist/{devtools → components}/Toaster.svelte.d.ts +0 -0
  47. /package/dist/features/config/{components/AppSettingSelector.svelte.d.ts → AppSettingSelector.svelte.d.ts} +0 -0
  48. /package/dist/features/config/{components/CurrencySelector.svelte.d.ts → CurrencySelector.svelte.d.ts} +0 -0
  49. /package/dist/features/config/{components/LanguageSelector.svelte.d.ts → LanguageSelector.svelte.d.ts} +0 -0
  50. /package/dist/features/config/{components/ThemeSelector.svelte.d.ts → ThemeSelector.svelte.d.ts} +0 -0
  51. /package/dist/{devtools → internal}/message-resolver.d.ts +0 -0
  52. /package/dist/{devtools → internal}/message-resolver.js +0 -0
  53. /package/dist/{devtools → state}/createConfigStore.svelte.d.ts +0 -0
  54. /package/dist/{devtools → state}/createConfigStore.svelte.js +0 -0
package/README.md CHANGED
@@ -58,6 +58,43 @@ npm install rune-lab
58
58
  bun install rune-lab
59
59
  ```
60
60
 
61
+ ## Quick Start
62
+
63
+ Get your application shell running in less than 20 lines. Inside your
64
+ `+layout.svelte`:
65
+
66
+ ```svelte
67
+ <script lang="ts">
68
+ import { RuneProvider, WorkspaceLayout, ConnectedNavigationPanel } from "rune-lab";
69
+ import { cookieDriver } from "rune-lab";
70
+
71
+ let { children } = $props();
72
+
73
+ // Example navigation
74
+ const sections = [{ id: "main", title: "Main", items: [{ id: "home", label: "Home" }] }];
75
+ </script>
76
+
77
+ <RuneProvider
78
+ app={{ name: "My App", version: "1.0.0" }}
79
+ persistence={cookieDriver}
80
+ >
81
+ <WorkspaceLayout>
82
+ {#snippet navigationPanel()}
83
+ <ConnectedNavigationPanel {sections} />
84
+ {/snippet}
85
+
86
+ {#snippet content()}
87
+ <div class="p-8">
88
+ {@render children()}
89
+ </div>
90
+ {/snippet}
91
+ </WorkspaceLayout>
92
+ </RuneProvider>
93
+ ```
94
+
95
+ You now have a fully functional reactive layout, keyboard command palette, toast
96
+ notification system, and theme switcher ready to go.
97
+
61
98
  ## Project Configuration
62
99
 
63
100
  After installing, two configuration steps are required to ensure components are
@@ -103,6 +140,76 @@ also scan the `rune-lab` dist output:
103
140
  > component classes used by `rune-lab` will be included in your build and theme
104
141
  > switching will work across library components and your own code alike.
105
142
 
143
+ ## Persistence Drivers
144
+
145
+ Rune Lab provides built-in drivers to remember user preferences (like theme,
146
+ layout state, or language) across reloads. Pass one of these to the
147
+ `persistence` prop on `<RuneProvider>`:
148
+
149
+ - `cookieDriver`: Best for SSR applications (like SvelteKit) because the server
150
+ can read the cookie and prevent a "theme flash" on initial load.
151
+ - `localStorageDriver`: Best for client-only applications (SPA) looking for
152
+ long-term persistence.
153
+ - `sessionStorageDriver`: For preferences that should clear when the browser tab
154
+ closes.
155
+
156
+ ```svelte
157
+ <script lang="ts">
158
+ import { cookieDriver } from "rune-lab";
159
+ // Then pass directly: <RuneProvider persistence={cookieDriver}>
160
+ </script>
161
+ ```
162
+
163
+ ## Advanced Patterns
164
+
165
+ ### SvelteKit Route Syncing
166
+
167
+ To keep your layout's active navigation state synchronized with the SvelteKit
168
+ router, use an `$effect` inside your `+layout.svelte` right after the provider:
169
+
170
+ ```svelte
171
+ <script lang="ts">
172
+ import { page } from "$app/state";
173
+ import { getLayoutStore } from "rune-lab";
174
+
175
+ const layoutStore = getLayoutStore();
176
+
177
+ $effect(() => {
178
+ // Example: Use the first path segment as the active nav item
179
+ const segment = page.url.pathname.split("/")[1] || "home";
180
+ layoutStore.navigate(segment);
181
+ });
182
+ </script>
183
+ ```
184
+
185
+ _(Note: Use `$app/state`, not the older Svelte 4 `$app/stores`)_
186
+
187
+ ### Keyboard Shortcuts
188
+
189
+ Any component deep in your tree can register its own keyboard shortcuts
190
+ dynamically. To ensure they clean up when the component unmounts, **always
191
+ register them inside an `$effect` returning a cleanup function**:
192
+
193
+ ```svelte
194
+ <script lang="ts">
195
+ import { getShortcutStore, getToastStore } from "rune-lab";
196
+
197
+ const shortcuts = getShortcutStore();
198
+ const toasts = getToastStore();
199
+
200
+ $effect(() => {
201
+ shortcuts.register({
202
+ id: "feature.save",
203
+ keys: "ctrl s",
204
+ label: "Save Document",
205
+ handler: () => toasts.success("Document Saved!")
206
+ });
207
+
208
+ return () => shortcuts.unregister("feature.save"); // Important!
209
+ });
210
+ </script>
211
+ ```
212
+
106
213
  ## License
107
214
 
108
215
  MIT License - See [LICENSE](LICENSE) for details.
@@ -0,0 +1,3 @@
1
+ declare const ApiMonitor: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ApiMonitor = ReturnType<typeof ApiMonitor>;
3
+ export default ApiMonitor;
@@ -1,32 +1,72 @@
1
1
  <script lang="ts">
2
+ import { getAppStore } from "../state/app.svelte";
3
+
2
4
  /**
3
5
  * Simple Icon component for Rune Lab
4
6
  * Centralizes SVG paths to reduce boilerplate
5
7
  */
6
- let { name, size = "w-5 h-5", class: className = "" } = $props<{
7
- name: "search" | "chevron-down" | "info" | "shortcut" | "close" | "external";
8
+ let {
9
+ name,
10
+ size = "w-5 h-5",
11
+ class: className = "",
12
+ icons = {},
13
+ } = $props<{
14
+ name: string;
8
15
  size?: string;
9
16
  class?: string;
17
+ icons?: Record<string, string>;
10
18
  }>();
11
19
 
20
+ let appStore: ReturnType<typeof getAppStore> | undefined;
21
+ try {
22
+ appStore = getAppStore();
23
+ } catch {
24
+ // Graceful fallback if Icon is used outside RuneProvider
25
+ }
26
+
12
27
  const paths: Record<string, string> = {
13
- search: '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.3-4.3"></path>',
28
+ search:
29
+ '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.3-4.3"></path>',
14
30
  "chevron-down": '<path d="m6 9 6 6 6-6" />',
31
+ "chevron-right": '<path d="m9 18 6-6-6-6" />',
15
32
  info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
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"/>',
33
+ shortcut:
34
+ '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
17
35
  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"/>',
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"/>'
36
+ external:
37
+ '<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"/>',
38
+ plus: '<path d="M5 12h14"/><path d="M12 5v14"/>',
39
+ minus: '<path d="M5 12h14"/>',
40
+ check: '<path d="M20 6 9 17l-5-5"/>',
41
+ trash:
42
+ '<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>',
43
+ edit: '<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/>',
44
+ grid: '<rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/>',
45
+ list: '<line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/>',
46
+ table:
47
+ '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><line x1="3" x2="21" y1="9" y2="9"/><line x1="3" x2="21" y1="15" y2="15"/><line x1="9" x2="9" y1="3" y2="21"/><line x1="15" x2="15" y1="3" y2="21"/>',
48
+ "arrow-left": '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
49
+ "arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
50
+ menu: '<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>',
51
+ bell: '<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>',
52
+ user: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
53
+ settings:
54
+ '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
55
+ logout:
56
+ '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
57
+ unknown:
58
+ '<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"/>',
20
59
  };
21
60
 
22
61
  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;
62
+ if (icons[name]) return icons[name];
63
+ if (appStore?.customIcons?.[name]) return appStore.customIcons[name];
64
+ if (paths[name]) return paths[name];
65
+
66
+ if (import.meta.env?.DEV) {
67
+ console.warn(`[Icon] Unknown icon name: "${name}"`);
28
68
  }
29
- return paths[name];
69
+ return paths.unknown;
30
70
  });
31
71
  </script>
32
72
 
@@ -1,7 +1,8 @@
1
1
  type $$ComponentProps = {
2
- name: "search" | "chevron-down" | "info" | "shortcut" | "close" | "external";
2
+ name: string;
3
3
  size?: string;
4
4
  class?: string;
5
+ icons?: Record<string, string>;
5
6
  };
6
7
  declare const Icon: import("svelte").Component<$$ComponentProps, {}, "">;
7
8
  type Icon = ReturnType<typeof Icon>;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { setContext, untrack, type Snippet } from "svelte";
2
+ import { setContext, untrack, type Snippet, onMount } from "svelte";
3
3
  import {
4
4
  createAppStore,
5
5
  createLayoutStore,
@@ -11,14 +11,26 @@
11
11
  createCurrencyStore,
12
12
  createShortcutStore,
13
13
  } from "../state/index";
14
+ import { wire } from "../state/toast-bridge";
14
15
  import { Toaster, CommandPalette, ShortcutPalette } from "../index";
15
16
 
16
17
  import type { PersistenceDriver } from "../persistence/types";
17
18
  import { RUNE_LAB_CONTEXT } from "../context";
19
+ import type { AppData } from "../state/app.svelte";
18
20
 
19
- let { children, persistence } = $props<{
20
- children: Snippet;
21
+ export interface RuneLabConfig {
21
22
  persistence?: PersistenceDriver;
23
+ app?: Partial<AppData>;
24
+ apiUrl?: string;
25
+ apiHealthCheck?: () => Promise<boolean>;
26
+ favicon?: string;
27
+ manageHead?: boolean;
28
+ dictionary?: Record<string, any>;
29
+ }
30
+
31
+ let { children, config = {} } = $props<{
32
+ children: Snippet;
33
+ config?: RuneLabConfig;
22
34
  }>();
23
35
 
24
36
  // 1. Initialize Base Configuration Stores
@@ -26,11 +38,16 @@
26
38
  const apiStore = createApiStore();
27
39
  const toastStore = createToastStore();
28
40
 
41
+ // Wire global toast bridge
42
+ wire(toastStore);
43
+
29
44
  // Capture the initial persistence prop to avoid Svelte 5 reactive capture warnings
30
- const initialPersistence = untrack(() => persistence);
45
+ const initialPersistence = untrack(() => config.persistence);
31
46
 
32
47
  const themeStore = createThemeStore(initialPersistence);
33
- const languageStore = createLanguageStore(initialPersistence);
48
+ const languageStore = createLanguageStore({
49
+ driver: initialPersistence,
50
+ });
34
51
  const currencyStore = createCurrencyStore(initialPersistence);
35
52
  const shortcutStore = createShortcutStore();
36
53
 
@@ -57,6 +74,24 @@
57
74
  setContext(RUNE_LAB_CONTEXT.commands, commandStore);
58
75
  setContext(RUNE_LAB_CONTEXT.persistence, initialPersistence);
59
76
 
77
+ const initialDictionary = untrack(() => config.dictionary);
78
+ if (initialDictionary) {
79
+ setContext("rl:dictionary", initialDictionary);
80
+ }
81
+
82
+ // Track config changes dynamically
83
+ $effect(() => {
84
+ if (config.app) appStore.init(config.app);
85
+ });
86
+
87
+ $effect(() => {
88
+ if (config.apiUrl) apiStore.init(config.apiUrl, "v1", config.apiHealthCheck);
89
+ });
90
+
91
+ onMount(() => {
92
+ layoutStore.init();
93
+ });
94
+
60
95
  // Meta tags derived from app store state
61
96
  const metaTags = $derived([
62
97
  { name: "description", content: appStore.description },
@@ -65,11 +100,15 @@
65
100
  </script>
66
101
 
67
102
  <svelte:head>
68
- <title>{appStore.name}</title>
69
- <link rel="icon" href={"/img/rune.png"} />
70
- {#each metaTags as meta}
71
- <meta name={meta.name} content={meta.content} />
72
- {/each}
103
+ {#if config.manageHead !== false}
104
+ <title>{appStore.name}</title>
105
+ {#if config.favicon}
106
+ <link rel="icon" href={config.favicon} />
107
+ {/if}
108
+ {#each metaTags as meta}
109
+ <meta name={meta.name} content={meta.content} />
110
+ {/each}
111
+ {/if}
73
112
  </svelte:head>
74
113
 
75
114
  <!-- Global Overlays -->
@@ -1,8 +1,18 @@
1
1
  import { type Snippet } from "svelte";
2
2
  import type { PersistenceDriver } from "../persistence/types";
3
+ import type { AppData } from "../state/app.svelte";
4
+ export interface RuneLabConfig {
5
+ persistence?: PersistenceDriver;
6
+ app?: Partial<AppData>;
7
+ apiUrl?: string;
8
+ apiHealthCheck?: () => Promise<boolean>;
9
+ favicon?: string;
10
+ manageHead?: boolean;
11
+ dictionary?: Record<string, any>;
12
+ }
3
13
  type $$ComponentProps = {
4
14
  children: Snippet;
5
- persistence?: PersistenceDriver;
15
+ config?: RuneLabConfig;
6
16
  };
7
17
  declare const RuneProvider: import("svelte").Component<$$ComponentProps, {}, "">;
8
18
  type RuneProvider = ReturnType<typeof RuneProvider>;
@@ -15,7 +15,7 @@
15
15
  "bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400",
16
16
  iconColor: "text-blue-500",
17
17
  iconPath:
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",
18
+ "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 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z",
19
19
  },
20
20
  success: {
21
21
  colors:
@@ -29,7 +29,7 @@
29
29
  "bg-amber-500/10 border-amber-500/20 text-amber-600 dark:text-amber-400",
30
30
  iconColor: "text-amber-500",
31
31
  iconPath:
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
32
+ "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z", // Proper warning icon
33
33
  },
34
34
  error: {
35
35
  colors: "bg-red-500/10 border-red-500/20 text-red-600 dark:text-red-400",
@@ -46,6 +46,7 @@
46
46
  <button
47
47
  type="button"
48
48
  class="btn btn-ghost btn-sm m-1 h-auto min-h-[2rem] px-2"
49
+ aria-haspopup="dialog"
49
50
  onclick={() => modal?.showModal()}
50
51
  >
51
52
  <span class="flex items-center gap-2">
@@ -66,9 +67,12 @@
66
67
  </form>
67
68
  </div>
68
69
  <div class="max-h-[60vh] overflow-y-auto p-2">
69
- <ul class="menu bg-base-100 w-full p-0">
70
+ <ul class="menu bg-base-100 w-full p-0" role="menu">
70
71
  {#each options as option}
71
- <li class="border-b border-base-100 last:border-0">
72
+ <li
73
+ class="border-b border-base-100 last:border-0"
74
+ role="menuitem"
75
+ >
72
76
  <button
73
77
  class="w-full text-left py-3"
74
78
  onclick={() => modal?.close()}
@@ -96,6 +100,7 @@
96
100
  <div
97
101
  tabindex="0"
98
102
  role="button"
103
+ aria-haspopup="menu"
99
104
  class="btn btn-ghost btn-sm m-1 {tooltip
100
105
  ? 'tooltip tooltip-bottom'
101
106
  : ''}"
@@ -107,10 +112,11 @@
107
112
  </div>
108
113
 
109
114
  <ul
115
+ role="menu"
110
116
  class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow-xl border border-base-200 max-h-96 overflow-y-auto"
111
117
  >
112
118
  {#each options as option}
113
- <li>
119
+ <li role="menuitem">
114
120
  {@render item(option)}
115
121
  </li>
116
122
  {/each}
@@ -3,12 +3,12 @@
3
3
  import {
4
4
  getCurrencyStore,
5
5
  type Currency,
6
- } from "../../../state/currency.svelte";
6
+ } from "../../state/currency.svelte";
7
7
 
8
8
  const currencyStore = getCurrencyStore();
9
9
 
10
- import * as m from "../../../paraglide/messages.js";
11
- import { createMessageResolver } from "../../../devtools/message-resolver";
10
+ import { getContext } from "svelte";
11
+ import { createMessageResolver } from "../../internal/message-resolver";
12
12
 
13
13
  let {
14
14
  codes = [],
@@ -20,7 +20,9 @@
20
20
  onchange?: (value: string) => void;
21
21
  } = $props();
22
22
 
23
- const getLabel = createMessageResolver<Currency>(m as any, {
23
+ const dictionary = getContext<Record<string, any>>("rl:dictionary") || {};
24
+
25
+ const getLabel = createMessageResolver<Currency>(dictionary as any, {
24
26
  keyExtractor: (c) => String(c.code),
25
27
  });
26
28
 
@@ -3,16 +3,15 @@
3
3
  import {
4
4
  getLanguageStore,
5
5
  type Language,
6
- } from "../../../state/language.svelte";
6
+ } from "../../state/language.svelte";
7
7
 
8
8
  const languageStore = getLanguageStore();
9
9
 
10
- import { setLocale, locales } from "../../../paraglide/runtime";
11
- import * as m from "../../../paraglide/messages.js";
12
- import { createMessageResolver } from "../../../devtools/message-resolver";
10
+ import { getContext } from "svelte";
11
+ import { createMessageResolver } from "../../internal/message-resolver";
13
12
 
14
13
  let {
15
- languages: allowedLocales = locales,
14
+ languages: allowedLocales = languageStore.available.map((l) => l.code),
16
15
  current = $bindable(languageStore.current),
17
16
  onchange,
18
17
  }: {
@@ -21,7 +20,9 @@
21
20
  onchange?: (value: string) => void;
22
21
  } = $props();
23
22
 
24
- const getLabel = createMessageResolver<Language>(m as any, {
23
+ const dictionary = getContext<Record<string, any>>("rl:dictionary") || {};
24
+
25
+ const getLabel = createMessageResolver<Language>(dictionary as any, {
25
26
  keyExtractor: (l) => l.code,
26
27
  });
27
28
 
@@ -50,7 +51,6 @@
50
51
  class="flex items-center gap-3 w-full"
51
52
  onclick={() => {
52
53
  languageStore.set(l.code);
53
- setLocale(l.code as any);
54
54
  current = l.code;
55
55
  onchange?.(l.code);
56
56
  }}
@@ -1,11 +1,11 @@
1
1
  <script lang="ts">
2
2
  import AppSettingSelector from "./AppSettingSelector.svelte";
3
- import { getThemeStore, type Theme } from "../../../state/theme.svelte";
3
+ import { getThemeStore, type Theme } from "../../state/theme.svelte";
4
4
 
5
5
  const themeStore = getThemeStore();
6
6
 
7
- import * as m from "../../../paraglide/messages.js";
8
- import { createMessageResolver } from "../../../devtools/message-resolver";
7
+ import { getContext } from "svelte";
8
+ import { createMessageResolver } from "../../internal/message-resolver";
9
9
 
10
10
  let {
11
11
  themes = [],
@@ -17,7 +17,9 @@
17
17
  onchange?: (value: string) => void;
18
18
  } = $props();
19
19
 
20
- const getThemeLabel = createMessageResolver<Theme>(m as any, {
20
+ const dictionary = getContext<Record<string, any>>("rl:dictionary") || {};
21
+
22
+ const getThemeLabel = createMessageResolver<Theme>(dictionary as any, {
21
23
  keyExtractor: (t) => t.name,
22
24
  });
23
25
 
@@ -1,7 +1,5 @@
1
1
  <script lang="ts">
2
- import { getLayoutStore } from "../../state/layout.svelte";
3
-
4
- const layoutStore = getLayoutStore();
2
+ import { showcaseState } from "../../showcase/state.svelte";
5
3
 
6
4
  import { getToastStore } from "../../state/toast.svelte";
7
5
 
@@ -21,7 +19,7 @@
21
19
  "visual",
22
20
  ];
23
21
 
24
- const currentTabId = $derived(tabs[layoutStore.activeShowcaseTab]);
22
+ const currentTabId = $derived(tabs[showcaseState.activeTab]);
25
23
  const components = $derived(SHOWCASE_COMPONENTS[currentTabId] || []);
26
24
  const snippet = $derived(SHOWCASE_SNIPPETS[currentTabId] || "");
27
25
 
@@ -52,8 +50,8 @@
52
50
  {#each tabs as tab, i}
53
51
  <li>
54
52
  <button
55
- class:active={layoutStore.activeShowcaseTab === i}
56
- onclick={() => layoutStore.setShowcaseTab(i)}
53
+ class:active={showcaseState.activeTab === i}
54
+ onclick={() => (showcaseState.activeTab = i)}
57
55
  class="capitalize"
58
56
  >
59
57
  {tab.replace("-", " ")}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { createConfigStore } from "./devtools/createConfigStore.svelte";
2
- export { createMessageResolver } from "./devtools/message-resolver";
1
+ export { createConfigStore } from "./state/createConfigStore.svelte";
2
+ export { createMessageResolver } from "./internal/message-resolver";
3
3
  export { RUNE_LAB_CONTEXT } from "./context";
4
4
  export type { PersistenceDriver } from "./persistence/types";
5
5
  export { cookieDriver, inMemoryDriver, localStorageDriver, sessionStorageDriver, } from "./persistence/drivers";
@@ -8,24 +8,23 @@ export { usePersistence } from "./composables/usePersistence.js";
8
8
  export { portal } from "./actions/portal";
9
9
  export { default as RuneProvider } from "./components/RuneProvider.svelte";
10
10
  export { default as Icon } from "./components/Icon.svelte";
11
- export { default as Toaster } from "./devtools/Toaster.svelte";
12
- export { default as ApiMonitor } from "./devtools/API_Monitor.svelte";
11
+ export { default as Toaster } from "./components/Toaster.svelte";
12
+ export { default as ApiMonitor } from "./components/ApiMonitor.svelte";
13
13
  export { default as CommandPalette } from "./features/command-palette/CommandPalette.svelte";
14
14
  export { default as ShortcutPalette } from "./features/shortcuts/ShortcutPalette.svelte";
15
- export { default as WorkspaceLayout } from "./layout/WorkspaceLayout.svelte";
15
+ export { default as WorkspaceLayout, type WorkspaceLayoutProps, } from "./layout/WorkspaceLayout.svelte";
16
16
  export { default as WorkspaceStrip } from "./layout/WorkspaceStrip.svelte";
17
17
  export { default as NavigationPanel } from "./layout/NavigationPanel.svelte";
18
18
  export { default as ContentArea } from "./layout/ContentArea.svelte";
19
19
  export { default as DetailPanel } from "./layout/DetailPanel.svelte";
20
20
  export { default as ConnectedNavigationPanel } from "./features/layout/smart/ConnectedNavigationPanel.svelte";
21
21
  export { default as ConnectedWorkspaceStrip } from "./features/layout/smart/ConnectedWorkspaceStrip.svelte";
22
- export { default as AppSettingSelector } from "./features/config/components/AppSettingSelector.svelte";
23
- export { default as ThemeSelector } from "./features/config/components/ThemeSelector.svelte";
24
- export { default as LanguageSelector } from "./features/config/components/LanguageSelector.svelte";
25
- export { default as CurrencySelector } from "./features/config/components/CurrencySelector.svelte";
26
- export { createApiStore, createAppStore, createCommandStore, createCurrencyStore, createLanguageStore, createLayoutStore, createShortcutStore, createThemeStore, createToastStore, getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore, } from "./state/index";
22
+ export { default as AppSettingSelector } from "./features/config/AppSettingSelector.svelte";
23
+ export { default as ThemeSelector } from "./features/config/ThemeSelector.svelte";
24
+ export { default as LanguageSelector } from "./features/config/LanguageSelector.svelte";
25
+ export { default as CurrencySelector } from "./features/config/CurrencySelector.svelte";
26
+ export { createApiStore, createAppStore, createCommandStore, createCurrencyStore, createLanguageStore, createLayoutStore, createShortcutStore, createThemeStore, createToastBridge, createToastStore, getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore, notify, } from "./state/index";
27
27
  export { default as AppStateInspector } from "./showcase/AppStateInspector.svelte";
28
- export { default as ShowcaseMain } from "./showcase/Showcase.svelte";
29
- export { default as ShowcaseCard } from "./showcase/ShowcaseCard.svelte";
30
- export type { AppData, Command, Currency, Language, NavigationItem, NavigationSection, ShortcutEntry, Theme, WorkspaceItem, } from "./state/index";
28
+ export type { AppData, Command, ConfigStore, Currency, Language, NavigationItem, NavigationSection, ShortcutEntry, Theme, WorkspaceItem, } from "./state/index";
29
+ export type { ConnectionState } from "./state/api.svelte";
31
30
  export * as sdkMessages from "./paraglide/messages.js";
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/lib/index.ts
2
2
  // Public surface of rune-lab.
3
3
  // ── Devtools utilities ────────────────────────────────────────────────────────
4
- export { createConfigStore } from "./devtools/createConfigStore.svelte";
5
- export { createMessageResolver } from "./devtools/message-resolver";
4
+ export { createConfigStore } from "./state/createConfigStore.svelte";
5
+ export { createMessageResolver } from "./internal/message-resolver";
6
6
  export { RUNE_LAB_CONTEXT } from "./context";
7
7
  export { cookieDriver, inMemoryDriver, localStorageDriver, sessionStorageDriver, } from "./persistence/drivers";
8
8
  // ── Composables ───────────────────────────────────────────────────────────────
@@ -15,12 +15,12 @@ export { portal } from "./actions/portal";
15
15
  export { default as RuneProvider } from "./components/RuneProvider.svelte";
16
16
  export { default as Icon } from "./components/Icon.svelte";
17
17
  // Core overlays
18
- export { default as Toaster } from "./devtools/Toaster.svelte";
19
- export { default as ApiMonitor } from "./devtools/API_Monitor.svelte";
18
+ export { default as Toaster } from "./components/Toaster.svelte";
19
+ export { default as ApiMonitor } from "./components/ApiMonitor.svelte";
20
20
  export { default as CommandPalette } from "./features/command-palette/CommandPalette.svelte";
21
21
  export { default as ShortcutPalette } from "./features/shortcuts/ShortcutPalette.svelte";
22
22
  // Layout - Dumb Primitives
23
- export { default as WorkspaceLayout } from "./layout/WorkspaceLayout.svelte";
23
+ export { default as WorkspaceLayout, } from "./layout/WorkspaceLayout.svelte";
24
24
  export { default as WorkspaceStrip } from "./layout/WorkspaceStrip.svelte";
25
25
  export { default as NavigationPanel } from "./layout/NavigationPanel.svelte";
26
26
  export { default as ContentArea } from "./layout/ContentArea.svelte";
@@ -29,15 +29,12 @@ export { default as DetailPanel } from "./layout/DetailPanel.svelte";
29
29
  export { default as ConnectedNavigationPanel } from "./features/layout/smart/ConnectedNavigationPanel.svelte";
30
30
  export { default as ConnectedWorkspaceStrip } from "./features/layout/smart/ConnectedWorkspaceStrip.svelte";
31
31
  // Setting selectors
32
- export { default as AppSettingSelector } from "./features/config/components/AppSettingSelector.svelte";
33
- export { default as ThemeSelector } from "./features/config/components/ThemeSelector.svelte";
34
- export { default as LanguageSelector } from "./features/config/components/LanguageSelector.svelte";
35
- export { default as CurrencySelector } from "./features/config/components/CurrencySelector.svelte";
32
+ export { default as AppSettingSelector } from "./features/config/AppSettingSelector.svelte";
33
+ export { default as ThemeSelector } from "./features/config/ThemeSelector.svelte";
34
+ export { default as LanguageSelector } from "./features/config/LanguageSelector.svelte";
35
+ export { default as CurrencySelector } from "./features/config/CurrencySelector.svelte";
36
36
  // ── Stores ────────────────────────────────────────────────────────────────────
37
- export { createApiStore, createAppStore, createCommandStore, createCurrencyStore, createLanguageStore, createLayoutStore, createShortcutStore, createThemeStore, createToastStore, getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore, } from "./state/index";
38
- // ── Showcase ──────────────────────────────────────────────────────────────────
37
+ export { createApiStore, createAppStore, createCommandStore, createCurrencyStore, createLanguageStore, createLayoutStore, createShortcutStore, createThemeStore, createToastBridge, createToastStore, getApiStore, getAppStore, getCommandStore, getCurrencyStore, getLanguageStore, getLayoutStore, getShortcutStore, getThemeStore, getToastStore, notify, } from "./state/index";
39
38
  export { default as AppStateInspector } from "./showcase/AppStateInspector.svelte";
40
- export { default as ShowcaseMain } from "./showcase/Showcase.svelte";
41
- export { default as ShowcaseCard } from "./showcase/ShowcaseCard.svelte";
42
39
  // ── Paraglide compiled messages ───────────────────────────────────────────────
43
40
  export * as sdkMessages from "./paraglide/messages.js";