rune-lab 0.0.19 → 0.0.21

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 (88) 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 +81 -0
  5. package/dist/components/RuneProvider.svelte.d.ts +9 -0
  6. package/dist/composables/usePersistence.d.ts +2 -0
  7. package/dist/composables/usePersistence.js +5 -0
  8. package/dist/composables/useRuneLab.d.ts +17 -0
  9. package/dist/composables/useRuneLab.js +19 -0
  10. package/dist/config.d.ts +4 -85
  11. package/dist/config.js +6 -32
  12. package/dist/context.d.ts +12 -0
  13. package/dist/context.js +13 -0
  14. package/dist/devtools/API_Monitor.svelte +5 -2
  15. package/dist/devtools/Toaster.svelte +14 -12
  16. package/dist/devtools/createConfigStore.svelte.d.ts +11 -1
  17. package/dist/devtools/createConfigStore.svelte.js +13 -11
  18. package/dist/features/command-palette/CommandPalette.svelte +12 -4
  19. package/dist/features/config/components/AppSettingSelector.svelte +85 -3
  20. package/dist/features/config/components/AppSettingSelector.svelte.d.ts +2 -0
  21. package/dist/features/config/components/CurrencySelector.svelte +27 -9
  22. package/dist/features/config/components/CurrencySelector.svelte.d.ts +3 -1
  23. package/dist/features/config/components/LanguageSelector.svelte +21 -6
  24. package/dist/features/config/components/LanguageSelector.svelte.d.ts +3 -1
  25. package/dist/features/config/components/ThemeSelector.svelte +23 -8
  26. package/dist/features/config/components/ThemeSelector.svelte.d.ts +3 -1
  27. package/dist/features/detail-panels/DashboardPanel.svelte +34 -17
  28. package/dist/features/detail-panels/ShortcutsPanel.svelte +185 -88
  29. package/dist/features/detail-panels/ShowcasePanel.svelte +20 -6
  30. package/dist/features/layout/smart/ConnectedNavigationPanel.svelte +30 -0
  31. package/dist/features/layout/smart/ConnectedNavigationPanel.svelte.d.ts +10 -0
  32. package/dist/features/layout/smart/ConnectedWorkspaceStrip.svelte +23 -0
  33. package/dist/features/layout/smart/ConnectedWorkspaceStrip.svelte.d.ts +9 -0
  34. package/dist/features/shortcuts/ShortcutPalette.svelte +14 -6
  35. package/dist/index.d.ts +14 -2
  36. package/dist/index.js +19 -8
  37. package/dist/layout/NavigationPanel.svelte +97 -121
  38. package/dist/layout/NavigationPanel.svelte.d.ts +5 -1
  39. package/dist/layout/WorkspaceLayout.svelte +109 -30
  40. package/dist/layout/WorkspaceLayout.svelte.d.ts +2 -2
  41. package/dist/layout/WorkspaceStrip.svelte +17 -12
  42. package/dist/layout/WorkspaceStrip.svelte.d.ts +3 -1
  43. package/dist/layout/index.d.ts +1 -1
  44. package/dist/layout/index.js +1 -1
  45. package/dist/paraglide/runtime.d.ts +43 -2
  46. package/dist/paraglide/runtime.js +143 -23
  47. package/dist/paraglide/server.js +37 -14
  48. package/dist/persistence/drivers.d.ts +10 -0
  49. package/dist/persistence/drivers.js +71 -0
  50. package/dist/persistence/types.d.ts +17 -0
  51. package/dist/persistence/types.js +2 -0
  52. package/dist/showcase/AppStateInspector.svelte +26 -11
  53. package/dist/showcase/Showcase.svelte +4 -1
  54. package/dist/state/api.svelte.d.ts +3 -3
  55. package/dist/state/api.svelte.js +9 -2
  56. package/dist/state/app.svelte.d.ts +3 -3
  57. package/dist/state/app.svelte.js +14 -4
  58. package/dist/state/commands.svelte.d.ts +21 -4
  59. package/dist/state/commands.svelte.js +22 -94
  60. package/dist/state/currency.svelte.d.ts +4 -1
  61. package/dist/state/currency.svelte.js +16 -8
  62. package/dist/state/index.d.ts +9 -9
  63. package/dist/state/index.js +10 -9
  64. package/dist/state/language.svelte.d.ts +4 -1
  65. package/dist/state/language.svelte.js +27 -8
  66. package/dist/state/layout.svelte.d.ts +10 -3
  67. package/dist/state/layout.svelte.js +56 -14
  68. package/dist/state/shortcuts.svelte.d.ts +56 -6
  69. package/dist/state/shortcuts.svelte.js +21 -69
  70. package/dist/state/theme.svelte.d.ts +4 -1
  71. package/dist/state/theme.svelte.js +16 -8
  72. package/dist/state/toast.svelte.d.ts +3 -3
  73. package/dist/state/toast.svelte.js +9 -2
  74. package/package.json +9 -9
  75. package/dist/features/command-palette/commands.svelte.d.ts +0 -8
  76. package/dist/features/command-palette/commands.svelte.js +0 -5
  77. package/dist/features/config/stores/api.svelte.d.ts +0 -13
  78. package/dist/features/config/stores/api.svelte.js +0 -5
  79. package/dist/features/config/stores/app.svelte.d.ts +0 -13
  80. package/dist/features/config/stores/app.svelte.js +0 -5
  81. package/dist/features/config/stores/currency.svelte.d.ts +0 -8
  82. package/dist/features/config/stores/currency.svelte.js +0 -5
  83. package/dist/features/config/stores/language.svelte.d.ts +0 -8
  84. package/dist/features/config/stores/language.svelte.js +0 -5
  85. package/dist/features/config/stores/theme.svelte.d.ts +0 -8
  86. package/dist/features/config/stores/theme.svelte.js +0 -5
  87. package/dist/features/config/stores/toast.svelte.d.ts +0 -9
  88. package/dist/features/config/stores/toast.svelte.js +0 -5
