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
@@ -1,6 +1,33 @@
1
1
  <!-- src/lib/layout/WorkspaceLayout.svelte -->
2
- <script lang="ts">
2
+ <script module lang="ts">
3
3
  import type { Snippet } from "svelte";
4
+
5
+ export interface WorkspaceLayoutProps {
6
+ /** Content for the far-left vertical strip */
7
+ workspaceStrip?: Snippet;
8
+ /** Main navigation tree panel */
9
+ navigationPanel?: Snippet;
10
+ /** The primary application view */
11
+ content?: Snippet;
12
+ /** Right-side contextual detail panel */
13
+ detailPanel?: Snippet;
14
+ namespace?: string;
15
+
16
+ /** Width of the left workspace strip */
17
+ stripWidth?: string;
18
+ /** Width of the navigation tree panel */
19
+ navWidth?: string;
20
+ /** Width of the contextual detail panel */
21
+ detailWidth?: string;
22
+
23
+ /** Controlled override for navigation panel visibility */
24
+ navOpen?: boolean;
25
+ /** Controlled override for detail panel visibility */
26
+ detailOpen?: boolean;
27
+ }
28
+ </script>
29
+
30
+ <script lang="ts">
4
31
  import { getLayoutStore } from "../state/layout.svelte";
5
32
  import {
6
33
  getShortcutStore,
@@ -9,22 +36,36 @@
9
36
  } from "../state/shortcuts.svelte";
10
37
  import { onMount } from "svelte";
11
38
 
39
+ /**
40
+ * @component WorkspaceLayout
41
+ * The main application shell managing navigational zones.
42
+ */
43
+ const layoutStore = getLayoutStore();
44
+
12
45
  let {
46
+ /** Content for the far-left vertical strip */
13
47
  workspaceStrip,
48
+ /** Main navigation tree panel */
14
49
  navigationPanel,
50
+ /** The primary application view */
15
51
  content,
52
+ /** Right-side contextual detail panel */
16
53
  detailPanel,
17
54
  namespace = "default",
18
- } = $props<{
19
- workspaceStrip?: Snippet;
20
- navigationPanel?: Snippet;
21
- content: Snippet;
22
- detailPanel?: Snippet;
23
- namespace?: string;
24
- }>();
25
55
 
26
- // Consume layout store from context (provided by RuneProvider)
27
- const layoutStore = getLayoutStore();
56
+ /** Width of the left workspace strip */
57
+ stripWidth = "72px",
58
+ /** Width of the navigation tree panel */
59
+ navWidth = "240px",
60
+ /** Width of the contextual detail panel */
61
+ detailWidth = "320px",
62
+
63
+ /** Controlled override for navigation panel visibility */
64
+ navOpen = $bindable(layoutStore.navOpen),
65
+ /** Controlled override for detail panel visibility */
66
+ detailOpen = $bindable(layoutStore.detailOpen),
67
+ }: WorkspaceLayoutProps = $props();
68
+
28
69
  const shortcutStore = getShortcutStore();
29
70
 
30
71
  // Initial configuration based on props
@@ -35,17 +76,18 @@
35
76
  const shortcuts = [
36
77
  {
37
78
  ...LAYOUT_SHORTCUTS.TOGGLE_NAV,
38
- handler: () => layoutStore.toggleNav(),
79
+ handler: (e: KeyboardEvent) => {
80
+ e.preventDefault();
81
+ navOpen = !navOpen;
82
+ },
39
83
  },
40
84
  {
41
85
  ...LAYOUT_SHORTCUTS.TOGGLE_DETAIL,
42
- handler: () => layoutStore.toggleDetail(),
86
+ handler: (e: KeyboardEvent) => {
87
+ e.preventDefault();
88
+ detailOpen = !detailOpen;
89
+ },
43
90
  },
44
- // Workspace shortcuts (ctrl+1 to ctrl+9)
45
- ...Array.from({ length: 9 }, (_, i) => ({
46
- ...(LAYOUT_SHORTCUTS as any)[`WORKSPACE_${i + 1}`],
47
- handler: () => layoutStore.activateWorkspaceByIndex(i),
48
- })),
49
91
  ];
50
92
 
