@toa.io/extensions.configuration 1.0.0-alpha.273 → 1.0.0-alpha.274
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/CHANGELOG.md +202 -0
- package/components/configuration.values/manifest.toa.yaml +158 -0
- package/components/configuration.values/source/create.ts +56 -0
- package/components/configuration.values/source/fetch.ts +24 -0
- package/components/configuration.values/source/get.ts +33 -0
- package/components/configuration.values/source/lib/map.test.ts +34 -0
- package/components/configuration.values/source/lib/map.ts +34 -0
- package/components/configuration.values/source/lib/resolve.ts +54 -0
- package/components/configuration.values/source/list.ts +19 -0
- package/components/configuration.values/tsconfig.json +9 -0
- package/cucumber.js +8 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/index.html +6 -0
- package/features/steps/UI.ts +88 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/ui.feature +85 -0
- package/package.json +12 -5
- package/readme.md +150 -34
- package/schemas/annotation.cos.yaml +27 -2
- package/source/Aspect.test.ts +84 -8
- package/source/Aspect.ts +59 -5
- package/source/Client.test.ts +104 -0
- package/source/Client.ts +258 -0
- package/source/Composition.ts +57 -0
- package/source/Factory.ts +28 -5
- package/source/Secret.test.ts +29 -0
- package/source/Secret.ts +29 -0
- package/source/UI.ts +197 -0
- package/source/configuration.test.ts +84 -52
- package/source/configuration.ts +28 -9
- package/source/const.ts +17 -0
- package/source/deployment.test.ts +80 -1
- package/source/deployment.ts +73 -23
- package/source/epoch.test.ts +26 -0
- package/source/epoch.ts +21 -0
- package/source/index.ts +2 -1
- package/transpiled/Aspect.d.ts +18 -4
- package/transpiled/Aspect.js +47 -3
- package/transpiled/Aspect.js.map +1 -1
- package/transpiled/Aspect.test.js +59 -9
- package/transpiled/Aspect.test.js.map +1 -1
- package/transpiled/Client.d.ts +61 -0
- package/transpiled/Client.js +173 -0
- package/transpiled/Client.js.map +1 -0
- package/transpiled/Client.test.d.ts +1 -0
- package/transpiled/Client.test.js +77 -0
- package/transpiled/Client.test.js.map +1 -0
- package/transpiled/Composition.d.ts +14 -0
- package/transpiled/Composition.js +43 -0
- package/transpiled/Composition.js.map +1 -0
- package/transpiled/Factory.d.ts +8 -2
- package/transpiled/Factory.js +17 -2
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Secret.d.ts +13 -0
- package/transpiled/Secret.js +26 -0
- package/transpiled/Secret.js.map +1 -0
- package/transpiled/Secret.test.d.ts +1 -0
- package/transpiled/Secret.test.js +26 -0
- package/transpiled/Secret.test.js.map +1 -0
- package/transpiled/UI.d.ts +25 -0
- package/transpiled/UI.js +186 -0
- package/transpiled/UI.js.map +1 -0
- package/transpiled/configuration.d.ts +7 -2
- package/transpiled/configuration.js +30 -12
- package/transpiled/configuration.js.map +1 -1
- package/transpiled/configuration.test.js +77 -51
- package/transpiled/configuration.test.js.map +1 -1
- package/transpiled/const.d.ts +11 -0
- package/transpiled/const.js +14 -0
- package/transpiled/const.js.map +1 -0
- package/transpiled/deployment.d.ts +10 -4
- package/transpiled/deployment.js +57 -20
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/deployment.test.js +60 -0
- package/transpiled/deployment.test.js.map +1 -1
- package/transpiled/epoch.d.ts +2 -0
- package/transpiled/epoch.js +21 -0
- package/transpiled/epoch.js.map +1 -0
- package/transpiled/epoch.test.d.ts +1 -0
- package/transpiled/epoch.test.js +22 -0
- package/transpiled/epoch.test.js.map +1 -0
- package/transpiled/index.d.ts +2 -1
- package/transpiled/index.js +4 -1
- package/transpiled/index.js.map +1 -1
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/ui/.agents/skills/antcn/SKILL.md +91 -0
- package/ui/.agents/skills/antcn/references/analytics.md +78 -0
- package/ui/.agents/skills/antcn/references/bridge.md +41 -0
- package/ui/.agents/skills/antcn/references/clipboard.md +23 -0
- package/ui/.agents/skills/antcn/references/dismissable.md +75 -0
- package/ui/.agents/skills/antcn/references/dropdown.md +153 -0
- package/ui/.agents/skills/antcn/references/fragments.md +29 -0
- package/ui/.agents/skills/antcn/references/fullscreen.md +157 -0
- package/ui/.agents/skills/antcn/references/geo.md +27 -0
- package/ui/.agents/skills/antcn/references/globals.md +31 -0
- package/ui/.agents/skills/antcn/references/grammatical-form.md +69 -0
- package/ui/.agents/skills/antcn/references/hints.md +44 -0
- package/ui/.agents/skills/antcn/references/hold.md +34 -0
- package/ui/.agents/skills/antcn/references/iam.md +117 -0
- package/ui/.agents/skills/antcn/references/input-form.md +34 -0
- package/ui/.agents/skills/antcn/references/locale.md +66 -0
- package/ui/.agents/skills/antcn/references/net.md +33 -0
- package/ui/.agents/skills/antcn/references/panel.md +44 -0
- package/ui/.agents/skills/antcn/references/passkeys.md +27 -0
- package/ui/.agents/skills/antcn/references/payments.md +99 -0
- package/ui/.agents/skills/antcn/references/realtime.md +69 -0
- package/ui/.agents/skills/antcn/references/scrollable.md +39 -0
- package/ui/.agents/skills/antcn/references/selector.md +71 -0
- package/ui/.agents/skills/antcn/references/shake.md +36 -0
- package/ui/.agents/skills/antcn/references/share.md +28 -0
- package/ui/.agents/skills/antcn/references/shell.md +139 -0
- package/ui/.agents/skills/antcn/references/stack.md +53 -0
- package/ui/.agents/skills/antcn/references/system.md +149 -0
- package/ui/.agents/skills/antcn/references/taptap.md +28 -0
- package/ui/.agents/skills/antcn/references/tools.md +68 -0
- package/ui/.agents/skills/antcn/references/transmission.md +96 -0
- package/ui/.agents/skills/shadcn-svelte/SKILL.md +227 -0
- package/ui/.agents/skills/shadcn-svelte/agents/openai.yml +5 -0
- package/ui/.agents/skills/shadcn-svelte/assets/shadcn-svelte-small.png +0 -0
- package/ui/.agents/skills/shadcn-svelte/assets/shadcn-svelte.png +0 -0
- package/ui/.agents/skills/shadcn-svelte/cli.md +166 -0
- package/ui/.agents/skills/shadcn-svelte/customization.md +213 -0
- package/ui/.agents/skills/shadcn-svelte/evals/evals.json +47 -0
- package/ui/.agents/skills/shadcn-svelte/rules/composition.md +244 -0
- package/ui/.agents/skills/shadcn-svelte/rules/forms.md +234 -0
- package/ui/.agents/skills/shadcn-svelte/rules/icons.md +107 -0
- package/ui/.agents/skills/shadcn-svelte/rules/styling.md +195 -0
- package/ui/.agents/skills/svas/SKILL.md +169 -0
- package/ui/.agents/skills/svas/references/patterns.md +95 -0
- package/ui/.agents/skills/svintl/SKILL.md +176 -0
- package/ui/.claude/settings.json +15 -0
- package/ui/.codex/hooks.json +15 -0
- package/ui/.cursor/hooks.json +11 -0
- package/ui/.env.example +1 -0
- package/ui/.prettierrc +9 -0
- package/ui/.vscode/extensions.json +7 -0
- package/ui/.vscode/settings.json +27 -0
- package/ui/AGENTS.md +51 -0
- package/ui/architecture.md +390 -0
- package/ui/components.json +20 -0
- package/ui/eslint.config.js +152 -0
- package/ui/features/base.ts +2 -0
- package/ui/features/boot.feature +5 -0
- package/ui/features/steps/authentication.ts +34 -0
- package/ui/features/steps/navigaton.ts +37 -0
- package/ui/features/steps/typing.ts +24 -0
- package/ui/package-lock.json +8173 -0
- package/ui/package.json +76 -0
- package/ui/playwright.config.ts +13 -0
- package/ui/skills-lock.json +30 -0
- package/ui/src/@/configuration/index.ts +1 -0
- package/ui/src/@/configuration/svc/create.ts +29 -0
- package/ui/src/@/configuration/svc/index.ts +3 -0
- package/ui/src/@/configuration/svc/net/Configuration.ts +18 -0
- package/ui/src/@/configuration/svc/net/index.ts +2 -0
- package/ui/src/@/configuration/svc/net/interface.ts +35 -0
- package/ui/src/@/configuration/svc/store.ts +14 -0
- package/ui/src/@/configuration/ui/Configurations.svelte +85 -0
- package/ui/src/@/configuration/ui/Configurations.ts +5 -0
- package/ui/src/@/configuration/ui/Create.svelte +64 -0
- package/ui/src/@/configuration/ui/Create.ts +7 -0
- package/ui/src/@/configuration/ui/Form.svelte +75 -0
- package/ui/src/@/configuration/ui/Form.ts +79 -0
- package/ui/src/@/configuration/ui/Row.svelte +44 -0
- package/ui/src/@/configuration/ui/Row.ts +9 -0
- package/ui/src/@/configuration/ui/Value.svelte +33 -0
- package/ui/src/@/configuration/ui/Value.ts +6 -0
- package/ui/src/@/configuration/ui/index.ts +9 -0
- package/ui/src/@/configuration/ui/intl/built.js +38 -0
- package/ui/src/@/configuration/ui/intl/context.yaml +51 -0
- package/ui/src/@/configuration/ui/intl/en-US.yaml +21 -0
- package/ui/src/@/configuration/ui/intl/index.ts +9 -0
- package/ui/src/@/configuration/ui/intl/types.ts +31 -0
- package/ui/src/@/configuration/ui/read.test.ts +75 -0
- package/ui/src/@/configuration/ui/read.ts +102 -0
- package/ui/src/@/configuration/ui/ui.test.ts +69 -0
- package/ui/src/@/configuration/ui/ui.ts +115 -0
- package/ui/src/@/iam/index.ts +1 -0
- package/ui/src/@/iam/rc.ts +22 -0
- package/ui/src/@/iam/svc/authenticated.ts +17 -0
- package/ui/src/@/iam/svc/basic.ts +23 -0
- package/ui/src/@/iam/svc/index.ts +10 -0
- package/ui/src/@/iam/svc/logout.ts +5 -0
- package/ui/src/@/iam/svc/named.ts +16 -0
- package/ui/src/@/iam/svc/net/Echo.ts +9 -0
- package/ui/src/@/iam/svc/net/basic/index.ts +1 -0
- package/ui/src/@/iam/svc/net/basic/interface.ts +15 -0
- package/ui/src/@/iam/svc/net/federation/index.ts +1 -0
- package/ui/src/@/iam/svc/net/federation/interface.ts +12 -0
- package/ui/src/@/iam/svc/net/index.ts +6 -0
- package/ui/src/@/iam/svc/net/interface.ts +17 -0
- package/ui/src/@/iam/svc/net/otp/index.ts +1 -0
- package/ui/src/@/iam/svc/net/otp/interface.ts +14 -0
- package/ui/src/@/iam/svc/net/passkeys/index.ts +1 -0
- package/ui/src/@/iam/svc/net/passkeys/interface.ts +12 -0
- package/ui/src/@/iam/svc/oidc/apple.ts +47 -0
- package/ui/src/@/iam/svc/oidc/authenticate.ts +62 -0
- package/ui/src/@/iam/svc/oidc/google.ts +45 -0
- package/ui/src/@/iam/svc/oidc/hello.ts +82 -0
- package/ui/src/@/iam/svc/oidc/index.ts +3 -0
- package/ui/src/@/iam/svc/oidc/nonce.ts +5 -0
- package/ui/src/@/iam/svc/oidc/providers.ts +28 -0
- package/ui/src/@/iam/svc/oidc/standard.ts +28 -0
- package/ui/src/@/iam/svc/otp.ts +18 -0
- package/ui/src/@/iam/svc/passkeys/create.ts +20 -0
- package/ui/src/@/iam/svc/passkeys/index.ts +2 -0
- package/ui/src/@/iam/svc/passkeys/login.ts +21 -0
- package/ui/src/@/iam/svc/store.ts +36 -0
- package/ui/src/@/iam/svc/sync.ts +18 -0
- package/ui/src/@/iam/svc/transfer.ts +11 -0
- package/ui/src/@/iam/svc/update.ts +8 -0
- package/ui/src/@/iam/ui/AccountLike.ts +4 -0
- package/ui/src/@/iam/ui/Authenticated.svelte +37 -0
- package/ui/src/@/iam/ui/Authenticated.ts +10 -0
- package/ui/src/@/iam/ui/Authentication.svelte +50 -0
- package/ui/src/@/iam/ui/Authentication.ts +9 -0
- package/ui/src/@/iam/ui/Authorized.svelte +32 -0
- package/ui/src/@/iam/ui/Authorized.ts +65 -0
- package/ui/src/@/iam/ui/Card.svelte +14 -0
- package/ui/src/@/iam/ui/Card.ts +7 -0
- package/ui/src/@/iam/ui/Refresh.svelte +39 -0
- package/ui/src/@/iam/ui/credentials/Credentials.svelte +26 -0
- package/ui/src/@/iam/ui/credentials/Credentials.ts +5 -0
- package/ui/src/@/iam/ui/credentials/Provider.svelte +24 -0
- package/ui/src/@/iam/ui/credentials/Provider.ts +5 -0
- package/ui/src/@/iam/ui/credentials/index.ts +1 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Key.svelte +42 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Key.ts +29 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Manager.svelte +83 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Manager.ts +5 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Passkeys.svelte +39 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Summary.svelte +27 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Summary.ts +5 -0
- package/ui/src/@/iam/ui/credentials/passkeys/index.ts +1 -0
- package/ui/src/@/iam/ui/credentials/providers.ts +15 -0
- package/ui/src/@/iam/ui/credentials/transfer/Qr.svelte +24 -0
- package/ui/src/@/iam/ui/credentials/transfer/Qr.ts +3 -0
- package/ui/src/@/iam/ui/credentials/transfer/Transfer.svelte +48 -0
- package/ui/src/@/iam/ui/credentials/transfer/Transfer.ts +5 -0
- package/ui/src/@/iam/ui/credentials/transfer/index.ts +1 -0
- package/ui/src/@/iam/ui/index.ts +3 -0
- package/ui/src/@/iam/ui/intl/built.js +79 -0
- package/ui/src/@/iam/ui/intl/context.yaml +91 -0
- package/ui/src/@/iam/ui/intl/en-US.yaml +64 -0
- package/ui/src/@/iam/ui/intl/index.ts +10 -0
- package/ui/src/@/iam/ui/intl/types.ts +70 -0
- package/ui/src/@/iam/ui/oidc/Authentication.svelte +16 -0
- package/ui/src/@/iam/ui/oidc/Authentication.ts +5 -0
- package/ui/src/@/iam/ui/oidc/Button.svelte +36 -0
- package/ui/src/@/iam/ui/oidc/Button.ts +11 -0
- package/ui/src/@/iam/ui/oidc/Refresh.svelte +16 -0
- package/ui/src/@/iam/ui/oidc/icons/Apple.svelte +19 -0
- package/ui/src/@/iam/ui/oidc/icons/Google.svelte +31 -0
- package/ui/src/@/iam/ui/oidc/icons/index.ts +11 -0
- package/ui/src/@/iam/ui/oidc/index.ts +2 -0
- package/ui/src/@/iam/ui/passkey/Authentication.svelte +64 -0
- package/ui/src/@/iam/ui/passkey/Authentication.ts +8 -0
- package/ui/src/@/iam/ui/passkey/Create.svelte +53 -0
- package/ui/src/@/iam/ui/passkey/Create.ts +7 -0
- package/ui/src/@/iam/ui/passkey/Login.svelte +29 -0
- package/ui/src/@/iam/ui/passkey/Refresh.svelte +29 -0
- package/ui/src/@/iam/ui/passkey/index.ts +2 -0
- package/ui/src/@/iam/ui/password/Authentication.svelte +19 -0
- package/ui/src/@/iam/ui/password/Authentication.ts +8 -0
- package/ui/src/@/iam/ui/password/Form.svelte +151 -0
- package/ui/src/@/iam/ui/password/Form.ts +6 -0
- package/ui/src/@/iam/ui/password/Password.svelte +39 -0
- package/ui/src/@/iam/ui/password/Password.ts +6 -0
- package/ui/src/@/iam/ui/password/Refresh.svelte +5 -0
- package/ui/src/@/iam/ui/password/index.ts +2 -0
- package/ui/src/@/net/index.ts +1 -0
- package/ui/src/@/net/svc/index.ts +3 -0
- package/ui/src/@/net/svc/origin.ts +8 -0
- package/ui/src/@/net/svc/query.test.ts +45 -0
- package/ui/src/@/net/svc/query.ts +25 -0
- package/ui/src/@/passkeys/index.ts +1 -0
- package/ui/src/@/passkeys/svc/add.ts +15 -0
- package/ui/src/@/passkeys/svc/create.ts +58 -0
- package/ui/src/@/passkeys/svc/credentials/convert.ts +32 -0
- package/ui/src/@/passkeys/svc/credentials/create.ts +34 -0
- package/ui/src/@/passkeys/svc/credentials/get.ts +14 -0
- package/ui/src/@/passkeys/svc/credentials/index.ts +3 -0
- package/ui/src/@/passkeys/svc/credentials/key.ts +10 -0
- package/ui/src/@/passkeys/svc/get.ts +10 -0
- package/ui/src/@/passkeys/svc/index.ts +6 -0
- package/ui/src/@/passkeys/svc/net/CreationResponse.ts +15 -0
- package/ui/src/@/passkeys/svc/net/Passkey.ts +8 -0
- package/ui/src/@/passkeys/svc/net/RequestResponse.ts +12 -0
- package/ui/src/@/passkeys/svc/net/challenges/Options.ts +21 -0
- package/ui/src/@/passkeys/svc/net/challenges/index.ts +2 -0
- package/ui/src/@/passkeys/svc/net/challenges/interface.ts +16 -0
- package/ui/src/@/passkeys/svc/net/index.ts +4 -0
- package/ui/src/@/passkeys/svc/net/interface.ts +29 -0
- package/ui/src/@/passkeys/svc/remove.ts +13 -0
- package/ui/src/@/passkeys/svc/request.ts +50 -0
- package/ui/src/@/passkeys/svc/store.ts +13 -0
- package/ui/src/@/passkeys/svc/supported.ts +1 -0
- package/ui/src/@/passkeys/ui/aaguid.json +150 -0
- package/ui/src/@/realtime/events.ts +1 -0
- package/ui/src/@/realtime/index.ts +1 -0
- package/ui/src/@/realtime/rc.ts +13 -0
- package/ui/src/@/realtime/svc/connect.ts +67 -0
- package/ui/src/@/realtime/svc/events.ts +17 -0
- package/ui/src/@/realtime/svc/index.ts +5 -0
- package/ui/src/@/realtime/svc/net/Message.ts +6 -0
- package/ui/src/@/realtime/svc/net/index.ts +2 -0
- package/ui/src/@/realtime/svc/net/interface.ts +13 -0
- package/ui/src/@/realtime/svc/reset.ts +5 -0
- package/ui/src/@/realtime/svc/status.ts +12 -0
- package/ui/src/@/realtime/svc/store.ts +30 -0
- package/ui/src/@/realtime/svc/time.ts +15 -0
- package/ui/src/app.d.ts +20 -0
- package/ui/src/app.html +13 -0
- package/ui/src/config/index.ts +70 -0
- package/ui/src/lib/assets/favicon.svg +1 -0
- package/ui/src/lib/components/clipboard/Clipboard.svelte +63 -0
- package/ui/src/lib/components/clipboard/Clipboard.ts +12 -0
- package/ui/src/lib/components/clipboard/index.ts +1 -0
- package/ui/src/lib/components/dismissable/Dismissable.svelte +81 -0
- package/ui/src/lib/components/dismissable/Dismissable.ts +7 -0
- package/ui/src/lib/components/dismissable/index.ts +1 -0
- package/ui/src/lib/components/dropdown/Back.svelte +14 -0
- package/ui/src/lib/components/dropdown/Back.ts +3 -0
- package/ui/src/lib/components/dropdown/Content.svelte +76 -0
- package/ui/src/lib/components/dropdown/Content.ts +13 -0
- package/ui/src/lib/components/dropdown/Context.ts +18 -0
- package/ui/src/lib/components/dropdown/Group.svelte +24 -0
- package/ui/src/lib/components/dropdown/Group.ts +8 -0
- package/ui/src/lib/components/dropdown/GroupContext.ts +15 -0
- package/ui/src/lib/components/dropdown/Item.svelte +31 -0
- package/ui/src/lib/components/dropdown/Item.ts +5 -0
- package/ui/src/lib/components/dropdown/Layer.svelte +11 -0
- package/ui/src/lib/components/dropdown/Layer.ts +7 -0
- package/ui/src/lib/components/dropdown/Root.svelte +88 -0
- package/ui/src/lib/components/dropdown/Root.ts +6 -0
- package/ui/src/lib/components/dropdown/Separator.svelte +13 -0
- package/ui/src/lib/components/dropdown/Separator.ts +5 -0
- package/ui/src/lib/components/dropdown/Trigger.svelte +33 -0
- package/ui/src/lib/components/dropdown/Trigger.ts +5 -0
- package/ui/src/lib/components/dropdown/index.ts +8 -0
- package/ui/src/lib/components/fullscreen/Fullscreen.svelte +97 -0
- package/ui/src/lib/components/fullscreen/Fullscreen.ts +18 -0
- package/ui/src/lib/components/fullscreen/Overlay.svelte +15 -0
- package/ui/src/lib/components/fullscreen/Overlay.ts +7 -0
- package/ui/src/lib/components/fullscreen/index.ts +2 -0
- package/ui/src/lib/components/hold/Hold.svelte +133 -0
- package/ui/src/lib/components/hold/Hold.ts +23 -0
- package/ui/src/lib/components/hold/index.ts +1 -0
- package/ui/src/lib/components/scrollable/Scrollable.svelte +49 -0
- package/ui/src/lib/components/scrollable/Scrollable.ts +117 -0
- package/ui/src/lib/components/scrollable/index.ts +2 -0
- package/ui/src/lib/components/shake/Shake.svelte +62 -0
- package/ui/src/lib/components/shake/Shake.ts +7 -0
- package/ui/src/lib/components/shake/index.ts +1 -0
- package/ui/src/lib/components/share/Share.svelte +58 -0
- package/ui/src/lib/components/share/Share.ts +9 -0
- package/ui/src/lib/components/share/index.ts +1 -0
- package/ui/src/lib/components/shell/Attention.svelte +11 -0
- package/ui/src/lib/components/shell/Attention.ts +6 -0
- package/ui/src/lib/components/shell/Header.svelte +18 -0
- package/ui/src/lib/components/shell/Header.ts +7 -0
- package/ui/src/lib/components/shell/Screen.svelte +28 -0
- package/ui/src/lib/components/shell/Screen.ts +11 -0
- package/ui/src/lib/components/shell/Sticky.svelte +23 -0
- package/ui/src/lib/components/shell/Sticky.ts +8 -0
- package/ui/src/lib/components/shell/Underlay.svelte +21 -0
- package/ui/src/lib/components/shell/Underlay.ts +6 -0
- package/ui/src/lib/components/shell/index.ts +6 -0
- package/ui/src/lib/components/shell/nav/Actions.svelte +31 -0
- package/ui/src/lib/components/shell/nav/Actions.ts +16 -0
- package/ui/src/lib/components/shell/nav/Back.svelte +20 -0
- package/ui/src/lib/components/shell/nav/Back.ts +9 -0
- package/ui/src/lib/components/shell/nav/Bar.svelte +24 -0
- package/ui/src/lib/components/shell/nav/Bar.ts +3 -0
- package/ui/src/lib/components/shell/nav/Button.svelte +64 -0
- package/ui/src/lib/components/shell/nav/Button.ts +7 -0
- package/ui/src/lib/components/shell/nav/Nav.svelte +62 -0
- package/ui/src/lib/components/shell/nav/Nav.ts +35 -0
- package/ui/src/lib/components/shell/nav/Return.svelte +29 -0
- package/ui/src/lib/components/shell/nav/Return.ts +15 -0
- package/ui/src/lib/components/shell/nav/Sections.svelte +58 -0
- package/ui/src/lib/components/shell/nav/Sections.ts +6 -0
- package/ui/src/lib/components/shell/nav/Toolbar.svelte +73 -0
- package/ui/src/lib/components/shell/nav/Toolbar.ts +3 -0
- package/ui/src/lib/components/shell/nav/history.ts +36 -0
- package/ui/src/lib/components/shell/nav/index.ts +5 -0
- package/ui/src/lib/components/shell/nav/store.ts +24 -0
- package/ui/src/lib/components/ui/alert/alert-action.svelte +20 -0
- package/ui/src/lib/components/ui/alert/alert-description.svelte +23 -0
- package/ui/src/lib/components/ui/alert/alert-title.svelte +23 -0
- package/ui/src/lib/components/ui/alert/alert.svelte +43 -0
- package/ui/src/lib/components/ui/alert/index.ts +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +27 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +27 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +32 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +23 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte +20 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/index.ts +40 -0
- package/ui/src/lib/components/ui/badge/badge.svelte +49 -0
- package/ui/src/lib/components/ui/badge/index.ts +2 -0
- package/ui/src/lib/components/ui/button/button.svelte +82 -0
- package/ui/src/lib/components/ui/button/index.ts +17 -0
- package/ui/src/lib/components/ui/button-group/button-group-separator.svelte +23 -0
- package/ui/src/lib/components/ui/button-group/button-group-text.svelte +28 -0
- package/ui/src/lib/components/ui/button-group/button-group.svelte +46 -0
- package/ui/src/lib/components/ui/button-group/index.ts +15 -0
- package/ui/src/lib/components/ui/card/card-action.svelte +23 -0
- package/ui/src/lib/components/ui/card/card-content.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-description.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-header.svelte +23 -0
- package/ui/src/lib/components/ui/card/card-title.svelte +20 -0
- package/ui/src/lib/components/ui/card/card.svelte +22 -0
- package/ui/src/lib/components/ui/card/index.ts +25 -0
- package/ui/src/lib/components/ui/checkbox/checkbox.svelte +39 -0
- package/ui/src/lib/components/ui/checkbox/index.ts +6 -0
- package/ui/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
- package/ui/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/ui/src/lib/components/ui/collapsible/collapsible.svelte +20 -0
- package/ui/src/lib/components/ui/collapsible/index.ts +13 -0
- package/ui/src/lib/components/ui/input/index.ts +7 -0
- package/ui/src/lib/components/ui/input/input.svelte +48 -0
- package/ui/src/lib/components/ui/input-otp/index.ts +15 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-group.svelte +20 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-separator.svelte +27 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-slot.svelte +31 -0
- package/ui/src/lib/components/ui/input-otp/input-otp.svelte +23 -0
- package/ui/src/lib/components/ui/item/index.ts +34 -0
- package/ui/src/lib/components/ui/item/item-actions.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-content.svelte +23 -0
- package/ui/src/lib/components/ui/item/item-description.svelte +23 -0
- package/ui/src/lib/components/ui/item/item-footer.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-group.svelte +21 -0
- package/ui/src/lib/components/ui/item/item-header.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-media.svelte +42 -0
- package/ui/src/lib/components/ui/item/item-separator.svelte +19 -0
- package/ui/src/lib/components/ui/item/item-title.svelte +20 -0
- package/ui/src/lib/components/ui/item/item.svelte +61 -0
- package/ui/src/lib/components/ui/kbd/index.ts +10 -0
- package/ui/src/lib/components/ui/kbd/kbd-group.svelte +20 -0
- package/ui/src/lib/components/ui/kbd/kbd.svelte +23 -0
- package/ui/src/lib/components/ui/label/index.ts +7 -0
- package/ui/src/lib/components/ui/label/label.svelte +20 -0
- package/ui/src/lib/components/ui/progress/index.ts +7 -0
- package/ui/src/lib/components/ui/progress/progress.svelte +27 -0
- package/ui/src/lib/components/ui/select/index.ts +37 -0
- package/ui/src/lib/components/ui/select/select-content.svelte +45 -0
- package/ui/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/ui/src/lib/components/ui/select/select-group.svelte +17 -0
- package/ui/src/lib/components/ui/select/select-item.svelte +40 -0
- package/ui/src/lib/components/ui/select/select-label.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/ui/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/ui/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/ui/src/lib/components/ui/select/select.svelte +11 -0
- package/ui/src/lib/components/ui/separator/index.ts +7 -0
- package/ui/src/lib/components/ui/separator/separator.svelte +23 -0
- package/ui/src/lib/components/ui/spinner/index.ts +1 -0
- package/ui/src/lib/components/ui/spinner/spinner.svelte +18 -0
- package/ui/src/lib/components/ui/tabs/index.ts +18 -0
- package/ui/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/ui/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
- package/ui/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
- package/ui/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/ui/src/lib/components/ui/textarea/index.ts +7 -0
- package/ui/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/ui/src/lib/intl/bcp.ts +25 -0
- package/ui/src/lib/intl/built.js +18 -0
- package/ui/src/lib/intl/context.yaml +18 -0
- package/ui/src/lib/intl/en-US.yaml +6 -0
- package/ui/src/lib/intl/index.ts +65 -0
- package/ui/src/lib/intl/types.ts +19 -0
- package/ui/src/lib/tools/ago.ts +10 -0
- package/ui/src/lib/tools/assert.ts +3 -0
- package/ui/src/lib/tools/cap.ts +5 -0
- package/ui/src/lib/tools/clicks.ts +25 -0
- package/ui/src/lib/tools/contextmenu.ts +19 -0
- package/ui/src/lib/tools/convert.ts +32 -0
- package/ui/src/lib/tools/currency.test.ts +10 -0
- package/ui/src/lib/tools/currency.ts +55 -0
- package/ui/src/lib/tools/date.test.ts +21 -0
- package/ui/src/lib/tools/date.ts +35 -0
- package/ui/src/lib/tools/delay.ts +7 -0
- package/ui/src/lib/tools/image.ts +39 -0
- package/ui/src/lib/tools/index.ts +20 -0
- package/ui/src/lib/tools/location.ts +41 -0
- package/ui/src/lib/tools/metronome.ts +11 -0
- package/ui/src/lib/tools/mq.ts +84 -0
- package/ui/src/lib/tools/onsubmit.ts +21 -0
- package/ui/src/lib/tools/random.ts +15 -0
- package/ui/src/lib/tools/search.ts +25 -0
- package/ui/src/lib/tools/timeout.ts +26 -0
- package/ui/src/lib/tools/transition.ts +207 -0
- package/ui/src/lib/tools/types.ts +3 -0
- package/ui/src/lib/tools/uuid.test.ts +28 -0
- package/ui/src/lib/tools/uuid.ts +9 -0
- package/ui/src/lib/typeset.css +502 -0
- package/ui/src/lib/utils.ts +12 -0
- package/ui/src/routes/(private)/+layout.svelte +112 -0
- package/ui/src/routes/(private)/+page.svelte +7 -0
- package/ui/src/routes/(private)/[component]/+page.svelte +77 -0
- package/ui/src/routes/+layout.svelte +25 -0
- package/ui/src/routes/+layout.ts +14 -0
- package/ui/src/routes/layout.css +232 -0
- package/ui/static/robots.txt +3 -0
- package/ui/svelte.config.js +36 -0
- package/ui/tools/feat.js +43 -0
- package/ui/tools/hooks/README.md +13 -0
- package/ui/tools/hooks/guard-intl.sh +40 -0
- package/ui/tools/shad.js +7 -0
- package/ui/tsconfig.json +20 -0
- package/ui/vite.config.ts +8 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# geo
|
|
2
|
+
|
|
3
|
+
Consent-zone lookup. Resolves whether the current visitor sits in a prior-consent jurisdiction (where a consent banner must be shown before loading trackers) by querying your API through the `@/net` origin. Headless — service only, no UI.
|
|
4
|
+
|
|
5
|
+
## Notes
|
|
6
|
+
|
|
7
|
+
- **Backend contract** — exposes `geo()`, which `GET`s `/geo/` on your API (resolved against `$config.origin` via `@/net`). The endpoint must answer with `{ "privacy": boolean, "country"?: string }`, where `privacy: true` means the visitor is inside a prior-consent zone. Typically your backend derives this from the request IP / edge geolocation headers.
|
|
8
|
+
- **Dev override** — when running in dev, a `?privacy=0|1` query param short-circuits the backend lookup so you can force either branch locally (`?privacy=1` → in-zone, `?privacy=0` → out-of-zone). The override is dev-only and browser-only; production always defers to the API.
|
|
9
|
+
- **Failure** — a network/parse failure resolves to an `Error` (not a throw); the caller decides the safe default (typically: show the banner).
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { geo } from '@/geo'
|
|
15
|
+
|
|
16
|
+
const zone = await geo()
|
|
17
|
+
|
|
18
|
+
if (zone instanceof Error) {
|
|
19
|
+
// lookup failed — default to the privacy-safe path (show the banner)
|
|
20
|
+
} else if (zone.privacy) {
|
|
21
|
+
// prior-consent jurisdiction — collect consent before loading trackers
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
None — this Solution exports a single `geo()` function and the `Geo` type. It renders nothing.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Globals
|
|
2
|
+
|
|
3
|
+
Shared theme tokens, Tailwind variants, and utility classes for antcn components.
|
|
4
|
+
|
|
5
|
+
## What's included
|
|
6
|
+
|
|
7
|
+
| Kind | Examples |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| CSS variables | `--constructive`, `--constructive-foreground`, `--destructive-foreground`, `--shadow-color` → `bg-constructive`, `text-constructive-foreground`, `text-destructive-foreground`, popover shadows |
|
|
10
|
+
| Custom variants | `standalone:`, `tim:` |
|
|
11
|
+
| Shared layers | `@layer transitions` — `transition-morph` / `transition-spring` / `transition-instant` timing, reduced-motion guard |
|
|
12
|
+
| Utility classes | `.no-scrollbar` — hide scrollbars while keeping scroll; `.selected` — accent ring for selected rows |
|
|
13
|
+
| Typography | `.typeset` — document rhythm for rendered Markdown, sized relative to its container |
|
|
14
|
+
|
|
15
|
+
Component-specific view-transition rules stay in each component's `<style>` block.
|
|
16
|
+
|
|
17
|
+
## Typography
|
|
18
|
+
|
|
19
|
+
`.typeset` is [shadcn/typeset](https://ui.shadcn.com/docs/typeset), snapshotted from <https://ui.shadcn.com/typeset.css> on 2026-08-05. It has no registry entry and no npm package — it ships as a file plus a web builder, so it is vendored here as `typeset.css` and refreshing it is a diff against a fresh download.
|
|
20
|
+
|
|
21
|
+
The file ships verbatim: `globals` carries it as a `registry:file` targeting `src/lib/typeset.css`, and its `css` carries the matching `@import '$lib/typeset.css'` key, which the CLI merges into your stylesheet — so after install you own a readable `typeset.css` and it is already wired. Edit it like any other file in your project.
|
|
22
|
+
|
|
23
|
+
The deviation, one rule: upstream nests lists with a bare `:where(ul ul)`, so a structural `<ul>` _outside_ the typeset container counts as a nesting level and Markdown lists render `circle`. It is scoped to `.typeset ul ul` (and `.typeset ul ul ul`) instead. This is generic — any layout that puts a Markdown container inside a list hits it.
|
|
24
|
+
|
|
25
|
+
Put the class on the element that roots a whole document, not on each Markdown fragment, so headings and prose share one typographic root. Elements inside it that are structure rather than prose opt out with explicit utilities — note that Tailwind v4 implements `space-y-*` as `margin-block-end`, which collides with typeset's list spacing, so spacing between structural items wants an explicit margin instead.
|
|
26
|
+
|
|
27
|
+
## After install
|
|
28
|
+
|
|
29
|
+
Installing or re-installing globals merges its `css` and `cssVars` into your project CSS (usually `src/app.css`). Simple tokens dedupe reliably; **complex selectors** — `@layer transitions`, `@custom-variant`, comma-grouped `::view-transition-*` rules — match by exact formatting. If your file is pretty-printed differently from what the CLI emits, overwrite may fail and rules get **appended as duplicates**.
|
|
30
|
+
|
|
31
|
+
After each install, check your CSS and **delete duplicate blocks** (most often under `@layer transitions`). Replace the whole bloated section with one copy — don't leave stacked repeats.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Grammatical form
|
|
2
|
+
|
|
3
|
+
In some languages the wording of a message depends on the subject's grammatical gender (`{name} added` vs Russian `{name} добавил` / `добавила`). svintl turns this into a translation concern, and the app stores a per-user choice — the same way it stores a chosen locale. It is **not** part of any one Solution: the mechanics live in the svintl host, the field on the account, and the picker in the consumer's profile.
|
|
4
|
+
|
|
5
|
+
## How svintl exposes it
|
|
6
|
+
|
|
7
|
+
Enable gender guidance once on the host dictionary (`$lib/intl`), listing the gender tokens your strings vary by — the **last** one is treated as the neutral/fallback form:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx intl genders he she none # writes `genders: [he, she, none]` to context.yaml
|
|
11
|
+
npx intl genders # prints the current values
|
|
12
|
+
npx intl build # regenerates types + dictionaries
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`build` then emits a `Grammar` union from those tokens:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// $lib/intl
|
|
19
|
+
export type Grammar = 'he' | 'she' | 'none' // = string when genders is unset
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Gender-dependent phrases become `!js` functions whose **last** argument is the gender; the phrase's own arguments (if any) come first. A phrase with no other placeholders is just `(gender) => …`:
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
# one own argument (name) + gender
|
|
26
|
+
example:
|
|
27
|
+
!js
|
|
28
|
+
(name, gender) => gender === 'he' ? `${name} добавил` : gender === 'she' ? `${name} добавила` : `${name} добавил(а)`
|
|
29
|
+
# no own arguments — single gender param
|
|
30
|
+
status:
|
|
31
|
+
!js
|
|
32
|
+
(gender) => gender === 'she' ? 'готова' : gender === 'he' ? 'готов' : 'готов(а)'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A language with no gender distinction for a phrase returns a plain string, not a function.
|
|
36
|
+
|
|
37
|
+
## API field
|
|
38
|
+
|
|
39
|
+
The subject's gender lives on the account as `grammar?: Grammar | null` (`Grammar` imported from `$lib/intl`). antcn's `iam` Solution already carries it on `Echo`. Persist the user's choice there.
|
|
40
|
+
|
|
41
|
+
## Two consumption modes
|
|
42
|
+
|
|
43
|
+
- **Current user** — for "you did X" texts, feed a `grammar` store derived from the active account (`$lib/intl` can expose `grammar = derived(account, $a => $a?.grammar ?? 'none')`).
|
|
44
|
+
- **Per subject** — for "{name} did X" texts about *other* people, pass that account's own field explicitly: `$dict.contacts.transferred.received(name, amount, account.grammar)`. Forgetting this is the common bug — the dictionary function needs the *subject's* gender, not the viewer's.
|
|
45
|
+
|
|
46
|
+
## Picker (interface pattern — not shipped by antcn)
|
|
47
|
+
|
|
48
|
+
Mirror the language switcher: a small control that edits `account.grammar` and reports the choice for persistence, exactly like the `locale` Solution's `Languages` / `Language` report a `Locale` via `onselect`.
|
|
49
|
+
|
|
50
|
+
```svelte
|
|
51
|
+
<script lang="ts">
|
|
52
|
+
import type { Grammar } from '$lib/intl'
|
|
53
|
+
import { me } from '@/accounts'
|
|
54
|
+
|
|
55
|
+
// value seeded from account.grammar; toggle-group over the gender tokens
|
|
56
|
+
function persist(grammar: Grammar) {
|
|
57
|
+
void me.update({ grammar })
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Suggested interface:
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Notes |
|
|
65
|
+
| ---------- | ----------------------------- | -------------------------------------------------- |
|
|
66
|
+
| `value` | `Grammar \| ''` | Current account gender; `''` for unset |
|
|
67
|
+
| `onselect` | `(grammar: Grammar) => void` | Fires after the choice changes; persist it |
|
|
68
|
+
|
|
69
|
+
A toggle-group of the gender tokens (e.g. `he` / `she` / `none` with icons) is the proven shape.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## hint
|
|
2
|
+
|
|
3
|
+
Per-account dismissable onboarding hints — acknowledge a hint permanently, or snooze it until a timestamp. State is persisted under the signed-in `@/iam` account, so each user sees each hint once (or once per snooze window).
|
|
4
|
+
|
|
5
|
+
`Hints` is intentionally an **empty interface extending `Record<string, Hint | undefined>`**. Declare your hint keys by extending it; `acknowledge` / `later` / `unfamiliar` then narrow to those keys.
|
|
6
|
+
|
|
7
|
+
## Notes
|
|
8
|
+
|
|
9
|
+
- **Requires `@/iam`.** The hint store binds to `@/iam`'s `account` and clears on logout — no account, no persisted hints. Pulled automatically via `local:iam`.
|
|
10
|
+
- **No UI ships.** This Solution is service-only; render the prompt with whatever surface you like (a `Card`, banner, dialog). `unfamiliar(key)` gates rendering; `acknowledge` / `later` dismiss it.
|
|
11
|
+
- **Extending `Hints` is how you wire keys.** Without an `interface Hints` augmentation in your project, every key is valid but untyped. The `transmission` Solution does this for `transmission_permission`.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Declare a hint key, then gate a prompt on `unfamiliar`:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// src/@/your-feature/svc/hints.ts
|
|
19
|
+
import type { Hint } from '@/hints'
|
|
20
|
+
|
|
21
|
+
declare module '@/hints' {
|
|
22
|
+
interface Hints {
|
|
23
|
+
welcome?: Hint
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```svelte
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import { unfamiliar, acknowledge, later } from '@/hints'
|
|
31
|
+
|
|
32
|
+
const day = 24 * 60 * 60 * 1000
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if unfamiliar('welcome')}
|
|
36
|
+
<Card>
|
|
37
|
+
<p>Welcome aboard.</p>
|
|
38
|
+
<Button onclick={() => later('welcome', 7 * day)}>Later</Button>
|
|
39
|
+
<Button onclick={() => acknowledge('welcome')}>Got it</Button>
|
|
40
|
+
</Card>
|
|
41
|
+
{/if}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`unfamiliar(key)` returns `true` when there is no record for `key` **or** the record is a snooze (`value: false`) whose `expiration` has passed. `acknowledge(key)` writes `{ value: true }` (hides forever). `later(key, delay)` writes `{ value: false, expiration: Date.now() + delay }` (hides until then).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Hold
|
|
2
|
+
|
|
3
|
+
`Alt`+click fires `onclick` immediately (power users / tests). Right-click is swallowed.
|
|
4
|
+
|
|
5
|
+
Use a unique `name` per instance when several holds share a page (CSS anchor). Set `portal={false}` inside transformed ancestors — Safari CSS anchor positioning quirk.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```svelte
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { Hold } from '$com/hold'
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Hold label="hold to log out" onclick={() => signOut()}>
|
|
15
|
+
Log out
|
|
16
|
+
</Hold>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Notes |
|
|
22
|
+
| ---------- | ------------------------------------------------- | --------- | ---------------------------------------------------------------------------- |
|
|
23
|
+
| `name` | `string` | `'hold'` | CSS anchor name. Use a unique value per instance if multiple on one page. |
|
|
24
|
+
| `duration` | `number` (ms) | `800` | How long the user must hold before `onclick` fires. |
|
|
25
|
+
| `label` | `string` | — | Hint text shown in the tooltip while held. Use `message` snippet for richer markup. |
|
|
26
|
+
| `message` | `Snippet` | — | Overrides `label` for non-string content. |
|
|
27
|
+
| `position` | `'center' \| 'left' \| 'right' \| 'top' \| 'bottom'` | `'left'` | Tooltip side (CSS anchor `position-area` first axis). |
|
|
28
|
+
| `align` | `'center' \| 'left' \| 'right' \| 'top' \| 'bottom'` | `'center'` | Tooltip alignment along the chosen side. |
|
|
29
|
+
| `portal` | `boolean` | `true` | Teleport tooltip to `body`. Set `false` inside transformed ancestors (Safari anchor quirk). |
|
|
30
|
+
| `onpress` | `() => void` | — | Fires when the hold starts. |
|
|
31
|
+
| `onclick` | `() => void` | — | Fires after the full duration is held, or immediately on `Alt`+click. |
|
|
32
|
+
| `variant` | `ButtonVariant` | `'ghost'` | Forwarded to shadcn `Button`. |
|
|
33
|
+
|
|
34
|
+
All other props pass through to the underlying `Button`.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# iam
|
|
2
|
+
|
|
3
|
+
Identity & access — a complete authentication surface plus the services behind it. UI covers passkey, password, one-time-passcode, and OIDC (Google/Apple) sign-in/sign-up, an `Authenticated` gate, an `Authorized` conditional-render guard, and session refresh on expiry. Services expose the `account` store, `logout`, `update`, `named`, and the `basic` / `otp` / `passkeys` / `oidc` flows.
|
|
4
|
+
|
|
5
|
+
## `$config`
|
|
6
|
+
|
|
7
|
+
| Field | Type | Notes |
|
|
8
|
+
| ------------------ | ------------------- | ---------------------------------------------------- |
|
|
9
|
+
| `navigation.entry` | `{ entry: string }` | Route to land on after a successful OIDC redirect. |
|
|
10
|
+
|
|
11
|
+
Network access (`origin`, `sleep`) flows through `@/net`.
|
|
12
|
+
|
|
13
|
+
## Environment
|
|
14
|
+
|
|
15
|
+
OIDC client IDs come from `$env/dynamic/public`. Omit or leave empty to hide the provider button:
|
|
16
|
+
|
|
17
|
+
| Var | Notes |
|
|
18
|
+
| ------------------------- | -------------------------------------------------- |
|
|
19
|
+
| `PUBLIC_GOOGLE_CLIENT_ID` | Google OIDC client ID. Empty hides the Google button. |
|
|
20
|
+
| `PUBLIC_APPLE_CLIENT_ID` | Apple OIDC client ID. Empty hides the Apple button. |
|
|
21
|
+
|
|
22
|
+
## Notes
|
|
23
|
+
|
|
24
|
+
- **OIDC is opt-in.** Missing/empty `PUBLIC_*_CLIENT_ID` disables the provider buttons — no wiring crashes if you only want password/passkey/OTP.
|
|
25
|
+
- **Account shape.** Components accept an `AccountLike` (`Pick` of the account fields they render); the `account` store holds the authenticated `Echo`. Extend `Echo` (`svc/net/Echo.ts`) if your API returns extra profile fields.
|
|
26
|
+
- **`Credentials` manager.** Renders the signed-in account's security surface: a passkey manager (register / list / hold-to-delete — a thin layer over `@/passkeys`), inert provider rows (email/Google/Apple; the `Connect` CTA carries no behaviour yet), and a "sign in on another device" action whose QR encodes the current session `challenge`. It gates itself on the `account` store, so just mount it anywhere inside your authenticated area.
|
|
27
|
+
- **Session transfer is two-sided.** The QR is the *sending* device; the *receiving* device adopts the session by reading the fragment `challenge` on load. `rc()` already does this on init (shared `consume()`), so transfer completes as long as `rc()` runs once at app start.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Gate an app behind authentication (`Authenticated` shows the auth surface until signed in, then renders its children):
|
|
32
|
+
|
|
33
|
+
```svelte
|
|
34
|
+
<script lang="ts">
|
|
35
|
+
import { Authenticated } from '@/iam/ui'
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<Authenticated>
|
|
39
|
+
<Dashboard />
|
|
40
|
+
</Authenticated>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Conditionally render UI with `Authorized`. It renders its children only when **at least one** passed check passes: `dev` (matches `$app/environment` `dev`), `beta` (host starts with `beta.`), `debug` (`dev` or `beta`), or `role` (scoped match against the account's `roles` — `app` matches `app`, `app:codes`, and deeper; `app:codes` matches only `app:codes` and deeper). No matching check → nothing renders.
|
|
44
|
+
|
|
45
|
+
```svelte
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { Authorized } from '@/iam/ui'
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<Authorized role="admin">
|
|
51
|
+
<AdminPanel />
|
|
52
|
+
</Authorized>
|
|
53
|
+
|
|
54
|
+
<Authorized dev>
|
|
55
|
+
<DebugToolbar />
|
|
56
|
+
</Authorized>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Read the session reactively and sign out. `account` is a `svas` store of the authenticated `Echo` (`null` when signed out):
|
|
60
|
+
|
|
61
|
+
```svelte
|
|
62
|
+
<script lang="ts">
|
|
63
|
+
import { account, logout } from '@/iam'
|
|
64
|
+
import { goto } from '$app/navigation'
|
|
65
|
+
|
|
66
|
+
async function getout() {
|
|
67
|
+
logout()
|
|
68
|
+
void goto('/')
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
{#if $account}
|
|
73
|
+
<p>Signed in as {$account.name}</p>
|
|
74
|
+
<button onclick={getout}>Log out</button>
|
|
75
|
+
{/if}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Block an action until the account has a display name — `named()` resolves once `account.name` is set (prompting through the auth surface if missing), so guarded flows can `await` it:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { having } from 'svas'
|
|
82
|
+
import { account, named } from '@/iam'
|
|
83
|
+
|
|
84
|
+
async function onaccept() {
|
|
85
|
+
await having(account) // wait for a session
|
|
86
|
+
await named() // …and a chosen name
|
|
87
|
+
await invitations.accept(id)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Patch the local session optimistically (e.g. after a profile edit) — `update` merges a partial into the `account` store without a round-trip:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { update } from '@/iam'
|
|
95
|
+
|
|
96
|
+
update({ name: 'Ada Lovelace' })
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Gate non-UI logic on the `authenticated` derived boolean (true only when a challenge + account exist and nothing is processing):
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { authenticated } from '@/iam'
|
|
103
|
+
|
|
104
|
+
authenticated.subscribe((ok) => ok && sync())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Render the account's credentials manager inside an authenticated area (passkeys, connected providers, device transfer). It only renders once a session exists, so no extra gating is needed:
|
|
108
|
+
|
|
109
|
+
```svelte
|
|
110
|
+
<script lang="ts">
|
|
111
|
+
import { Credentials } from '@/iam/ui'
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<Credentials />
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`Credentials` takes a single optional `class` (`ClassValue`) — everything else is driven by the `account` and `@/passkeys` stores.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# InputForm
|
|
2
|
+
|
|
3
|
+
Centered text field in a form that submits on blur when the value changed, or on Enter. Empty blur resets to the initial value. `onsubmit` may return a new string to sync the field after save.
|
|
4
|
+
|
|
5
|
+
## Notes
|
|
6
|
+
|
|
7
|
+
Exposes an imperative `focus()` method — bind the instance (`bind:this`) and call it to move focus to the field (e.g. when revealing the form).
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```svelte
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { InputForm } from '$com/input-form'
|
|
14
|
+
|
|
15
|
+
let name = $state('Alex')
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<InputForm
|
|
19
|
+
value={name}
|
|
20
|
+
onsubmit={async (value) => {
|
|
21
|
+
name = value
|
|
22
|
+
return value
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
| Prop | Type | Default | Notes |
|
|
30
|
+
| ---------- | ----------------------------------------------------------------- | ------- | -------------------------------------------------- |
|
|
31
|
+
| `value` | `string` | — | Initial/current value; resets to this when cleared. |
|
|
32
|
+
| `class` | `ClassValue` | — | Extra classes on the shadcn `Input`. |
|
|
33
|
+
| `placeholder` | `string` | `'Enter name'`| Placeholder text when the field is empty. |
|
|
34
|
+
| `onsubmit` | `(value: string) => Promise<string \| undefined> \| string \| undefined` | — | Called on blur (if changed) or form submit. Return a string to update the field. |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# locale
|
|
2
|
+
|
|
3
|
+
Locale switching over the host svintl store. Two interchangeable switchers — `Languages` (snap-scroller) and `Language` (`Select` dropdown) — both set the active locale and fire `onselect` for persistence.
|
|
4
|
+
|
|
5
|
+
## Languages
|
|
6
|
+
|
|
7
|
+
Horizontal snap-scroller of native language names.
|
|
8
|
+
|
|
9
|
+
### Usage
|
|
10
|
+
|
|
11
|
+
```svelte
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { Languages } from '@/locale/ui'
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<Languages />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`onselect` fires after the store updates — use it for side effects like persistence:
|
|
20
|
+
|
|
21
|
+
```svelte
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import { Languages } from '@/locale/ui'
|
|
24
|
+
import { me } from '@/accounts'
|
|
25
|
+
import type { Locale } from '$lib/intl'
|
|
26
|
+
|
|
27
|
+
function persist(locale: Locale) {
|
|
28
|
+
void me.update({ locale })
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<Languages onselect={persist} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Props
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Notes |
|
|
38
|
+
| ---------- | -------------------------- | ------- | ------------------------------------------------------------------------------------------- |
|
|
39
|
+
| `onselect` | `(locale: Locale) => void` | — | Fires after the host store is set. Use it to persist or track the choice. |
|
|
40
|
+
| `infinite` | `boolean` | _auto_ | Forces the loop/clone scroller on or off. Defaults to on when `locales.length >= 10`. |
|
|
41
|
+
|
|
42
|
+
### Notes
|
|
43
|
+
|
|
44
|
+
The active locale stays centered in the track. A long set (`>= 10`) loops endlessly; a short one renders once and centers via 50% gutters — dead-center whether it underfills or overflows. Centering is relative to the track's own width, so wrap it in a narrower box to constrain the window. `infinite` overrides the auto choice.
|
|
45
|
+
|
|
46
|
+
## Language
|
|
47
|
+
|
|
48
|
+
Compact `Select` for a settings row.
|
|
49
|
+
|
|
50
|
+
### Usage
|
|
51
|
+
|
|
52
|
+
```svelte
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import { Language } from '@/locale/ui'
|
|
55
|
+
import { me } from '@/accounts'
|
|
56
|
+
import type { Locale } from '$lib/intl'
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<Language onselect={(l: Locale) => me.update({ locale: l })} />
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Notes |
|
|
65
|
+
| ---------- | -------------------------- | ------- | ------------------------------------------------------------------------- |
|
|
66
|
+
| `onselect` | `(locale: Locale) => void` | — | Fires after the host store is set. Use it to persist or track the choice. |
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# net
|
|
2
|
+
|
|
3
|
+
The networking foundation every other Solution builds on. Connects a single `@toa.io/origin` client to your API and re-exports it as `origin`, plus the `query` helper for building criteria query strings. Sibling Solutions import `origin` from `@/net` to declare their resources.
|
|
4
|
+
|
|
5
|
+
## `$config`
|
|
6
|
+
|
|
7
|
+
`net` reads two fields from your `$config` module (`import * as config from '$config'`):
|
|
8
|
+
|
|
9
|
+
| Field | Type | Notes |
|
|
10
|
+
| -------- | ------------------------------- | --------------------------------------------------------------------------- |
|
|
11
|
+
| `origin` | `string` | Absolute API origin; `''` is invalid (resolved via `new URL(path, origin)`). |
|
|
12
|
+
| `sleep` | `[number, number] \| undefined` | Dev-only artificial latency range; `undefined` disables it. |
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Declare a resource against the shared client and call it:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { origin, query, type RequestOptions } from '@/net'
|
|
20
|
+
|
|
21
|
+
const echo = origin.resource<{ message: string }>('/accounts/echo/')
|
|
22
|
+
|
|
23
|
+
async function ping(): Promise<{ message: string } | Error> {
|
|
24
|
+
return echo.json({ method: 'GET' } satisfies RequestOptions)
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`query` turns a `URLSearchParams` into a criteria string; `omit`/`limit`/`search` stay as plain pairs, everything else becomes `criteria=key==value;…`:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const params = new URLSearchParams({ status: 'active', limit: '20' })
|
|
32
|
+
query(params) // '?criteria=status==active&limit=20'
|
|
33
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Panel
|
|
2
|
+
|
|
3
|
+
List row built on shadcn `Button`: main content in `left`/`right`/`icon` snippets, optional swipe-reveal `actions` on the trailing edge. Set `h` (e.g. `h-14`) when using `collapsed` — height animates to zero. Pass `selected` for accent ring styling (from `globals`).
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { Panel, type Action } from '$com/panel'
|
|
10
|
+
|
|
11
|
+
const actions: Action[] = [{ id: 'delete', onclick: () => remove() }]
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Panel h="h-14" class="bg-card border border-border" href="/items/1">
|
|
15
|
+
{#snippet left()}
|
|
16
|
+
<span class="font-medium">Item title</span>
|
|
17
|
+
{/snippet}
|
|
18
|
+
{#snippet right()}
|
|
19
|
+
<span class="text-muted-foreground">$12</span>
|
|
20
|
+
{/snippet}
|
|
21
|
+
{#snippet action(id)}
|
|
22
|
+
{#if id === 'delete'}Delete{/if}
|
|
23
|
+
{/snippet}
|
|
24
|
+
{actions}
|
|
25
|
+
</Panel>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Props
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Notes |
|
|
31
|
+
| ----------- | ---------------------------- | ----------- | --------------------------------------------------------------------- |
|
|
32
|
+
| `left` | `Snippet` | — | Main label/content (required). |
|
|
33
|
+
| `right` | `Snippet` | — | Trailing slot (balance, badge, etc.). |
|
|
34
|
+
| `icon` | `Snippet` | — | Leading icon/avatar before `left`. |
|
|
35
|
+
| `collapsed` | `boolean` | `false` | Animate height to zero. Requires `h`. |
|
|
36
|
+
| `selected` | `boolean` | `false` | Applies `.selected` ring styling. |
|
|
37
|
+
| `h` | `string` | — | Tailwind height class on the row and collapse wrapper. |
|
|
38
|
+
| `actions` | `Action[]` | — | Swipe-reveal toolbar items. Pair with `action` snippet. |
|
|
39
|
+
| `action` | `Snippet<[string]>` | — | Renders one action by `id`. Required when `actions` is set. |
|
|
40
|
+
| `variant` | `ButtonVariant` | `'outline'` | Forwarded to shadcn `Button`. |
|
|
41
|
+
|
|
42
|
+
`Action`: `{ id, class?: ClassValue, href?, onclick? }` — tapping an action scrolls the row back; `href` navigates via SvelteKit `goto`.
|
|
43
|
+
|
|
44
|
+
All other props pass through to the underlying `Button` (`href`, `onclick`, `class`, etc.).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# passkeys
|
|
2
|
+
|
|
3
|
+
WebAuthn credential management — registers, lists, and removes passkeys for the signed-in account against your `/identity/passkeys` API. Wraps the browser `navigator.credentials` ceremony (challenge → `create`/`get` → attestation) so callers work with plain async operations that return values or `Error`.
|
|
4
|
+
|
|
5
|
+
## Notes
|
|
6
|
+
|
|
7
|
+
- **Requires `@/iam`.** The `passkeys` store keys credentials by the current `@/iam` `account`; `get`/`remove`/`store` all read it. No signed-in account → empty store. `iam` is **not** pulled automatically (that would form an install cycle — `iam` already depends on `passkeys`), so install it yourself: `npx shadcn-svelte add http://cn.ants.dev/r/iam.json`. Installing `iam` brings `passkeys` along, so most consumers never add `passkeys` directly.
|
|
8
|
+
- **Feature detection.** `supported` is a boolean (`window.PublicKeyCredential !== undefined`, `true` on the server). Gate registration UI on it — the ceremony rejects on unsupported browsers.
|
|
9
|
+
- **`ui/aaguid.json`** ships the FIDO AAGUID → authenticator-metadata map (name + light/dark icon, base64 SVG). Look up `aaguid[credential.aaguid]` to label a passkey by its authenticator (e.g. "Google Password Manager", "iCloud Keychain"). Optional — nothing imports it for you.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { supported, add, request, remove, passkeys } from '@/passkeys'
|
|
15
|
+
|
|
16
|
+
if (supported) {
|
|
17
|
+
const out = await add(identity, name) // full register: ceremony → POST → store
|
|
18
|
+
if (out instanceof Error) handle(out)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// reactive list for the current account
|
|
22
|
+
passkeys.subscribe((list) => render(list))
|
|
23
|
+
|
|
24
|
+
await remove(id) // revoke one
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`request(id?)` runs the authentication (login) ceremony and returns the assertion (`RequestResponse`) for your auth flow to exchange. `create(name, identity?)` is the lower-level ceremony `add` builds on.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# payments
|
|
2
|
+
|
|
3
|
+
Subscription paywall stack: a tier selector, animated benefits table, pay button, platform-mandated legal disclaimers, and a processing overlay, backed by four payment processors (App Store, Google Play, Stripe, a dev mock) behind one `Processor` interface. Ships a neutral SaaS example (Storage / Seats / Priority / API) you adapt to your product.
|
|
4
|
+
|
|
5
|
+
## `$config`
|
|
6
|
+
|
|
7
|
+
| Field | Used by |
|
|
8
|
+
| -------- | ----------------------------------------------------------------------------------------- |
|
|
9
|
+
| `origin` | All three live processors resolve their backend endpoints through `@/net`'s `origin`, which reads `$config.origin`. |
|
|
10
|
+
|
|
11
|
+
The App Store / Google Play processors transact through the native shell (`@/bridge`); the dev processor stands in whenever `$app/environment`'s `dev` is true and there is no native host. No other `$config` field is read.
|
|
12
|
+
|
|
13
|
+
## Entitlements
|
|
14
|
+
|
|
15
|
+
`tier` is the only entitlement the shipped UI reads. Everything else in `Entitlements` is a neutral example you replace with your product's metered (`number`) and flag (`boolean`) entitlements — edit the interface in `svc/entitlements.ts`:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
export interface Entitlements {
|
|
19
|
+
tier: Tier
|
|
20
|
+
seats: number // metered → gated by usage(account, 'seats') >= seats
|
|
21
|
+
storage: number // metered
|
|
22
|
+
priority: boolean // flag → gated by the boolean itself
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`cant(account, key?)` gates generically off the value type — no per-key `switch`:
|
|
27
|
+
|
|
28
|
+
- no `key` → `true` unless the account has unexpired premium.
|
|
29
|
+
- `boolean` entitlement → `true` when the flag is off.
|
|
30
|
+
- `number` entitlement → `true` when summed usage (`account.usage`) has reached the limit.
|
|
31
|
+
- anything else (e.g. the `tier` string) → never gated.
|
|
32
|
+
|
|
33
|
+
`usages()` (the `Entitlements` UI) derives one usage bar per **numeric** entitlement automatically, so adding/removing a metered key needs no UI change. When you rename a key, add a matching `used.<key>` and `benefits.<key>` entry to the intl mount.
|
|
34
|
+
|
|
35
|
+
The account shape is `AccountLike` (`id`, optional `premium`, `processor`, `interval`, `entitlements`, `usage`) — a structural subset of your identity account; pass your `@/iam` account (or any object satisfying it) to `cant`/`usages`/the UI. `interval` is the account's current billing period as an ISO-8601 duration (`P1M`/`P1Y`); the paywall reads it to preselect the matching interval tab.
|
|
36
|
+
|
|
37
|
+
## Tiers
|
|
38
|
+
|
|
39
|
+
The upgrade ladder is `tiers = ['basic', 'premium', 'expert']` in `svc/tier.ts` (`rank()` orders upgrades off it). Override it by editing that array in place — there is no config hook. Keep the `tier` values, the `Entitlements.tier` type, the `groups`/`plans` intl keys, and your store product groups in sync with it.
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- **Products come from the store, not this Solution.** Each processor fetches its own catalog: App Store / Google Play via the native bridge over a fixed set of product IDs (`<tier>_<month|year>`, edit the `IDS` array per processor), Stripe via `GET /stripe/checkout/products`. The dev processor serves the static `svc/processors/dev/products.ts` list so the paywall renders with no backend. Align your store product IDs / Stripe price `metadata.tier` with your `tiers`.
|
|
44
|
+
- **Backend endpoints.** Live processors call, under `$config.origin`: `POST/PATCH /appstore/transactions/{id}`, `POST /googleplay/transactions/{id}`, `GET /stripe/checkout/products`, `POST /stripe/checkout/{id}` (+ `/{id}/portal`), `POST /stripe/transactions/{id}`. You implement these.
|
|
45
|
+
- **Stripe return.** Stripe checkout redirects back to `#stripe={CHECKOUT_SESSION_ID}`; `rc()` reads that one-shot hash through `@/fragments` and commits the transaction behind the processing overlay. Call `rc()` once on app start.
|
|
46
|
+
- **Google Play package id.** `svc/processors/googleplay/processor.ts` builds the manage-subscription URL from a placeholder `com.example.twa` — replace it with your Trusted Web Activity package id (the same one you set in `@/bridge`).
|
|
47
|
+
- **Analytics.** The processors emit `checkout` / `purchase` events through `@/analytics`; `Payguard` emits `paywall`. `analytics.d.ts` augments the analytics `Events` interface with the `paywall` / `checkout` shapes.
|
|
48
|
+
- **Legal copy & links.** The disclaimers are neutral, store-compliant defaults; `Legal.svelte` links to `/terms/` and `/privacy/` — point them at your pages. Ships its own svintl mount in the registry's four locales; after install reconcile it: `npx intl import payments src/@/payments/ui/intl`.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
Commit a pending Stripe return once on app start, then drop the paywall behind anything that needs premium:
|
|
53
|
+
|
|
54
|
+
```svelte
|
|
55
|
+
<script lang="ts">
|
|
56
|
+
import { onMount } from 'svelte'
|
|
57
|
+
import { rc } from '@/payments/rc'
|
|
58
|
+
import { Payguard, Entitlements } from '@/payments/ui'
|
|
59
|
+
import { Button } from '$ui/button'
|
|
60
|
+
import { account } from '@/iam'
|
|
61
|
+
|
|
62
|
+
onMount(rc)
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<!-- gates the action: opens the paywall when the account can't use `seats` -->
|
|
66
|
+
<Payguard entitlement="seats">
|
|
67
|
+
<Button>Invite a teammate</Button>
|
|
68
|
+
</Payguard>
|
|
69
|
+
|
|
70
|
+
{#if $account}
|
|
71
|
+
<Entitlements account={$account} />
|
|
72
|
+
{/if}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`<Paywall>` is the full-screen plan picker `<Payguard>` opens; mount it directly when you want a standalone pricing page.
|
|
76
|
+
|
|
77
|
+
## Props
|
|
78
|
+
|
|
79
|
+
### `Payguard`
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Default | Notes |
|
|
82
|
+
| ------------- | ---------------------- | ------- | --------------------------------------------------------------------------- |
|
|
83
|
+
| `children` | `Snippet` | — | The gated trigger; rendered as-is when the account already has entitlement. |
|
|
84
|
+
| `entitlement` | `keyof Entitlements` | — | When set, gates on that specific entitlement; omit to gate on premium only. |
|
|
85
|
+
| `crown` | `boolean` | `true` | Show the crown badge on the gated trigger. |
|
|
86
|
+
| `class` | `ClassValue` | — | Forwarded to the trigger button. |
|
|
87
|
+
|
|
88
|
+
### `Paywall`
|
|
89
|
+
|
|
90
|
+
| Prop | Type | Default | Notes |
|
|
91
|
+
| ------------ | ------------ | ------- | -------------------------------------- |
|
|
92
|
+
| `oncomplete` | `() => void` | — | Fired after a successful checkout or dismissal. |
|
|
93
|
+
|
|
94
|
+
### `Entitlements`
|
|
95
|
+
|
|
96
|
+
| Prop | Type | Default | Notes |
|
|
97
|
+
| --------- | ------------- | ------- | ------------------------------------------------------ |
|
|
98
|
+
| `account` | `AccountLike` | — | Renders the current plan, usage bars, and manage/upgrade action. |
|
|
99
|
+
| `class` | `ClassValue` | — | Root class. |
|