@@ -37,6 +37,18 @@ export const strategy = [
37
37
  "globalVariable",
38
38
  "baseLocale"
39
39
  ];
40
+ /**
41
+ * Route-level strategy overrides.
42
+ *
43
+ * `match` uses URLPattern syntax.
44
+ *
45
+ * @type {Array<{
46
+ * match: string;
47
+ * strategy?: Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage" | `custom-${string}`>;
48
+ * exclude?: boolean;
49
+ * }>}
50
+ */
51
+ export const routeStrategies = [];
40
52
  /**
41
53
  * The used URL patterns.
42
54
  *
@@ -101,6 +113,63 @@ export const urlPatterns = [
101
113
  ]
102
114
  }
103
115
  ];
116
+ /** @type {string | undefined} */
117
+ let cachedRouteStrategyUrl;
118
+ /** @type {{ match: string; strategy?: Array<string>; exclude?: boolean } | undefined} */
119
+ let cachedRouteStrategy;
120
+ /**
121
+ * @param {string | URL} url
122
+ * @returns {{ match: string; strategy?: Array<string>; exclude?: boolean } | undefined}
123
+ */
124
+ function findMatchingRouteStrategy(url) {
125
+ if (routeStrategies.length === 0) {
126
+ return undefined;
127
+ }
128
+ const urlString = typeof url === "string" ? url : url.href;
129
+ if (cachedRouteStrategyUrl === urlString) {
130
+ return cachedRouteStrategy;
131
+ }
132
+ const urlObject = new URL(urlString, "http://dummy.com");
133
+ let match;
134
+ for (const routeStrategy of routeStrategies) {
135
+ const pattern = new URLPattern(routeStrategy.match, urlObject.href);
136
+ if (pattern.exec(urlObject.href)) {
137
+ match = routeStrategy;
138
+ break;
139
+ }
140
+ }
141
+ cachedRouteStrategyUrl = urlString;
142
+ cachedRouteStrategy = match;
143
+ return match;
144
+ }
145
+ /**
146
+ * Returns the strategy to use for a specific URL.
147
+ *
148
+ * If no route strategy matches (or the matching rule is `exclude: true`),
149
+ * the global strategy is returned.
150
+ *
151
+ * @param {string | URL} url
152
+ * @returns {typeof strategy}
153
+ */
154
+ export function getStrategyForUrl(url) {
155
+ const routeStrategy = findMatchingRouteStrategy(url);
156
+ if (routeStrategy &&
157
+ routeStrategy.exclude !== true &&
158
+ Array.isArray(routeStrategy.strategy)) {
159
+ // @ts-ignore - runtime value is injected and validated by compiler types.
160
+ return routeStrategy.strategy;
161
+ }
162
+ return strategy;
163
+ }
164
+ /**
165
+ * Returns whether the given URL is excluded from middleware i18n processing.
166
+ *
167
+ * @param {string | URL} url
168
+ * @returns {boolean}
169
+ */
170
+ export function isExcludedByRouteStrategy(url) {
171
+ return findMatchingRouteStrategy(url)?.exclude === true;
172
+ }
104
173
  /**
105
174
  * @typedef {{
106
175
  * getStore(): {
@@ -182,8 +251,6 @@ export let getLocale = () => {
182
251
  if (experimentalStaticLocale !== undefined) {
183
252
  return assertIsLocale(experimentalStaticLocale);
184
253
  }
185
- /** @type {string | undefined} */
186
- let locale;
187
254
  // if running in a server-side rendering context
