rune-lab 0.3.0 → 0.4.0
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.
- package/README.md +1 -1
- package/dist/core/design-tokens/props.d.ts +52 -0
- package/dist/core/design-tokens/props.d.ts.map +1 -0
- package/dist/core/design-tokens/props.js +34 -0
- package/dist/core/exchange-rate/strategies.d.ts +52 -0
- package/dist/core/exchange-rate/strategies.d.ts.map +1 -0
- package/dist/core/exchange-rate/strategies.js +72 -0
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -3
- package/dist/core/internal/message-resolver.d.ts +1 -1
- package/dist/core/internal/message-resolver.d.ts.map +1 -1
- package/dist/core/layout/types.d.ts +60 -0
- package/dist/core/layout/types.d.ts.map +1 -0
- package/dist/core/layout/types.js +4 -0
- package/dist/core/money/index.d.ts +1 -1
- package/dist/core/money/index.d.ts.map +1 -1
- package/dist/core/money/index.js +1 -1
- package/dist/core/money/money-primitive.d.ts +101 -0
- package/dist/core/money/money-primitive.d.ts.map +1 -0
- package/dist/core/money/money-primitive.js +161 -0
- package/dist/core/money/money.d.ts +74 -2
- package/dist/core/money/money.d.ts.map +1 -1
- package/dist/core/money/money.js +120 -2
- package/dist/core/shortcuts/types.d.ts +60 -0
- package/dist/core/shortcuts/types.d.ts.map +1 -0
- package/dist/core/shortcuts/types.js +4 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -4
- package/dist/state/api.svelte.js +2 -2
- package/dist/state/app.svelte.js +1 -1
- package/dist/state/auth/index.d.ts +2 -2
- package/dist/state/auth/index.js +1 -1
- package/dist/state/auth/session.svelte.d.ts +1 -1
- package/dist/state/auth/session.svelte.js +7 -5
- package/dist/state/auth/types.d.ts +1 -1
- package/dist/state/cart.svelte.d.ts +1 -1
- package/dist/state/cart.svelte.js +1 -1
- package/dist/state/commands.svelte.d.ts +7 -7
- package/dist/state/commands.svelte.js +1 -1
- package/dist/state/composables/useMoney.d.ts +19 -3
- package/dist/state/composables/useMoney.js +70 -6
- package/dist/state/composables/useMoneyFilter.d.ts +20 -0
- package/dist/state/composables/useMoneyFilter.js +81 -0
- package/dist/state/composables/usePersistence.d.ts +1 -1
- package/dist/state/composables/usePersistence.js +1 -1
- package/dist/state/composables/useRuneLab.d.ts +1 -1
- package/dist/state/composables/useRuneLab.js +2 -2
- package/dist/state/composables/useShortcuts.d.ts +33 -0
- package/dist/state/composables/useShortcuts.js +75 -0
- package/dist/state/context.d.ts +1 -0
- package/dist/state/context.js +1 -0
- package/dist/state/createConfigStore.svelte.d.ts +4 -31
- package/dist/state/createConfigStore.svelte.js +62 -51
- package/dist/state/currency.svelte.d.ts +13 -9
- package/dist/state/currency.svelte.js +26 -10
- package/dist/state/currency.test.d.ts +1 -0
- package/dist/state/currency.test.js +35 -0
- package/dist/state/exchange-rate.svelte.d.ts +43 -0
- package/dist/state/exchange-rate.svelte.js +145 -0
- package/dist/state/exchange-rate.test.d.ts +1 -0
- package/dist/state/exchange-rate.test.js +75 -0
- package/dist/state/index.d.ts +26 -19
- package/dist/state/index.js +25 -18
- package/dist/state/language.svelte.d.ts +3 -10
- package/dist/state/language.svelte.js +4 -5
- package/dist/state/layout.svelte.d.ts +1 -1
- package/dist/state/layout.svelte.js +4 -4
- package/dist/state/persistence/drivers.d.ts +1 -1
- package/dist/state/persistence/drivers.js +9 -7
- package/dist/state/persistence/drivers.test.d.ts +1 -0
- package/dist/state/persistence/drivers.test.js +79 -0
- package/dist/state/persistence/provider.d.ts +23 -0
- package/dist/state/persistence/provider.js +43 -0
- package/dist/state/persistence/provider.test.d.ts +1 -0
- package/dist/state/persistence/provider.test.js +51 -0
- package/dist/state/registry/index.d.ts +44 -0
- package/dist/state/registry/index.js +58 -0
- package/dist/state/registry/registry.test.d.ts +1 -0
- package/dist/state/registry/registry.test.js +112 -0
- package/dist/state/registry/types.d.ts +20 -0
- package/dist/state/registry/types.js +3 -0
- package/dist/state/shortcuts.svelte.js +4 -4
- package/dist/state/theme.svelte.d.ts +3 -10
- package/dist/state/theme.svelte.js +8 -8
- package/dist/state/toast-bridge.d.ts +1 -1
- package/dist/state/toast.svelte.js +1 -1
- package/dist/ui/components/ApiMonitor.svelte +2 -2
- package/dist/ui/components/Icon.svelte +1 -1
- package/dist/ui/components/RuneProvider.svelte +28 -8
- package/dist/ui/components/RuneProvider.svelte.d.ts +12 -5
- package/dist/ui/components/Toaster.svelte +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte +91 -18
- package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +15 -3
- package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte.test.js +45 -2
- package/dist/ui/components/money/MoneyInput.svelte +123 -42
- package/dist/ui/components/money/MoneyInput.svelte.d.ts +14 -5
- package/dist/ui/features/command-palette/CommandPalette.svelte +3 -3
- package/dist/ui/features/config/APP_CONFIGURATIONS.d.ts +29 -0
- package/dist/ui/features/config/APP_CONFIGURATIONS.js +38 -0
- package/dist/ui/features/config/CurrencySelector.svelte +10 -36
- package/dist/ui/features/config/LanguageSelector.svelte +10 -33
- package/dist/ui/features/config/ResourceSelector.svelte +92 -0
- package/dist/ui/features/config/ResourceSelector.svelte.d.ts +25 -0
- package/dist/ui/features/config/ThemeSelector.svelte +11 -34
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte +17 -0
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte.d.ts +7 -0
- package/dist/ui/features/shortcuts/ShortcutPalette.svelte +3 -3
- package/dist/ui/index.d.ts +5 -1
- package/dist/ui/index.js +8 -3
- package/dist/ui/layout/ConnectedNavigationPanel.svelte +7 -8
- package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte +5 -3
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/NavigationPanel.svelte +1 -1
- package/dist/ui/layout/NavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/WorkspaceLayout.svelte +9 -1
- package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +7 -0
- package/dist/ui/layout/WorkspaceStrip.svelte +1 -1
- package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/connection-factory.d.ts +50 -0
- package/dist/ui/layout/connection-factory.js +58 -0
- package/dist/ui/layout/index.d.ts +2 -2
- package/dist/ui/layout/index.js +1 -1
- package/dist/ui/paraglide/README.md +53 -0
- package/dist/ui/paraglide/runtime.d.ts +105 -124
- package/dist/ui/paraglide/runtime.js +162 -127
- package/dist/ui/paraglide/server.d.ts +6 -17
- package/dist/ui/paraglide/server.js +11 -20
- package/dist/ui/primitives/DatePicker.svelte +1 -1
- package/package.json +8 -8
- package/dist/state/daisyui.d.ts +0 -4
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { resolveDriver } from "./provider.ts";
|
|
3
|
+
describe("DriverProvider", () => {
|
|
4
|
+
describe("resolveDriver", () => {
|
|
5
|
+
it("should return inMemoryDriver when given undefined", () => {
|
|
6
|
+
const driver = resolveDriver(undefined);
|
|
7
|
+
expect(driver).toBeDefined();
|
|
8
|
+
expect(typeof driver.get).toBe("function");
|
|
9
|
+
expect(typeof driver.set).toBe("function");
|
|
10
|
+
expect(typeof driver.remove).toBe("function");
|
|
11
|
+
});
|
|
12
|
+
it("should pass through a concrete PersistenceDriver", () => {
|
|
13
|
+
const mockDriver = {
|
|
14
|
+
get: () => null,
|
|
15
|
+
set: () => { },
|
|
16
|
+
remove: () => { },
|
|
17
|
+
};
|
|
18
|
+
const driver = resolveDriver(mockDriver);
|
|
19
|
+
expect(driver).toBe(mockDriver);
|
|
20
|
+
});
|
|
21
|
+
it("should resolve a factory function to a concrete driver", () => {
|
|
22
|
+
const mockDriver = {
|
|
23
|
+
get: () => null,
|
|
24
|
+
set: () => { },
|
|
25
|
+
remove: () => { },
|
|
26
|
+
};
|
|
27
|
+
const driver = resolveDriver(() => mockDriver);
|
|
28
|
+
expect(driver).toBe(mockDriver);
|
|
29
|
+
});
|
|
30
|
+
it("should fall back to inMemoryDriver when factory returns undefined", () => {
|
|
31
|
+
const driver = resolveDriver(() => undefined);
|
|
32
|
+
expect(driver).toBeDefined();
|
|
33
|
+
expect(typeof driver.get).toBe("function");
|
|
34
|
+
});
|
|
35
|
+
it("inMemoryDriver fallback should be functional", () => {
|
|
36
|
+
const driver = resolveDriver(undefined);
|
|
37
|
+
driver.set("test-key", "test-value");
|
|
38
|
+
expect(driver.get("test-key")).toBe("test-value");
|
|
39
|
+
driver.remove("test-key");
|
|
40
|
+
expect(driver.get("test-key")).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
it("should create isolated inMemoryDriver instances per call", () => {
|
|
43
|
+
const driverA = resolveDriver(undefined);
|
|
44
|
+
const driverB = resolveDriver(undefined);
|
|
45
|
+
driverA.set("key", "A");
|
|
46
|
+
driverB.set("key", "B");
|
|
47
|
+
expect(driverA.get("key")).toBe("A");
|
|
48
|
+
expect(driverB.get("key")).toBe("B");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { StoreRegistryEntry } from "./types.ts";
|
|
2
|
+
export type { StoreFactory, StoreRegistryEntry } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The global store registry.
|
|
5
|
+
* Pre-populated with built-in stores by RuneProvider.
|
|
6
|
+
* Third-party plugins can register additional entries before mounting.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // Plugin registration (before RuneProvider mounts)
|
|
11
|
+
* registerStore({
|
|
12
|
+
* key: "analytics",
|
|
13
|
+
* factory: (config, driver) => createAnalyticsStore(config),
|
|
14
|
+
* optional: true,
|
|
15
|
+
* noPersistence: true,
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare const STORE_REGISTRY: Map<string, StoreRegistryEntry<unknown, unknown>>;
|
|
20
|
+
/**
|
|
21
|
+
* Register a store entry in the global registry.
|
|
22
|
+
* Must be called before RuneProvider mounts for the store to be auto-wired.
|
|
23
|
+
*
|
|
24
|
+
* @param entry - Store registry entry with key, factory, and options.
|
|
25
|
+
* @throws Error if a store with the same key is already registered.
|
|
26
|
+
*/
|
|
27
|
+
export declare function registerStore(entry: StoreRegistryEntry): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get a registered store entry by key.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getRegisteredStore(key: string): StoreRegistryEntry | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Get all registered store entries.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getAllRegisteredStores(): Map<string, StoreRegistryEntry>;
|
|
36
|
+
/**
|
|
37
|
+
* Remove a store registration (primarily useful for testing).
|
|
38
|
+
*/
|
|
39
|
+
export declare function unregisterStore(key: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Clear all store registrations (primarily useful for testing).
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearRegistry(): void;
|
|
44
|
+
export { STORE_REGISTRY };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// sdk/state/src/registry/index.ts
|
|
2
|
+
// Store Registry — declarative map of context keys to store factories.
|
|
3
|
+
// RuneProvider iterates this at mount time to auto-wire all stores.
|
|
4
|
+
/**
|
|
5
|
+
* The global store registry.
|
|
6
|
+
* Pre-populated with built-in stores by RuneProvider.
|
|
7
|
+
* Third-party plugins can register additional entries before mounting.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Plugin registration (before RuneProvider mounts)
|
|
12
|
+
* registerStore({
|
|
13
|
+
* key: "analytics",
|
|
14
|
+
* factory: (config, driver) => createAnalyticsStore(config),
|
|
15
|
+
* optional: true,
|
|
16
|
+
* noPersistence: true,
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
const STORE_REGISTRY = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Register a store entry in the global registry.
|
|
23
|
+
* Must be called before RuneProvider mounts for the store to be auto-wired.
|
|
24
|
+
*
|
|
25
|
+
* @param entry - Store registry entry with key, factory, and options.
|
|
26
|
+
* @throws Error if a store with the same key is already registered.
|
|
27
|
+
*/
|
|
28
|
+
export function registerStore(entry) {
|
|
29
|
+
if (STORE_REGISTRY.has(entry.key)) {
|
|
30
|
+
console.warn(`[StoreRegistry] Overwriting existing store registration for key "${entry.key}"`);
|
|
31
|
+
}
|
|
32
|
+
STORE_REGISTRY.set(entry.key, entry);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get a registered store entry by key.
|
|
36
|
+
*/
|
|
37
|
+
export function getRegisteredStore(key) {
|
|
38
|
+
return STORE_REGISTRY.get(key);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get all registered store entries.
|
|
42
|
+
*/
|
|
43
|
+
export function getAllRegisteredStores() {
|
|
44
|
+
return STORE_REGISTRY;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Remove a store registration (primarily useful for testing).
|
|
48
|
+
*/
|
|
49
|
+
export function unregisterStore(key) {
|
|
50
|
+
return STORE_REGISTRY.delete(key);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clear all store registrations (primarily useful for testing).
|
|
54
|
+
*/
|
|
55
|
+
export function clearRegistry() {
|
|
56
|
+
STORE_REGISTRY.clear();
|
|
57
|
+
}
|
|
58
|
+
export { STORE_REGISTRY };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach } from "vitest";
|
|
2
|
+
import { registerStore, getRegisteredStore, getAllRegisteredStores, unregisterStore, clearRegistry, STORE_REGISTRY, } from "./index.ts";
|
|
3
|
+
describe("StoreRegistry", () => {
|
|
4
|
+
// Clean slate for each test
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
clearRegistry();
|
|
7
|
+
});
|
|
8
|
+
describe("registerStore", () => {
|
|
9
|
+
it("should register a store entry", () => {
|
|
10
|
+
registerStore({
|
|
11
|
+
key: "test",
|
|
12
|
+
factory: () => ({ value: 42 }),
|
|
13
|
+
});
|
|
14
|
+
expect(STORE_REGISTRY.size).toBe(1);
|
|
15
|
+
expect(STORE_REGISTRY.has("test")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it("should allow overwriting with a warning", () => {
|
|
18
|
+
registerStore({ key: "test", factory: () => "v1" });
|
|
19
|
+
registerStore({ key: "test", factory: () => "v2" });
|
|
20
|
+
expect(STORE_REGISTRY.size).toBe(1);
|
|
21
|
+
const entry = getRegisteredStore("test");
|
|
22
|
+
// Factory should be the second one
|
|
23
|
+
expect(entry?.factory({}, {})).toBe("v2");
|
|
24
|
+
});
|
|
25
|
+
it("should register multiple entries", () => {
|
|
26
|
+
registerStore({ key: "a", factory: () => "A" });
|
|
27
|
+
registerStore({ key: "b", factory: () => "B" });
|
|
28
|
+
registerStore({ key: "c", factory: () => "C" });
|
|
29
|
+
expect(STORE_REGISTRY.size).toBe(3);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe("getRegisteredStore", () => {
|
|
33
|
+
it("should return the entry for a registered key", () => {
|
|
34
|
+
registerStore({
|
|
35
|
+
key: "theme",
|
|
36
|
+
factory: () => "theme-store",
|
|
37
|
+
optional: false,
|
|
38
|
+
});
|
|
39
|
+
const entry = getRegisteredStore("theme");
|
|
40
|
+
expect(entry).toBeDefined();
|
|
41
|
+
expect(entry.key).toBe("theme");
|
|
42
|
+
expect(entry.optional).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it("should return undefined for unregistered keys", () => {
|
|
45
|
+
expect(getRegisteredStore("nonexistent")).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("getAllRegisteredStores", () => {
|
|
49
|
+
it("should return all registered stores", () => {
|
|
50
|
+
registerStore({ key: "a", factory: () => "A" });
|
|
51
|
+
registerStore({ key: "b", factory: () => "B" });
|
|
52
|
+
const all = getAllRegisteredStores();
|
|
53
|
+
expect(all.size).toBe(2);
|
|
54
|
+
expect(all.has("a")).toBe(true);
|
|
55
|
+
expect(all.has("b")).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("unregisterStore", () => {
|
|
59
|
+
it("should remove a registered store", () => {
|
|
60
|
+
registerStore({ key: "test", factory: () => "val" });
|
|
61
|
+
expect(STORE_REGISTRY.has("test")).toBe(true);
|
|
62
|
+
const result = unregisterStore("test");
|
|
63
|
+
expect(result).toBe(true);
|
|
64
|
+
expect(STORE_REGISTRY.has("test")).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it("should return false for non-existent keys", () => {
|
|
67
|
+
expect(unregisterStore("nope")).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("clearRegistry", () => {
|
|
71
|
+
it("should remove all entries", () => {
|
|
72
|
+
registerStore({ key: "a", factory: () => "A" });
|
|
73
|
+
registerStore({ key: "b", factory: () => "B" });
|
|
74
|
+
expect(STORE_REGISTRY.size).toBe(2);
|
|
75
|
+
clearRegistry();
|
|
76
|
+
expect(STORE_REGISTRY.size).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("StoreFactory contract", () => {
|
|
80
|
+
it("factory receives config and driver, returns store", () => {
|
|
81
|
+
const mockDriver = {
|
|
82
|
+
get: () => null,
|
|
83
|
+
set: () => { },
|
|
84
|
+
remove: () => { },
|
|
85
|
+
};
|
|
86
|
+
registerStore({
|
|
87
|
+
key: "analytics",
|
|
88
|
+
factory: (config, driver) => ({
|
|
89
|
+
type: "analytics",
|
|
90
|
+
hasDriver: !!driver,
|
|
91
|
+
}),
|
|
92
|
+
optional: true,
|
|
93
|
+
noPersistence: true,
|
|
94
|
+
});
|
|
95
|
+
const entry = getRegisteredStore("analytics");
|
|
96
|
+
const store = entry.factory({ apiUrl: "test" }, mockDriver);
|
|
97
|
+
expect(store).toEqual({
|
|
98
|
+
type: "analytics",
|
|
99
|
+
hasDriver: true,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
it("optional factory can return null", () => {
|
|
103
|
+
registerStore({
|
|
104
|
+
key: "optional-feature",
|
|
105
|
+
factory: () => null,
|
|
106
|
+
optional: true,
|
|
107
|
+
});
|
|
108
|
+
const entry = getRegisteredStore("optional-feature");
|
|
109
|
+
expect(entry.factory({}, {})).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PersistenceDriver } from "@internal/core";
|
|
2
|
+
/**
|
|
3
|
+
* Factory function signature for creating a store.
|
|
4
|
+
* Receives the resolved config and driver, returns a store instance or null (for optional stores).
|
|
5
|
+
*/
|
|
6
|
+
export type StoreFactory<TConfig = unknown, TStore = unknown> = (config: TConfig, driver: PersistenceDriver) => TStore | null;
|
|
7
|
+
/**
|
|
8
|
+
* An entry in the Store Registry.
|
|
9
|
+
* Maps a context key to its factory function and metadata.
|
|
10
|
+
*/
|
|
11
|
+
export interface StoreRegistryEntry<TConfig = unknown, TStore = unknown> {
|
|
12
|
+
/** Unique key matching the RUNE_LAB_CONTEXT key (e.g., "theme", "language") */
|
|
13
|
+
key: string;
|
|
14
|
+
/** Factory function that creates the store */
|
|
15
|
+
factory: StoreFactory<TConfig, TStore>;
|
|
16
|
+
/** If true, the store is optional — null return from factory means "skip" */
|
|
17
|
+
optional?: boolean;
|
|
18
|
+
/** If true, the store does not use persistence at all */
|
|
19
|
+
noPersistence?: boolean;
|
|
20
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import hotkeys from "hotkeys-js";
|
|
2
2
|
import { getContext, untrack } from "svelte";
|
|
3
|
-
import { RUNE_LAB_CONTEXT } from "./context";
|
|
3
|
+
import { RUNE_LAB_CONTEXT } from "./context.ts";
|
|
4
4
|
import { DEV } from "esm-env";
|
|
5
5
|
/**
|
|
6
6
|
* Built-in layout shortcuts
|
|
@@ -166,7 +166,7 @@ export function shortcutListener(node, shortcutStore) {
|
|
|
166
166
|
for (const entry of shortcutStore.entries) {
|
|
167
167
|
if (entry.enabled === false)
|
|
168
168
|
continue;
|
|
169
|
-
hotkeys(entry.keys, "all", (event,
|
|
169
|
+
hotkeys(entry.keys, "all", (event, _handler) => {
|
|
170
170
|
// Scoping logic: check if the event target is within the required scope
|
|
171
171
|
if (entry.scope === "global") {
|
|
172
172
|
// Hotkeys.js already filters inputs by default
|
|
@@ -202,9 +202,9 @@ export function shortcutListener(node, shortcutStore) {
|
|
|
202
202
|
hotkeys.setScope("global");
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
|
-
|
|
205
|
+
globalThis.addEventListener("focusin", handleFocusIn);
|
|
206
206
|
return () => {
|
|
207
|
-
|
|
207
|
+
globalThis.removeEventListener("focusin", handleFocusIn);
|
|
208
208
|
};
|
|
209
209
|
});
|
|
210
210
|
return {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type ConfigStore } from "./createConfigStore.svelte";
|
|
1
|
+
import { type ConfigStore } from "./createConfigStore.svelte.ts";
|
|
2
2
|
export interface Theme {
|
|
3
3
|
name: string;
|
|
4
4
|
icon: string;
|
|
5
5
|
}
|
|
6
|
-
import type { PersistenceDriver } from "
|
|
6
|
+
import type { PersistenceDriver } from "@internal/core";
|
|
7
7
|
export interface ThemeStoreOptions {
|
|
8
8
|
driver?: PersistenceDriver | (() => PersistenceDriver | undefined);
|
|
9
9
|
/** Additional custom themes to append to the built-in DaisyUI set */
|
|
@@ -11,12 +11,5 @@ export interface ThemeStoreOptions {
|
|
|
11
11
|
/** Fallback theme if no persisted value exists (after system preference check) */
|
|
12
12
|
defaultTheme?: string;
|
|
13
13
|
}
|
|
14
|
-
export declare function createThemeStore(driverOrOptions?: PersistenceDriver | (() => PersistenceDriver | undefined) | ThemeStoreOptions):
|
|
15
|
-
current: string;
|
|
16
|
-
available: Theme[];
|
|
17
|
-
set(id: string): void;
|
|
18
|
-
get(id: string): Theme | undefined;
|
|
19
|
-
getProp<K extends keyof Theme>(prop: K, id?: string | undefined): Theme[K] | undefined;
|
|
20
|
-
addItems(newItems: Theme[]): void;
|
|
21
|
-
};
|
|
14
|
+
export declare function createThemeStore(driverOrOptions?: PersistenceDriver | (() => PersistenceDriver | undefined) | ThemeStoreOptions): ConfigStore<Theme>;
|
|
22
15
|
export declare function getThemeStore(): ConfigStore<Theme>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
// @ts-nocheck: legacy daisyui imports and complex ConfigStore typing
|
|
1
2
|
// client/packages/ui/src/state/theme-config.svelte.ts
|
|
2
|
-
import { createConfigStore, } from "./createConfigStore.svelte";
|
|
3
|
+
import { createConfigStore, } from "./createConfigStore.svelte.ts";
|
|
3
4
|
import { getContext } from "svelte";
|
|
4
|
-
import { RUNE_LAB_CONTEXT } from "./context";
|
|
5
|
+
import { RUNE_LAB_CONTEXT } from "./context.ts";
|
|
5
6
|
// Icon map for known themes - unknown ones fall back to 🎨
|
|
6
7
|
const THEME_ICONS = {
|
|
7
8
|
light: "🌞",
|
|
@@ -40,13 +41,14 @@ const THEME_ICONS = {
|
|
|
40
41
|
abyss: "🌌",
|
|
41
42
|
silk: "🎀",
|
|
42
43
|
};
|
|
44
|
+
import { resolveDriver } from "./persistence/provider.ts";
|
|
45
|
+
import themeOrder from "daisyui/functions/themeOrder.js";
|
|
46
|
+
import { BROWSER } from "esm-env";
|
|
43
47
|
// Derived from daisyUI directly — stays in sync automatically
|
|
44
48
|
const THEMES = themeOrder.map((name) => ({
|
|
45
49
|
name,
|
|
46
50
|
icon: THEME_ICONS[name] ?? "🎨",
|
|
47
51
|
}));
|
|
48
|
-
import themeOrder from "daisyui/functions/themeOrder.js";
|
|
49
|
-
import { BROWSER } from "esm-env";
|
|
50
52
|
export function createThemeStore(driverOrOptions) {
|
|
51
53
|
// Normalize overloaded argument
|
|
52
54
|
const opts = driverOrOptions && typeof driverOrOptions === "object" &&
|
|
@@ -55,9 +57,7 @@ export function createThemeStore(driverOrOptions) {
|
|
|
55
57
|
: {
|
|
56
58
|
driver: driverOrOptions,
|
|
57
59
|
};
|
|
58
|
-
const resolvedDriver =
|
|
59
|
-
? opts.driver()
|
|
60
|
-
: opts.driver;
|
|
60
|
+
const resolvedDriver = resolveDriver(opts.driver);
|
|
61
61
|
const store = createConfigStore({
|
|
62
62
|
items: THEMES,
|
|
63
63
|
storageKey: "theme",
|
|
@@ -72,7 +72,7 @@ export function createThemeStore(driverOrOptions) {
|
|
|
72
72
|
}
|
|
73
73
|
// System preference detection — only if no persisted value was loaded
|
|
74
74
|
if (!resolvedDriver?.get("theme") && BROWSER) {
|
|
75
|
-
const prefersDark =
|
|
75
|
+
const prefersDark = globalThis.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
76
76
|
const systemDefault = prefersDark ? "dark" : "light";
|
|
77
77
|
const chosen = opts.defaultTheme ?? systemDefault;
|
|
78
78
|
if (store.get(chosen)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { getApiStore } from "
|
|
3
|
-
import { getToastStore } from "
|
|
2
|
+
import { getApiStore } from "@internal/state";
|
|
3
|
+
import { getToastStore } from "@internal/state";
|
|
4
4
|
|
|
5
5
|
const apiStore = getApiStore();
|
|
6
6
|
const toastStore = getToastStore();
|
|
@@ -12,15 +12,17 @@
|
|
|
12
12
|
createShortcutStore,
|
|
13
13
|
createCartStore,
|
|
14
14
|
createSessionStore,
|
|
15
|
+
createExchangeRateStore,
|
|
15
16
|
type CartStoreConfig,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
import type {
|
|
22
|
-
import type {
|
|
23
|
-
import {
|
|
17
|
+
type ExchangeRateStore,
|
|
18
|
+
} from "@internal/state";
|
|
19
|
+
import type { PersistenceDriver } from "@internal/core";
|
|
20
|
+
import { localStorageDriver } from "@internal/state";
|
|
21
|
+
import { RUNE_LAB_CONTEXT } from "@internal/state";
|
|
22
|
+
import type { AppData } from "@internal/state";
|
|
23
|
+
import type { Theme } from "@internal/state";
|
|
24
|
+
import type { Currency } from "@internal/state";
|
|
25
|
+
import { CommandPalette, ShortcutPalette, Toaster } from "../../mod";
|
|
24
26
|
|
|
25
27
|
export interface RuneLabConfig {
|
|
26
28
|
persistence?: PersistenceDriver;
|
|
@@ -46,6 +48,13 @@
|
|
|
46
48
|
currencies?: Currency[];
|
|
47
49
|
/** Default currency code when no persisted value exists */
|
|
48
50
|
defaultCurrency?: string;
|
|
51
|
+
/** Bootstrap-time exchange rates */
|
|
52
|
+
exchangeRates?: {
|
|
53
|
+
base: string;
|
|
54
|
+
rates: Record<string, number>;
|
|
55
|
+
};
|
|
56
|
+
/** Callback when rates are initialized/updated */
|
|
57
|
+
onRatesUpdate?: (store: ExchangeRateStore) => void;
|
|
49
58
|
|
|
50
59
|
// Cart (opt-in)
|
|
51
60
|
/** CartStore configuration — when provided, a CartStore is created and registered in context */
|
|
@@ -80,9 +89,15 @@
|
|
|
80
89
|
const initialDefaultTheme = untrack(() => config.defaultTheme);
|
|
81
90
|
const initialCustomCurrencies = untrack(() => config.currencies);
|
|
82
91
|
const initialDefaultCurrency = untrack(() => config.defaultCurrency);
|
|
92
|
+
const initialExchangeRates = untrack(() => config.exchangeRates);
|
|
83
93
|
const initialCartConfig = untrack(() => config.cart);
|
|
84
94
|
const initialAuthConfig = untrack(() => config.auth);
|
|
85
95
|
|
|
96
|
+
const exchangeRateStore = createExchangeRateStore();
|
|
97
|
+
if (initialExchangeRates) {
|
|
98
|
+
exchangeRateStore.setRates(initialExchangeRates.base, initialExchangeRates.rates);
|
|
99
|
+
}
|
|
100
|
+
|
|
86
101
|
const themeStore = createThemeStore({
|
|
87
102
|
driver: initialPersistence,
|
|
88
103
|
customThemes: initialCustomThemes,
|
|
@@ -96,6 +111,7 @@
|
|
|
96
111
|
driver: initialPersistence,
|
|
97
112
|
customCurrencies: initialCustomCurrencies,
|
|
98
113
|
defaultCurrency: initialDefaultCurrency,
|
|
114
|
+
exchangeRateStore,
|
|
99
115
|
});
|
|
100
116
|
const shortcutStore = createShortcutStore();
|
|
101
117
|
|
|
@@ -117,6 +133,7 @@
|
|
|
117
133
|
setContext(RUNE_LAB_CONTEXT.theme, themeStore);
|
|
118
134
|
setContext(RUNE_LAB_CONTEXT.language, languageStore);
|
|
119
135
|
setContext(RUNE_LAB_CONTEXT.currency, currencyStore);
|
|
136
|
+
setContext(RUNE_LAB_CONTEXT.exchangeRate, exchangeRateStore);
|
|
120
137
|
setContext(RUNE_LAB_CONTEXT.shortcut, shortcutStore);
|
|
121
138
|
setContext(RUNE_LAB_CONTEXT.layout, layoutStore);
|
|
122
139
|
setContext(RUNE_LAB_CONTEXT.commands, commandStore);
|
|
@@ -165,6 +182,9 @@
|
|
|
165
182
|
|
|
166
183
|
onMount(() => {
|
|
167
184
|
layoutStore.init();
|
|
185
|
+
if (config.onRatesUpdate) {
|
|
186
|
+
config.onRatesUpdate(exchangeRateStore);
|
|
187
|
+
}
|
|
168
188
|
});
|
|
169
189
|
|
|
170
190
|
// Meta tags derived from app store state
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Snippet } from "svelte";
|
|
2
|
-
import { type CartStoreConfig } from "
|
|
3
|
-
import type { PersistenceDriver } from "
|
|
4
|
-
import type { AppData } from "
|
|
5
|
-
import type { Theme } from "
|
|
6
|
-
import type { Currency } from "
|
|
2
|
+
import { type CartStoreConfig, type ExchangeRateStore } from "@internal/state";
|
|
3
|
+
import type { PersistenceDriver } from "@internal/core";
|
|
4
|
+
import type { AppData } from "@internal/state";
|
|
5
|
+
import type { Theme } from "@internal/state";
|
|
6
|
+
import type { Currency } from "@internal/state";
|
|
7
7
|
export interface RuneLabConfig {
|
|
8
8
|
persistence?: PersistenceDriver;
|
|
9
9
|
app?: Partial<AppData>;
|
|
@@ -24,6 +24,13 @@ export interface RuneLabConfig {
|
|
|
24
24
|
currencies?: Currency[];
|
|
25
25
|
/** Default currency code when no persisted value exists */
|
|
26
26
|
defaultCurrency?: string;
|
|
27
|
+
/** Bootstrap-time exchange rates */
|
|
28
|
+
exchangeRates?: {
|
|
29
|
+
base: string;
|
|
30
|
+
rates: Record<string, number>;
|
|
31
|
+
};
|
|
32
|
+
/** Callback when rates are initialized/updated */
|
|
33
|
+
onRatesUpdate?: (store: ExchangeRateStore) => void;
|
|
27
34
|
/** CartStore configuration — when provided, a CartStore is created and registered in context */
|
|
28
35
|
cart?: CartStoreConfig<any>;
|
|
29
36
|
/** When true, creates and registers a SessionStore in context */
|