51
93
  for (const s of shortcuts) {
@@ -55,11 +97,6 @@
55
97
  return () => {
56
98
  shortcutStore.unregister(LAYOUT_SHORTCUTS.TOGGLE_NAV.id);
57
99
  shortcutStore.unregister(LAYOUT_SHORTCUTS.TOGGLE_DETAIL.id);
58
- for (let i = 1; i <= 9; i++) {
59
- shortcutStore.unregister(
60
- (LAYOUT_SHORTCUTS as any)[`WORKSPACE_${i}`].id,
61
- );
62
- }
63
100
  };
64
101
  });
65
102
  </script>
@@ -79,10 +116,11 @@
79
116
  {/if}
80
117
 
81
118
  <!-- Mobile Nav Backdrop -->
82
- {#if navigationPanel && layoutStore.navOpen}
119
+ {#if navigationPanel && navOpen}
83
120
  <button
84
- class="fixed inset-0 bg-black/50 z-[40] md:hidden cursor-default border-none"
85
- onclick={() => (layoutStore.navOpen = false)}
121
+ class="fixed inset-0 bg-black/50 md:hidden cursor-default border-none"
122
+ style="z-index: var(--rl-z-backdrop, 40);"
123
+ onclick={() => (navOpen = false)}
86
124
  aria-label="Close navigation"
87
125
  ></button>
88
126
  {/if}
@@ -90,19 +128,18 @@
90
128
  <!-- Zone 2: Navigation Panel -->
91
129
  {#if navigationPanel}
92
130
  <aside
93
- class="rl-nav h-full shrink-0 bg-base-200 overflow-hidden flex flex-col transition-all duration-300 ease-in-out z-[50]"
94
- class:border-r={layoutStore.navOpen}
131
+ class="rl-nav h-full shrink-0 bg-base-200 overflow-hidden flex flex-col transition-all duration-300 ease-in-out"
132
+ class:border-r={navOpen}
95
133
  class:border-base-content={true}
96
134
  style="
97
135
  border-opacity: 0.05;
98
- width: {layoutStore.navOpen
99
- ? 'var(--rl-nav-width, 240px)'
100
- : '0px'};
136
+ z-index: var(--rl-z-nav, 50);
137
+ width: {navOpen ? 'var(--rl-nav-width, 240px)' : '0px'};
101
138
  left: {workspaceStrip ? 'var(--rl-strip-width, 72px)' : '0px'};
102
139
  "
103
140
  class:max-md:!w-[var(--rl-nav-width,240px)]={true}
104
141
  class:max-md:fixed={true}
105
- class:max-md:-translate-x-[200%]={!layoutStore.navOpen}
142
+ class:max-md:-translate-x-[200%]={!navOpen}
106
143
  data-rl-panel="navigation"
107
144
  >
108
145
  <div class="w-[var(--rl-nav-width,240px)] h-full flex flex-col">
@@ -115,14 +152,19 @@
115
152
  <main
116
153
  class="rl-content h-full flex-1 min-w-0 bg-base-100 flex flex-col overflow-hidden relative"
117
154
  >
118
- {@render content()}
155
+ {#if content}
156
+ {@render content()}
157
+ {:else}
158
+ <div></div>
159
+ {/if}
119
160
  </main>
120
161
 
121
162
  <!-- Mobile Detail Backdrop -->
122
- {#if detailPanel && layoutStore.detailOpen}
163
+ {#if detailPanel && detailOpen}
123
164
  <button
124
- class="fixed inset-0 bg-black/50 z-[40] md:hidden cursor-default border-none"
125
- onclick={() => (layoutStore.detailOpen = false)}
165
+ class="fixed inset-0 bg-black/50 md:hidden cursor-default border-none"
166
+ style="z-index: var(--rl-z-backdrop, 40);"
167
+ onclick={() => (detailOpen = false)}
126
168
  aria-label="Close detail panel"
127
169
  ></button>
128
170
  {/if}
@@ -130,14 +172,13 @@
130
172
  <!-- Zone 4: Detail Panel -->
131
173
  {#if detailPanel}
132
174
  <aside
133
- class="rl-detail h-full shrink-0 bg-base-100 overflow-y-auto overflow-x-hidden transition-all duration-300 ease-in-out z-[50]"
134
- class:border-l={layoutStore.detailOpen}
175
+ class="rl-detail h-full shrink-0 bg-base-100 overflow-y-auto overflow-x-hidden transition-all duration-300 ease-in-out"
176
+ class:border-l={detailOpen}
135
177
  class:border-base-content={true}
136
178
  style="
137
179
  border-opacity: 0.05;
138
- width: {layoutStore.detailOpen
139
- ? 'var(--rl-detail-width, 320px)'
140
- : '0px'};
180
+ z-index: var(--rl-z-detail, 50);
181
+ width: {detailOpen ? 'var(--rl-detail-width, 320px)' : '0px'};
141
182
  "
142
183
  class:max-md:!w-[var(--rl-detail-width,320px)]={true}
143
184
  class:max-md:fixed={true}
@@ -1,11 +1,25 @@
1
1
  import type { Snippet } from "svelte";
2
- type $$ComponentProps = {
2
+ export interface WorkspaceLayoutProps {
3
+ /** Content for the far-left vertical strip */
3
4
  workspaceStrip?: Snippet;
5
+ /** Main navigation tree panel */
4
6
  navigationPanel?: Snippet;
5
- content: Snippet;
7
+ /** The primary application view */
8
+ content?: Snippet;
9
+ /** Right-side contextual detail panel */
6
10
  detailPanel?: Snippet;
7
11
  namespace?: string;
8
- };
9
- declare const WorkspaceLayout: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ /** Width of the left workspace strip */
13
+ stripWidth?: string;
14
+ /** Width of the navigation tree panel */
15
+ navWidth?: string;
16
+ /** Width of the contextual detail panel */
17
+ detailWidth?: string;
18
+ /** Controlled override for navigation panel visibility */
19
+ navOpen?: boolean;
20
+ /** Controlled override for detail panel visibility */
21
+ detailOpen?: boolean;
22
+ }
23
+ declare const WorkspaceLayout: import("svelte").Component<WorkspaceLayoutProps, {}, "navOpen" | "detailOpen">;
10
24
  type WorkspaceLayout = ReturnType<typeof WorkspaceLayout>;
11
25
  export default WorkspaceLayout;
@@ -1,3 +1,4 @@
1
+ import { browser } from "$app/environment";
1
2
  export function createInMemoryDriver() {
2
3
  const store = new Map();
3
4
  return {
@@ -9,50 +10,48 @@ export function createInMemoryDriver() {
9
10
  export const inMemoryDriver = createInMemoryDriver();
10
11
  export const localStorageDriver = {
11
12
  get: (key) => {
12
- if (typeof window === "undefined")
13
+ if (!browser)
13
14
  return null;
14
15
  return window.localStorage.getItem(key);
15
16
  },
16
17
  set: (key, value) => {
17
- if (typeof window === "undefined")
18
+ if (!browser)
18
19
  return;
19
20
  window.localStorage.setItem(key, value);
20
21
  },
21
22
  remove: (key) => {
22
- if (typeof window === "undefined")
23
+ if (!browser)
23
24
  return;
24
25
  window.localStorage.removeItem(key);
25
26
  },
26
27
  };
27
28
  export const sessionStorageDriver = {
28
29
  get: (key) => {
29
- if (typeof window === "undefined")
30
+ if (!browser)
30
31
  return null;
31
32
  return window.sessionStorage.getItem(key);
32
33
  },
33
34
  set: (key, value) => {
34
- if (typeof window === "undefined")
35
+ if (!browser)
35
36
  return;
36
37
  window.sessionStorage.setItem(key, value);
37
38
  },
38
39
  remove: (key) => {
39
- if (typeof window === "undefined")
40
+ if (!browser)
40
41
  return;
41
42
  window.sessionStorage.removeItem(key);
42
43
  },
43
44
  };
44
45
  export const cookieDriver = (options = {}) => ({
45
46
  get: (key) => {
46
- if (typeof window === "undefined" || typeof document === "undefined") {
47
+ if (!browser)
47
48
  return null;
48
- }
49
49
  const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
50
50
  return match ? decodeURIComponent(match[2]) : null;
51
51
  },
52
52
  set: (key, value) => {
53
- if (typeof window === "undefined" || typeof document === "undefined") {
53
+ if (!browser)
54
54
  return;
55
- }
56
55
  let cookie = `${key}=${encodeURIComponent(value)}`;
57
56
  if (options.path)
58
57
  cookie += `; path=${options.path}`;
@@ -63,9 +62,8 @@ export const cookieDriver = (options = {}) => ({
63
62
  document.cookie = cookie;
64
63
  },
65
64
  remove: (key) => {
66
- if (typeof window === "undefined" || typeof document === "undefined") {
65
+ if (!browser)
67
66
  return;
68
- }
69
67
  document.cookie = `${key}=; max-age=0; path=${options.path || "/"}`;
70
68
  },
71
69
  });
@@ -0,0 +1,15 @@
1
+ import type { Handle } from "@sveltejs/kit";
2
+ /**
3
+ * A SvelteKit Handle hook that prevents Flash of Unstyled Content (FOUC)
4
+ * by injecting the saved theme straight into the HTML before it leaves the server.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * // src/hooks.server.ts
9
+ * import { sequence } from "@sveltejs/kit/hooks";
10
+ * import { runeLabThemeHandler } from "rune-lab/server";
11
+ *
12
+ * export const handle = sequence(runeLabThemeHandler);
13
+ * ```
14
+ */
15
+ export declare const runeLabThemeHandler: Handle;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A SvelteKit Handle hook that prevents Flash of Unstyled Content (FOUC)
3
+ * by injecting the saved theme straight into the HTML before it leaves the server.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * // src/hooks.server.ts
8
+ * import { sequence } from "@sveltejs/kit/hooks";
9
+ * import { runeLabThemeHandler } from "rune-lab/server";
10
+ *
11
+ * export const handle = sequence(runeLabThemeHandler);
12
+ * ```
13
+ */
14
+ export const runeLabThemeHandler = async ({ event, resolve }) => {
15
+ // Assuming 'theme' is the default storageKey mapped from cookieDriver
16
+ const theme = event.cookies.get("theme") || "system";
17
+ return resolve(event, {
18
+ transformPageChunk: ({ html }) => html.replace("<html", `<html data-theme="${theme}"`),
19
+ });
20
+ };
@@ -1,4 +1,15 @@
1
1
  <script lang="ts">
2
+ import { getContext } from "svelte";
3
+
4
+ /**
5
+ * @devOnly Renders a live debug view of all Rune Lab store state.
6
+ * Should only be rendered in development:
7
+ * ```svelte
8
+ * {#if import.meta.env.DEV}
9
+ * <AppStateInspector />
10
+ * {/if}
11
+ * ```
12
+ */
2
13
  import * as m from "../paraglide/messages.js";
3
14
  import StoreDetailCard from "./StoreDetailCard.svelte";
4
15
  import { getAppStore } from "../state/app.svelte";
@@ -83,8 +94,15 @@
83
94
  }
84
95
  }
85
96
 
86
- function t(fn: any, fallback: string) {
87
- return typeof fn === "function" ? fn() : fallback;
97
+ const dictionary = getContext<Record<string, any>>("rl:dictionary") || {};
98
+
99
+ function t(key: string, fallback: string) {
100
+ if (dictionary[key]) {
101
+ return typeof dictionary[key] === "function"
102
+ ? dictionary[key]()
103
+ : dictionary[key];
104
+ }
105
+ return fallback;
88
106
  }
89
107
  </script>
90
108
 
@@ -94,11 +112,11 @@
94
112
  <div class="flex flex-col gap-2 px-4">
95
113
  <h2 class="text-3xl font-black tracking-tight flex items-center gap-3">
96
114
  <span class="p-2 bg-primary/10 rounded-xl">🔍</span>
97
- {t(m.live_store_dashboard, "Live Store Dashboard")}
115
+ {t("live_store_dashboard", "Live Store Dashboard")}
98
116
  </h2>
99
117
  <p class="text-base-content/50 font-medium">
100
118
  {t(
101
- m.real_time_monitor_desc,
119
+ "real_time_monitor_desc",
102
120
  "Real-time reactive state monitor for Rune Lab stores",
103
121
  )}
104
122
  </p>
@@ -112,7 +130,7 @@
112
130
  <div
113
131
  class="stat-title uppercase text-[10px] font-bold tracking-widest opacity-60"
114
132
  >
115
- {t(m.api_status, "API Status")}
133
+ {t("api_status", "API Status")}
116
134
  </div>
117
135
  <div
118
136
  class="stat-value capitalize {getStatusClass(
@@ -132,13 +150,13 @@
132
150
  <div
133
151
  class="stat-title uppercase text-[10px] font-bold tracking-widest opacity-60"
134
152
  >
135
- {t(m.active_toasts, "Active Toasts")}
153
+ {t("active_toasts", "Active Toasts")}
136
154
  </div>
137
155
  <div class="stat-value text-2xl">
138
156
  {toastStore.toasts.length}
139
157
  </div>
140
158
  <div class="stat-desc font-mono text-[10px] opacity-50">
141
- {t(m.currently_in_queue, "Currently in queue")}
159
+ {t("currently_in_queue", "Currently in queue")}
142
160
  </div>
143
161
  </div>
144
162
 
@@ -146,13 +164,13 @@
146
164
  <div
147
165
  class="stat-title uppercase text-[10px] font-bold tracking-widest opacity-60"
148
166
  >
149
- {t(m.commands_label, "Commands")}
167
+ {t("commands_label", "Commands")}
150
168
  </div>
151
169
  <div class="stat-value text-2xl">
152
170
  {commandStore.commands.length}
153
171
  </div>
154
172
  <div class="stat-desc font-mono text-[10px] opacity-50">
155
- {t(m.registered_in_registry, "Registered in registry")}
173
+ {t("registered_in_registry", "Registered in registry")}
156
174
  </div>
157
175
  </div>
158
176
  </div>
@@ -14,10 +14,11 @@
14
14
  { label: "Actions", icon: "⚡", component: Actions },
15
15
  { label: "Data Input", icon: "📥", component: DataInput },
16
16
  { label: "Display", icon: "📊", component: Display },
17
- { label: "Navigation", icon: "🧭", component: Navigation },
18
17
  { label: "Feedback", icon: "💬", component: Feedback },
19
18
  { label: "Visual", icon: "🎨", component: Visual },
20
19
  ];
20
+
21
+ import { showcaseState } from "./state.svelte";
21
22
  </script>
22
23
 
23
24
  <section
@@ -27,11 +28,11 @@
27
28
  {#each tabs as tab, i (tab.label)}
28
29
  <button
29
30
  role="tab"
30
- class="tab gap-2 transition-all duration-300 {layoutStore.activeShowcaseTab ===
31
+ class="tab gap-2 transition-all duration-300 {showcaseState.activeTab ===
31
32
  i
32
33
  ? 'tab-active shadow-lg'
33
34
  : ''}"
34
- onclick={() => layoutStore.setShowcaseTab(i)}
35
+ onclick={() => (showcaseState.activeTab = i)}
35
36
  >
36
37
  <span class="text-xl">{tab.icon}</span>
37
38
  <span class="hidden md:inline font-bold">{tab.label}</span>
@@ -43,7 +44,7 @@
43
44
  class="min-h-[600px] bg-base-200/50 rounded-3xl border border-base-content/5 overflow-hidden"
44
45
  >
45
46
  {#each tabs as tab, i (tab.label)}
46
- {#if layoutStore.activeShowcaseTab === i}
47
+ {#if showcaseState.activeTab === i}
47
48
  <div class="animate-in fade-in zoom-in-95 duration-500">
48
49
  <tab.component />
49
50
  </div>
@@ -0,0 +1,3 @@
1
+ export declare const showcaseState: {
2
+ activeTab: number;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const showcaseState = $state({
2
+ activeTab: 0,
3
+ });
@@ -3,6 +3,7 @@
3
3
  */
4
4
  export type ConnectionState = "connected" | "connecting" | "disconnected";
5
5
  export declare class ApiStore {
6
+ #private;
6
7
  url: string;
7
8
  version: string;
8
9
  connectionState: ConnectionState;
@@ -19,7 +20,7 @@ export declare class ApiStore {
19
20
  /**
20
21
  * Initialize API settings
21
22
  */
22
- init(url: string, version?: string): void;
23
+ init(url: string, version?: string, healthCheck?: () => Promise<boolean>): void;
23
24
  }
24
25
  export declare function createApiStore(): ApiStore;
25
26
  export declare function getApiStore(): ApiStore;
@@ -5,6 +5,7 @@ export class ApiStore {
5
5
  url = $state("http://localhost:8000");
6
6
  version = $state("v1");
7
7
  connectionState = $state("disconnected");
8
+ #healthCheck;
8
9
  // Derived
9
10
  isConnected = $derived(this.connectionState === "connected");
10
11
  isLoading = $derived(this.connectionState === "connecting");
@@ -26,10 +27,19 @@ export class ApiStore {
26
27
  */
27
28
  async reconnect() {
28
29
  this.connectionState = "connecting";
29
- // Simulate connection
30
30
  try {
31
- await new Promise((resolve) => setTimeout(resolve, 1500));
32
- this.connectionState = "connected";
31
+ if (this.#healthCheck) {
32
+ const isHealthy = await this.#healthCheck();
33
+ this.connectionState = isHealthy ? "connected" : "disconnected";
34
+ }
35
+ else {
36
+ // Simulate connection if no health check provided
37
+ if (import.meta.env.DEV) {
38
+ console.warn("[rune-lab] ApiStore: No healthCheck provided to init(). Using simulated connection delay.");
39
+ }
40
+ await new Promise((resolve) => setTimeout(resolve, 1500));
41
+ this.connectionState = "connected";
42
+ }
33
43
  }
34
44
  catch (e) {
35
45
  this.connectionState = "disconnected";
@@ -38,9 +48,11 @@ export class ApiStore {
38
48
  /**
39
49
  * Initialize API settings
40
50
  */
41
- init(url, version = "v1") {
51
+ init(url, version = "v1", healthCheck) {
42
52
  this.url = url;
43
53
  this.version = version;
54
+ if (healthCheck)
55
+ this.#healthCheck = healthCheck;
44
56
  this.reconnect();
45
57
  }
46
58
  }
@@ -23,6 +23,7 @@ export declare class AppStore {
23
23
  repository: string;
24
24
  license: string;
25
25
  homepage: string;
26
+ customIcons: Record<string, string>;
26
27
  /**
27
28
  * Initialize app store with metadata
28
29
  */
@@ -31,6 +32,10 @@ export declare class AppStore {
31
32
  * Get full app information object
32
33
  */
33
34
  get info(): AppData;
35
+ /**
36
+ * Registers custom SVG icons to be available globally in the Icon component
37
+ */
38
+ registerIcons(icons: Record<string, string>): void;
34
39
  }
35
40
  export declare function createAppStore(): AppStore;
36
41
  export declare function getAppStore(): AppStore;
@@ -14,6 +14,7 @@ export class AppStore {
14
14
  repository = $state("https://github.com/Yrrrrrf/rune-lab");
15
15
  license = $state("MIT");
16
16
  homepage = $state("https://jsr.io/@yrrrrrf/rune-lab");
17
+ customIcons = $state({});
17
18
  #initialized = false;
18
19
  /**
19
20
  * Initialize app store with metadata
@@ -55,6 +56,12 @@ export class AppStore {
55
56
  homepage: this.homepage,
56
57
  };
57
58
  }
59
+ /**
60
+ * Registers custom SVG icons to be available globally in the Icon component
61
+ */
62
+ registerIcons(icons) {
63
+ this.customIcons = { ...this.customIcons, ...icons };
64
+ }
58
65
  }
59
66
  // Export singleton instance
60
67
  export function createAppStore() {
@@ -1,6 +1,6 @@
1
1
  import type { AppStore } from "./app.svelte";
2
2
  import type { ApiStore } from "./api.svelte";
3
- import type { ConfigStore } from "../devtools/createConfigStore.svelte";
3
+ import type { ConfigStore } from "./createConfigStore.svelte";
4
4
  import type { ToastStore } from "./toast.svelte";
5
5
  import type { Theme } from "./theme.svelte";
6
6
  import type { Language } from "./language.svelte";
@@ -1,4 +1,4 @@
1
- import { type ConfigStore } from "../devtools/createConfigStore.svelte";
1
+ import { type ConfigStore } from "./createConfigStore.svelte";
2
2
  /**
3
3
  * Currency configuration
4
4
  * Based on ISO 4217 currency codes
@@ -1,4 +1,4 @@
1
- import { createConfigStore, } from "../devtools/createConfigStore.svelte";
1
+ import { createConfigStore, } from "./createConfigStore.svelte";
2
2
  import { getContext } from "svelte";
3
3
  import { RUNE_LAB_CONTEXT } from "../context";
4
4
  const CURRENCIES = [
@@ -1,9 +1,11 @@
1
1
  export { type AppData, AppStore, createAppStore, getAppStore, } from "./app.svelte";
2
2
  export { createLayoutStore, getLayoutStore, LayoutStore, type NavigationItem, type NavigationSection, type WorkspaceItem, } from "./layout.svelte";
3
- export { ApiStore, createApiStore, getApiStore } from "./api.svelte";
3
+ export { ApiStore, type ConnectionState, createApiStore, getApiStore, } from "./api.svelte";
4
4
  export { createLanguageStore, getLanguageStore, type Language, } from "./language.svelte";
5
5
  export { createCurrencyStore, type Currency, getCurrencyStore, } from "./currency.svelte";
6
6
  export { createToastStore, getToastStore, ToastStore } from "./toast.svelte";
7
7
  export { type Command, CommandStore, createCommandStore, getCommandStore, } from "./commands.svelte";
8
8
  export { createShortcutStore, getShortcutStore, LAYOUT_SHORTCUTS, type ShortcutEntry, shortcutListener, type ShortcutMeta, ShortcutStore, } from "./shortcuts.svelte";
9
9
  export { createThemeStore, getThemeStore, type Theme } from "./theme.svelte";
10
+ export { type ConfigStore, createConfigStore, } from "./createConfigStore.svelte";
11
+ export { createToastBridge, notify } from "./toast-bridge";
@@ -3,10 +3,12 @@
3
3
  // Stores
4
4
  export { AppStore, createAppStore, getAppStore, } from "./app.svelte";
5
5
  export { createLayoutStore, getLayoutStore, LayoutStore, } from "./layout.svelte";
6
- export { ApiStore, createApiStore, getApiStore } from "./api.svelte";
6
+ export { ApiStore, createApiStore, getApiStore, } from "./api.svelte";
7
7
  export { createLanguageStore, getLanguageStore, } from "./language.svelte";
8
8
  export { createCurrencyStore, getCurrencyStore, } from "./currency.svelte";
9
9
  export { createToastStore, getToastStore, ToastStore } from "./toast.svelte";
10
10
  export { CommandStore, createCommandStore, getCommandStore, } from "./commands.svelte";
11
11
  export { createShortcutStore, getShortcutStore, LAYOUT_SHORTCUTS, shortcutListener, ShortcutStore, } from "./shortcuts.svelte";
12
12
  export { createThemeStore, getThemeStore } from "./theme.svelte";
13
+ export { createConfigStore, } from "./createConfigStore.svelte";
14
+ export { createToastBridge, notify } from "./toast-bridge";
@@ -1,4 +1,4 @@
1
- import { type ConfigStore } from "../devtools/createConfigStore.svelte";
1
+ import { type ConfigStore } from "./createConfigStore.svelte";
2
2
  /**
3
3
  * Language configuration
4
4
  * Represents a supported language in the application
@@ -48,7 +48,11 @@ export declare const LANGUAGES: readonly [{
48
48
  readonly flag: "🇻🇳";
49
49
  }];
50
50
  import type { PersistenceDriver } from "../persistence/types";
51
- export declare function createLanguageStore(driver?: PersistenceDriver | (() => PersistenceDriver | undefined)): {
51
+ export interface LanguageStoreOptions {
52
+ driver?: PersistenceDriver | (() => PersistenceDriver | undefined);
53
+ onLocaleChange?: (code: string) => void;
54
+ }
55
+ export declare function createLanguageStore(options?: LanguageStoreOptions): {
52
56
  current: string | undefined;
53
57
  available: Language[];
54
58
  set(id: string | undefined): void;