rune-lab 0.4.2-beta.2 → 0.4.2-beta.3

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 (173) hide show
  1. package/package.json +29 -2
  2. package/src/RuneProvider.svelte.d.ts +32 -0
  3. package/src/{lib/i18n/message-resolver.ts → i18n/message-resolver.d.ts} +9 -47
  4. package/src/i18n/message-resolver.js +49 -0
  5. package/src/{lib/i18n/message-resolver.test.ts → i18n/message-resolver.test.js} +3 -23
  6. package/src/kernel/src/actions/portal.d.ts +10 -0
  7. package/src/{lib/kernel/src/actions/portal.ts → kernel/src/actions/portal.js} +3 -11
  8. package/src/kernel/src/actions/shortcut-listener.d.ts +18 -0
  9. package/src/{lib/kernel/src/actions/shortcut-listener.ts → kernel/src/actions/shortcut-listener.js} +5 -20
  10. package/src/kernel/src/context/app.svelte.d.ts +45 -0
  11. package/src/kernel/src/context/app.svelte.js +89 -0
  12. package/src/kernel/src/context/context.d.ts +15 -0
  13. package/src/{lib/kernel/src/context/context.ts → kernel/src/context/context.js} +2 -17
  14. package/src/kernel/src/context/stores.svelte.d.ts +67 -0
  15. package/src/kernel/src/context/stores.svelte.js +49 -0
  16. package/src/{lib/kernel/src/context/types.ts → kernel/src/context/types.d.ts} +0 -12
  17. package/src/kernel/src/context/types.js +1 -0
  18. package/src/{lib/kernel/src/context/useRuneLab.ts → kernel/src/context/useRuneLab.d.ts} +1 -14
  19. package/src/kernel/src/context/useRuneLab.js +26 -0
  20. package/src/kernel/src/mod.js +13 -0
  21. package/src/kernel/src/persistence/createConfigStore.svelte.d.ts +30 -0
  22. package/src/kernel/src/persistence/createConfigStore.svelte.js +76 -0
  23. package/src/kernel/src/persistence/drivers.d.ts +12 -0
  24. package/src/kernel/src/persistence/drivers.js +87 -0
  25. package/src/{lib/kernel/src/persistence/drivers.test.ts → kernel/src/persistence/drivers.test.js} +1 -17
  26. package/src/kernel/src/persistence/provider.d.ts +30 -0
  27. package/src/{lib/kernel/src/persistence/provider.ts → kernel/src/persistence/provider.js} +4 -17
  28. package/src/{lib/kernel/src/persistence/provider.test.ts → kernel/src/persistence/provider.test.js} +2 -9
  29. package/src/{lib/kernel/src/persistence/types.ts → kernel/src/persistence/types.d.ts} +0 -2
  30. package/src/kernel/src/persistence/types.js +2 -0
  31. package/src/kernel/src/persistence/usePersistence.d.ts +2 -0
  32. package/src/kernel/src/persistence/usePersistence.js +5 -0
  33. package/src/{lib/kernel/src/registry/mod.ts → kernel/src/registry/mod.d.ts} +14 -116
  34. package/src/kernel/src/registry/mod.js +140 -0
  35. package/src/{lib/kernel/src/registry/registry.test.ts → kernel/src/registry/registry.test.js} +17 -58
  36. package/src/{lib/kernel/src/tokens/props.ts → kernel/src/tokens/props.d.ts} +5 -32
  37. package/src/kernel/src/tokens/props.js +35 -0
  38. package/src/mod.d.ts +5 -0
  39. package/src/{lib/mod.ts → mod.js} +0 -2
  40. package/src/{lib/runes/layout/src/APP_CONFIGURATIONS.ts → runes/layout/src/APP_CONFIGURATIONS.d.ts} +1 -28
  41. package/src/runes/layout/src/APP_CONFIGURATIONS.js +38 -0
  42. package/src/runes/layout/src/AppSettingSelector.svelte.d.ts +18 -0
  43. package/src/runes/layout/src/ConnectedNavigationPanel.svelte.d.ts +18 -0
  44. package/src/runes/layout/src/ConnectedWorkspaceStrip.svelte.d.ts +18 -0
  45. package/src/runes/layout/src/ContentArea.svelte.d.ts +18 -0
  46. package/src/runes/layout/src/DetailPanel.svelte.d.ts +18 -0
  47. package/src/runes/layout/src/Icon.svelte.d.ts +15 -0
  48. package/src/runes/layout/src/LanguageSelector.svelte.d.ts +18 -0
  49. package/src/runes/layout/src/NavigationPanel.svelte.d.ts +18 -0
  50. package/src/runes/layout/src/ResourceSelector.svelte.d.ts +21 -0
  51. package/src/runes/layout/src/ThemeSelector.svelte.d.ts +18 -0
  52. package/src/runes/layout/src/WorkspaceLayout.svelte.d.ts +47 -0
  53. package/src/runes/layout/src/WorkspaceStrip.svelte.d.ts +18 -0
  54. package/src/{lib/runes/layout/src/connection-factory.ts → runes/layout/src/connection-factory.d.ts} +7 -41
  55. package/src/runes/layout/src/connection-factory.js +58 -0
  56. package/src/runes/layout/src/language.svelte.d.ts +50 -0
  57. package/src/{lib/runes/layout/src/language.svelte.ts → runes/layout/src/language.svelte.js} +9 -23
  58. package/src/runes/layout/src/mod.d.ts +22 -0
  59. package/src/{lib/runes/layout/src/mod.ts → runes/layout/src/mod.js} +14 -18
  60. package/src/runes/layout/src/store.svelte.d.ts +37 -0
  61. package/src/{lib/runes/layout/src/store.svelte.ts → runes/layout/src/store.svelte.js} +32 -57
  62. package/src/runes/layout/src/theme.svelte.d.ts +6 -0
  63. package/src/{lib/runes/layout/src/theme.svelte.ts → runes/layout/src/theme.svelte.js} +5 -15
  64. package/src/runes/layout/src/types.d.ts +6 -0
  65. package/src/{lib/runes/layout/src/types.ts → runes/layout/src/types.js} +0 -3
  66. package/src/runes/palettes/src/commands/CommandPalette.svelte.d.ts +18 -0
  67. package/src/runes/palettes/src/commands/mod.js +2 -0
  68. package/src/runes/palettes/src/commands/store.svelte.d.ts +33 -0
  69. package/src/runes/palettes/src/commands/store.svelte.js +63 -0
  70. package/src/runes/palettes/src/mod.d.ts +9 -0
  71. package/src/{lib/runes/palettes/src/mod.ts → runes/palettes/src/mod.js} +5 -9
  72. package/src/runes/palettes/src/notifications/NotificationBell.svelte.d.ts +26 -0
  73. package/src/runes/palettes/src/notifications/Toaster.svelte.d.ts +15 -0
  74. package/src/runes/palettes/src/notifications/bridge.d.ts +33 -0
  75. package/src/{lib/runes/palettes/src/notifications/bridge.ts → runes/palettes/src/notifications/bridge.js} +10 -22
  76. package/src/runes/palettes/src/notifications/mod.js +4 -0
  77. package/src/runes/palettes/src/notifications/store.svelte.d.ts +23 -0
  78. package/src/{lib/runes/palettes/src/notifications/store.svelte.ts → runes/palettes/src/notifications/store.svelte.js} +9 -24
  79. package/src/runes/palettes/src/shortcuts/ShortcutPalette.svelte.d.ts +23 -0
  80. package/src/runes/palettes/src/shortcuts/mod.js +4 -0
  81. package/src/runes/palettes/src/shortcuts/store.svelte.d.ts +37 -0
  82. package/src/{lib/runes/palettes/src/shortcuts/store.svelte.ts → runes/palettes/src/shortcuts/store.svelte.js} +35 -34
  83. package/src/runes/palettes/src/shortcuts/types.d.ts +1 -0
  84. package/src/{lib/runes/palettes/src/shortcuts/types.ts → runes/palettes/src/shortcuts/types.js} +1 -2
  85. package/src/runes/palettes/src/shortcuts/useShortcuts.d.ts +32 -0
  86. package/src/{lib/runes/palettes/src/shortcuts/useShortcuts.ts → runes/palettes/src/shortcuts/useShortcuts.js} +7 -16
  87. package/src/runes/plugins/money/src/CurrencySelector.svelte.d.ts +18 -0
  88. package/src/runes/plugins/money/src/MoneyDisplay.svelte.d.ts +18 -0
  89. package/src/runes/plugins/money/src/MoneyInput.svelte.d.ts +49 -0
  90. package/src/runes/plugins/money/src/currency.svelte.d.ts +29 -0
  91. package/src/{lib/runes/plugins/money/src/currency.svelte.ts → runes/plugins/money/src/currency.svelte.js} +19 -42
  92. package/src/{lib/runes/plugins/money/src/currency.test.ts → runes/plugins/money/src/currency.test.js} +2 -10
  93. package/src/runes/plugins/money/src/exchange-rate.svelte.d.ts +44 -0
  94. package/src/{lib/runes/plugins/money/src/exchange-rate.svelte.ts → runes/plugins/money/src/exchange-rate.svelte.js} +43 -49
  95. package/src/{lib/runes/plugins/money/src/exchange-rate.test.ts → runes/plugins/money/src/exchange-rate.test.js} +2 -23
  96. package/src/runes/plugins/money/src/mod.d.ts +13 -0
  97. package/src/{lib/runes/plugins/money/src/mod.ts → runes/plugins/money/src/mod.js} +10 -34
  98. package/src/runes/plugins/money/src/money-primitive.d.ts +106 -0
  99. package/src/{lib/runes/plugins/money/src/money-primitive.ts → runes/plugins/money/src/money-primitive.js} +26 -68
  100. package/src/{lib/runes/plugins/money/src/money-primitive.test.ts → runes/plugins/money/src/money-primitive.test.js} +0 -40
  101. package/src/runes/plugins/money/src/money.d.ts +170 -0
  102. package/src/{lib/runes/plugins/money/src/money.ts → runes/plugins/money/src/money.js} +32 -129
  103. package/src/{lib/runes/plugins/money/src/money.test.ts → runes/plugins/money/src/money.test.js} +0 -29
  104. package/src/runes/plugins/money/src/strategies.d.ts +55 -0
  105. package/src/{lib/runes/plugins/money/src/strategies.ts → runes/plugins/money/src/strategies.js} +12 -27
  106. package/src/{lib/runes/plugins/money/src/strategies.test.ts → runes/plugins/money/src/strategies.test.js} +2 -30
  107. package/src/runes/plugins/money/src/types.js +21 -0
  108. package/src/runes/plugins/money/src/useMoney.d.ts +69 -0
  109. package/src/{lib/runes/plugins/money/src/useMoney.ts → runes/plugins/money/src/useMoney.js} +12 -131
  110. package/src/runes/plugins/money/src/useMoneyFilter.d.ts +23 -0
  111. package/src/{lib/runes/plugins/money/src/useMoneyFilter.ts → runes/plugins/money/src/useMoneyFilter.js} +5 -40
  112. package/.gitignore +0 -12
  113. package/deno.json +0 -50
  114. package/justfile +0 -5
  115. package/scripts/build_pkg.ts +0 -57
  116. package/scripts/ci.just +0 -26
  117. package/scripts/deploy.just +0 -26
  118. package/scripts/dev.just +0 -4
  119. package/scripts/inject.just +0 -72
  120. package/src/app.d.ts +0 -13
  121. package/src/app.html +0 -12
  122. package/src/lib/kernel/src/context/app.svelte.ts +0 -98
  123. package/src/lib/kernel/src/context/stores.svelte.ts +0 -96
  124. package/src/lib/kernel/src/persistence/createConfigStore.svelte.ts +0 -120
  125. package/src/lib/kernel/src/persistence/drivers.ts +0 -77
  126. package/src/lib/kernel/src/persistence/usePersistence.ts +0 -9
  127. package/src/lib/runes/palettes/src/commands/store.svelte.ts +0 -80
  128. package/src/routes/+page.svelte +0 -3
  129. package/tsconfig.json +0 -41
  130. package/vite.config.ts +0 -22
  131. /package/src/{lib/RuneProvider.svelte → RuneProvider.svelte} +0 -0
  132. /package/src/{lib/i18n → i18n}/project.inlang/settings.json +0 -0
  133. /package/src/{lib/i18n → i18n}/translations/ar.json +0 -0
  134. /package/src/{lib/i18n → i18n}/translations/de.json +0 -0
  135. /package/src/{lib/i18n → i18n}/translations/en.json +0 -0
  136. /package/src/{lib/i18n → i18n}/translations/es.json +0 -0
  137. /package/src/{lib/i18n → i18n}/translations/fr.json +0 -0
  138. /package/src/{lib/i18n → i18n}/translations/hi.json +0 -0
  139. /package/src/{lib/i18n → i18n}/translations/it.json +0 -0
  140. /package/src/{lib/i18n → i18n}/translations/ja.json +0 -0
  141. /package/src/{lib/i18n → i18n}/translations/ko.json +0 -0
  142. /package/src/{lib/i18n → i18n}/translations/pt.json +0 -0
  143. /package/src/{lib/i18n → i18n}/translations/ru.json +0 -0
  144. /package/src/{lib/i18n → i18n}/translations/vi.json +0 -0
  145. /package/src/{lib/i18n → i18n}/translations/zh.json +0 -0
  146. /package/src/{lib/kernel → kernel}/deno.json +0 -0
  147. /package/src/{lib/kernel/src/mod.ts → kernel/src/mod.d.ts} +0 -0
  148. /package/src/{lib/runes → runes}/layout/deno.json +0 -0
  149. /package/src/{lib/runes → runes}/layout/src/AppSettingSelector.svelte +0 -0
  150. /package/src/{lib/runes → runes}/layout/src/ConnectedNavigationPanel.svelte +0 -0
  151. /package/src/{lib/runes → runes}/layout/src/ConnectedWorkspaceStrip.svelte +0 -0
  152. /package/src/{lib/runes → runes}/layout/src/ContentArea.svelte +0 -0
  153. /package/src/{lib/runes → runes}/layout/src/DetailPanel.svelte +0 -0
  154. /package/src/{lib/runes → runes}/layout/src/Icon.svelte +0 -0
  155. /package/src/{lib/runes → runes}/layout/src/LanguageSelector.svelte +0 -0
  156. /package/src/{lib/runes → runes}/layout/src/NavigationPanel.svelte +0 -0
  157. /package/src/{lib/runes → runes}/layout/src/ResourceSelector.svelte +0 -0
  158. /package/src/{lib/runes → runes}/layout/src/ThemeSelector.svelte +0 -0
  159. /package/src/{lib/runes → runes}/layout/src/WorkspaceLayout.svelte +0 -0
  160. /package/src/{lib/runes → runes}/layout/src/WorkspaceStrip.svelte +0 -0
  161. /package/src/{lib/runes → runes}/palettes/deno.json +0 -0
  162. /package/src/{lib/runes → runes}/palettes/src/commands/CommandPalette.svelte +0 -0
  163. /package/src/{lib/runes/palettes/src/commands/mod.ts → runes/palettes/src/commands/mod.d.ts} +0 -0
  164. /package/src/{lib/runes → runes}/palettes/src/notifications/NotificationBell.svelte +0 -0
  165. /package/src/{lib/runes → runes}/palettes/src/notifications/Toaster.svelte +0 -0
  166. /package/src/{lib/runes/palettes/src/notifications/mod.ts → runes/palettes/src/notifications/mod.d.ts} +0 -0
  167. /package/src/{lib/runes → runes}/palettes/src/shortcuts/ShortcutPalette.svelte +0 -0
  168. /package/src/{lib/runes/palettes/src/shortcuts/mod.ts → runes/palettes/src/shortcuts/mod.d.ts} +0 -0
  169. /package/src/{lib/runes → runes}/plugins/money/deno.json +0 -0
  170. /package/src/{lib/runes → runes}/plugins/money/src/CurrencySelector.svelte +0 -0
  171. /package/src/{lib/runes → runes}/plugins/money/src/MoneyDisplay.svelte +0 -0
  172. /package/src/{lib/runes → runes}/plugins/money/src/MoneyInput.svelte +0 -0
  173. /package/src/{lib/runes/plugins/money/src/types.ts → runes/plugins/money/src/types.d.ts} +0 -0
