rune-lab 0.0.12 → 0.0.13

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 (40) hide show
  1. package/dist/features/CommandPalette.svelte +4 -17
  2. package/dist/features/CommandPalette.svelte.d.ts +1 -8
  3. package/dist/features/config/API_Monitor.svelte +102 -0
  4. package/dist/features/config/API_Monitor.svelte.d.ts +3 -0
  5. package/dist/features/config/CurrencySelector.svelte +9 -2
  6. package/dist/features/config/CurrencySelector.svelte.d.ts +4 -1
  7. package/dist/features/config/LanguageSelector.svelte +14 -1
  8. package/dist/features/config/LanguageSelector.svelte.d.ts +4 -1
  9. package/dist/features/config/ThemeSelector.svelte +9 -1
  10. package/dist/features/config/ThemeSelector.svelte.d.ts +4 -1
  11. package/dist/features/config/Toaster.svelte +33 -0
  12. package/dist/features/config/Toaster.svelte.d.ts +18 -0
  13. package/dist/features/config/api.svelte.d.ts +25 -0
  14. package/dist/features/config/api.svelte.js +45 -0
  15. package/dist/features/config/app.svelte.d.ts +3 -44
  16. package/dist/features/config/app.svelte.js +8 -88
  17. package/dist/features/config/commands.svelte.d.ts +27 -0
  18. package/dist/features/config/commands.svelte.js +28 -0
  19. package/dist/features/config/toast.svelte.d.ts +27 -0
  20. package/dist/features/config/toast.svelte.js +36 -0
  21. package/dist/index.d.ts +9 -0
  22. package/dist/index.js +10 -6
  23. package/dist/paraglide/README.md +1 -1
  24. package/dist/paraglide/messages/_index.d.ts +5 -0
  25. package/dist/paraglide/messages/_index.js +5 -0
  26. package/dist/paraglide/messages/all_currencies.d.ts +16 -0
  27. package/dist/paraglide/messages/all_currencies.js +84 -0
  28. package/dist/paraglide/messages/all_languages.d.ts +16 -0
  29. package/dist/paraglide/messages/all_languages.js +84 -0
  30. package/dist/paraglide/messages/all_themes.d.ts +16 -0
  31. package/dist/paraglide/messages/all_themes.js +84 -0
  32. package/dist/paraglide/messages/extended_controls.d.ts +16 -0
  33. package/dist/paraglide/messages/extended_controls.js +84 -0
  34. package/dist/paraglide/messages/themes.d.ts +16 -0
  35. package/dist/paraglide/messages/themes.js +84 -0
  36. package/dist/showcase/AppSettingsManager.svelte +73 -0
  37. package/dist/showcase/AppSettingsManager.svelte.d.ts +8 -0
  38. package/package.json +7 -3
  39. package/dist/mod.d.ts +0 -1
  40. package/dist/mod.js +0 -2
@@ -1,29 +1,16 @@
1
1
  <!-- src/client/sdk/ui/src/features/config/CommandPalette.svelte -->
2
2
  <script lang="ts">
3
3
  import { onMount } from "svelte";
4
+ import { commandStore } from "./config/commands.svelte";
4
5
 
5
- interface Command {
6
- id: string;
7
- title: string;
8
- category?: string;
9
- icon?: string;
10
- action: () => void;
11
- }
12
-
13
- let { commands = [] }: { commands: Command[] } = $props();
6
+ let { shortcutKey = "k" } = $props<{ shortcutKey?: string }>();
14
7
 
15
8
  let dialog: HTMLDialogElement;
16
9
  let query = $state("");
17
10
  let isOpen = $state(false);
18
11
 
19
12
  // Filter commands based on query
20
- let filtered = $derived(
21
- query === ""
22
- ? commands
23
- : commands.filter((c) =>
24
- c.title.toLowerCase().includes(query.toLowerCase()),
25
- ),
26
- );
13
+ let filtered = $derived(commandStore.search(query));
27
14
 
