Phased refactor of the Branding Layer UI onto ShadCN, with host-driven theming via setStyle, and cleaner state boundaries. Work proceeds one phase (or one modal) per query.
- Integrator customization — Host calls
proxy.rpc("setStyle", options)before / during display; wallet applies CSS tokens + copy overrides. Re-callable for the future customization tester site. - ShadCN UI — Replace bespoke Tailwind shells/modals with tokenized shadcn/ui components.
- State that scales — Theme in Context; wallet session / modal queue in Zustand; server data (later) via TanStack Query.
Host OWSProxy
└── rpc("setStyle", IStyleOptions) # branding-app registerRpc
└── StyleProvider (React Context)
├── CSS variables on document / root
└── copy / labels object
Wallet session + modal queue → Zustand stores
Durable keys / addresses → localStorage (existing storage.ts)
Relayer / credential fetch → TanStack Query (later phase)
- No hardcoded brand colors — use semantic tokens (
bg-primary,text-muted-foreground, etc.). - User-visible copy comes from StyleContext defaults / overrides, not scattered string literals.
setStylemerges into current style state (additive, re-applicable).- Defaults = 1Shot branding when host never calls
setStyle. - Style state stays separate from unlock / chain / modal runtime state.
| Surface | File | Notes |
|---|---|---|
| App shell | src/App.tsx |
Loading / onboarding / main switch |
| Embedded chrome | src/components/WalletChrome.tsx |
Title + hide |
| Onboarding | src/components/OnboardingPanel.tsx |
Login vs create |
| Main panel | src/components/MainPanel.tsx |
Status, addresses, chain, actions |
| Modal shell | src/components/Modal.tsx |
Custom dialog — replace with shadcn Dialog |
| Modal router | src/components/ModalHost.tsx |
Switches on activeModal.kind |
| Signer host | src/components/SignerHost.tsx |
Keep minimal; no visual design |
| Kind | Component file | Purpose |
|---|---|---|
walletSetup |
SetupModals.tsx → WalletSetupModal |
Login / create / cancel |
passkeyName |
SetupModals.tsx → PasskeyNameModal |
Name for new passkey |
connect |
SetupModals.tsx → ConnectModal |
Account connect consent |
personalSign |
SignModals.tsx → PersonalSignModal |
EIP-191 consent |
typedData |
SignModals.tsx → TypedDataModal |
EIP-712 consent |
credentialOffer |
CredentialModals.tsx → CredentialOfferModal |
OID4VCI accept |
credentialPresentation |
CredentialModals.tsx → CredentialPresentationModal |
OID4VP disclose |
credentialList |
(removed) | Replaced by Credentials tab |
credentials |
CredentialsTab.tsx + CredentialDetailDialog.tsx |
Stored credentials table + detail |
createBackup |
BackupModals.tsx → CreateBackupModal |
Recovery create + overlay |
restoreBackup |
BackupModals.tsx → RestoreBackupModal |
Recovery restore + overlay |
Outcome: Host can call setStyle; CSS vars + copy map exist; shadcn theme CSS restored alongside wallet layout.
- Define
IStyleOptions(theme tokens + copy/labels + feature flags as needed). - Defaults module (
DEFAULT_STYLE/ 1Shot branding). -
applyStyleToDocument(options)→ set CSS variables ondocument.documentElement(or#root). -
StyleProvider+useStyle()context. - Register
wallet.registerRpc("setStyle", …)beforewallet.start()(merge + apply). - Restore / merge shadcn theme into
src/index.css(css variables + Tailwind) without breaking/signer/host styles. - Smoke: call
setStylefrom a temporary button or host console; primary/background tokens update. - Bootstrap Host integrator skill (
skills/oneshot-embedded-wallet) documentingsetStyle+ wallet URL.
Exit criteria: Defaults render; RPC path works; no modal UI rewritten yet.
Outcome: App chrome uses Button / layout primitives; reads brand name / labels from StyleContext. Host test app exercises setStyle over Host ↔ Branding RPC (not an in-wallet debug button).
- Wire
StyleProvideraround app inmain.tsx. - Refactor
WalletChrome→ tokens + styled title from copy map. - Refactor
OnboardingPanel→ shadcn Button / typography. - Refactor
MainPanel→ shadcn Button, Select (chain). - Replace shell ad-hoc
<button>styles with@/components/ui/button. - Ensure shell respects
setStylecolors and product name. - Add
host/test Host Layer (from OWSexamples/host) with setStyle knobs;npm run dev:host.
Exit criteria: Standalone + embedded shell looks like a coherent 1Shot product; theme knobs on the host exercise host↔branding communication.
Outcome: One shadcn Dialog-based Modal / AppDialog used by all kinds; ModalHost unchanged in routing.
-
npx shadcn add dialog(and Input / Label / Textarea as needed). - Replace
src/components/Modal.tsxwith shadcn Dialog wrapper (actions, title, description slots). - Keep
ModalHostswitch; each modal migrates in later phases. - Accessibility: focus trap, Escape → reject/cancel where appropriate.
Exit criteria: At least one modal (prefer connect) uses the new Dialog shell end-to-end.
Migrate one kind at a time:
-
connect—copy.connect.*via StyleContext; Dialog + Button tokens -
walletSetup—copy.walletSetup.*via StyleContext; Dialog + Button tokens -
passkeyName—copy.passkeyName.*via StyleContext; Dialog + Button tokens
Copy via StyleContext (titles, button labels). Tokens only for colors.
-
personalSign—copy.personalSign.*via StyleContext; token-styled detail blocks -
typedData—copy.typedData.*via StyleContext; token-styled detail blocks
Preserve request detail display; avoid overstuffing the first viewport of the flyout.
-
credentialOffer—copy.credentialOffer.*via StyleContext ({issuerName}/{issuerId}templates) -
credentialPresentation—copy.credentialPresentation.*via StyleContext -
credentials—copy.credentials.*via StyleContext (tab + detail dialog)
Keep OID4 wiring in registerCredentialsProvider / WalletProvider; UI-only change.
-
createBackup—copy.createBackup.*via StyleContext;overlaySignerIframe+waitForSignerSlotunchanged -
restoreBackup—copy.restoreBackup.*via StyleContext; Signing Layer labels from copy
Do not reparent the signer iframe — keep overlaySignerIframe. Passphrase UI can sit in Dialog; signer overlay behavior stays as today.
Outcome: WalletProvider sheds UI state into stores; OWS handlers call stores from outside React.
- Add
zustand. -
useWalletSessionStore— ready, unlocked, addresses, chain, credentialCount, bootError, walletCreated. -
useModalStore— queue, push/pop,activeModal. - Thin
WalletProvider(boot, refs,ensureReady, register handlers) that syncs into stores. - Update consumers (
useWallet→ store selectors where appropriate).
Exit criteria: Opening a modal does not force unrelated shell re-renders; handlers remain correct.
Outcome: Host tester is a professional playground for setStyle; wallet docs catch up.
- Document
IStyleOptionsand example hostsetStylecall in README. - Expand knobs as needed (logo URL, colors, radius, font; showBackup later).
- Host tester: React + ShadCN shell; style knobs encapsulated in
WalletConfigurator(organization TBD). - Host layout v1: 1Shot-branded header + Sidebar (Test / Design); Design embeds wallet inline with configurator.
- Wallet presentation: MetaMask-like 360×600; create-time
presentationMode; Test/Design destroy+recreate proxy (no reparent). - Reorganize
WalletConfigurator(Basic / Style / Text tabs; color picker + accordion). - Dark mode strategy if we support host toggle via
setStyle.
- TanStack Query for relayer credential blob / balances.
- Full host-side customization tester site (separate app using
ows-provider). - Production 1Shot visual identity beyond shadcn nova defaults (brand fonts/imagery rules).
| Component | Used by |
|---|---|
button |
Shell, all modals (already present) |
dialog |
Modal system |
input / label |
passkey name, backup passphrase |
textarea |
typed data / backup paste |
select |
chain switch |
separator |
Main panel sections |
badge / scroll-area |
credential list (optional) |
- One phase or one modal per chat query unless explicitly batched.
- Prefer grepping
examples/general-wallet+ this roadmap over reinventing flows. - Keep OWS boot order: register handlers (including
setStyle) →wallet.start()→ deferred signer. - No deprecation shims — update call sites when renaming.
| Phase | Status |
|---|---|
| 0 Foundations | done |
| 1 Shell | pending |
| 2 Modal system | pending |
| 3 Setup modals | pending |
| 4 Sign modals | pending |
| 5 Credential modals | pending |
| 6 Backup modals | pending |
| 7 Zustand | pending |
| 8 Polish | pending |