package/package.json CHANGED
@@ -1,5 +1,32 @@
1
1
  {
2
2
  "name": "rune-lab",
3
- "version": "0.4.2-beta.2",
4
- "packageManager": "pnpm@10.33.0"
3
+ "version": "0.4.2-beta.3",
4
+ "description": "Modern toolkit for Svelte 5 Runes applications.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Yrrrrrf/rune-lab.git"
9
+ },
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/mod.d.ts",
14
+ "default": "./src/mod.js"
15
+ }
16
+ },
17
+ "peerDependencies": {
18
+ "svelte": "^5.55.0",
19
+ "@sveltejs/kit": "^2.55.0",
20
+ "@inlang/paraglide-js": "^2.15.1",
21
+ "hotkeys-js": "^4.0.2",
22
+ "dinero.js": "^2.0.2",
23
+ "esm-env": "^1.2.2"
24
+ },
25
+ "keywords": [
26
+ "svelte",
27
+ "svelte-5",
28
+ "runes",
29
+ "ui",
30
+ "components"
31
+ ]
5
32
  }
@@ -0,0 +1,32 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { PersistenceDriver } from "@rune-lab/kernel";
3
+ /**
4
+ * Namespaced configuration for Rune Lab plugins.
5
+ * Keyed by plugin.id.
6
+ */
7
+ export interface RuneLabConfig {
8
+ persistence?: PersistenceDriver;
9
+ /** Optional head management properties */
10
+ favicon?: string;
11
+ manageHead?: boolean;
12
+ icons?: "material" | "none";
13
+ /** Namespaced config for plugins */
14
+ [pluginId: string]: unknown;
15
+ }
16
+ declare const __propDef: {
17
+ props: Record<string, never>;
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ };
23
+ export type RuneProviderProps = typeof __propDef.props;
24
+ export type RuneProviderEvents = typeof __propDef.events;
25
+ export type RuneProviderSlots = typeof __propDef.slots;
26
+ export default class RuneProvider extends SvelteComponentTyped<
27
+ RuneProviderProps,
28
+ RuneProviderEvents,
29
+ RuneProviderSlots
30
+ > {
31
+ }
32
+ export {};
@@ -1,29 +1,22 @@
1
- // sdk/devtools/src/patterns/message-resolver.ts
2
- import { DEV } from "esm-env";
3
-
4
1
  /**
5
2
  * Dynamic i18n Message Resolver Pattern
6
3
  *
7
4
  * Enables dynamic message function calls for config selectors.
8
5
  * Creates a resolver that dynamically looks up and calls message functions.
9
6
  */