188
255
  // retrieve the locale from the async local storage
189
256
  if (serverAsyncLocalStorage) {
@@ -192,7 +259,48 @@ export let getLocale = () => {
192
259
  return locale;
193
260
  }
194
261
  }
195
- for (const strat of strategy) {
262
+ let strategyToUse = strategy;
263
+ if (!isServer && typeof window !== "undefined" && window.location?.href) {
264
+ strategyToUse = getStrategyForUrl(window.location.href);
265
+ }
266
+ const resolved = resolveLocaleWithStrategies(strategyToUse, typeof window !== "undefined" ? window.location?.href : undefined);
267
+ if (resolved) {
268
+ if (!localeInitiallySet) {
269
+ _locale = resolved;
270
+ // https://github.com/opral/inlang-paraglide-js/issues/455
271
+ localeInitiallySet = true;
272
+ setLocale(resolved, { reload: false });
273
+ }
274
+ return resolved;
275
+ }
276
+ throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
277
+ };
278
+ /**
279
+ * Resolve locale for a given URL using route-aware strategies.
280
+ *
281
+ * @param {string | URL} url
282
+ * @returns {Locale}
283
+ */
284
+ export function getLocaleForUrl(url) {
285
+ if (experimentalStaticLocale !== undefined) {
286
+ return assertIsLocale(experimentalStaticLocale);
287
+ }
288
+ const strategyToUse = getStrategyForUrl(url);
289
+ const resolved = resolveLocaleWithStrategies(strategyToUse, typeof url === "string" ? url : url.href);
290
+ if (resolved) {
291
+ return resolved;
292
+ }
293
+ throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
294
+ }
295
+ /**
296
+ * @param {typeof strategy} strategyToUse
297
+ * @param {string | undefined} urlForUrlStrategy
298
+ * @returns {Locale | undefined}
299
+ */
300
+ function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
301
+ /** @type {string | undefined} */
302
+ let locale;
303
+ for (const strat of strategyToUse) {
196
304
  if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
197
305
  locale = extractLocaleFromCookie();
198
306
  }
@@ -202,8 +310,8 @@ export let getLocale = () => {
202
310
  else if (TREE_SHAKE_URL_STRATEGY_USED &&
203
311
  strat === "url" &&
204
312
  !isServer &&
205
- typeof window !== "undefined") {
206
- locale = extractLocaleFromUrl(window.location.href);
313
+ typeof urlForUrlStrategy === "string") {
314
+ locale = extractLocaleFromUrl(urlForUrlStrategy);
207
315
  }
208
316
  else if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
209
317
  strat === "globalVariable" &&
@@ -232,20 +340,12 @@ export let getLocale = () => {
232
340
  locale = result;
233
341
  }
234
342
  }