28
15
  function toggle() {
29
16
  isOpen = !isOpen;
@@ -38,7 +25,7 @@
38
25
  // Global keyboard listener
39
26
  onMount(() => {
40
27
  function handleKeydown(e: KeyboardEvent) {
41
- if ((e.metaKey || e.ctrlKey) && e.key === "k") {
28
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === shortcutKey.toLowerCase()) {
42
29
  e.preventDefault();
43
30
  toggle();
44
31
  }
@@ -1,12 +1,5 @@
1
- interface Command {
2
- id: string;
3
- title: string;
4
- category?: string;
5
- icon?: string;
6
- action: () => void;
7
- }
8
1
  type $$ComponentProps = {
9
- commands: Command[];
2
+ shortcutKey?: string;
10
3
  };
11
4
  declare const CommandPalette: import("svelte").Component<$$ComponentProps, {}, "">;
12
5
  type CommandPalette = ReturnType<typeof CommandPalette>;
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ import { Book, Copy, CheckCircle, RefreshCw } from "lucide-svelte";
3
+ import { apiStore } from "./api.svelte";
4
+ import { toastStore } from "./toast.svelte";
5
+
6
+ let copied = $state(false);
7
+ let isVisible = $state(true);
8
+
9
+ function copyToClipboard() {
10
+ navigator.clipboard.writeText(apiStore.URL);
11
+ copied = true;
12
+ toastStore.success("API URL copied to clipboard!");
13
+ setTimeout(() => (copied = false), 2000);
14
+ }
15
+
16
+ function handleKeyDown(event: KeyboardEvent) {
17
+ if (event.key === "Enter" || event.key === " ") {
18
+ copyToClipboard();
19
+ }
20
+ }
21
+
22
+ const containerClass = $derived(`
23
+ fixed bottom-4 left-4 z-50
24
+ flex items-center space-x-2
25
+ transition-all duration-300 transform
26
+ ${isVisible ? "translate-y-0 opacity-100" : "translate-y-16 opacity-0"}
27
+ `);
28
+ </script>
29
+
30
+ <div class={containerClass}>
31
+ <div
32
+ class="flex items-center space-x-2 backdrop-blur-sm bg-base-300/80 rounded-xl p-1 shadow-lg border border-base-content/10"
33
+ >
34
+ <!-- API Status Indicator -->
35
+ {#if apiStore.IS_CONNECTED}
36
+ <div class="tooltip tooltip-right" data-tip="Connected">
37
+ <div
38
+ aria-label="success"
39
+ class="status status-success status-md"
40
+ ></div>
41
+ </div>
42
+ {:else if apiStore.IS_LOADING}
43
+ <div class="tooltip tooltip-right" data-tip="Connecting...">
44
+ <div class="inline-grid *:[grid-area:1/1]">
45
+ <div class="status status-warning animate-ping"></div>
46
+ <div class="status status-warning"></div>
47
+ </div>
48
+ </div>
49
+ {:else}
50
+ <div class="tooltip tooltip-right" data-tip="Disconnected">
51
+ <div class="status status-error animate-bounce"></div>
52
+ </div>
53
+ {/if}
54
+
55
+ <!-- API Docs / Reload -->
56
+ {#if apiStore.connectionState === "connected"}
57
+ <button
58
+ class="btn btn-circle btn-xs btn-primary"
59
+ onclick={() => window.open(`${apiStore.URL}/docs`, "_blank")}
60
+ aria-label="Go to API Documentation"
61
+ >
62
+ <Book size={14} />
63
+ </button>
64
+ {:else}
65
+ <button
66
+ class="btn btn-circle btn-xs btn-error"
67
+ onclick={() => apiStore.reconnect()}
68
+ aria-label="Reconnect to API"
69
+ disabled={apiStore.connectionState === "connecting"}
70
+ >
71
+ <RefreshCw
72
+ size={14}
73
+ class={apiStore.connectionState === "connecting"
74
+ ? "animate-spin"
75
+ : ""}
76
+ />
77
+ </button>
78
+ {/if}
79
+
80
+ <button
81
+ class="group flex items-center space-x-2 px-3 py-1 rounded-lg hover:bg-base-200 transition-colors duration-200"
82
+ onclick={copyToClipboard}
83
+ onkeydown={handleKeyDown}
84
+ aria-label="Copy API URL"
85
+ >
86
+ <span class="text-xs font-mono"
87
+ >{apiStore.URL.replace(
88
+ /^https?:\/\//,
89
+ "",
90
+ )}/{apiStore.VERSION}</span
91
+ >
92
+ {#if copied}
93
+ <CheckCircle size={14} class="text-success" />
94
+ {:else}
95
+ <Copy
96
+ size={14}
97
+ class="opacity-50 group-hover:opacity-100 transition-all duration-200"
98
+ />
99
+ {/if}
100
+ </button>
101
+ </div>
102
+ </div>
@@ -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,10 +1,11 @@
1
1
  <script lang="ts">
2
2
  import AppSettingSelector from "./AppSettingSelector.svelte";
3
3
  import { currencyStore, type Currency } from "./currency.svelte";
4
- import { setLocale, locales } from "../../paraglide/runtime";
5
4
  import * as m from "../../paraglide/messages.js";
6
5
  import { createMessageResolver } from "../../devtools/message-resolver";
7
6
 
7
+ let { codes = [] }: { codes?: string[] } = $props();
8
+
8
9
  const getLabel = createMessageResolver<Currency>(m as any, {
9
10
  keyExtractor: (c) => c.code,
10
11
  });
@@ -12,9 +13,15 @@
12
13
  let active = $derived(
13
14
  currencyStore.get(currencyStore.current) ?? currencyStore.available[0],
14
15
  );
16
+
17
+ let available = $derived(
18
+ codes.length > 0
19
+ ? currencyStore.available.filter(c => codes.includes(c.code))
20
+ : currencyStore.available
21
+ );
15
22
  </script>
16
23
 
17
- <AppSettingSelector value={active} options={currencyStore.available}>
24
+ <AppSettingSelector value={active} options={available}>
18
25
  {#snippet triggerLabel(c)}
19
26
  <span class="font-bold">{c.symbol}</span>
20
27
  <span class="uppercase font-medium text-xs tracking-wide">{c.code}</span
@@ -1,3 +1,6 @@
1
- declare const CurrencySelector: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ codes?: string[];
3
+ };
4
+ declare const CurrencySelector: import("svelte").Component<$$ComponentProps, {}, "">;
2
5
  type CurrencySelector = ReturnType<typeof CurrencySelector>;
3
6
  export default CurrencySelector;
@@ -5,6 +5,13 @@
5
5
  import * as m from "../../paraglide/messages.js";
6
6
  import { createMessageResolver } from "../../devtools/message-resolver";
7
7
 
8
+ let { languages: allowedLocales = locales }: { languages?: string[] } =
9
+ $props();
10
+ // what the line above does:
11
+ // it allows the component to be used in two ways:
12
+ // 1. <LanguageSelector /> - this will use all available locales
13
+ // 2. <LanguageSelector languages={["en", "es"]} /> - this will only use the specified locales
14
+
8
15
  const getLabel = createMessageResolver<Language>(m as any, {
9
16
  keyExtractor: (l) => l.code,
10
17
  });
@@ -12,9 +19,15 @@
12
19
  let active = $derived(
13
20
  languageStore.get(languageStore.current) ?? languageStore.available[0],
14
21
  );
22
+
23
+ let available = $derived(
24
+ languageStore.available.filter((l) =>
25
+ allowedLocales.includes(l.code as any),
26
+ ),
27
+ );
15
28
  </script>
16
29
 
17
- <AppSettingSelector value={active} options={languageStore.available}>
30
+ <AppSettingSelector value={active} options={available}>
18
31
  {#snippet triggerLabel(l)}
19
32
  <span class="text-lg">{l?.flag ?? active.flag}</span>
20
33
  <span class="uppercase font-medium text-xs tracking-wide"
@@ -1,3 +1,6 @@
1
- declare const LanguageSelector: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ languages?: string[];
3
+ };
4
+ declare const LanguageSelector: import("svelte").Component<$$ComponentProps, {}, "">;
2
5
  type LanguageSelector = ReturnType<typeof LanguageSelector>;
3
6
  export default LanguageSelector;
@@ -4,6 +4,8 @@
4
4
  import * as m from "../../paraglide/messages.js";
5
5
  import { createMessageResolver } from "../../devtools/message-resolver";
6
6
 
7
+ let { themes = [] }: { themes?: string[] } = $props();
8
+
7
9
  const getThemeLabel = createMessageResolver<Theme>(m as any, {
8
10
  keyExtractor: (t) => t.name,
9
11
  });
@@ -11,9 +13,15 @@
11
13
  let activeTheme = $derived(
12
14
  themeStore.get(themeStore.current) ?? themeStore.available[0],
13
15
  );
16
+
17
+ let available = $derived(
18
+ themes.length > 0
19
+ ? themeStore.available.filter(t => themes.includes(t.name))
20
+ : themeStore.available
21
+ );
14
22
  </script>
15
23
 
16
- <AppSettingSelector value={activeTheme} options={themeStore.available}>
24
+ <AppSettingSelector value={activeTheme} options={available}>
17
25
  {#snippet triggerLabel(t)}
18
26
  <span class="text-lg">{t?.icon ?? activeTheme.icon}</span>
19
27
  <!-- Translate the name in the button -->
@@ -1,3 +1,6 @@
1
- declare const ThemeSelector: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ themes?: string[];
3
+ };
4
+ declare const ThemeSelector: import("svelte").Component<$$ComponentProps, {}, "">;
2
5
  type ThemeSelector = ReturnType<typeof ThemeSelector>;
3
6
  export default ThemeSelector;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import { toastStore } from "./toast.svelte";
3
+ import { flip } from "svelte/animate";
4
+ import { fade, fly } from "svelte/transition";
5
+
6
+ const typeClasses = {
7
+ info: "alert-info",
8
+ success: "alert-success",
9
+ warning: "alert-warning",
10
+ error: "alert-error"
11
+ };
12
+ </script>
13
+
14
+ <div class="toast toast-end toast-bottom z-[100] p-4">
15
+ {#each toastStore.toasts as toast (toast.id)}
16
+ <div
17
+ animate:flip={{ duration: 300 }}
18
+ in:fly={{ y: 20, duration: 300 }}
19
+ out:fade={{ duration: 200 }}
20
+ class="alert {typeClasses[toast.type]} shadow-lg min-w-[250px] border-none"
21
+ >
22
+ <div class="flex items-center gap-2 w-full">
23
+ <span>{toast.message}</span>
24
+ <button
25
+ class="btn btn-ghost btn-xs btn-circle ml-auto"
26
+ onclick={() => toastStore.dismiss(toast.id)}
27
+ >
28
+
29
+ </button>
30
+ </div>
31
+ </div>
32
+ {/each}
33
+ </div>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Toaster: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type Toaster = InstanceType<typeof Toaster>;
18
+ export default Toaster;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * API connection state and configuration
3
+ */
4
+ export type ConnectionState = "connected" | "connecting" | "disconnected";
5
+ declare class ApiStore {
6
+ url: string;
7
+ version: string;
8
+ connectionState: ConnectionState;
9
+ isConnected: boolean;
10
+ isLoading: boolean;
11
+ get URL(): string;
12
+ get VERSION(): string;
13
+ get IS_CONNECTED(): boolean;
14
+ get IS_LOADING(): boolean;
15
+ /**
16
+ * Reconnect to the API
17
+ */
18
+ reconnect(): Promise<void>;
19
+ /**
20
+ * Initialize API settings
21
+ */
22
+ init(url: string, version?: string): void;
23
+ }
24
+ export declare const apiStore: ApiStore;
25
+ export {};
@@ -0,0 +1,45 @@
1
+ class ApiStore {
2
+ // State
3
+ url = $state("http://localhost:8000");
4
+ version = $state("v1");
5
+ connectionState = $state("disconnected");
6
+ // Derived
7
+ isConnected = $derived(this.connectionState === "connected");
8
+ isLoading = $derived(this.connectionState === "connecting");
9
+ // Short aliases for compatibility with the user's snippet
10
+ get URL() {
11
+ return this.url;
12
+ }
13
+ get VERSION() {
14
+ return this.version;
15
+ }
16
+ get IS_CONNECTED() {
17
+ return this.isConnected;
18
+ }
19
+ get IS_LOADING() {
20
+ return this.isLoading;
21
+ }
22
+ /**
23
+ * Reconnect to the API
24
+ */
25
+ async reconnect() {
26
+ this.connectionState = "connecting";
27
+ // Simulate connection
28
+ try {
29
+ await new Promise((resolve) => setTimeout(resolve, 1500));
30
+ this.connectionState = "connected";
31
+ }
32
+ catch (e) {
33
+ this.connectionState = "disconnected";
34
+ }
35
+ }
36
+ /**
37
+ * Initialize API settings
38
+ */
39
+ init(url, version = "v1") {
40
+ this.url = url;
41
+ this.version = version;
42
+ this.reconnect();
43
+ }
44
+ }
45
+ export const apiStore = new ApiStore();
@@ -13,65 +13,24 @@ export interface AppData {
13
13
  /**
14
14
  * App Store
15
15
  * Manages application metadata and identity
16
- *
17
- * Features:
18
- * - App name, version, description
19
- * - Author and repository information
20
- * - Initialization with partial updates
21
- * - Console branding on init
22
16
  */
23
17
  declare class AppStore {
24
18
  name: string;
25
19
  version: string;
26
20
  description: string;
27
21
  author: string;
28
- repository?: string;
29
- license?: string;
30
- homepage?: string;
22
+ repository: string;
23
+ license: string;
24
+ homepage: string;
31
25
  private initialized;
32
26
  /**
33
27
  * Initialize app store with metadata
34
- *
35
- * @param data - Application metadata (partial update)
36
- * @param data.name - Application name
37
- * @param data.version - Application version (semver recommended)
38
- * @param data.description - Application description
39
- * @param data.author - Application author
40
- * @param data.repository - Repository URL (optional)
41
- * @param data.license - License identifier (optional)
42
- * @param data.homepage - Homepage URL (optional)
43
28
  */
44
29
  init(data: Partial<AppData>): void;
45
30
  /**
46
31
  * Get full app information object
47
32
  */
48
33
  get info(): AppData;
49
- /**
50
- * Get formatted version string
51
- * Useful for display purposes
52
- */
53
- get versionString(): string;
54
- /**
55
- * Get app title with version
56
- */
57
- get titleWithVersion(): string;
58
- /**
59
- * Check if repository is available
60
- */
61
- get hasRepository(): boolean;
62
- /**
63
- * Check if homepage is available
64
- */
65
- get hasHomepage(): boolean;
66
- /**
67
- * Update app version
68
- * Useful for version management during development
69
- */
70
- updateVersion(version: string): void;
71
- /**
72
- * Log app initialization to console with styling
73
- */
74
- private _logAppInit;
75
34
  }
76
35
  export declare const appStore: AppStore;
77
36
  export {};
@@ -3,41 +3,23 @@
3
3
  /**
4
4
  * App Store
5
5
  * Manages application metadata and identity
6
- *
7
- * Features:
8
- * - App name, version, description
9
- * - Author and repository information
10
- * - Initialization with partial updates
11
- * - Console branding on init
12
6
  */
13
7
  class AppStore {
14
8
  // State
15
- name = $state("My Application");
16
- version = $state("0.1.0");
17
- description = $state("A modern application");
18
- author = $state("Unknown");
19
- repository = $state(undefined);
20
- license = $state(undefined);
21
- homepage = $state(undefined);
9
+ name = $state("Rune Lab");
10
+ version = $state("0.0.1");
11
+ description = $state("Modern toolkit for Svelte 5 Runes");
12
+ author = $state("Yrrrrrf");
13
+ repository = $state("https://github.com/Yrrrrrf/rune-lab");
14
+ license = $state("MIT");
15
+ homepage = $state("https://jsr.io/@yrrrrrf/rune-lab");
22
16
  initialized = false;
23
17
  /**
24
18
  * Initialize app store with metadata
25
- *
26
- * @param data - Application metadata (partial update)
27
- * @param data.name - Application name
28
- * @param data.version - Application version (semver recommended)
29
- * @param data.description - Application description
30
- * @param data.author - Application author
31
- * @param data.repository - Repository URL (optional)
32
- * @param data.license - License identifier (optional)
33
- * @param data.homepage - Homepage URL (optional)
34
19
  */
35
20
  init(data) {
36
- if (this.initialized) {
37
- console.warn("⚠️ AppStore already initialized");
21
+ if (this.initialized)
38
22
  return;
39
- }
40
- // Update only provided values
41
23
  if (data.name)
42
24
  this.name = data.name;
43
25
  if (data.version)
@@ -53,8 +35,6 @@ class AppStore {
53
35
  if (data.homepage)
54
36
  this.homepage = data.homepage;
55
37
  this.initialized = true;
56
- // Pretty console output
57
- this._logAppInit();
58
38
  }
59
39
  /**
60
40
  * Get full app information object
@@ -70,66 +50,6 @@ class AppStore {
70
50
  homepage: this.homepage,
71
51
  };
72
52
  }
73
- /**
74
- * Get formatted version string
75
- * Useful for display purposes
76
- */
77
- get versionString() {
78
- return `v${this.version}`;
79
- }
80
- /**
81
- * Get app title with version
82
- */
83
- get titleWithVersion() {
84
- return `${this.name} v${this.version}`;
85
- }
86
- /**
87
- * Check if repository is available
88
- */
89
- get hasRepository() {
90
- return !!this.repository;
91
- }
92
- /**
93
- * Check if homepage is available
94
- */
95
- get hasHomepage() {
96
- return !!this.homepage;
97
- }
98
- /**
99
- * Update app version
100
- * Useful for version management during development
101
- */
102
- updateVersion(version) {
103
- this.version = version;
104
- console.log(`📦 Version updated to: v${version}`);
105
- }
106
- // Private methods
107
- /**
108
- * Log app initialization to console with styling
109
- */
110
- _logAppInit() {
111
- console.clear();
112
- // Banner style
113
- console.log("%c╔══════════════════════════════════════════════╗", "color: #00d9ff; font-weight: bold;");
114
- console.log(`%c║ ${this.name.padEnd(42)} ║`, "color: #00d9ff; font-weight: bold;");
115
- console.log("%c╚══════════════════════════════════════════════╝", "color: #00d9ff; font-weight: bold;");
116
- // App details
117
- console.log("\n📦 Application Details:");
118
- console.log(` Name: ${this.name}`);
119
- console.log(` Version: v${this.version}`);
120
- console.log(` Author: ${this.author}`);
121
- console.log(` Description: ${this.description}`);
122
- if (this.repository) {
123
- console.log(` Repository: ${this.repository}`);
124
- }
125
- if (this.license) {
126
- console.log(` License: ${this.license}`);
127
- }
128
- if (this.homepage) {
129
- console.log(` Homepage: ${this.homepage}`);
130
- }
131
- console.log("\n✨ Application initialized successfully!\n");
132
- }
133
53
  }
134
54
  // Export singleton instance
135
55
  export const appStore = new AppStore();
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Command Palette Store
3
+ */
4
+ export interface Command {
5
+ id: string;
6
+ title: string;
7
+ category?: string;
8
+ icon?: string;
9
+ action: () => void;
10
+ }
11
+ declare class CommandStore {
12
+ commands: Command[];
13
+ /**
14
+ * Register a new command
15
+ */
16
+ register(command: Command): void;
17
+ /**
18
+ * Remove a command
19
+ */
20
+ unregister(id: string): void;
21
+ /**
22
+ * Search commands
23
+ */
24
+ search(query: string): Command[];
25
+ }
26
+ export declare const commandStore: CommandStore;
27
+ export {};
@@ -0,0 +1,28 @@
1
+ class CommandStore {
2
+ commands = $state([]);
3
+ /**
4
+ * Register a new command
5
+ */
6
+ register(command) {
7
+ if (this.commands.find((c) => c.id === command.id))
8
+ return;
9
+ this.commands.push(command);
10
+ }
11
+ /**
12
+ * Remove a command
13
+ */
14
+ unregister(id) {
15
+ this.commands = this.commands.filter((c) => c.id !== id);
16
+ }
17
+ /**
18
+ * Search commands
19
+ */
20
+ search(query) {
21
+ if (!query)
22
+ return this.commands;
23
+ const q = query.toLowerCase();
24
+ return this.commands.filter((c) => c.title.toLowerCase().includes(q) ||
25
+ c.category?.toLowerCase().includes(q));
26
+ }
27
+ }
28
+ export const commandStore = new CommandStore();