10
-
11
7
  type MessageBundle = Record<string, (...args: unknown[]) => string>;
12
-
13
8
  interface MessageResolverConfig<T> {
14
9
  /**
15
10
  * Function to extract the message key from an option
16
11
  * @example (currency) => currency.code // "USD"
17
12
  */
18
13
  keyExtractor: (option: T) => string;
19
-
20
14
  /**
21
15
  * Optional transformer for the key before message lookup
22
16
  * @example (key) => key.toLowerCase() // "USD" -> "usd"
23
17
  */
24
18
  keyTransformer?: (key: string) => string;
25
19
  }
26
-
27
20
  /**
28
21
  * Creates a message resolver function for dynamic i18n lookups
29
22
  *
@@ -36,54 +29,23 @@ interface MessageResolverConfig<T> {
36
29
  * getCurrencyLabel({ code: "EUR", symbol: "€" }); // "Euro"
37
30
  * ```
38
31
  */
39
- export function createMessageResolver<T>(
32
+ export declare function createMessageResolver<T>(
40
33
  messages: MessageBundle,
41
34
  config: MessageResolverConfig<T>,
42
- ) {
43
- return (option: T): string => {
44
- const key = config.keyExtractor(option);
45
- const transformedKey = config.keyTransformer
46
- ? config.keyTransformer(key)
47
- : key;
48
-
49
- const messageFn = messages[transformedKey];
50
-
51
- if (!messageFn || typeof messageFn !== "function") {
52
- if (DEV) {
53
- console.warn(
54
- `[MessageResolver] Missing translation for key: "${transformedKey}"`,
55
- );
56
- }
57
- return key; // Fallback to key
58
- }
59
-
60
- return messageFn();
61
- };
62
- }
63
-
35
+ ): (option: T) => string;
64
36
  /**
65
37
  * Type guard to check if a message key exists
66
38
  */