235
- // check if match, else continue loop
236
343
  if (locale !== undefined) {
237
- const asserted = assertIsLocale(locale);
238
- if (!localeInitiallySet) {
239
- _locale = asserted;
240
- // https://github.com/opral/inlang-paraglide-js/issues/455
241
- localeInitiallySet = true;
242
- setLocale(asserted, { reload: false });
243
- }
244
- return asserted;
344
+ return assertIsLocale(locale);
245
345
  }
246
346
  }
247
- throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
248
- };
347
+ return undefined;
348
+ }
249
349
  /**
250
350
  * Overwrite the `getLocale()` function.
251
351
  *
@@ -324,7 +424,11 @@ export let setLocale = (newLocale, options) => {
324
424
  const customSetLocalePromises = [];
325
425
  /** @type {string | undefined} */
326
426
  let newLocation = undefined;
327
- for (const strat of strategy) {
427
+ let strategyToUse = strategy;
428
+ if (!isServer && typeof window !== "undefined" && window.location?.href) {
429
+ strategyToUse = getStrategyForUrl(window.location.href);
430
+ }
431
+ for (const strat of strategyToUse) {
328
432
  if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
329
433
  strat === "globalVariable") {
330
434
  // a default for a custom strategy to get started quickly
@@ -507,9 +611,19 @@ export function assertIsLocale(input) {
507
611
  * @type {(request: Request) => Locale}
508
612
  */
509
613
  export const extractLocaleFromRequest = (request) => {
614
+ return extractLocaleFromRequestWithStrategies(request, getStrategyForUrl(request.url));
615
+ };
616
+ /**
617
+ * Extracts a locale from a request using the provided strategy order.
618
+ *
619
+ * @param {Request} request
620
+ * @param {typeof strategy} strategies
621
+ * @returns {Locale}
622
+ */
623
+ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
510
624
  /** @type {string|undefined} */
511
625
  let locale;
512
- for (const strat of strategy) {
626
+ for (const strat of strategies) {
513
627
  if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
514
628
  locale = request.headers
515
629
  .get("cookie")
@@ -582,6 +696,7 @@ export const extractLocaleFromRequest = (request) => {
582
696
  export const extractLocaleFromRequestAsync = async (request) => {
583
697
  /** @type {string|undefined} */
584
698
  let locale;
699
+ const strategy = getStrategyForUrl(request.url);
585
700
  // Process custom strategies first, in order
586
701
  for (const strat of strategy) {
587
702
  if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
@@ -597,7 +712,7 @@ export const extractLocaleFromRequestAsync = async (request) => {
597
712
  }
598
713
  }
599
714
  // If no custom strategy provided a valid locale, fall back to sync version
600
- locale = extractLocaleFromRequest(request);
715
+ locale = extractLocaleFromRequestWithStrategies(request, strategy);
601
716
  return assertIsLocale(locale);
602
717
  };
603
718
 
@@ -1128,11 +1243,12 @@ export function aggregateGroups(match) {
1128
1243
  * @returns {Promise<ShouldRedirectResult>}
1129
1244
  */
1130
1245
  export async function shouldRedirect(input = {}) {
1131
- const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input));
1132
- if (!strategy.includes("url")) {
1246
+ const currentUrl = resolveUrl(input);
1247
+ const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input, currentUrl));
1248
+ const strategy = getStrategyForUrl(currentUrl.href);
1249
+ if (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes("url")) {
1133
1250
  return { shouldRedirect: false, locale, redirectUrl: undefined };
1134
1251
  }
1135
- const currentUrl = resolveUrl(input);
1136
1252
  const localizedUrl = localizeUrl(currentUrl.href, { locale });
1137
1253
  const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
1138
1254
  return {
@@ -1145,15 +1261,19 @@ export async function shouldRedirect(input = {}) {
1145
1261
  * Resolves the locale either from the provided input or by using the configured strategies.
1146
1262
  *
1147
1263
  * @param {ShouldRedirectInput} input
1264
+ * @param {URL} currentUrl
1148
1265
  * @returns {Promise<ReturnType<typeof assertIsLocale>>}
1149
1266
  */
1150
- async function resolveLocale(input) {
1267
+ async function resolveLocale(input, currentUrl) {
1151
1268
  if (input.locale) {
1152
1269
  return assertIsLocale(input.locale);
1153
1270
  }
1154
1271
  if (input.request) {
1155
1272
  return extractLocaleFromRequestAsync(input.request);
1156
1273
  }
1274
+ if (typeof input.url !== "undefined") {
1275
+ return getLocaleForUrl(currentUrl.href);
1276
+ }
1157
1277
  return getLocale();
1158
1278
  }
1159
1279
  /**
@@ -106,6 +106,15 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
106
106
  } else if (!runtime.serverAsyncLocalStorage) {
107
107
  runtime.overwriteServerAsyncLocalStorage(createMockAsyncLocalStorage());
108
108
  }
109
+ if (runtime.isExcludedByRouteStrategy(request.url)) {
110
+ const locale = runtime.baseLocale;
111
+ const origin = new URL(request.url).origin;
112
+ const newRequest = cloneRequestWithFallback(request);
113
+ /** @type {Set<string>} */
114
+ const messageCalls = new Set();
115
+ return /** @type {Response} */ (await runtime.serverAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
116
+ }
117
+ const strategy = runtime.getStrategyForUrl(request.url);
109
118
  const decision = await runtime.shouldRedirect({ request });
110
119
  const locale = decision.locale;
111
120
  const origin = new URL(request.url).origin;
@@ -117,7 +126,7 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
117
126
  // Create headers object with Vary header if preferredLanguage strategy is used
118
127
  /** @type {Record<string, string>} */
119
128
  const headers = {};
120
- if (runtime.strategy.includes("preferredLanguage")) {
129
+ if (strategy.includes("preferredLanguage")) {
121
130
  headers["Vary"] = "Accept-Language";
122
131
  }
123
132
  const response = new Response(null, {
@@ -137,22 +146,11 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
137
146
  // de-localized URL e.g. `/en/about` to `/about`. Otherwise,
138
147
  // the server can't render the correct page.
139
148
  let newRequest;
140
- if (runtime.strategy.includes("url")) {
149
+ if (strategy.includes("url")) {
141
150
  newRequest = new Request(runtime.deLocalizeUrl(request.url), request);
142
151
  }
143
152
  else {
144
- // Some metaframeworks (NextJS) require a new Request object
145
- // https://github.com/opral/inlang-paraglide-js/issues/411
146
- // However, some frameworks (TanStack Start 1.143+) use custom Request
147
- // implementations that cannot be cloned with `new Request(request)`
148
- // https://github.com/opral/paraglide-js/issues/573
149
- // Try to clone the request, but fall back to the original if cloning fails
150
- try {
151
- newRequest = new Request(request);
152
- }
153
- catch {
154
- newRequest = request;
155
- }
153
+ newRequest = cloneRequestWithFallback(request);
156
154
  }
157
155
  // the message functions that have been called in this request
158
156
  /** @type {Set<string>} */
@@ -185,6 +183,31 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
185
183
  }
186
184
  return response;
187
185
  }
186
+ /**
187
+ * Some metaframeworks (NextJS) require a new Request object.
188
+ * https://github.com/opral/inlang-paraglide-js/issues/411
189
+ *
190
+ * However, some frameworks (TanStack Start 1.143+) use custom Request
191
+ * implementations that cannot be cloned with `new Request(request)`.
192
+ * https://github.com/opral/paraglide-js/issues/573
193
+ *
194
+ * @param {Request} request
195
+ * @returns {Request}
196
+ */
197
+ function cloneRequestWithFallback(request) {
198
+ try {
199
+ // Clone first so building a new Request does not consume the original body stream.
200
+ return new Request(request.clone());
201
+ }
202
+ catch {
203
+ try {
204
+ return new Request(request);
205
+ }
206
+ catch {
207
+ return request;
208
+ }
209
+ }
210
+ }
188
211
  /**
189
212
  * Creates a mock AsyncLocalStorage implementation for environments where
190
213
  * native AsyncLocalStorage is not available or disabled.
@@ -0,0 +1,10 @@
1
+ import type { PersistenceDriver } from "./types";
2
+ export declare function createInMemoryDriver(): PersistenceDriver;
3
+ export declare const inMemoryDriver: PersistenceDriver;
4
+ export declare const localStorageDriver: PersistenceDriver;
5
+ export declare const sessionStorageDriver: PersistenceDriver;
6
+ export declare const cookieDriver: (options?: {
7
+ path?: string;
8
+ maxAge?: number;
9
+ sameSite?: "Lax" | "Strict" | "None";
10
+ }) => PersistenceDriver;
@@ -0,0 +1,71 @@
1
+ export function createInMemoryDriver() {
2
+ const store = new Map();
3
+ return {
4
+ get: (key) => store.get(key) ?? null,
5
+ set: (key, value) => store.set(key, value),
6
+ remove: (key) => store.delete(key),
7
+ };
8
+ }
9
+ export const inMemoryDriver = createInMemoryDriver();
10
+ export const localStorageDriver = {
11
+ get: (key) => {
12
+ if (typeof window === "undefined")
13
+ return null;
14
+ return window.localStorage.getItem(key);
15
+ },
16
+ set: (key, value) => {
17
+ if (typeof window === "undefined")
18
+ return;
19
+ window.localStorage.setItem(key, value);
20
+ },
21
+ remove: (key) => {
22
+ if (typeof window === "undefined")
23
+ return;
24
+ window.localStorage.removeItem(key);
25
+ },
26
+ };
27
+ export const sessionStorageDriver = {
28
+ get: (key) => {
29
+ if (typeof window === "undefined")
30
+ return null;
31
+ return window.sessionStorage.getItem(key);
32
+ },
33
+ set: (key, value) => {
34
+ if (typeof window === "undefined")
35
+ return;
36
+ window.sessionStorage.setItem(key, value);
37
+ },
38
+ remove: (key) => {
39
+ if (typeof window === "undefined")
40
+ return;
41
+ window.sessionStorage.removeItem(key);
42
+ },
43
+ };
44
+ export const cookieDriver = (options = {}) => ({
45
+ get: (key) => {
46
+ if (typeof window === "undefined" || typeof document === "undefined") {
47
+ return null;
48
+ }
49
+ const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
50
+ return match ? decodeURIComponent(match[2]) : null;
51
+ },
52
+ set: (key, value) => {
53
+ if (typeof window === "undefined" || typeof document === "undefined") {
54
+ return;
55
+ }
56
+ let cookie = `${key}=${encodeURIComponent(value)}`;
57
+ if (options.path)
58
+ cookie += `; path=${options.path}`;
59
+ if (options.maxAge)
60
+ cookie += `; max-age=${options.maxAge}`;
61
+ if (options.sameSite)
62
+ cookie += `; samesite=${options.sameSite}`;
63
+ document.cookie = cookie;
64
+ },
65
+ remove: (key) => {
66
+ if (typeof window === "undefined" || typeof document === "undefined") {
67
+ return;
68
+ }
69
+ document.cookie = `${key}=; max-age=0; path=${options.path || "/"}`;
70
+ },
71
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Synchronous persistence driver interface for Svelte stores
3
+ */
4
+ export interface PersistenceDriver {
5
+ /**
6
+ * Get a value by key. Should return null if not found.
7
+ */
8
+ get(key: string): string | null;
9
+ /**
10
+ * Set a value by key.
11
+ */
12
+ set(key: string, value: string): void;
13
+ /**
14
+ * Remove a value by key.
15
+ */
16
+ remove(key: string): void;
17
+ }
@@ -0,0 +1,2 @@
1
+ // src/lib/persistence/types.ts
2
+ export {};
@@ -1,7 +1,22 @@
1
1
  <script lang="ts">
2
- import { appConfig } from "../config";
3
2
  import * as m from "../paraglide/messages.js";
4
3
  import StoreDetailCard from "./StoreDetailCard.svelte";
4
+ import { getAppStore } from "../state/app.svelte";
5
+ import { getApiStore } from "../state/api.svelte";
6
+ import { getThemeStore } from "../state/theme.svelte";
7
+ import { getLanguageStore } from "../state/language.svelte";
8
+ import { getCurrencyStore } from "../state/currency.svelte";
9
+ import { getToastStore } from "../state/toast.svelte";
10
+ import { getCommandStore } from "../state/commands.svelte";
11
+
12
+ // Inject context stores
13
+ const appStore = getAppStore();
14
+ const apiStore = getApiStore();
15
+ const themeStore = getThemeStore();
16
+ const languageStore = getLanguageStore();
17
+ const currencyStore = getCurrencyStore();
18
+ const toastStore = getToastStore();
19
+ const commandStore = getCommandStore();
5
20
 
6
21
  const storeState = $derived([
7
22
  {
@@ -13,12 +28,12 @@
13
28
  {
14
29
  key: "Name",
15
30
  labelKey: "name_label",
16
- value: appConfig.app.name,
31
+ value: appStore.name,
17
32
  },
18
33
  {
19
34
  key: "Version",
20
35
  labelKey: "version_label",
21
- value: appConfig.app.version,
36
+ value: appStore.version,
22
37
  },
23
38
  ],
24
39
  },
@@ -31,7 +46,7 @@
31
46
  {
32
47
  key: "Theme",
33
48
  labelKey: "current_theme",
34
- value: `${appConfig.theme.getProp("icon") || ""} ${appConfig.theme.current}`,
49
+ value: `${themeStore.getProp("icon") || ""} ${themeStore.current}`,
35
50
  },
36
51
  ],
37
52
  },
@@ -44,12 +59,12 @@
44
59
  {
45
60
  key: "Language",
46
61
  labelKey: "current_language",
47
- value: `${appConfig.language.getProp("flag") || ""} ${appConfig.language.current}`,
62
+ value: `${languageStore.getProp("flag") || ""} ${languageStore.current}`,
48
63
  },
49
64
  {
50
65
  key: "Currency",
51
66
  labelKey: "current_currency",
52
- value: `${appConfig.currency.getProp("symbol") || ""} ${appConfig.currency.current}`,
67
+ value: `${currencyStore.getProp("symbol") || ""} ${currencyStore.current}`,
53
68
  },
54
69
  ],
55
70
  },
@@ -101,15 +116,15 @@
101
116
  </div>
102
117
  <div
103
118
  class="stat-value capitalize {getStatusClass(
104
- appConfig.api.connectionState,
119
+ apiStore.connectionState,
105
120
  )} text-2xl"
106
121
  >
107
- {appConfig.api.connectionState}
122
+ {apiStore.connectionState}
108
123
  </div>
109
124
  <div
110
125
  class="stat-desc font-mono text-[10px] opacity-50 truncate max-w-[200px]"
111
126
  >
112
- {appConfig.api.URL}
127
+ {apiStore.URL}
113
128
  </div>
114
129
  </div>
115
130
 
@@ -120,7 +135,7 @@
120
135
  {t(m.active_toasts, "Active Toasts")}
121
136
  </div>
122
137
  <div class="stat-value text-2xl">
123
- {appConfig.toast.toasts.length}
138
+ {toastStore.toasts.length}
124
139
  </div>
125
140
  <div class="stat-desc font-mono text-[10px] opacity-50">
126
141
  {t(m.currently_in_queue, "Currently in queue")}
@@ -134,7 +149,7 @@
134
149
  {t(m.commands_label, "Commands")}
135
150
  </div>
136
151
  <div class="stat-value text-2xl">
137
- {appConfig.commands.commands.length}
152
+ {commandStore.commands.length}
138
153
  </div>
139
154
  <div class="stat-desc font-mono text-[10px] opacity-50">
140
155
  {t(m.registered_in_registry, "Registered in registry")}
@@ -1,5 +1,8 @@
1
1
  <script lang="ts">
2
- import { layoutStore } from "../state/layout.svelte";
2
+ import { getLayoutStore } from "../state/layout.svelte";
3
+
4
+ const layoutStore = getLayoutStore();
5
+
3
6
  import Actions from "./tabs/Actions.svelte";
4
7
  import DataInput from "./tabs/DataInput.svelte";
5
8
  import Display from "./tabs/Display.svelte";
@@ -2,7 +2,7 @@
2
2
  * API connection state and configuration
3
3
  */
4
4
  export type ConnectionState = "connected" | "connecting" | "disconnected";
5
- declare class ApiStore {
5
+ export declare class ApiStore {
6
6
  url: string;
7
7
  version: string;
8
8
  connectionState: ConnectionState;
@@ -21,5 +21,5 @@ declare class ApiStore {
21
21
  */
22
22
  init(url: string, version?: string): void;
23
23
  }
24
- export declare const apiStore: ApiStore;
25
- export {};
24
+ export declare function createApiStore(): ApiStore;
25
+ export declare function getApiStore(): ApiStore;
@@ -1,4 +1,6 @@
1
- class ApiStore {
1
+ import { getContext } from "svelte";
2
+ import { RUNE_LAB_CONTEXT } from "../context";
3
+ export class ApiStore {
2
4
  // State
3
5
  url = $state("http://localhost:8000");
4
6
  version = $state("v1");
@@ -42,4 +44,9 @@ class ApiStore {
42
44
  this.reconnect();
43
45
  }
44
46
  }
45
- export const apiStore = new ApiStore();
47
+ export function createApiStore() {
48
+ return new ApiStore();
49
+ }
50
+ export function getApiStore() {
51
+ return getContext(RUNE_LAB_CONTEXT.api);
52
+ }
@@ -14,7 +14,7 @@ export interface AppData {
14
14
  * App Store
15
15
  * Manages application metadata and identity
16
16
  */
17
- declare class AppStore {
17
+ export declare class AppStore {
18
18
  #private;
19
19
  name: string;
20
20
  version: string;
@@ -32,5 +32,5 @@ declare class AppStore {
32
32
  */
33
33
  get info(): AppData;
34
34
  }
35
- export declare const appStore: AppStore;
36
- export {};
35
+ export declare function createAppStore(): AppStore;
36
+ export declare function getAppStore(): AppStore;
@@ -1,10 +1,11 @@
1
1
  // src/lib/stores/app-config.svelte.ts
2
- // SDK Package - Application Metadata Management
2
+ import { getContext } from "svelte";
3
+ import { RUNE_LAB_CONTEXT } from "../context";
3
4
  /**
4
5
  * App Store
5
6
  * Manages application metadata and identity
6
7
  */
7
- class AppStore {
8
+ export class AppStore {
8
9
  // State
9
10
  name = $state("Rune Lab");
10
11
  version = $state("0.0.1");
@@ -18,8 +19,12 @@ class AppStore {
18
19
  * Initialize app store with metadata
19
20
  */
20
21
  init(data) {
21
- if (this.#initialized)
22
+ if (this.#initialized) {
23
+ if (import.meta.env?.DEV) {
24
+ console.warn("AppStore.init() called multiple times. Ignoring subsequent calls.", "Overwritten properties would have been:", data);
25
+ }
22
26
  return;
27
+ }
23
28
  if (data.name)
24
29
  this.name = data.name;
25
30
  if (data.version)
@@ -52,4 +57,9 @@ class AppStore {
52
57
  }
53
58
  }
54
59
  // Export singleton instance
55
- export const appStore = new AppStore();
60
+ export function createAppStore() {
61
+ return new AppStore();
62
+ }
63
+ export function getAppStore() {
64
+ return getContext(RUNE_LAB_CONTEXT.app);
65
+ }