rune-lab 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +12 -0
- package/README.md +4 -4
- package/deno.json +50 -0
- package/justfile +5 -0
- package/package.json +3 -80
- package/scripts/build_pkg.ts +51 -0
- package/scripts/ci.just +26 -0
- package/scripts/deploy.just +21 -0
- package/scripts/dev.just +4 -0
- package/scripts/inject.just +72 -0
- package/src/RuneProvider.svelte +105 -0
- package/src/i18n/message-resolver.test.ts +103 -0
- package/src/i18n/message-resolver.ts +89 -0
- package/src/i18n/project.inlang/settings.json +26 -0
- package/src/i18n/translations/ar.json +89 -0
- package/src/i18n/translations/de.json +89 -0
- package/src/i18n/translations/en.json +89 -0
- package/src/i18n/translations/es.json +89 -0
- package/src/i18n/translations/fr.json +89 -0
- package/src/i18n/translations/hi.json +89 -0
- package/src/i18n/translations/it.json +89 -0
- package/src/i18n/translations/ja.json +89 -0
- package/src/i18n/translations/ko.json +89 -0
- package/src/i18n/translations/pt.json +89 -0
- package/src/i18n/translations/ru.json +89 -0
- package/src/i18n/translations/vi.json +89 -0
- package/src/i18n/translations/zh.json +89 -0
- package/src/kernel/deno.json +5 -0
- package/src/kernel/src/actions/portal.ts +41 -0
- package/src/kernel/src/actions/shortcut-listener.ts +50 -0
- package/src/kernel/src/context/app.svelte.ts +98 -0
- package/src/kernel/src/context/context.ts +31 -0
- package/src/kernel/src/context/stores.svelte.ts +96 -0
- package/src/kernel/src/context/types.ts +110 -0
- package/src/kernel/src/context/useRuneLab.ts +38 -0
- package/src/kernel/src/mod.ts +13 -0
- package/src/kernel/src/persistence/createConfigStore.svelte.ts +120 -0
- package/src/kernel/src/persistence/drivers.test.ts +95 -0
- package/src/kernel/src/persistence/drivers.ts +77 -0
- package/src/kernel/src/persistence/provider.test.ts +58 -0
- package/src/kernel/src/persistence/provider.ts +58 -0
- package/src/kernel/src/persistence/types.ts +19 -0
- package/src/kernel/src/persistence/usePersistence.ts +9 -0
- package/src/kernel/src/registry/mod.ts +221 -0
- package/src/kernel/src/registry/registry.test.ts +162 -0
- package/src/kernel/src/tokens/props.ts +94 -0
- package/src/mod.ts +10 -0
- package/src/runes/layout/deno.json +5 -0
- package/src/runes/layout/src/APP_CONFIGURATIONS.ts +56 -0
- package/{dist/ui/features/config → src/runes/layout/src}/AppSettingSelector.svelte +1 -1
- package/{dist/ui/layout → src/runes/layout/src}/ConnectedNavigationPanel.svelte +7 -8
- package/{dist/ui/layout → src/runes/layout/src}/ConnectedWorkspaceStrip.svelte +5 -3
- package/src/runes/layout/src/Icon.svelte +7 -0
- package/src/runes/layout/src/LanguageSelector.svelte +45 -0
- package/{dist/ui/layout → src/runes/layout/src}/NavigationPanel.svelte +1 -4
- package/src/runes/layout/src/ResourceSelector.svelte +90 -0
- package/src/runes/layout/src/ThemeSelector.svelte +50 -0
- package/{dist/ui/layout → src/runes/layout/src}/WorkspaceLayout.svelte +13 -3
- package/{dist/ui/layout → src/runes/layout/src}/WorkspaceStrip.svelte +1 -1
- package/src/runes/layout/src/connection-factory.ts +95 -0
- package/src/runes/layout/src/language.svelte.ts +44 -0
- package/src/runes/layout/src/mod.ts +71 -0
- package/src/runes/layout/src/store.svelte.ts +142 -0
- package/src/runes/layout/src/theme.svelte.ts +73 -0
- package/src/runes/layout/src/types.ts +11 -0
- package/src/runes/palettes/deno.json +8 -0
- package/{dist/ui/features/command-palette → src/runes/palettes/src/commands}/CommandPalette.svelte +4 -4
- package/src/runes/palettes/src/commands/mod.ts +2 -0
- package/src/runes/palettes/src/commands/store.svelte.ts +80 -0
- package/src/runes/palettes/src/mod.ts +51 -0
- package/{dist/ui/components → src/runes/palettes/src/notifications}/Toaster.svelte +1 -2
- package/src/runes/palettes/src/notifications/bridge.ts +56 -0
- package/src/runes/palettes/src/notifications/mod.ts +4 -0
- package/src/runes/palettes/src/notifications/store.svelte.ts +58 -0
- package/{dist/ui/features → src/runes/palettes/src}/shortcuts/ShortcutPalette.svelte +4 -4
- package/src/runes/palettes/src/shortcuts/mod.ts +4 -0
- package/src/runes/palettes/src/shortcuts/store.svelte.ts +85 -0
- package/src/runes/palettes/src/shortcuts/types.ts +4 -0
- package/src/runes/palettes/src/shortcuts/useShortcuts.ts +85 -0
- package/src/runes/plugins/money/deno.json +8 -0
- package/src/runes/plugins/money/src/CurrencySelector.svelte +41 -0
- package/src/runes/plugins/money/src/MoneyDisplay.svelte +142 -0
- package/src/runes/plugins/money/src/MoneyInput.svelte +208 -0
- package/src/runes/plugins/money/src/currency.svelte.ts +95 -0
- package/src/runes/plugins/money/src/currency.test.ts +40 -0
- package/src/runes/plugins/money/src/exchange-rate.svelte.ts +176 -0
- package/src/runes/plugins/money/src/exchange-rate.test.ts +95 -0
- package/src/runes/plugins/money/src/mod.ts +79 -0
- package/src/runes/plugins/money/src/money-primitive.test.ts +217 -0
- package/src/runes/plugins/money/src/money-primitive.ts +220 -0
- package/src/runes/plugins/money/src/money.test.ts +183 -0
- package/src/runes/plugins/money/src/money.ts +382 -0
- package/src/runes/plugins/money/src/strategies.test.ts +143 -0
- package/src/runes/plugins/money/src/strategies.ts +95 -0
- package/src/runes/plugins/money/src/types.ts +27 -0
- package/src/runes/plugins/money/src/useMoney.ts +261 -0
- package/src/runes/plugins/money/src/useMoneyFilter.ts +132 -0
- package/tsconfig.json +41 -0
- package/vite.config.ts +44 -0
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -3
- package/dist/core/internal/message-resolver.d.ts +0 -42
- package/dist/core/internal/message-resolver.d.ts.map +0 -1
- package/dist/core/internal/message-resolver.js +0 -47
- package/dist/core/money/index.d.ts +0 -2
- package/dist/core/money/index.d.ts.map +0 -1
- package/dist/core/money/index.js +0 -2
- package/dist/core/money/money.d.ts +0 -57
- package/dist/core/money/money.d.ts.map +0 -1
- package/dist/core/money/money.js +0 -132
- package/dist/core/persistence/types.d.ts +0 -18
- package/dist/core/persistence/types.d.ts.map +0 -1
- package/dist/core/persistence/types.js +0 -2
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- package/dist/state/api.svelte.d.ts +0 -26
- package/dist/state/api.svelte.js +0 -65
- package/dist/state/app.svelte.d.ts +0 -48
- package/dist/state/app.svelte.js +0 -82
- package/dist/state/auth/index.d.ts +0 -2
- package/dist/state/auth/index.js +0 -2
- package/dist/state/auth/session.svelte.d.ts +0 -40
- package/dist/state/auth/session.svelte.js +0 -57
- package/dist/state/auth/types.d.ts +0 -30
- package/dist/state/auth/types.js +0 -3
- package/dist/state/cart.svelte.d.ts +0 -64
- package/dist/state/cart.svelte.js +0 -122
- package/dist/state/commands.svelte.d.ts +0 -46
- package/dist/state/commands.svelte.js +0 -62
- package/dist/state/composables/useMoney.d.ts +0 -15
- package/dist/state/composables/useMoney.js +0 -62
- package/dist/state/composables/usePersistence.d.ts +0 -2
- package/dist/state/composables/usePersistence.js +0 -5
- package/dist/state/composables/useRuneLab.d.ts +0 -19
- package/dist/state/composables/useRuneLab.js +0 -30
- package/dist/state/context.d.ts +0 -14
- package/dist/state/context.js +0 -15
- package/dist/state/createConfigStore.svelte.d.ts +0 -55
- package/dist/state/createConfigStore.svelte.js +0 -61
- package/dist/state/currency.svelte.d.ts +0 -28
- package/dist/state/currency.svelte.js +0 -80
- package/dist/state/daisyui.d.ts +0 -4
- package/dist/state/index.d.ts +0 -19
- package/dist/state/index.js +0 -25
- package/dist/state/language.svelte.d.ts +0 -64
- package/dist/state/language.svelte.js +0 -44
- package/dist/state/layout.svelte.d.ts +0 -49
- package/dist/state/layout.svelte.js +0 -108
- package/dist/state/persistence/drivers.d.ts +0 -12
- package/dist/state/persistence/drivers.js +0 -73
- package/dist/state/shortcuts.svelte.d.ts +0 -121
- package/dist/state/shortcuts.svelte.js +0 -222
- package/dist/state/theme.svelte.d.ts +0 -22
- package/dist/state/theme.svelte.js +0 -92
- package/dist/state/toast-bridge.d.ts +0 -29
- package/dist/state/toast-bridge.js +0 -43
- package/dist/state/toast.svelte.d.ts +0 -27
- package/dist/state/toast.svelte.js +0 -43
- package/dist/ui/actions/portal.d.ts +0 -7
- package/dist/ui/actions/portal.js +0 -32
- package/dist/ui/components/ApiMonitor.svelte +0 -162
- package/dist/ui/components/ApiMonitor.svelte.d.ts +0 -3
- package/dist/ui/components/Icon.svelte +0 -108
- package/dist/ui/components/Icon.svelte.d.ts +0 -14
- package/dist/ui/components/Kyntharil.svelte +0 -48
- package/dist/ui/components/Kyntharil.svelte.d.ts +0 -6
- package/dist/ui/components/RuneProvider.svelte +0 -198
- package/dist/ui/components/RuneProvider.svelte.d.ts +0 -42
- package/dist/ui/components/Toaster.svelte.d.ts +0 -18
- package/dist/ui/components/money/MoneyDisplay.svelte +0 -69
- package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +0 -21
- package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +0 -1
- package/dist/ui/components/money/MoneyDisplay.svelte.test.js +0 -74
- package/dist/ui/components/money/MoneyInput.svelte +0 -131
- package/dist/ui/components/money/MoneyInput.svelte.d.ts +0 -25
- package/dist/ui/components/money/index.d.ts +0 -2
- package/dist/ui/components/money/index.js +0 -3
- package/dist/ui/components/user/UserAvatar.svelte +0 -66
- package/dist/ui/components/user/UserAvatar.svelte.d.ts +0 -13
- package/dist/ui/components/user/UserProfile.svelte +0 -79
- package/dist/ui/components/user/UserProfile.svelte.d.ts +0 -17
- package/dist/ui/components/user/index.d.ts +0 -2
- package/dist/ui/components/user/index.js +0 -3
- package/dist/ui/features/command-palette/CommandPalette.svelte.d.ts +0 -6
- package/dist/ui/features/config/AppSettingSelector.svelte.d.ts +0 -13
- package/dist/ui/features/config/CurrencySelector.svelte +0 -67
- package/dist/ui/features/config/CurrencySelector.svelte.d.ts +0 -8
- package/dist/ui/features/config/LanguageSelector.svelte +0 -66
- package/dist/ui/features/config/LanguageSelector.svelte.d.ts +0 -8
- package/dist/ui/features/config/ThemeSelector.svelte +0 -73
- package/dist/ui/features/config/ThemeSelector.svelte.d.ts +0 -8
- package/dist/ui/features/notifications/NotificationBell.svelte.d.ts +0 -11
- package/dist/ui/features/notifications/index.d.ts +0 -1
- package/dist/ui/features/notifications/index.js +0 -2
- package/dist/ui/features/shortcuts/ShortcutPalette.svelte.d.ts +0 -6
- package/dist/ui/index.d.ts +0 -26
- package/dist/ui/index.js +0 -41
- package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +0 -10
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +0 -9
- package/dist/ui/layout/ContentArea.svelte.d.ts +0 -8
- package/dist/ui/layout/DetailPanel.svelte.d.ts +0 -7
- package/dist/ui/layout/NavigationPanel.svelte.d.ts +0 -14
- package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +0 -25
- package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +0 -11
- package/dist/ui/layout/index.d.ts +0 -7
- package/dist/ui/layout/index.js +0 -7
- package/dist/ui/paraglide/README.md +0 -94
- package/dist/ui/paraglide/messages/_index.d.ts +0 -87
- package/dist/ui/paraglide/messages/_index.js +0 -88
- package/dist/ui/paraglide/messages/abyss.d.ts +0 -16
- package/dist/ui/paraglide/messages/abyss.js +0 -84
- package/dist/ui/paraglide/messages/acid.d.ts +0 -16
- package/dist/ui/paraglide/messages/acid.js +0 -84
- package/dist/ui/paraglide/messages/active_toasts.d.ts +0 -16
- package/dist/ui/paraglide/messages/active_toasts.js +0 -84
- package/dist/ui/paraglide/messages/aed3.d.ts +0 -17
- package/dist/ui/paraglide/messages/aed3.js +0 -85
- package/dist/ui/paraglide/messages/all_currencies.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_currencies.js +0 -84
- package/dist/ui/paraglide/messages/all_languages.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_languages.js +0 -84
- package/dist/ui/paraglide/messages/all_themes.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_themes.js +0 -84
- package/dist/ui/paraglide/messages/api_status.d.ts +0 -16
- package/dist/ui/paraglide/messages/api_status.js +0 -84
- package/dist/ui/paraglide/messages/app_info.d.ts +0 -16
- package/dist/ui/paraglide/messages/app_info.js +0 -84
- package/dist/ui/paraglide/messages/appearance.d.ts +0 -16
- package/dist/ui/paraglide/messages/appearance.js +0 -84
- package/dist/ui/paraglide/messages/aqua.d.ts +0 -16
- package/dist/ui/paraglide/messages/aqua.js +0 -84
- package/dist/ui/paraglide/messages/ar.d.ts +0 -16
- package/dist/ui/paraglide/messages/ar.js +0 -84
- package/dist/ui/paraglide/messages/autumn.d.ts +0 -16
- package/dist/ui/paraglide/messages/autumn.js +0 -84
- package/dist/ui/paraglide/messages/black.d.ts +0 -16
- package/dist/ui/paraglide/messages/black.js +0 -84
- package/dist/ui/paraglide/messages/brl3.d.ts +0 -17
- package/dist/ui/paraglide/messages/brl3.js +0 -85
- package/dist/ui/paraglide/messages/bumblebee.d.ts +0 -16
- package/dist/ui/paraglide/messages/bumblebee.js +0 -84
- package/dist/ui/paraglide/messages/business.d.ts +0 -16
- package/dist/ui/paraglide/messages/business.js +0 -84
- package/dist/ui/paraglide/messages/cad3.d.ts +0 -17
- package/dist/ui/paraglide/messages/cad3.js +0 -85
- package/dist/ui/paraglide/messages/caramellatte.d.ts +0 -16
- package/dist/ui/paraglide/messages/caramellatte.js +0 -84
- package/dist/ui/paraglide/messages/cmyk.d.ts +0 -16
- package/dist/ui/paraglide/messages/cmyk.js +0 -84
- package/dist/ui/paraglide/messages/cny3.d.ts +0 -17
- package/dist/ui/paraglide/messages/cny3.js +0 -85
- package/dist/ui/paraglide/messages/coffee.d.ts +0 -16
- package/dist/ui/paraglide/messages/coffee.js +0 -84
- package/dist/ui/paraglide/messages/commands_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/commands_label.js +0 -84
- package/dist/ui/paraglide/messages/corporate.d.ts +0 -16
- package/dist/ui/paraglide/messages/corporate.js +0 -84
- package/dist/ui/paraglide/messages/cupcake.d.ts +0 -16
- package/dist/ui/paraglide/messages/cupcake.js +0 -84
- package/dist/ui/paraglide/messages/currency.d.ts +0 -16
- package/dist/ui/paraglide/messages/currency.js +0 -84
- package/dist/ui/paraglide/messages/current_currency.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_currency.js +0 -84
- package/dist/ui/paraglide/messages/current_language.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_language.js +0 -84
- package/dist/ui/paraglide/messages/current_theme.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_theme.js +0 -84
- package/dist/ui/paraglide/messages/currently_in_queue.d.ts +0 -16
- package/dist/ui/paraglide/messages/currently_in_queue.js +0 -84
- package/dist/ui/paraglide/messages/cyberpunk.d.ts +0 -16
- package/dist/ui/paraglide/messages/cyberpunk.js +0 -84
- package/dist/ui/paraglide/messages/dark.d.ts +0 -16
- package/dist/ui/paraglide/messages/dark.js +0 -84
- package/dist/ui/paraglide/messages/de.d.ts +0 -16
- package/dist/ui/paraglide/messages/de.js +0 -84
- package/dist/ui/paraglide/messages/dim.d.ts +0 -16
- package/dist/ui/paraglide/messages/dim.js +0 -84
- package/dist/ui/paraglide/messages/dracula.d.ts +0 -16
- package/dist/ui/paraglide/messages/dracula.js +0 -84
- package/dist/ui/paraglide/messages/emerald.d.ts +0 -16
- package/dist/ui/paraglide/messages/emerald.js +0 -84
- package/dist/ui/paraglide/messages/en.d.ts +0 -16
- package/dist/ui/paraglide/messages/en.js +0 -84
- package/dist/ui/paraglide/messages/es.d.ts +0 -16
- package/dist/ui/paraglide/messages/es.js +0 -84
- package/dist/ui/paraglide/messages/eur3.d.ts +0 -17
- package/dist/ui/paraglide/messages/eur3.js +0 -85
- package/dist/ui/paraglide/messages/extended_controls.d.ts +0 -16
- package/dist/ui/paraglide/messages/extended_controls.js +0 -84
- package/dist/ui/paraglide/messages/fantasy.d.ts +0 -16
- package/dist/ui/paraglide/messages/fantasy.js +0 -84
- package/dist/ui/paraglide/messages/forest.d.ts +0 -16
- package/dist/ui/paraglide/messages/forest.js +0 -84
- package/dist/ui/paraglide/messages/fr.d.ts +0 -16
- package/dist/ui/paraglide/messages/fr.js +0 -84
- package/dist/ui/paraglide/messages/garden.d.ts +0 -16
- package/dist/ui/paraglide/messages/garden.js +0 -84
- package/dist/ui/paraglide/messages/gbp3.d.ts +0 -17
- package/dist/ui/paraglide/messages/gbp3.js +0 -85
- package/dist/ui/paraglide/messages/halloween.d.ts +0 -16
- package/dist/ui/paraglide/messages/halloween.js +0 -84
- package/dist/ui/paraglide/messages/hello_world.d.ts +0 -18
- package/dist/ui/paraglide/messages/hello_world.js +0 -84
- package/dist/ui/paraglide/messages/hi.d.ts +0 -16
- package/dist/ui/paraglide/messages/hi.js +0 -84
- package/dist/ui/paraglide/messages/inr3.d.ts +0 -17
- package/dist/ui/paraglide/messages/inr3.js +0 -85
- package/dist/ui/paraglide/messages/it.d.ts +0 -16
- package/dist/ui/paraglide/messages/it.js +0 -84
- package/dist/ui/paraglide/messages/ja.d.ts +0 -16
- package/dist/ui/paraglide/messages/ja.js +0 -84
- package/dist/ui/paraglide/messages/jpy3.d.ts +0 -17
- package/dist/ui/paraglide/messages/jpy3.js +0 -85
- package/dist/ui/paraglide/messages/ko.d.ts +0 -16
- package/dist/ui/paraglide/messages/ko.js +0 -84
- package/dist/ui/paraglide/messages/krw3.d.ts +0 -17
- package/dist/ui/paraglide/messages/krw3.js +0 -85
- package/dist/ui/paraglide/messages/languages.d.ts +0 -16
- package/dist/ui/paraglide/messages/languages.js +0 -84
- package/dist/ui/paraglide/messages/lemonade.d.ts +0 -16
- package/dist/ui/paraglide/messages/lemonade.js +0 -84
- package/dist/ui/paraglide/messages/light.d.ts +0 -16
- package/dist/ui/paraglide/messages/light.js +0 -84
- package/dist/ui/paraglide/messages/live_store_dashboard.d.ts +0 -16
- package/dist/ui/paraglide/messages/live_store_dashboard.js +0 -84
- package/dist/ui/paraglide/messages/localization.d.ts +0 -16
- package/dist/ui/paraglide/messages/localization.js +0 -84
- package/dist/ui/paraglide/messages/lofi.d.ts +0 -16
- package/dist/ui/paraglide/messages/lofi.js +0 -84
- package/dist/ui/paraglide/messages/luxury.d.ts +0 -16
- package/dist/ui/paraglide/messages/luxury.js +0 -84
- package/dist/ui/paraglide/messages/mxn3.d.ts +0 -17
- package/dist/ui/paraglide/messages/mxn3.js +0 -85
- package/dist/ui/paraglide/messages/name_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/name_label.js +0 -84
- package/dist/ui/paraglide/messages/night.d.ts +0 -16
- package/dist/ui/paraglide/messages/night.js +0 -84
- package/dist/ui/paraglide/messages/nord.d.ts +0 -16
- package/dist/ui/paraglide/messages/nord.js +0 -84
- package/dist/ui/paraglide/messages/pastel.d.ts +0 -16
- package/dist/ui/paraglide/messages/pastel.js +0 -84
- package/dist/ui/paraglide/messages/pt.d.ts +0 -16
- package/dist/ui/paraglide/messages/pt.js +0 -84
- package/dist/ui/paraglide/messages/real_time_monitor_desc.d.ts +0 -16
- package/dist/ui/paraglide/messages/real_time_monitor_desc.js +0 -84
- package/dist/ui/paraglide/messages/registered_in_registry.d.ts +0 -16
- package/dist/ui/paraglide/messages/registered_in_registry.js +0 -84
- package/dist/ui/paraglide/messages/retro.d.ts +0 -16
- package/dist/ui/paraglide/messages/retro.js +0 -84
- package/dist/ui/paraglide/messages/ru.d.ts +0 -16
- package/dist/ui/paraglide/messages/ru.js +0 -84
- package/dist/ui/paraglide/messages/silk.d.ts +0 -16
- package/dist/ui/paraglide/messages/silk.js +0 -84
- package/dist/ui/paraglide/messages/state_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/state_label.js +0 -84
- package/dist/ui/paraglide/messages/sunset.d.ts +0 -16
- package/dist/ui/paraglide/messages/sunset.js +0 -84
- package/dist/ui/paraglide/messages/synthwave.d.ts +0 -16
- package/dist/ui/paraglide/messages/synthwave.js +0 -84
- package/dist/ui/paraglide/messages/system.d.ts +0 -16
- package/dist/ui/paraglide/messages/system.js +0 -84
- package/dist/ui/paraglide/messages/theme.d.ts +0 -16
- package/dist/ui/paraglide/messages/theme.js +0 -84
- package/dist/ui/paraglide/messages/themes.d.ts +0 -16
- package/dist/ui/paraglide/messages/themes.js +0 -84
- package/dist/ui/paraglide/messages/url_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/url_label.js +0 -84
- package/dist/ui/paraglide/messages/usd3.d.ts +0 -17
- package/dist/ui/paraglide/messages/usd3.js +0 -85
- package/dist/ui/paraglide/messages/valentine.d.ts +0 -16
- package/dist/ui/paraglide/messages/valentine.js +0 -84
- package/dist/ui/paraglide/messages/version_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/version_label.js +0 -84
- package/dist/ui/paraglide/messages/vi.d.ts +0 -16
- package/dist/ui/paraglide/messages/vi.js +0 -84
- package/dist/ui/paraglide/messages/winter.d.ts +0 -16
- package/dist/ui/paraglide/messages/winter.js +0 -84
- package/dist/ui/paraglide/messages/wireframe.d.ts +0 -16
- package/dist/ui/paraglide/messages/wireframe.js +0 -84
- package/dist/ui/paraglide/messages/zh.d.ts +0 -16
- package/dist/ui/paraglide/messages/zh.js +0 -84
- package/dist/ui/paraglide/messages.d.ts +0 -2
- package/dist/ui/paraglide/messages.js +0 -4
- package/dist/ui/paraglide/registry.d.ts +0 -21
- package/dist/ui/paraglide/registry.js +0 -31
- package/dist/ui/paraglide/runtime.d.ts +0 -749
- package/dist/ui/paraglide/runtime.js +0 -1777
- package/dist/ui/paraglide/server.d.ts +0 -103
- package/dist/ui/paraglide/server.js +0 -248
- package/dist/ui/primitives/DatePicker.svelte +0 -257
- package/dist/ui/primitives/DatePicker.svelte.d.ts +0 -17
- package/dist/ui/primitives/index.d.ts +0 -1
- package/dist/ui/primitives/index.js +0 -3
- /package/{dist/ui/layout → src/runes/layout/src}/ContentArea.svelte +0 -0
- /package/{dist/ui/layout → src/runes/layout/src}/DetailPanel.svelte +0 -0
- /package/{dist/ui/features → src/runes/palettes/src}/notifications/NotificationBell.svelte +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getLanguageStore, getCurrencyStore } from "@rune-lab/kernel";
|
|
3
|
+
import {
|
|
4
|
+
formatAmount,
|
|
5
|
+
toMinorUnit,
|
|
6
|
+
type ISO4217Code,
|
|
7
|
+
MoneyPrimitive,
|
|
8
|
+
} from "@rune-lab/money";
|
|
9
|
+
import { DEV } from "esm-env";
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
amount: amountProp,
|
|
13
|
+
money,
|
|
14
|
+
unit = "minor",
|
|
15
|
+
fallback = "—",
|
|
16
|
+
currency,
|
|
17
|
+
sourceCurrency,
|
|
18
|
+
showSourceCurrency = false,
|
|
19
|
+
noRatesFallback,
|
|
20
|
+
locale,
|
|
21
|
+
compact = false,
|
|
22
|
+
} = $props<{
|
|
23
|
+
/** Amount in minor or major units (see unit prop) */
|
|
24
|
+
amount?: number | null | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* MoneyPrimitive instance. When provided, amount, currency, and unit
|
|
27
|
+
* are derived from the primitive. Takes precedence over raw amount.
|
|
28
|
+
*/
|
|
29
|
+
money?: MoneyPrimitive;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the amount is in 'major' (e.g., pesos) or 'minor' (e.g., centavos) units.
|
|
32
|
+
* @deprecated Use `money` prop with MoneyPrimitive instead.
|
|
33
|
+
* Defaults to 'minor' for backward compatibility.
|
|
34
|
+
*/
|
|
35
|
+
unit?: "major" | "minor";
|
|
36
|
+
/** String to display if amount is null, undefined, or NaN. Defaults to "—" */
|
|
37
|
+
fallback?: string;
|
|
38
|
+
/** Override currency code (defaults to CurrencyStore.current) */
|
|
39
|
+
currency?: ISO4217Code | string;
|
|
40
|
+
/** The currency the amount is stored in (e.g. MXN) */
|
|
41
|
+
sourceCurrency?: ISO4217Code | string;
|
|
42
|
+
/** Show the original currency as a label */
|
|
43
|
+
showSourceCurrency?: boolean;
|
|
44
|
+
/** Fallback text if conversion is needed but rates are missing */
|
|
45
|
+
noRatesFallback?: string;
|
|
46
|
+
/** Override locale (defaults to LanguageStore.current) */
|
|
47
|
+
locale?: string;
|
|
48
|
+
/** Use compact notation (e.g., $1.2M) */
|
|
49
|
+
compact?: boolean;
|
|
50
|
+
}>();
|
|
51
|
+
|
|
52
|
+
// Dev warning for deprecated usage
|
|
53
|
+
$effect(() => {
|
|
54
|
+
if (DEV && money && unit !== "minor") {
|
|
55
|
+
console.warn(
|
|
56
|
+
"[MoneyDisplay] The `unit` prop is deprecated when using `money: MoneyPrimitive`. " +
|
|
57
|
+
"The unit is derived from the MoneyPrimitive instance.",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Derive effective values from MoneyPrimitive or legacy props
|
|
63
|
+
const amount = $derived(money ? money.minor : amountProp);
|
|
64
|
+
const effectiveSourceCurrency = $derived(
|
|
65
|
+
money ? money.currencyCode : sourceCurrency,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const currencyStore = getCurrencyStore();
|
|
69
|
+
const languageStore = getLanguageStore();
|
|
70
|
+
|
|
71
|
+
const resolvedDisplayCurrency = $derived(
|
|
72
|
+
currency ?? String(currencyStore.current),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const resolvedSourceCurrency = $derived(
|
|
76
|
+
effectiveSourceCurrency ?? resolvedDisplayCurrency,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const resolvedLocale = $derived(
|
|
80
|
+
locale ?? (String(languageStore.current) || "en"),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const currencyMeta = $derived(currencyStore.get(resolvedDisplayCurrency));
|
|
84
|
+
const decimals = $derived(currencyMeta?.decimals ?? 2);
|
|
85
|
+
|
|
86
|
+
const formatted = $derived.by(() => {
|
|
87
|
+
// If amount is null/undefined/NaN, use fallback
|
|
88
|
+
if (
|
|
89
|
+
amount === null ||
|
|
90
|
+
amount === undefined ||
|
|
91
|
+
(typeof amount === "number" && isNaN(amount))
|
|
92
|
+
) {
|
|
93
|
+
return fallback;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Convert to minor units if necessary (in source currency)
|
|
97
|
+
const minorAmount =
|
|
98
|
+
unit === "major"
|
|
99
|
+
? toMinorUnit(Number(amount), resolvedSourceCurrency)
|
|
100
|
+
: amount;
|
|
101
|
+
|
|
102
|
+
// Conversion logic
|
|
103
|
+
let displayAmount = Number(minorAmount);
|
|
104
|
+
let displayCurrency = resolvedDisplayCurrency;
|
|
105
|
+
|
|
106
|
+
if (resolvedSourceCurrency !== resolvedDisplayCurrency) {
|
|
107
|
+
if (currencyStore.canConvert) {
|
|
108
|
+
displayAmount = currencyStore.convertAmount(
|
|
109
|
+
Number(minorAmount),
|
|
110
|
+
resolvedSourceCurrency,
|
|
111
|
+
resolvedDisplayCurrency,
|
|
112
|
+
);
|
|
113
|
+
} else if (noRatesFallback) {
|
|
114
|
+
return noRatesFallback;
|
|
115
|
+
} else {
|
|
116
|
+
// Fallback to source currency display if rates missing
|
|
117
|
+
displayCurrency = resolvedSourceCurrency;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (compact) {
|
|
122
|
+
const displayDecimals =
|
|
123
|
+
currencyStore.get(displayCurrency)?.decimals ?? 2;
|
|
124
|
+
const majorUnits = displayAmount / Math.pow(10, displayDecimals);
|
|
125
|
+
return new Intl.NumberFormat(resolvedLocale, {
|
|
126
|
+
style: "currency",
|
|
127
|
+
currency: displayCurrency,
|
|
128
|
+
notation: "compact",
|
|
129
|
+
maximumFractionDigits: 1,
|
|
130
|
+
}).format(majorUnits);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return formatAmount(displayAmount, displayCurrency, resolvedLocale);
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<data value={amount} class="rl-money-display tabular-nums">
|
|
138
|
+
{formatted}
|
|
139
|
+
{#if showSourceCurrency && sourceCurrency && sourceCurrency !== resolvedDisplayCurrency}
|
|
140
|
+
<small class="opacity-50 ml-1">({sourceCurrency})</small>
|
|
141
|
+
{/if}
|
|
142
|
+
</data>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { ISO4217Code } from "@rune-lab/money";
|
|
3
|
+
import { MoneyPrimitive } from "@rune-lab/money";
|
|
4
|
+
|
|
5
|
+
export interface MoneyInputProps {
|
|
6
|
+
/** Current value in minor or major units (see unit prop) */
|
|
7
|
+
amount?: number | null | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* MoneyPrimitive instance. When provided, initial amount and currency
|
|
10
|
+
* are derived from the primitive. Read-only input — output still fires oninput().
|
|
11
|
+
*/
|
|
12
|
+
money?: MoneyPrimitive;
|
|
13
|
+
/**
|
|
14
|
+
* Whether the amount is in 'major' (e.g., pesos) or 'minor' (e.g., centavos) units.
|
|
15
|
+
* @deprecated Use `money` prop with MoneyPrimitive instead.
|
|
16
|
+
* Defaults to 'minor' for backward compatibility.
|
|
17
|
+
*/
|
|
18
|
+
unit?: "major" | "minor";
|
|
19
|
+
/** Override currency code (defaults to CurrencyStore.current) */
|
|
20
|
+
currency?: ISO4217Code | string;
|
|
21
|
+
/** The currency the amount is stored in (e.g. MXN) */
|
|
22
|
+
storageCurrency?: ISO4217Code | string;
|
|
23
|
+
/** Minimum value in same units as amount (in storageCurrency) */
|
|
24
|
+
min?: number;
|
|
25
|
+
/** Maximum value in same units as amount (in storageCurrency) */
|
|
26
|
+
max?: number;
|
|
27
|
+
/** Placeholder text */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Fired when the value changes (unit matches the unit prop, in storageCurrency) */
|
|
30
|
+
oninput?: (val: number) => void;
|
|
31
|
+
/** Input disabled state */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import { getCurrencyStore } from "@rune-lab/kernel";
|
|
38
|
+
import { toMinorUnit } from "@rune-lab/money";
|
|
39
|
+
import { DEV } from "esm-env";
|
|
40
|
+
|
|
41
|
+
let {
|
|
42
|
+
amount = $bindable(0),
|
|
43
|
+
money,
|
|
44
|
+
unit = "minor",
|
|
45
|
+
currency,
|
|
46
|
+
storageCurrency,
|
|
47
|
+
min,
|
|
48
|
+
max,
|
|
49
|
+
placeholder = "0.00",
|
|
50
|
+
oninput,
|
|
51
|
+
disabled = false,
|
|
52
|
+
}: MoneyInputProps = $props();
|
|
53
|
+
|
|
54
|
+
const currencyStore = getCurrencyStore();
|
|
55
|
+
|
|
56
|
+
// Dev warning for deprecated usage
|
|
57
|
+
$effect(() => {
|
|
58
|
+
if (DEV && money && unit !== "minor") {
|
|
59
|
+
console.warn(
|
|
60
|
+
"[MoneyInput] The `unit` prop is deprecated when using `money: MoneyPrimitive`. " +
|
|
61
|
+
"The unit is derived from the MoneyPrimitive instance.",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Seed initial values from MoneyPrimitive if provided
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (money && amount === 0) {
|
|
69
|
+
amount = money.minor;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const resolvedDisplayCurrency = $derived(
|
|
74
|
+
money?.currencyCode ?? currency ?? String(currencyStore.current),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const resolvedStorageCurrency = $derived(
|
|
78
|
+
storageCurrency ?? money?.currencyCode ?? resolvedDisplayCurrency,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const displayMeta = $derived(currencyStore.get(resolvedDisplayCurrency));
|
|
82
|
+
const symbol = $derived(displayMeta?.symbol ?? "$");
|
|
83
|
+
const decimals = $derived(displayMeta?.decimals ?? 2);
|
|
84
|
+
|
|
85
|
+
// The display value is reactive to amount (which is in storage currency)
|
|
86
|
+
const displayValue = $derived.by(() => {
|
|
87
|
+
let val = amount ?? 0;
|
|
88
|
+
|
|
89
|
+
// If storage !== display, convert for display
|
|
90
|
+
if (
|
|
91
|
+
resolvedStorageCurrency !== resolvedDisplayCurrency &&
|
|
92
|
+
currencyStore.canConvert
|
|
93
|
+
) {
|
|
94
|
+
const minorAmount =
|
|
95
|
+
unit === "major"
|
|
96
|
+
? toMinorUnit(val, resolvedStorageCurrency)
|
|
97
|
+
: val;
|
|
98
|
+
const convertedMinor = currencyStore.convertAmount(
|
|
99
|
+
Number(minorAmount),
|
|
100
|
+
resolvedStorageCurrency,
|
|
101
|
+
resolvedDisplayCurrency,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (decimals === 0) return String(Math.round(convertedMinor));
|
|
105
|
+
return (convertedMinor / Math.pow(10, decimals)).toFixed(decimals);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Standard display (no conversion or no rates)
|
|
109
|
+
if (unit === "major") {
|
|
110
|
+
return Number(val).toFixed(decimals);
|
|
111
|
+
}
|
|
112
|
+
if (decimals === 0) return String(val);
|
|
113
|
+
const divisor = Math.pow(10, decimals);
|
|
114
|
+
return (Number(val) / divisor).toFixed(decimals);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function handleInput(e: Event) {
|
|
118
|
+
const target = e.target as HTMLInputElement;
|
|
119
|
+
const raw = target.value.replace(/[^0-9.,-]/g, "");
|
|
120
|
+
|
|
121
|
+
if (!raw) {
|
|
122
|
+
amount = 0;
|
|
123
|
+
oninput?.(0);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// String-based parsing: split on decimal point
|
|
128
|
+
const parts = raw.split(".");
|
|
129
|
+
const integerPart = parts[0] || "0";
|
|
130
|
+
|
|
131
|
+
// Pad or truncate the fractional part to exactly `decimals` digits
|
|
132
|
+
let fractionalPart = (parts[1] || "").slice(0, decimals);
|
|
133
|
+
fractionalPart = fractionalPart.padEnd(decimals, "0");
|
|
134
|
+
|
|
135
|
+
// Combine as integer minor units (in display currency)
|
|
136
|
+
const combined =
|
|
137
|
+
decimals === 0 ? integerPart : integerPart + fractionalPart;
|
|
138
|
+
|
|
139
|
+
let displayCents = parseInt(combined, 10);
|
|
140
|
+
if (isNaN(displayCents)) return;
|
|
141
|
+
|
|
142
|
+
// Convert back to storage currency
|
|
143
|
+
let storageCents = displayCents;
|
|
144
|
+
if (
|
|
145
|
+
resolvedStorageCurrency !== resolvedDisplayCurrency &&
|
|
146
|
+
currencyStore.canConvert
|
|
147
|
+
) {
|
|
148
|
+
storageCents = currencyStore.convertAmount(
|
|
149
|
+
displayCents,
|
|
150
|
+
resolvedDisplayCurrency,
|
|
151
|
+
resolvedStorageCurrency,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Apply constraints in storage currency minor units
|
|
156
|
+
let finalStorageCents = storageCents;
|
|
157
|
+
|
|
158
|
+
const storageDecimals =
|
|
159
|
+
currencyStore.get(resolvedStorageCurrency)?.decimals ?? 2;
|
|
160
|
+
const minCents =
|
|
161
|
+
min !== undefined
|
|
162
|
+
? unit === "major"
|
|
163
|
+
? toMinorUnit(min, resolvedStorageCurrency)
|
|
164
|
+
: min
|
|
165
|
+
: undefined;
|
|
166
|
+
const maxCents =
|
|
167
|
+
max !== undefined
|
|
168
|
+
? unit === "major"
|
|
169
|
+
? toMinorUnit(max, resolvedStorageCurrency)
|
|
170
|
+
: max
|
|
171
|
+
: undefined;
|
|
172
|
+
|
|
173
|
+
if (minCents !== undefined)
|
|
174
|
+
finalStorageCents = Math.max(finalStorageCents, minCents);
|
|
175
|
+
if (maxCents !== undefined)
|
|
176
|
+
finalStorageCents = Math.min(finalStorageCents, maxCents);
|
|
177
|
+
|
|
178
|
+
if (unit === "major") {
|
|
179
|
+
const majorValue =
|
|
180
|
+
finalStorageCents / Math.pow(10, storageDecimals);
|
|
181
|
+
amount = majorValue;
|
|
182
|
+
oninput?.(majorValue);
|
|
183
|
+
} else {
|
|
184
|
+
amount = finalStorageCents;
|
|
185
|
+
oninput?.(finalStorageCents);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
</script>
|
|
189
|
+
|
|
190
|
+
<label
|
|
191
|
+
class="input input-bordered flex items-center gap-1"
|
|
192
|
+
class:input-disabled={disabled}
|
|
193
|
+
>
|
|
194
|
+
<span class="text-base-content/50 font-medium select-none">{symbol}</span>
|
|
195
|
+
<input
|
|
196
|
+
type="text"
|
|
197
|
+
inputmode="decimal"
|
|
198
|
+
class="grow bg-transparent outline-none tabular-nums"
|
|
199
|
+
value={displayValue}
|
|
200
|
+
{placeholder}
|
|
201
|
+
{disabled}
|
|
202
|
+
oninput={handleInput}
|
|
203
|
+
aria-label={`Amount in ${resolvedDisplayCurrency}`}
|
|
204
|
+
/>
|
|
205
|
+
<span class="text-base-content/30 text-xs font-mono select-none"
|
|
206
|
+
>{resolvedDisplayCurrency}</span
|
|
207
|
+
>
|
|
208
|
+
</label>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ConfigStore,
|
|
3
|
+
createConfigStore,
|
|
4
|
+
getCurrencyStore,
|
|
5
|
+
} from "@rune-lab/kernel";
|
|
6
|
+
import type { Currency } from "@rune-lab/kernel";
|
|
7
|
+
import { type DineroCurrency, registerCurrency } from "@rune-lab/money";
|
|
8
|
+
import type { ExchangeRateStore } from "./exchange-rate.svelte.ts";
|
|
9
|
+
|
|
10
|
+
export type { Currency };
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Helper to build a minimal Dinero definition from Currency metadata.
|
|
14
|
+
* Assumes base 10 (standard decimal) for auto-registration.
|
|
15
|
+
*/
|
|
16
|
+
function buildDineroDef(meta: Currency) {
|
|
17
|
+
return {
|
|
18
|
+
code: meta.code,
|
|
19
|
+
base: 10,
|
|
20
|
+
exponent: meta.decimals,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const CURRENCIES: Currency[] = [
|
|
25
|
+
{ code: "USD", symbol: "$", decimals: 2 },
|
|
26
|
+
{ code: "EUR", symbol: "€", decimals: 2 },
|
|
27
|
+
{ code: "GBP", symbol: "£", decimals: 2 },
|
|
28
|
+
{ code: "MXN", symbol: "$", decimals: 2 },
|
|
29
|
+
{ code: "CAD", symbol: "C$", decimals: 2 },
|
|
30
|
+
{ code: "BRL", symbol: "R$", decimals: 2 },
|
|
31
|
+
{ code: "INR", symbol: "₹", decimals: 2 },
|
|
32
|
+
{ code: "CNY", symbol: "¥", decimals: 2 },
|
|
33
|
+
{ code: "JPY", symbol: "¥", decimals: 0 },
|
|
34
|
+
{ code: "KRW", symbol: "₩", decimals: 0 },
|
|
35
|
+
{ code: "AED", symbol: "د.إ", decimals: 2 },
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
const baseStore: ConfigStore<Currency> = createConfigStore<Currency>({
|
|
39
|
+
items: CURRENCIES,
|
|
40
|
+
storageKey: "currency",
|
|
41
|
+
displayName: "Currency",
|
|
42
|
+
idKey: "code",
|
|
43
|
+
icon: "💰",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// We hold a reference to exchangeRateStore if configured later
|
|
47
|
+
let _exchangeRateStore: ExchangeRateStore | undefined;
|
|
48
|
+
export function setExchangeRateStore(store: ExchangeRateStore) {
|
|
49
|
+
_exchangeRateStore = store;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extension: Atomic currency registration.
|
|
54
|
+
* Updates both the Dinero registry (core) and the reactive store (UI).
|
|
55
|
+
*
|
|
56
|
+
* @remarks Custom currencies with non-decimal base systems must use
|
|
57
|
+
* registerCurrency() from @rune-lab/kernel explicitly before addItems().
|
|
58
|
+
*/
|
|
59
|
+
function addCurrency(meta: Currency, dineroDef?: unknown) {
|
|
60
|
+
const def = dineroDef || buildDineroDef(meta);
|
|
61
|
+
registerCurrency(meta.code, def as DineroCurrency<number>);
|
|
62
|
+
baseStore.addItems([meta]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Converts an amount from one currency to another.
|
|
67
|
+
* Defaults to converting to the current store currency.
|
|
68
|
+
*/
|
|
69
|
+
function convertAmount(
|
|
70
|
+
amount: number,
|
|
71
|
+
fromCode: string,
|
|
72
|
+
toCode?: string,
|
|
73
|
+
): number {
|
|
74
|
+
const target = toCode ?? String((baseStore as ConfigStore<Currency>).current);
|
|
75
|
+
if (fromCode === target) return amount;
|
|
76
|
+
if (!_exchangeRateStore || !_exchangeRateStore.hasRates) return amount;
|
|
77
|
+
|
|
78
|
+
return _exchangeRateStore.convertAmount(amount, fromCode, target);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const currencyStore: ConfigStore<Currency> & {
|
|
82
|
+
addCurrency: typeof addCurrency;
|
|
83
|
+
convertAmount: typeof convertAmount;
|
|
84
|
+
readonly canConvert: boolean;
|
|
85
|
+
} = Object.assign(baseStore, {
|
|
86
|
+
addCurrency,
|
|
87
|
+
convertAmount,
|
|
88
|
+
get canConvert() {
|
|
89
|
+
return !!_exchangeRateStore?.hasRates;
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export type CurrencyStore = typeof currencyStore;
|
|
94
|
+
|
|
95
|
+
export { getCurrencyStore };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { currencyStore } from "./currency.svelte.ts";
|
|
3
|
+
|
|
4
|
+
describe("Currency Store", () => {
|
|
5
|
+
it("should have prototype methods from ConfigStore", () => {
|
|
6
|
+
// These come from the ConfigStore class prototype
|
|
7
|
+
expect(typeof currencyStore.get).toBe("function");
|
|
8
|
+
expect(typeof currencyStore.set).toBe("function");
|
|
9
|
+
expect(typeof currencyStore.getProp).toBe("function");
|
|
10
|
+
expect(typeof currencyStore.addItems).toBe("function");
|
|
11
|
+
|
|
12
|
+
// These are instance properties
|
|
13
|
+
expect(currencyStore.current).toBeDefined();
|
|
14
|
+
expect(Array.isArray(currencyStore.available)).toBe(true);
|
|
15
|
+
|
|
16
|
+
// This is the extension method
|
|
17
|
+
expect(typeof currencyStore.addCurrency).toBe("function");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should correctly use prototype methods", () => {
|
|
21
|
+
const usd = currencyStore.get("USD" as never);
|
|
22
|
+
|
|
23
|
+
expect(usd).toBeDefined();
|
|
24
|
+
expect(usd?.code).toBe("USD");
|
|
25
|
+
expect(usd?.symbol).toBe("$");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should add a new currency and keep methods", () => {
|
|
29
|
+
currencyStore.addCurrency({ code: "BTC", symbol: "₿", decimals: 8 });
|
|
30
|
+
|
|
31
|
+
const btc = currencyStore.get("BTC" as never);
|
|
32
|
+
expect(btc).toBeDefined();
|
|
33
|
+
expect(btc?.code).toBe("BTC");
|
|
34
|
+
expect(btc?.decimals).toBe(8);
|
|
35
|
+
|
|
36
|
+
// Methods still exist after interaction
|
|
37
|
+
expect(typeof currencyStore.get).toBe("function");
|
|
38
|
+
expect(typeof currencyStore.set).toBe("function");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// sdk/state/src/exchange-rate.svelte.ts
|
|
2
|
+
|
|
3
|
+
import { getContext } from "svelte";
|
|
4
|
+
import {
|
|
5
|
+
convertMoney,
|
|
6
|
+
createMoney,
|
|
7
|
+
CURRENCY_MAP,
|
|
8
|
+
directConversion,
|
|
9
|
+
inverseConversion,
|
|
10
|
+
type RateMap,
|
|
11
|
+
resolveRate,
|
|
12
|
+
type ScaledRate,
|
|
13
|
+
scaledRate,
|
|
14
|
+
toMoneySnapshot,
|
|
15
|
+
triangularConversion,
|
|
16
|
+
} from "@rune-lab/money";
|
|
17
|
+
import { RUNE_LAB_CONTEXT } from "@rune-lab/kernel";
|
|
18
|
+
|
|
19
|
+
export class ExchangeRateStore {
|
|
20
|
+
#rates = $state<RateMap>({});
|
|
21
|
+
#baseCurrency = $state<string>("USD");
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The current exchange rates relative to the base currency.
|
|
25
|
+
*/
|
|
26
|
+
get rates(): RateMap {
|
|
27
|
+
return this.#rates;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The base currency all rates are relative to.
|
|
32
|
+
*/
|
|
33
|
+
get baseCurrency(): string {
|
|
34
|
+
return this.#baseCurrency;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns true if exchange rates have been loaded.
|
|
39
|
+
*/
|
|
40
|
+
get hasRates(): boolean {
|
|
41
|
+
return Object.keys(this.#rates).length > 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Set exchange rates from a human-readable float map.
|
|
46
|
+
* @param base - The base currency code (e.g., "USD")
|
|
47
|
+
* @param rawRates - A map of currency codes to float rates (e.g., { MXN: 17.23 })
|
|
48
|
+
*/
|
|
49
|
+
setRates(base: string, rawRates: Record<string, number>) {
|
|
50
|
+
const nextRates: RateMap = {};
|
|
51
|
+
for (const [code, rate] of Object.entries(rawRates)) {
|
|
52
|
+
nextRates[code] = scaledRate(rate, 4);
|
|
53
|
+
}
|
|
54
|
+
this.#baseCurrency = base;
|
|
55
|
+
this.#rates = nextRates;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Directly set pre-scaled rates.
|
|
60
|
+
*/
|
|
61
|
+
setScaledRates(base: string, rates: RateMap) {
|
|
62
|
+
this.#baseCurrency = base;
|
|
63
|
+
this.#rates = rates;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolves the exchange rate between two currencies.
|
|
68
|
+
* Returns a ScaledRate representing how many units of toCode are in 1 unit of fromCode.
|
|
69
|
+
*/
|
|
70
|
+
getRate(fromCode: string, toCode: string): ScaledRate | number | undefined {
|
|
71
|
+
if (fromCode === toCode) return 1;
|
|
72
|
+
|
|
73
|
+
const fromCurrency = CURRENCY_MAP[fromCode];
|
|
74
|
+
const toCurrency = CURRENCY_MAP[toCode];
|
|
75
|
+
if (!fromCurrency || !toCurrency) return undefined;
|
|
76
|
+
|
|
77
|
+
if (!this.hasRates) return undefined;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// 1 unit of fromCode (major unit)
|
|
81
|
+
const oneUnit = Math.pow(
|
|
82
|
+
Array.isArray(fromCurrency.base)
|
|
83
|
+
? fromCurrency.base[0]
|
|
84
|
+
: fromCurrency.base,
|
|
85
|
+
fromCurrency.exponent,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const currentRates = this.#rates;
|
|
89
|
+
const currentBase = this.#baseCurrency;
|
|
90
|
+
|
|
91
|
+
const sourceMoney = createMoney(oneUnit, fromCode);
|
|
92
|
+
|
|
93
|
+
let targetMoney;
|
|
94
|
+
if (fromCode === currentBase) {
|
|
95
|
+
// Direct: base → target
|
|
96
|
+
targetMoney = convertMoney(sourceMoney, toCode, currentRates);
|
|
97
|
+
} else if (toCode === currentBase) {
|
|
98
|
+
// Inverse: target → base
|
|
99
|
+
const rateToBase = currentRates[fromCode];
|
|
100
|
+
if (!rateToBase) return undefined;
|
|
101
|
+
const r = resolveRate(rateToBase);
|
|
102
|
+
targetMoney = convertMoney(sourceMoney, toCode, {
|
|
103
|
+
[toCode]: scaledRate(1 / r, 6),
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
// Triangular: source → base → target
|
|
107
|
+
const rateToBase = currentRates[fromCode];
|
|
108
|
+
const rateToTarget = currentRates[toCode];
|
|
109
|
+
if (!rateToBase || !rateToTarget) return undefined;
|
|
110
|
+
|
|
111
|
+
const r1 = resolveRate(rateToBase);
|
|
112
|
+
const r2 = resolveRate(rateToTarget);
|
|
113
|
+
|
|
114
|
+
targetMoney = convertMoney(sourceMoney, toCode, {
|
|
115
|
+
[toCode]: scaledRate(r2 / r1, 6),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const snapshot = toMoneySnapshot(targetMoney);
|
|
120
|
+
return {
|
|
121
|
+
amount: snapshot.amount,
|
|
122
|
+
scale: snapshot.scale,
|
|
123
|
+
};
|
|
124
|
+
} catch (_err) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Internal conversion logic that handles triangulation through base currency.
|
|
131
|
+
* Delegates all math to ConversionStrategies (directConversion, inverseConversion, triangularConversion).
|
|
132
|
+
*/
|
|
133
|
+
convertAmount(amount: number, fromCode: string, toCode: string): number {
|
|
134
|
+
if (fromCode === toCode) return amount;
|
|
135
|
+
if (!this.hasRates) return amount;
|
|
136
|
+
|
|
137
|
+
// Direct: Base → Target
|
|
138
|
+
if (fromCode === this.#baseCurrency) {
|
|
139
|
+
const rateToTarget = this.#rates[toCode];
|
|
140
|
+
if (!rateToTarget) return amount;
|
|
141
|
+
return directConversion(amount, resolveRate(rateToTarget));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Inverse: Target → Base
|
|
145
|
+
if (toCode === this.#baseCurrency) {
|
|
146
|
+
const rateFromSource = this.#rates[fromCode];
|
|
147
|
+
if (!rateFromSource) return amount;
|
|
148
|
+
return inverseConversion(amount, resolveRate(rateFromSource));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Triangulation: From → Base → To
|
|
152
|
+
const rateFromSource = this.#rates[fromCode];
|
|
153
|
+
const rateToTarget = this.#rates[toCode];
|
|
154
|
+
if (!rateFromSource || !rateToTarget) return amount;
|
|
155
|
+
|
|
156
|
+
return triangularConversion(
|
|
157
|
+
amount,
|
|
158
|
+
resolveRate(rateFromSource),
|
|
159
|
+
resolveRate(rateToTarget),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Factory to create the ExchangeRateStore.
|
|
166
|
+
*/
|
|
167
|
+
export function createExchangeRateStore(): ExchangeRateStore {
|
|
168
|
+
return new ExchangeRateStore();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Consumer to retrieve the ExchangeRateStore from context.
|
|
173
|
+
*/
|
|
174
|
+
export function getExchangeRateStore(): ExchangeRateStore {
|
|
175
|
+
return getContext<ExchangeRateStore>(RUNE_LAB_CONTEXT.exchangeRate);
|
|
176
|
+
}
|