67
- export function hasMessage(messages: MessageBundle, key: string): boolean {
68
- return key in messages && typeof messages[key] === "function";
69
- }
70
-
39
+ export declare function hasMessage(
40
+ messages: MessageBundle,
41
+ key: string,
42
+ ): boolean;
71
43
  /**
72
44
  * Batch resolver for multiple options at once
73
45
  */
74
- export function batchResolveMessages<T>(
46
+ export declare function batchResolveMessages<T>(
75
47
  messages: MessageBundle,
76
48
  options: T[],
77
49
  config: MessageResolverConfig<T>,
78
- ): Record<string, string> {
79
- const resolver = createMessageResolver(messages, config);
80
-
81
- return options.reduce(
82
- (acc, option) => {
83
- const key = config.keyExtractor(option);
84
- acc[key] = resolver(option);
85
- return acc;
86
- },
87
- {} as Record<string, string>,
88
- );
89
- }
50
+ ): Record<string, string>;
51
+ export {};
@@ -0,0 +1,49 @@
1
+ // sdk/devtools/src/patterns/message-resolver.ts
2
+ import { DEV } from "esm-env";
3
+ /**
4
+ * Creates a message resolver function for dynamic i18n lookups
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const getCurrencyLabel = createMessageResolver<Currency>(m, {
9
+ * keyExtractor: (currency) => currency.code
10
+ * });
11
+ *
12
+ * getCurrencyLabel({ code: "EUR", symbol: "€" }); // "Euro"
13
+ * ```
14
+ */
15
+ export function createMessageResolver(messages, config) {
16
+ return (option) => {
17
+ const key = config.keyExtractor(option);
18
+ const transformedKey = config.keyTransformer
19
+ ? config.keyTransformer(key)
20
+ : key;
21
+ const messageFn = messages[transformedKey];
22
+ if (!messageFn || typeof messageFn !== "function") {
23
+ if (DEV) {
24
+ console.warn(
25
+ `[MessageResolver] Missing translation for key: "${transformedKey}"`,
26
+ );
27
+ }
28
+ return key; // Fallback to key
29
+ }
30
+ return messageFn();
31
+ };
32
+ }
33
+ /**
34
+ * Type guard to check if a message key exists
35
+ */
36
+ export function hasMessage(messages, key) {
37
+ return key in messages && typeof messages[key] === "function";
38
+ }
39
+ /**
40
+ * Batch resolver for multiple options at once
41
+ */
42
+ export function batchResolveMessages(messages, options, config) {
43
+ const resolver = createMessageResolver(messages, config);
44
+ return options.reduce((acc, option) => {
45
+ const key = config.keyExtractor(option);
46
+ acc[key] = resolver(option);
47
+ return acc;
48
+ }, {});
49
+ }
@@ -1,14 +1,11 @@
1
1
  import { describe, expect, it, vi } from "vite-plus/test";
2
-
3
2
  // Mock esm-env before importing the module
4
3
  vi.mock("esm-env", () => ({ DEV: true }));
5
-
6
4
  import {
7
5
  batchResolveMessages,
8
6
  createMessageResolver,
9
7
  hasMessage,
10
8
  } from "./message-resolver.ts";
11
-
12
9
  describe("MessageResolver", () => {
13
10
  const mockMessages = {
14
11
  usd: () => "US Dollar",
@@ -16,87 +13,70 @@ describe("MessageResolver", () => {
16
13
  mxn: () => "Mexican Peso",
17
14
  jpy: () => "Japanese Yen",
18
15
  };
19
-
20
16
  describe("createMessageResolver", () => {
21
17
  it("should resolve a key via keyExtractor", () => {
22
- const resolver = createMessageResolver<{ code: string }>(mockMessages, {
18
+ const resolver = createMessageResolver(mockMessages, {
23
19
  keyExtractor: (opt) => opt.code,
24
20
  keyTransformer: (key) => key.toLowerCase(),
25
21
  });
26
-
27
22
  expect(resolver({ code: "USD" })).toBe("US Dollar");
28
23
  expect(resolver({ code: "EUR" })).toBe("Euro");
29
24
  });
30
-
31
25
  it("should use untransformed key when no transformer provided", () => {
32
- const resolver = createMessageResolver<{ code: string }>(mockMessages, {
26
+ const resolver = createMessageResolver(mockMessages, {
33
27
  keyExtractor: (opt) => opt.code,
34
28
  });
35
-
36
29
  // Without transformer, keys must match exactly
37
30
  expect(resolver({ code: "usd" })).toBe("US Dollar");
38
31
  });
39
-
40
32
  it("should fall back to the raw key for missing translations", () => {
41
33
  const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
42
-
43
- const resolver = createMessageResolver<{ code: string }>(mockMessages, {
34
+ const resolver = createMessageResolver(mockMessages, {
44
35
  keyExtractor: (opt) => opt.code,
45
36
  keyTransformer: (key) => key.toLowerCase(),
46
37
  });
47
-
48
38
  const result = resolver({ code: "UNKNOWN" });
49
39
  expect(result).toBe("UNKNOWN"); // falls back to original key
50
40
  expect(warnSpy).toHaveBeenCalledWith(
51
41
  expect.stringContaining("Missing translation"),
52
42
  );
53
-
54
43
  warnSpy.mockRestore();
55
44
  });
56
45
  });
57
-
58
46
  describe("hasMessage", () => {
59
47
  it("should return true for existing keys", () => {
60
48
  expect(hasMessage(mockMessages, "usd")).toBe(true);
61
49
  expect(hasMessage(mockMessages, "eur")).toBe(true);
62
50
  });
63
-
64
51
  it("should return false for missing keys", () => {
65
52
  expect(hasMessage(mockMessages, "UNKNOWN")).toBe(false);
66
53
  expect(hasMessage(mockMessages, "")).toBe(false);
67
54
  });
68
55
  });
69
-
70
56
  describe("batchResolveMessages", () => {
71
57
  it("should resolve all options at once", () => {
72
58
  const options = [{ code: "USD" }, { code: "EUR" }, { code: "MXN" }];
73
-
74
59
  const result = batchResolveMessages(mockMessages, options, {
75
60
  keyExtractor: (opt) => opt.code,
76
61
  keyTransformer: (key) => key.toLowerCase(),
77
62
  });
78
-
79
63
  expect(result).toEqual({
80
64
  USD: "US Dollar",
81
65
  EUR: "Euro",
82
66
  MXN: "Mexican Peso",
83
67
  });
84
68
  });
85
-
86
69
  it("should include fallback values for missing keys", () => {
87
70
  vi.spyOn(console, "warn").mockImplementation(() => {});
88
-
89
71
  const options = [{ code: "USD" }, { code: "XYZ" }];
90
72
  const result = batchResolveMessages(mockMessages, options, {
91
73
  keyExtractor: (opt) => opt.code,
92
74
  keyTransformer: (key) => key.toLowerCase(),
93
75
  });
94
-
95
76
  expect(result).toEqual({
96
77
  USD: "US Dollar",
97
78
  XYZ: "XYZ", // fallback
98
79
  });
99
-
100
80
  vi.restoreAllMocks();
101
81
  });
102
82
  });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Svelte action to teleport a DOM element to a different target (e.g. body)
3
+ */
4
+ export declare function portal(
5
+ node: HTMLElement,
6
+ target?: string | HTMLElement,
7
+ ): {
8
+ update(newTarget: string | HTMLElement): void;
9
+ destroy(): void;
10
+ };
@@ -1,16 +1,11 @@
1
1
  import { DEV } from "esm-env";
2
-
3
2
  /**
4
3
  * Svelte action to teleport a DOM element to a different target (e.g. body)
5
4
  */
6
- export function portal(
7
- node: HTMLElement,
8
- target: string | HTMLElement = "body",
9
- ): { update(newTarget: string | HTMLElement): void; destroy(): void } {
10
- let targetNode: HTMLElement | null;
5
+ export function portal(node, target = "body") {
6
+ let targetNode;
11
7
  let isTeleported = false;
12
-
13
- function update(newTarget: string | HTMLElement) {
8
+ function update(newTarget) {
14
9
  if (typeof newTarget === "string") {
15
10
  targetNode = document.querySelector(newTarget);
16
11
  if (!targetNode && DEV) {
@@ -21,15 +16,12 @@ export function portal(
21
16
  } else {
22
17
  targetNode = newTarget;
23
18
  }
24
-
25
19
  if (targetNode) {
26
20
  targetNode.appendChild(node);
27
21
  isTeleported = true;
28
22
  }
29
23
  }
30
-
31
24
  update(target);
32
-
33
25
  return {
34
26
  update,
35
27
  destroy() {
@@ -0,0 +1,18 @@
1
+ export interface ShortcutStoreLike {
2
+ entries: {
3
+ enabled?: boolean;
4
+ keys: string;
5
+ when?: () => boolean;
6
+ handler: (event: KeyboardEvent) => void;
7
+ }[];
8
+ }
9
+ /**
10
+ * Svelte Action to listen for shortcuts registered in shortcutStore.
11
+ * Applied to the root element of the layout.
12
+ */
13
+ export declare function shortcutListener(
14
+ _node: HTMLElement,
15
+ shortcutStore: ShortcutStoreLike,
16
+ ): {
17
+ destroy(): void;
18
+ };
@@ -1,46 +1,31 @@
1
1
  import hotkeys from "hotkeys-js";
2
2
  import { untrack } from "svelte";
3
-
4
- export interface ShortcutStoreLike {
5
- entries: {
6
- enabled?: boolean;
7
- keys: string;
8
- when?: () => boolean;
9
- handler: (event: KeyboardEvent) => void;
10
- }[];
11
- }
12
-
13
3
  /**
14
4
  * Svelte Action to listen for shortcuts registered in shortcutStore.
15
5
  * Applied to the root element of the layout.
16
6
  */
17
- export function shortcutListener(
18
- _node: HTMLElement,
19
- shortcutStore: ShortcutStoreLike,
20
- ): { destroy(): void } {
7
+ export function shortcutListener(_node, shortcutStore) {
21
8
  // Use $effect to reactively sync shortcuts
22
9
  const cleanup = $effect.root(() => {
23
10
  $effect(() => {
24
11
  // Unbind everything first to ensure clean state
25
12
  hotkeys.unbind();
26
-
27
13
  // We read entries here, so this effect re-runs when entries change.
28
14
  for (const entry of shortcutStore.entries) {
29
15
  // @ts-ignore: enabled might not be on the interface but we check it anyway
30
- if (entry.enabled === false) continue;
31
-
16
+ if (entry.enabled === false) {
17
+ continue;
18
+ }
32
19
  hotkeys(entry.keys, "all", (event, _handler) => {
33
20
  // Check "when" predicate if it exists
34
- if (entry.when && !untrack(() => entry.when!())) {
21
+ if (entry.when && !untrack(() => entry.when())) {
35
22
  return;
36
23
  }
37
-
38
24
  entry.handler(event);
39
25
  });
40
26
  }
41
27
  });
42
28
  });
43
-
44
29
  return {
45
30
  destroy() {
46
31
  cleanup();
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Application metadata interface
3
+ */
4
+ export interface AppData {
5
+ name: string;
6
+ version: string;
7
+ description: string;
8
+ author: string;
9
+ repository?: string;
10
+ license?: string;
11
+ homepage?: string;
12
+ }
13
+ /**
14
+ * App Store
15
+ * Manages application metadata and identity
16
+ */
17
+ export declare class AppStore {
18
+ #private;
19
+ name: string;
20
+ version: string;
21
+ description: string;
22
+ author: string;
23
+ repository: string;
24
+ license: string;
25
+ homepage: string;
26
+ customIcons: Record<string, string>;
27
+ /**
28
+ * Initialize app store with metadata.
29
+ */
30
+ init(data: Partial<AppData>): void;
31
+ /**
32
+ * @internal Test-only. Resets initialization guard.
33
+ */
34
+ __reset(): void;
35
+ /**
36
+ * Get full app information object
37
+ */
38
+ get info(): AppData;
39
+ /**
40
+ * Registers custom SVG icons to be available globally in the Icon component
41
+ */
42
+ registerIcons(icons: Record<string, string>): void;
43
+ }
44
+ export declare function createAppStore(): AppStore;
45
+ export declare function getAppStore(): AppStore;
@@ -0,0 +1,89 @@
1
+ // src/kernel/src/context/app.svelte.ts
2
+ import { getContext } from "svelte";
3
+ import { RUNE_LAB_CONTEXT } from "./context.ts";
4
+ import { DEV } from "esm-env";
5
+ /**
6
+ * App Store
7
+ * Manages application metadata and identity
8
+ */
9
+ export class AppStore {
10
+ // State
11
+ name = $state("Rune Lab");
12
+ version = $state("0.0.1");
13
+ description = $state("Modern toolkit for Svelte 5 Runes");
14
+ author = $state("Yrrrrrf");
15
+ repository = $state("https://github.com/Yrrrrrf/rune-lab");
16
+ license = $state("MIT");
17
+ homepage = $state("https://jsr.io/@yrrrrrf/rune-lab");
18
+ customIcons = $state({});
19
+ #initialized = false;
20
+ /**
21
+ * Initialize app store with metadata.
22
+ */
23
+ init(data) {
24
+ if (this.#initialized) {
25
+ if (DEV) {
26
+ console.warn(
27
+ "AppStore.init() called multiple times. Ignoring subsequent calls.",
28
+ "Overwritten properties would have been:",
29
+ data,
30
+ );
31
+ }
32
+ return;
33
+ }
34
+ if (data.name) {
35
+ this.name = data.name;
36
+ }
37
+ if (data.version) {
38
+ this.version = data.version;
39
+ }
40
+ if (data.description) {
41
+ this.description = data.description;
42
+ }
43
+ if (data.author) {
44
+ this.author = data.author;
45
+ }
46
+ if (data.repository) {
47
+ this.repository = data.repository;
48
+ }
49
+ if (data.license) {
50
+ this.license = data.license;
51
+ }
52
+ if (data.homepage) {
53
+ this.homepage = data.homepage;
54
+ }
55
+ this.#initialized = true;
56
+ }
57
+ /**
58
+ * @internal Test-only. Resets initialization guard.
59
+ */
60
+ __reset() {
61
+ this.#initialized = false;
62
+ }
63
+ /**
64
+ * Get full app information object
65
+ */
66
+ get info() {
67
+ return {
68
+ name: this.name,
69
+ version: this.version,
70
+ description: this.description,
71
+ author: this.author,
72
+ repository: this.repository,
73
+ license: this.license,
74
+ homepage: this.homepage,
75
+ };
76
+ }
77
+ /**
78
+ * Registers custom SVG icons to be available globally in the Icon component
79
+ */
80
+ registerIcons(icons) {
81
+ this.customIcons = { ...this.customIcons, ...icons };
82
+ }
83
+ }
84
+ export function createAppStore() {
85
+ return new AppStore();
86
+ }
87
+ export function getAppStore() {
88
+ return getContext(RUNE_LAB_CONTEXT.app);
89
+ }
@@ -0,0 +1,15 @@
1
+ export declare const RUNE_LAB_CONTEXT: {
2
+ app: symbol;
3
+ api: symbol;
4
+ toast: symbol;
5
+ theme: symbol;
6
+ language: symbol;
7
+ currency: symbol;
8
+ shortcut: symbol;
9
+ layout: symbol;
10
+ commands: symbol;
11
+ persistence: symbol;
12
+ cart: symbol;
13
+ session: symbol;
14
+ exchangeRate: symbol;
15
+ };
@@ -1,20 +1,5 @@
1
1
  // src/lib/context.ts
2
-
3
- export const RUNE_LAB_CONTEXT: {
4
- app: symbol;
5
- api: symbol;
6
- toast: symbol;
7
- theme: symbol;
8
- language: symbol;
9
- currency: symbol;
10
- shortcut: symbol;
11
- layout: symbol;
12
- commands: symbol;
13
- persistence: symbol;
14
- cart: symbol;
15
- session: symbol;
16
- exchangeRate: symbol;
17
- } = {
2
+ export const RUNE_LAB_CONTEXT = {
18
3
  app: Symbol("rl:app"),
19
4
  api: Symbol("rl:api"),
20
5
  toast: Symbol("rl:toast"),
@@ -28,4 +13,4 @@ export const RUNE_LAB_CONTEXT: {
28
13
  cart: Symbol("rl:cart"),
29
14
  session: Symbol("rl:session"),
30
15
  exchangeRate: Symbol("rl:exchange-rate"),
31
- } as const;
16
+ };
@@ -0,0 +1,67 @@
1
+ import type { ConfigStore } from "../persistence/createConfigStore.svelte.ts";
2
+ import type {
3
+ Command,
4
+ Currency,
5
+ Language,
6
+ Theme,
7
+ Toast,
8
+ ToastType,
9
+ } from "./types.ts";
10
+ import type { LayoutStore } from "../../../runes/layout/src/store.svelte.ts";
11
+ import type { ShortcutStore } from "../../../runes/palettes/src/shortcuts/store.svelte.ts";
12
+ /**
13
+ * Common store interfaces
14
+ */
15
+ export interface ICommandStore {
16
+ commands: Command[];
17
+ register(command: Command): void;
18
+ unregister(id: string): void;
19
+ search(query: string, parentId?: string): Command[];
20
+ }
21
+ export interface IToastStore {
22
+ toasts: Toast[];
23
+ send(message: string, type?: ToastType, duration?: number): void;
24
+ success(msg: string): void;
25
+ error(msg: string): void;
26
+ warn(msg: string): void;
27
+ info(msg: string): void;
28
+ dismiss(id: string): void;
29
+ }
30
+ export interface ICurrencyStore extends ConfigStore<Currency> {
31
+ canConvert: boolean;
32
+ convertAmount(amount: number, fromCode: string, toCode?: string): number;
33
+ addCurrency(meta: Currency, dineroDef?: unknown): void;
34
+ }
35
+ /**
36
+ * Store context accessors
37
+ */
38
+ export declare function getLayoutStore(): LayoutStore;
39
+ export declare function getLanguageStore(): ConfigStore<Language>;
40
+ export declare function getThemeStore(): ConfigStore<Theme>;
41
+ export declare function getShortcutStore(): ShortcutStore;
42
+ export declare function getCommandStore(): ICommandStore;
43
+ export declare function getToastStore(): IToastStore;
44
+ export declare function getCurrencyStore(): ICurrencyStore;
45
+ /**
46
+ * Standard layout shortcuts
47
+ */
48
+ export declare const LAYOUT_SHORTCUTS: {
49
+ readonly TOGGLE_NAV: {
50
+ readonly id: "layout:toggle-nav";
51
+ readonly keys: "ctrl+b";
52
+ readonly label: "Toggle Sidebar";
53
+ readonly category: "Layout";
54
+ };
55
+ readonly TOGGLE_DETAIL: {
56
+ readonly id: "layout:toggle-detail";
57
+ readonly keys: "ctrl+alt+b";
58
+ readonly label: "Toggle Detail Panel";
59
+ readonly category: "Layout";
60
+ };
61
+ readonly OPEN_SHORTCUTS: {
62
+ readonly id: "layout:open-shortcuts";
63
+ readonly keys: "ctrl+/";
64
+ readonly label: "Show Shortcuts";
65
+ readonly category: "Layout";
66
+ };
67
+ };