From bd2e3c673814c95a511e939887dc48e52643c281 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Mon, 23 Mar 2026 23:55:31 -0700 Subject: [PATCH] add Euclid 3.0 settings screens, tunnel flow, and audit pipeline skills (#1858) * feat(webview-app): add Euclid 3.0 settings sub-screens Add SecurityScreen, NotificationPreferencesScreen, and DevModeScreen wrappers that import Euclid 3.0 components and wire them with React Router navigation and bridge adapters. Update SettingsScreen menu items to navigate to real routes instead of /coming-soon. Requires @selfxyz/euclid-web to be published with the new screen exports (SecurityScreen, NotificationPreferencesScreen, DevModeScreen) before type-check will pass. See docs/superpowers/plans/ for full context. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add settings integration plan and handover Co-Authored-By: Claude Opus 4.6 (1M context) * chore: use euclid 1.2.0 * PoC tunnel flow * updates * update skills --------- Co-authored-by: Tranquil-Flow Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Leszek Stachowski --- .claude/skills/gaps-to-issues/SKILL.md | 93 +++ .claude/skills/pr-audit/SKILL.md | 210 ++++++ .claude/skills/spec-from-audit/SKILL.md | 136 ++++ CLAUDE.md | 69 +- app/AGENTS.md | 4 +- ...2026-03-23-euclid-settings-tunnel-audit.md | 160 +++++ .../plans/2026-03-22-settings-handover.md | 102 +++ .../2026-03-22-settings-screen-integration.md | 667 ++++++++++++++++++ packages/mobile-sdk-alpha/AGENTS.md | 10 +- packages/webview-app/package.json | 4 +- packages/webview-app/src/App.tsx | 20 + .../src/screens/ComingSoonScreen.tsx | 4 +- .../src/screens/account/DevModeScreen.tsx | 96 +++ .../account/NotificationPreferencesScreen.tsx | 57 ++ .../src/screens/account/SecurityScreen.tsx | 83 +++ .../src/screens/account/SettingsScreen.tsx | 46 +- .../src/screens/home/HomeScreen.tsx | 4 +- .../ConfirmIdentificationScreen.tsx | 2 +- .../onboarding/CountryPickerScreen.tsx | 2 +- .../screens/onboarding/IDSelectionScreen.tsx | 4 +- .../onboarding/ProviderLaunchScreen.tsx | 2 +- .../onboarding/ProviderResultScreen.tsx | 2 +- .../src/screens/proving/ProvingScreen.tsx | 4 +- .../proving/VerificationResultScreen.tsx | 2 +- .../src/screens/tunnel/KycMockScreen.tsx | 32 + .../src/screens/tunnel/TourScreen.tsx | 37 + .../tunnel/TunnelCountryPickerScreen.tsx | 53 ++ .../src/screens/tunnel/TunnelIDTypeScreen.tsx | 56 ++ .../tunnel/TunnelProofReceiptScreen.tsx | 41 ++ .../screens/tunnel/TunnelProvingScreen.tsx | 32 + .../src/screens/tunnel/TunnelResultScreen.tsx | 26 + specs/projects/sdk/INDEX.md | 10 +- specs/projects/sdk/OVERVIEW.md | 26 +- .../sdk/workstreams/build-pipeline/SPEC.md | 32 +- .../workstreams/native-shells-lite/SPEC.md | 116 +-- .../projects/sdk/workstreams/webview/SPEC.md | 30 +- yarn.lock | 54 +- 37 files changed, 2135 insertions(+), 193 deletions(-) create mode 100644 .claude/skills/gaps-to-issues/SKILL.md create mode 100644 .claude/skills/pr-audit/SKILL.md create mode 100644 .claude/skills/spec-from-audit/SKILL.md create mode 100644 docs/reviews/2026-03-23-euclid-settings-tunnel-audit.md create mode 100644 docs/superpowers/plans/2026-03-22-settings-handover.md create mode 100644 docs/superpowers/plans/2026-03-22-settings-screen-integration.md create mode 100644 packages/webview-app/src/screens/account/DevModeScreen.tsx create mode 100644 packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx create mode 100644 packages/webview-app/src/screens/account/SecurityScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/KycMockScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TourScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TunnelCountryPickerScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TunnelIDTypeScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TunnelProofReceiptScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TunnelProvingScreen.tsx create mode 100644 packages/webview-app/src/screens/tunnel/TunnelResultScreen.tsx diff --git a/.claude/skills/gaps-to-issues/SKILL.md b/.claude/skills/gaps-to-issues/SKILL.md new file mode 100644 index 000000000..9d45b9c30 --- /dev/null +++ b/.claude/skills/gaps-to-issues/SKILL.md @@ -0,0 +1,93 @@ +--- +name: gaps-to-issues +description: Create Linear issues from a PR audit doc — one issue per PR bucket with acceptance criteria and linked audit findings. +disable-model-invocation: false +user-invocable: true +argument-hint: '[path-to-audit-doc]' +--- + +# Gaps to Issues + +You take a PR audit document (produced by `/pr-audit`) and create Linear issues from its PR buckets. + +This skill **stops at issue creation**. Use `/spec-from-audit` to generate specs for the created issues. + +## Input + +`$ARGUMENTS` — Path to the audit document (e.g., `docs/reviews/2026-03-23-branch-audit.md`). If not provided, look for the most recently modified file in `docs/reviews/`. + +## Workflow + +### Step 1: Read the Audit Document + +Read the audit doc. Verify it has: + +- A "PR Buckets" section with at least one bucket +- Each bucket has: name, estimated LOC, findings, files, acceptance criteria + +If the audit doc is missing buckets or acceptance criteria, tell the user and stop. + +### Step 2: Ask for Linear Context + +Ask the user: + +1. Which **Linear team** to create issues under (suggest the team from recent issues if detectable) +2. Which **Linear project** to add issues to (optional) +3. Whether to set **priority** per bucket or use a default +4. Whether to **consolidate** any buckets before creating issues + +Wait for confirmation before proceeding. + +### Step 3: Create Issues + +For each bucket, create a Linear issue using `mcp__linear-server__save_issue`: + +- **Title:** Bucket name (concise, under 70 characters) +- **Team:** From Step 2 +- **Project:** From Step 2 (if provided) +- **Priority:** Based on the highest severity finding in the bucket: + - Contains Critical findings → Urgent (1) + - Contains High findings → High (2) + - Contains Medium findings → Medium (3) + - Contains only Low findings → Low (4) +- **Description:** Keep it lightweight — the spec (created later by `/spec-from-audit`) is the source of truth. The issue body should contain: + - One-sentence goal + - List of finding IDs from the audit (e.g., "Covers findings #1, #3, #7") + - Dependencies on other issues if any + - A note: "See attached spec document for full implementation plan." + - Do NOT duplicate scope, file lists, or acceptance criteria — that belongs in the spec +- **Links:** Add the PR URL if one exists + +If the user asked to consolidate buckets, merge the relevant findings. + +### Step 4: Update the Audit Document + +Add a "Follow-up Issues" section to the top of the audit doc (below the header metadata) with a table of all created issues: + +```markdown +## Follow-up Issues + +| Issue | Priority | Title | +|-------|----------|-------| +| [SELF-NNNN](url) | Urgent | Bucket name | +| [SELF-NNNN](url) | High | Bucket name | +``` + +Update the audit doc status to: `Audit complete — issues created` + +### Step 5: Present Results + +Show the user: + +1. Table of created issues with IDs, priorities, and URLs +2. Any buckets that were skipped or consolidated +3. Remind them: "Use `/spec-from-audit` to generate specs for these issues." + +**Stop here.** Do not create specs. + +## Important Notes + +- Never run `git commit` or `git push`. +- Ask before creating — never create issues without user confirmation on team/project/priority. +- If a bucket seems too large (>2k LOC estimate), suggest splitting before creating. +- If buckets have dependencies between them, note this in the issue descriptions and use Linear's `blockedBy` field. diff --git a/.claude/skills/pr-audit/SKILL.md b/.claude/skills/pr-audit/SKILL.md new file mode 100644 index 000000000..7fe687d45 --- /dev/null +++ b/.claude/skills/pr-audit/SKILL.md @@ -0,0 +1,210 @@ +--- +name: pr-audit +description: Multi-agent PR review — component, integration, and routing analysis merged into a structured audit doc with severity and PR buckets. +disable-model-invocation: false +user-invocable: true +argument-hint: '[base-branch (default: dev)]' +--- + +# PR Audit + +You run a multi-pass review of the current branch, pull in external PR feedback, and produce a structured audit document with findings grouped into PR-sized fix buckets. + +This skill **stops at the audit doc**. It does not create issues or specs — use `/gaps-to-issues` and `/spec-from-audit` for those steps. + +## Input + +`$ARGUMENTS` — Optional base branch name (default: `dev`). + +## Workflow + +### Step 1: Gather the Diff + +Use `$ARGUMENTS` if provided, otherwise default to `dev`. + +Verify the base branch exists: + +```bash +git rev-parse --verify +``` + +If it does not exist, ask the user which branch to diff against. + +Gather the full diff and context: + +```bash +git log ..HEAD --oneline --no-decorate +git diff ...HEAD --stat +git diff ...HEAD +git diff ...HEAD --name-only +git status -s +``` + +Read the full diff carefully. You will reference it throughout the review passes. + +### Step 2: Multi-Agent Review (3 Parallel Passes) + +Run all three review passes **in parallel** using the Agent tool. Each agent receives the diff context and reviews through a specific lens. + +#### Pass A: Component-Level Review + +For every new or changed screen/component file, check: + +- Missing or incorrect imports (especially cross-package imports that don't resolve) +- Wrong or missing props vs the component's actual interface — read the component definition, don't guess +- Missing Lottie/animation assets or props that the component supports but aren't passed +- Placeholder logic left in production code (hardcoded values, no-op callbacks, `TODO`/`FIXME`) +- Missing error/loading/empty states +- Raw hex colors instead of design tokens +- Files exceeding 800 LOC + +#### Pass B: Integration-Level Review + +For the full set of changed files, check: + +- Provider/contract flow violations — does data flow match what specs define? +- State propagation between screens — is state passed correctly through navigation, or lost on refresh/direct-entry? +- Persistence gaps — state that should survive reload but only lives in `useState` +- Event ordering issues — race conditions, guards that swallow later events +- Breaking changes to shared interfaces consumed by other packages +- Build-time vs runtime config that should be dynamic + +#### Pass C: Route Wiring Review + +For all navigation-related changes, check: + +- Every `navigate()` / `push()` / `replace()` target has a corresponding route definition +- Every new route definition is reachable from at least one navigation call +- Orphaned routes — defined but unreachable +- Screens that receive `location.state` but have no guard for direct-entry (missing state) +- Deep link configuration consistency (if applicable) + +### Step 3: Pull in PR Feedback + +Check if a PR exists for this branch: + +```bash +gh pr view --json number,url 2>/dev/null +``` + +If a PR exists, fetch external review comments: + +```bash +gh api repos/{owner}/{repo}/pulls/{number}/comments --paginate +gh api repos/{owner}/{repo}/pulls/{number}/reviews --paginate +gh api repos/{owner}/{repo}/issues/{number}/comments --paginate +``` + +Extract actionable items from CodeRabbit, Codex, and human reviewers. Ignore resolved conversations and pure acknowledgment comments. + +If no PR exists, skip this step. + +### Step 4: Merge and Deduplicate + +Combine findings from all three passes and PR feedback. Deduplicate — if Pass A and a CodeRabbit comment flag the same issue, keep one entry and note both sources. + +Assign severity: + +| Severity | Criteria | +|----------|----------| +| **Critical** | Build-breaking, crashes, data loss, security holes, blocked user flows | +| **High** | Incorrect behavior visible to users, contract violations, missing error handling on critical paths | +| **Medium** | Missing states, prop mismatches, design token violations, placeholder logic | +| **Low** | Style, minor code quality, non-blocking TODOs | + +### Step 5: Group into PR-Sized Buckets + +Group findings into fix buckets. Each bucket: + +- Targets ≤2,000 LOC changed +- Groups related findings touching the same files or logical area +- Is independently mergeable (note dependencies if unavoidable) +- Has a clear name, estimated LOC, list of files, and acceptance criteria + +### Step 6: Write the Audit Document + +Determine the branch slug from the current branch name (strip `feat/`, `fix/`, etc., replace `/` with `-`). + +Create the audit document at: + +``` +docs/reviews/YYYY-MM-DD--audit.md +``` + +Create `docs/reviews/` if it does not exist. + +Structure: + +```markdown +# PR Audit — + +> Date: YYYY-MM-DD +> Branch: `` +> Base: `` +> PR: #NNN (if exists) +> Reviewers: Claude Code (component, integration, routing passes), [external reviewers] +> Status: Audit complete — ready for review + +## Summary + +[2-3 sentences describing what the branch does and the overall health assessment.] + +## Findings + +### Critical + +- [ ] [finding] — `path/to/file.ts:NN` + - **Source:** [Pass A | Pass B | Pass C | CodeRabbit | reviewer] + - **Fix:** [concrete action] + +### High + +- [ ] ... + +### Medium + +- [ ] ... + +### Low + +- [ ] ... + +## PR Buckets + +### Bucket 1: [name] +- **Estimated LOC:** ~NNN +- **Findings:** #1, #3, #7 +- **Files:** [list] +- **Dependencies:** [none | Bucket N must land first] +- **Acceptance criteria:** + - [ ] [criterion] + +### Bucket 2: [name] +... + +## What Works Well + +[List things the branch does correctly — not just problems.] +``` + +### Step 7: Present to User + +Show the user: + +1. Total finding count by severity +2. Each bucket with name, estimated LOC, and findings covered +3. Any findings that didn't fit cleanly into a bucket + +Tell the user: + +> "Audit complete. Review the findings and buckets above. When ready, use `/gaps-to-issues` to create Linear issues from these buckets, then `/spec-from-audit` to generate specs." + +**Stop here.** Do not create issues or specs. + +## Important Notes + +- Never run `git commit` or `git push`. Stage the audit doc but stop there. +- Read the full diff — do not summarize from file names alone. +- Findings must be specific and actionable with file paths and line numbers. +- When estimating LOC for buckets, be conservative. +- If the diff is very large (>10k LOC), warn the user and suggest focusing on critical/high only. diff --git a/.claude/skills/spec-from-audit/SKILL.md b/.claude/skills/spec-from-audit/SKILL.md new file mode 100644 index 000000000..ab0a600f0 --- /dev/null +++ b/.claude/skills/spec-from-audit/SKILL.md @@ -0,0 +1,136 @@ +--- +name: spec-from-audit +description: Generate one Linear spec document per issue — agent-executable implementation plans with file paths, validation commands, and acceptance criteria. +disable-model-invocation: false +user-invocable: true +argument-hint: '[issue IDs or path-to-audit-doc]' +--- + +# Spec from Audit + +You take Linear issues (created by `/gaps-to-issues`) and generate one spec per issue as a Linear document. Each spec is an agent-executable implementation plan — a new Claude Code session with no prior context should be able to pick up the spec and produce a correct PR. + +## Input + +`$ARGUMENTS` — Either: +- A list of Linear issue IDs (e.g., `SELF-2357 SELF-2358 SELF-2359`) +- A path to an audit doc that has a "Follow-up Issues" section + +If not provided, look for the most recently modified audit doc in `docs/reviews/` and extract issue IDs from it. + +## Workflow + +### Step 1: Gather Issue Context + +For each issue ID, fetch the issue details: + +- Use `mcp__linear-server__get_issue` to read the title, description, and acceptance criteria +- If an audit doc path is available, read the full audit doc for additional context on findings + +### Step 2: Read the Codebase + +For each issue, read the files referenced in its description/scope. You need to understand the **current state** of the code to write accurate specs with line numbers. + +Do not guess file contents — read them. Specs with wrong line numbers or stale code references are worse than no spec. + +### Step 3: Generate Specs + +For each issue, create a spec as a Linear document using `mcp__linear-server__create_document`. + +Link the document to the **issue** (not the project). + +**Title format:** `SPEC: ` + +**Spec structure:** + +```markdown +## Overview + +You are [doing what] in [which area]. [1-2 sentences on why this matters.] + +## Preconditions + +- [What must be true before starting — dependencies on other PRs, packages published, etc.] + +## [Problem 1 / Area 1]: [descriptive name] + +**File:** `path/to/file.ts` + +[Description of the current problem with exact line numbers.] + +### Fix + +[Step-by-step instructions. Be explicit about what to change and why.] + +## [Problem 2 / Area 2]: [descriptive name] + +... + +## Files to modify + +- `path/to/file.ts` — [what changes] +- `path/to/other.ts` — [what changes] + +## Files NOT to modify + +- `path/to/untouched/` — [why] + +## Dependencies + +- [Other issues/PRs that must land first, if any] + +## Validation + +```bash +[relevant validation commands from the repo] +``` + +## Definition of Done + +- [ ] [acceptance criterion from the issue] +- [ ] [acceptance criterion] +- [ ] All validation commands pass +``` + +### Spec-Writing Rules + +The cardinal rule: **if two reasonable engineers could implement different solutions from the same spec, the spec is too open.** Specs must contain decisions, not options. + +Follow these strictly: + +1. **Decisions, not options** — "Use local wrappers" not "Consider adding to Euclid or using local wrappers." Every ambiguous implementation choice must be resolved in the spec. If you genuinely can't decide, flag it as a blocker and ask the user — don't embed it as an option. +2. **Second person** — "You are fixing...", "You will modify..." +3. **Exact file paths with line numbers** — `src/utils/sumsubProvider.ts:118`, not "the provider file" +4. **Current code, not stale references** — you read the files in Step 2, use what you actually saw +5. **Explicit constraints** — "You will NOT modify..." sections prevent scope creep +6. **Required vs optional** — mark every item. Don't let agents infer priority. +7. **Validation command** — agents will run it. If it's not there, they'll skip validation. +8. **One spec = one PR ≤2k LOC** — if a spec feels like it would produce >2k LOC, flag it and suggest splitting +9. **Self-contained** — the spec must include enough context to execute without reading other specs or the full audit doc + +Every spec should follow this structure: +1. State the goal in one sentence +2. List decisions already made +3. Define scope and out-of-scope +4. Name the files to modify +5. Define done criteria in behavior terms + +### Step 4: Present Results + +Show the user: + +1. Table of created specs with issue IDs and document URLs +2. Any issues that were too complex and need manual spec-writing +3. Any issues where codebase reading revealed the problem is already fixed or different than described + +**Stop here.** + +## Important Notes + +- Never run `git commit` or `git push`. +- Always read the actual files before writing specs — stale line numbers are actively harmful. +- If an issue references files that don't exist, flag it rather than guessing. +- If you discover the issue description is wrong (e.g., the code was already fixed), note this and ask the user whether to still create the spec or update the issue. +- Specs are for agents, not humans. Write them as precise instructions, not explanatory documents. +- The spec is the source of truth, not the issue body. Issue bodies are lightweight pointers — the spec must be fully self-contained. Do not assume the agent has read the issue description. +- Link specs to issues (not projects). Use `mcp__linear-server__create_document` with the `issue` parameter. diff --git a/CLAUDE.md b/CLAUDE.md index f63605c44..6145b4e00 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,57 +46,54 @@ nvm use && corepack enable && yarn install ## Specs & Planning -**Every feature — even minor ones — uses the spec system.** Before implementing, read the relevant specs, write a plan to disk, then execute. No exceptions. A plan that only exists in session memory is a plan that will be lost. +**Every feature — even minor ones — needs a spec.** Specs live in **Linear** as documents attached to issues. Do not create spec files in the `specs/` folder — that folder is deprecated and being migrated to Linear. -### Spec System (`specs/`) +### Where Specs Live -| File | Purpose | When to Read | -| ------------------------------------------------ | ------------------------------------------- | ------------------------ | -| [Specs README](./specs/README.md) | Table of contents, reading order | First. Always. | -| [Templates](./specs/framework/TEMPLATES.md) | Copy-paste templates for all three tiers | When creating a new spec | -| [SDK Overview](./specs/projects/sdk/OVERVIEW.md) | Architecture, bridge protocol, module table | For system-level context | - -Workstream specs live in `specs/projects/sdk/workstreams/*/` with `SPEC.md` (living implementation details). - -### Spec-Reading Protocol (for chunk execution) - -To execute a chunk: - -1. Read `specs/projects/sdk/INDEX.md` — find your workstream -2. Read the workstream `SPEC.md` — find your chunk -3. If you need architecture context, read the project `OVERVIEW.md` - -That's it. Do not read framework docs unless you are writing a new spec. +- **Specs → Linear documents**, attached to the relevant Linear issue (not the project) +- **Architecture context → `specs/projects/sdk/OVERVIEW.md`** (read-only reference, still in repo) +- **Audit docs → `docs/reviews/`** (PR audit findings, kept in repo for git history) +- **`specs/` folder** — deprecated. Existing specs are being migrated to Linear. Do not create new files here. ### Planning Protocol -1. **Read** the relevant workstream specs and this file's Key Rules — understand the current state and constraints -2. **Write a plan to disk** — use the appropriate tier from `specs/framework/TEMPLATES.md`: - - **Large features / new workstreams:** Create a full implementation spec (`specs/projects/sdk/workstreams//SPEC.md`) - - **Medium features / multi-chunk work:** Create a plan file in `workstreams//plans/` named `-.md` and link it from the backlog in the relevant `SPEC.md` - - **Small features / single-chunk fixes:** Create a minimal plan file in `workstreams//plans/` named `-.md` or add the chunk to an existing active plan -3. **Include in every plan:** scope of work, files modified, I/O examples, validation command, definition of done -4. **Then implement** — update chunk status as you complete work -5. **After completion:** Mark chunks done in SPEC.md status tables. Review status checklists at session start — if something is marked "Done" that isn't, or "Pending" that's in progress, fix it first. +1. **Read** this file's Key Rules and any relevant Linear issue/spec — understand the current state and constraints +2. **Create a Linear issue** if one doesn't exist — include scope, files modified, acceptance criteria +3. **Create a spec as a Linear document** attached to the issue — this is the execution plan +4. **Then implement** — one spec = one PR ≤2k LOC +5. **After completion:** Update the Linear issue status. Close when done. ### Spec-Writing Guidelines -When writing specs, follow these principles so they work as AI agent prompts: +Specs are agent-executable prompts. A new Claude Code session with no prior context must be able to pick up the spec and produce a correct PR. -- **Use second person.** "You are making X portable" not "X should be made portable." +- **Make decisions, not options.** "Use local wrappers" not "Consider adding to Euclid or using local wrappers." Agents can't choose between approaches — tell them which one. +- **Use second person.** "You are fixing X" not "X should be fixed." - **Be explicit about constraints.** "You will NOT modify..." not just "Focus on..." -- **Provide exact file paths with line numbers.** `src/proving/provingMachine.ts:543` not "the proving machine file." +- **Provide exact file paths with line numbers.** `src/utils/sumsubProvider.ts:118` not "the provider file." - **State the validation command.** Agents will run it. If it's not there, they'll skip validation. -- **One chunk = one self-contained prompt.** The chunk must include enough context to execute without reading the full spec. -- **One PR = one plan file.** A plan file is the execution handoff. It must be self-contained enough that a new agent can pick it up after session loss. -- **Use `--remote` for M and L chunks.** Medium and large chunks benefit from `claude --remote` so work continues in the background. +- **One spec = one PR ≤2k LOC.** If a spec would produce >2k LOC, split it. +- **Mark items as required vs optional.** Don't let agents infer priority. +- **Include out-of-scope sections.** These are as important as in-scope sections for preventing drift. +- **Use `--remote` for medium+ work.** Medium and large specs benefit from `claude --remote` so work continues in the background. -### Why Even Minor Features +### Audit Pipeline Skills + +Three Claude Code skills automate the review-to-implementation pipeline: + +1. **`/pr-audit`** — Multi-agent review (component + integration + routing), produces audit doc in `docs/reviews/` +2. **`/gaps-to-issues`** — Creates Linear issues from audit PR buckets +3. **`/spec-from-audit`** — Generates agent-executable specs as Linear documents, one per issue + +Run them in sequence with review pauses between each step. + +### Why Specs - Prevents scope creep — writing "files NOT modified" forces focus -- Survives session loss — API errors, context overflow, `/clear` won't destroy the plan -- Enables parallel work — multiple agents can pick up chunks from the same plan +- Survives session loss — specs live in Linear, not session memory +- Enables parallel work — multiple agents can pick up specs from the same project - Creates audit trail — what was planned vs what was built +- Enables cross-tool review — anyone with Linear access can review specs, not just GitHub users ## Validation Commands diff --git a/app/AGENTS.md b/app/AGENTS.md index 77ff59089..059fad1a4 100644 --- a/app/AGENTS.md +++ b/app/AGENTS.md @@ -290,5 +290,5 @@ See `.cursor/rules/test-memory-optimization.mdc` for comprehensive guidelines, e The Self Wallet app serves as a **test environment** for the SDK refactor. For SDK architecture context: -- **[SDK Overview](../specs/projects/sdk/OVERVIEW.md)** — System architecture, bridge protocol, decision matrix -- **[SDK Project Index](../specs/projects/sdk/INDEX.md)** — Workstream links and entry point +- **[SDK Overview](../specs/projects/sdk/OVERVIEW.md)** — System architecture, bridge protocol, decision matrix (read-only reference) +- **Implementation specs** — Live in Linear as documents attached to issues. Check the relevant Linear issue for the current spec. Do not create spec files in `specs/` — that folder is deprecated. diff --git a/docs/reviews/2026-03-23-euclid-settings-tunnel-audit.md b/docs/reviews/2026-03-23-euclid-settings-tunnel-audit.md new file mode 100644 index 000000000..29d61992c --- /dev/null +++ b/docs/reviews/2026-03-23-euclid-settings-tunnel-audit.md @@ -0,0 +1,160 @@ +# PR Audit — feat/euclid-settings-screens-rd2 + +> Date: 2026-03-23 +> Branch: `feat/euclid-settings-screens-rd2` +> Base: `dev` +> PR: #1858 +> Reviewers: Claude Code, Codex +> Status: Ship as-is with follow-up issues for known gaps + +## Follow-up Issues + +| Issue | Priority | Title | +| ------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------ | +| [SELF-2357](https://linear.app/selfprotocol/issue/SELF-2357) | Urgent | Euclid migration — complete euclid-web → euclid and validate API compatibility | +| [SELF-2358](https://linear.app/selfprotocol/issue/SELF-2358) | High | Sumsub / WV-05 contract compliance | +| [SELF-2359](https://linear.app/selfprotocol/issue/SELF-2359) | High | Tunnel + proving flow data propagation and UI contract fixes | +| [SELF-2360](https://linear.app/selfprotocol/issue/SELF-2360) | Medium | Settings persistence, test coverage, and doc/spec cleanup | + +Each issue has a spec attached as a Linear document. + +## Summary + +This PR adds Euclid 3.0 settings sub-screens (Security, Notifications, Dev Mode), a PoC tunnel flow, the `euclid-web` → `euclid` migration, and a Sumsub Web SDK integration in the provider launch flow. It ships with known gaps that are tracked as follow-up issues. + +## Merged Findings + +Findings from both Claude Code and Codex reviews, deduplicated and grouped by follow-up issue. + +### Issue 1: Euclid migration — complete `euclid-web` → `euclid` and validate exports + +**Severity:** Critical (build-breaking) + +- 3 settings screen components (`SecurityScreen`, `NotificationPreferencesScreen`, `DevModeScreen`) are imported from `@selfxyz/euclid` but not yet exported from the package +- `LaunchTour1Screen`–`LaunchTour4Screen` don't exist — Euclid only exports a single `TourScreen` +- `ProofGenerationScreen` doesn't exist — only a `ProofGeneration` component (not a screen wrapper) +- ~~`ProviderResultScreen` still imports from `@selfxyz/euclid-web`~~ — **fixed** (migrated to `@selfxyz/euclid`) +- ~~`ProviderLaunchScreen` still imports from `@selfxyz/euclid-web`~~ — **fixed** (migrated to `@selfxyz/euclid`, removed unused `BodyText`) +- Multiple screens migrated from `euclid-web` to `euclid` — blast radius is package-wide, not just settings +- `euclid-web` is being retired; all imports should use `@selfxyz/euclid` +- Settings handover doc (`docs/superpowers/plans/2026-03-22-settings-handover.md (legacy location)`) says the app still imports `euclid-web` — stale + +**Acceptance criteria:** + +- All imports use `@selfxyz/euclid`, zero references to `euclid-web` +- All imported screen components exist in Euclid's exports +- `yarn workspace @selfxyz/webview-app build` passes + +### Issue 2: Sumsub / WV-05 contract compliance + +**Severity:** High (incorrect behavior) + +- `normalizeSumsubStatus()` marks `reviewAnswer === 'GREEN'` as `status: 'success'` without requiring `attestation` — breaks the KYC contract +- `onApplicantSubmitted` emits `status: 'partial'` through the `onComplete` callback (not `onError`), so `ProviderResultScreen` treats it as success and routes to `/proving` +- `emitOnce` guard means if `onApplicantSubmitted` fires before `applicantReviewComplete`, the actual terminal status is silently dropped +- `ProvingScreen` fabricates a successful `VerificationResult` without consuming any provider attestation payload +- `teeUrl` not parsed from launch URL/query params — `fetchSumsubAccessToken()` uses `VITE_SUMSUB_TEE_URL` or hardcoded default +- `fetchSumsubAccessToken` signature doesn't accept `teeUrl` as a parameter — even after parsing, plumbing is missing +- Token refresh callback in `launchSumsubWebSdk` (line 167) also calls `fetchSumsubAccessToken()` with no args — refresh hits hardcoded URL too +- `sumsub-websdk.d.ts` custom type declaration added — unclear if it matches actual SDK API or is a stub +- WV-05 plan says "code complete, needs testing" but `teeUrl` isn't parsed, service file structure wasn't created, and normalization doesn't match the contract +- Invalid step value: `TunnelProvingScreen` passes `step="generatingProof"` — valid values are `"registeringId" | "generatingProof1" | "awaitingVerification" | "finishingUp"` + +**Acceptance criteria:** + +- `normalizeSumsubStatus` requires attestation for `success` +- `partial` status blocked from entering proving flow +- `teeUrl` parsed from query params, threaded into token fetch and refresh +- WV-05 plan status downgraded to "partial implementation" + +### Issue 3: Tunnel flow correctness and state propagation + +**Severity:** High (data loss / broken UX) + +- Selected ID type discarded in `TunnelIDTypeScreen.onIDTypeSelect` — ignores the param, always navigates to `/tunnel/proof/receipt` +- State passed via `location.state` is fragile — lost on refresh or direct navigation, no fallback or route guard +- `TunnelProofReceiptScreen` passes invalid `documentType="passport"` prop to `ProofRequestScreen` (not a valid prop) +- `TunnelProofReceiptScreen` mock items lack icons and `onInfoPress` callbacks +- `KycMockScreen` is raw HTML divs with inline styles instead of Euclid components +- `TunnelResultScreen` missing `animationSource` (Lottie) — uses icon only +- `Date.now()` in `TunnelProofReceiptScreen` creates new timestamp on every render +- Main proving flow (`ProvingScreen.tsx`) hardcodes `documentType='passport'` — affects primary flow, not just tunnel + +**Acceptance criteria:** + +- ID type selection persisted and forwarded through the tunnel chain +- Screens handle missing state gracefully (redirect or error) +- Correct Euclid component props used throughout + +### Issue 4: Settings persistence and bridge-backed actions + +**Severity:** Medium (non-functional features) + +- `SecurityScreen` backup state is hardcoded `false` — should query actual state from bridge/storage +- `SecurityScreen` handlers for backup, recovery phrase, restore all navigate to `/coming-soon` +- `NotificationPreferencesScreen` toggles are local `useState` — lost on reload, never persisted +- `DevModeScreen` mock config is local state — lost on reload +- `DevModeScreen` "Generate mock document" fires analytics but doesn't create or store anything +- `SettingsScreen` "Manage Documents", "Get support", "Share Self" all navigate to `/coming-soon` + +**Acceptance criteria:** + +- Settings state backed by bridge/storage where applicable +- Placeholder routes documented as intentional + +### Issue 5: Test coverage + +**Severity:** Medium + +- No tests for Sumsub normalization/result mapping (`normalizeSumsubStatus`, `buildProviderResult`) +- No route tests for tunnel flow progression +- No smoke tests for settings wrapper navigation and action wiring +- No tests for provider launch/result flow + +**Acceptance criteria:** + +- Unit tests for Sumsub normalization logic +- Route/navigation tests for tunnel flow +- Smoke tests for settings screens + +### Issue 6: Doc/spec drift + +**Severity:** Low + +- Settings handover doc (`docs/superpowers/plans/2026-03-22-settings-handover.md (legacy location)`) is stale — references `euclid-web`, understates scope +- Settings integration plan (`docs/superpowers/plans/2026-03-22-settings-screen-integration.md (legacy location)`) scoped to settings only but branch includes tunnel/provider/migration +- WV-05 plan status overstated +- Orphaned route: `/onboarding/confirm` defined in App.tsx but never navigated to + +**Acceptance criteria:** + +- Docs reflect actual branch state +- WV-05 status corrected +- Orphaned route removed or documented + +## Actionable PR feedback (CodeRabbit + Codex connector) + +Extracted from PR #1858 inline comments. Items already covered above are not repeated. + +### Build-breaking (addressed in this branch) + +- **`@selfxyz/euclid-web` removed from `package.json` but still imported** — `ProviderLaunchScreen` and `ProviderResultScreen` still referenced it. Both migrated to `@selfxyz/euclid` in this branch. (CodeRabbit P1, Codex connector P1) + +### Actionable (not yet addressed) + +- **`TunnelIDTypeScreen`: pass selected `idType` to next screen** — `onIDTypeSelect` ignores the param. Suggested fix: `navigate('/tunnel/proof/receipt', { state: { documentType: idType.id } })`. (CodeRabbit, mapped to Issue 3) +- **`TunnelProofReceiptScreen`: read document type from route state** — Currently hardcodes `documentType="passport"`. Should read from `location.state` with fallback. (CodeRabbit, mapped to Issue 3) +- **`NotificationPreferencesScreen`: persist toggles** — Use `storage` adapter from `useSelfClient()` to save/load preferences. (CodeRabbit, mapped to Issue 4) +- **`DevModeScreen`: mock document generation is a no-op** — Should either persist the mock or show a toast indicating the feature isn't functional yet. (CodeRabbit, mapped to Issue 4) + +### Nitpicks (low priority) + +- **`NotificationPreferencesScreen`: memoize toggle handlers** — `toggles` array recreated every render. `useMemo` with `toggleValues` as dependency would be cleaner. + +## What works well + +- Route wiring is clean — every route has a file, every `navigate()` targets a defined route +- Settings menu restructure (App settings / Support & feedback / Developer tools) is well-organized +- Sumsub SDK lifecycle management (mount/destroy/abort) is properly handled +- Analytics events are consistently tracked across all screens +- `emitOnce` pattern prevents duplicate callbacks (though it has the silenced-review-status side effect) diff --git a/docs/superpowers/plans/2026-03-22-settings-handover.md b/docs/superpowers/plans/2026-03-22-settings-handover.md new file mode 100644 index 000000000..5a445eeea --- /dev/null +++ b/docs/superpowers/plans/2026-03-22-settings-handover.md @@ -0,0 +1,102 @@ +# Euclid 3.0 Settings Integration — Handover + +**Branch:** `feat/euclid-settings-screens` (based off `origin/main`) +**Commit:** `feat(webview-app): add Euclid 3.0 settings sub-screens` + +## What Was Done + +Three new wrapper screens were added to `packages/webview-app/src/screens/account/`: + +| File | Euclid Component | Route | +| ----------------------------------- | ------------------------------- | ------------------------- | +| `SecurityScreen.tsx` | `SecurityScreen` | `/settings/security` | +| `NotificationPreferencesScreen.tsx` | `NotificationPreferencesScreen` | `/settings/notifications` | +| `DevModeScreen.tsx` | `DevModeScreen` | `/settings/dev-mode` | + +Routes added to `App.tsx`. `SettingsScreen.tsx` updated with three sections (App settings, Support & feedback, Developer tools) — "Security", "Notifications", and "Dev mode" now navigate to real screens instead of `/coming-soon`. + +Each wrapper follows the existing pattern (see `CountryPickerScreen.tsx`): import Euclid component, wire with `useNavigate()` + `useSelfClient()` bridge adapters, manage local UI state. + +## Blocker: Euclid Package Publish + +The installed `@selfxyz/euclid-web@1.0.2` does **not** export `SecurityScreen`, `NotificationPreferencesScreen`, or `DevModeScreen`. These screens exist on `origin/main` of the **euclid repo** (`/Users/evinova-self/Documents/euclid`) but haven't been published to npm yet. + +**To unblock, you need to publish a new version of `@selfxyz/euclid`** (note: package was renamed from `euclid-web` to `euclid` on euclid's `origin/main`). + +Steps: + +1. In the euclid repo, check out `origin/main` and verify the screens export: `git show origin/main:packages/euclid/src/screens/index.ts` +2. Bump the version and publish (the euclid repo has automated publishing via PR merge of version bump PRs) +3. In the self repo, update `packages/webview-app/package.json` to use the new version (and rename dependency from `@selfxyz/euclid-web` to `@selfxyz/euclid` if the package name changed) +4. Run `yarn install` to pull the new package +5. Run `yarn workspace @selfxyz/webview-app exec tsc --noEmit` to verify type-check passes + +### Package Rename Note + +The euclid repo renamed the web package from `@selfxyz/euclid-web` to `@selfxyz/euclid`. The webview-app still imports from `@selfxyz/euclid-web`. When updating the dependency, you'll also need to update all import paths across the webview-app screens: + +``` +- import { ... } from '@selfxyz/euclid-web'; ++ import { ... } from '@selfxyz/euclid'; +``` + +## Pre-Existing Type Errors + +These errors exist on `origin/main` independent of our changes: + +- `BridgeProvider.tsx` — `browserHost` not in `WebViewBridgeOptions` +- `SelfClientProvider.tsx` — `lifecycle.dismiss()` argument mismatch +- `ConfirmIdentificationScreen.tsx`, `ProvingScreen.tsx`, `VerificationResultScreen.tsx` — `VerificationResult` type mismatch +- `ProviderLaunchScreen.tsx` — `lifecycle.dismiss()` argument mismatch + +## What Still Uses `/coming-soon` + +These items in SettingsScreen still navigate to `/coming-soon` (no Euclid screen exists yet): + +- **"Manage Documents"** — needs a document management screen +- **"Get support"** — needs external link / bridge intent +- **"Share Self"** — needs native share sheet via bridge + +Within the sub-screens, these actions are also stubbed: + +- **SecurityScreen**: "Backup your account", "Reveal recovery phrase", "Restore an account" → all go to `/coming-soon` (need native bridge integration for biometric auth, iCloud backup, wallet restore) +- **NotificationPreferencesScreen**: Toggle state is local-only (needs bridge storage persistence) +- **DevModeScreen**: "Generate mock document" tracks analytics and navigates home but doesn't actually create a mock document yet + +## Navigation Flow + +``` +/settings (SettingsViewScreen) + ├── Manage Documents → /coming-soon + ├── Security → /settings/security ✅ NEW + │ ├── Backup your account → /coming-soon + │ ├── Reveal recovery phrase → /coming-soon + │ ├── Restore an account → /coming-soon + │ └── Disable backups → local dialogue toggle + ├── Notifications → /settings/notifications ✅ NEW + │ └── Toggles → local state only + ├── Get support → /coming-soon + ├── Share Self → /coming-soon + ├── Dev mode → /settings/dev-mode ✅ NEW + │ ├── Steppers/toggles → local state + │ ├── Generate mock document → analytics + navigate home + │ └── Reset all values → reset state + └── Close Self → lifecycle.dismiss() +``` + +## Validation + +After unblocking the euclid dependency: + +```bash +yarn workspace @selfxyz/webview-app exec tsc --noEmit # type-check +yarn workspace @selfxyz/webview-app build # Vite production build +yarn lint # lint +``` + +## Related Resources + +- **Implementation plan:** `docs/superpowers/plans/2026-03-22-settings-screen-integration.md` +- **Euclid screen source:** `euclid repo origin/main:packages/euclid/src/screens/settings/` +- **Euclid storybook stories:** `euclid repo origin/main:packages/storybook/stories/*Screen.stories.tsx` +- **Linear tickets:** SELF-2223 (Settings), SELF-2311 (Update core app) diff --git a/docs/superpowers/plans/2026-03-22-settings-screen-integration.md b/docs/superpowers/plans/2026-03-22-settings-screen-integration.md new file mode 100644 index 000000000..a3167225b --- /dev/null +++ b/docs/superpowers/plans/2026-03-22-settings-screen-integration.md @@ -0,0 +1,667 @@ +# Settings Screen Integration — Import Euclid 3.0 Screens into WebView App + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the five `/coming-soon` navigations in the webview-app's SettingsScreen with real Euclid 3.0 sub-screens (Security, Notifications, Dev Mode), plus wire the remaining menu items to appropriate bridge actions. + +**Architecture:** Each settings sub-screen gets a thin wrapper in `packages/webview-app/src/screens/account/` that imports the Euclid component from `@selfxyz/euclid-web`, wires it with `useSelfClient()` bridge adapters and `useNavigate()` from React Router, and manages local UI state (e.g. dialogue visibility, toggle values). Routes are added to `App.tsx`. The existing wrapper pattern (see `CountryPickerScreen.tsx`, `ComingSoonScreen.tsx`) is followed exactly. + +**Tech Stack:** React, React Router, `@selfxyz/euclid-web` (Euclid 3.0 component library), `@selfxyz/webview-bridge` (bridge adapters) + +**Existing pattern to follow:** + +``` +// webview-app wrapper screen pattern: +import { EuclidScreen } from '@selfxyz/euclid-web'; +import { useSelfClient } from '../../providers/SelfClientProvider'; +import { useNavigate } from 'react-router-dom'; + +export const WrapperScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic, lifecycle } = useSelfClient(); + // Wire Euclid props to bridge adapters + React Router + return ; +}; +``` + +--- + +## File Map + +| Action | File | Responsibility | +| ------ | ---------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Modify | `packages/webview-app/src/App.tsx` | Add 3 new routes | +| Modify | `packages/webview-app/src/screens/account/SettingsScreen.tsx` | Replace `/coming-soon` navigations with real routes + bridge actions | +| Create | `packages/webview-app/src/screens/account/SecurityScreen.tsx` | Wrapper for Euclid `SecurityScreen` | +| Create | `packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx` | Wrapper for Euclid `NotificationPreferencesScreen` | +| Create | `packages/webview-app/src/screens/account/DevModeScreen.tsx` | Wrapper for Euclid `DevModeScreen` | + +--- + +## Task 1: SecurityScreen wrapper + +The most important sub-screen. Wires backup state, recovery phrase, and restore actions through the bridge. + +**Files:** + +- Create: `packages/webview-app/src/screens/account/SecurityScreen.tsx` + +- [ ] **Step 1: Create SecurityScreen wrapper** + +```tsx +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + SecurityScreen as EuclidSecurityScreen, + LeftArrowIcon, + CloudKeyIcon, + LockIcon, + ZapShieldIcon, +} from '@selfxyz/euclid-web'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +export const SecurityScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic, storage } = useSelfClient(); + const [isBackupEnabled, setIsBackupEnabled] = useState(false); + const [showDisableDialogue, setShowDisableDialogue] = useState(false); + + // TODO: Read actual backup state from bridge storage on mount + // useEffect(() => { storage.get('backup_enabled').then(...) }, []); + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const onBackupAccount = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_backup_account_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onRevealRecoveryPhrase = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_reveal_phrase_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onRestoreAccount = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_restore_account_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onDisableBackups = useCallback(() => { + haptic.trigger('warning'); + setShowDisableDialogue(true); + }, [haptic]); + + const onDisableICloudBackups = useCallback(() => { + haptic.trigger('warning'); + analytics.trackEvent('security_backups_disabled'); + setIsBackupEnabled(false); + setShowDisableDialogue(false); + // TODO: Persist via bridge storage + }, [haptic, analytics]); + + const onDismissDialogue = useCallback(() => { + haptic.trigger('selection'); + setShowDisableDialogue(false); + }, [haptic]); + + return ( + ( + + )} + cloudKeyIcon={CloudKeyIcon} + lockIcon={LockIcon} + zapShieldIcon={ZapShieldIcon} + isBackupEnabled={isBackupEnabled} + onBack={onBack} + onBackupAccount={onBackupAccount} + onRevealRecoveryPhrase={onRevealRecoveryPhrase} + onRestoreAccount={onRestoreAccount} + onDisableBackups={onDisableBackups} + showDisableDialogue={showDisableDialogue} + onDisableICloudBackups={onDisableICloudBackups} + onDismissDialogue={onDismissDialogue} + /> + ); +}; +``` + +- [ ] **Step 2: Verify type-check passes** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: No errors related to SecurityScreen + +- [ ] **Step 3: Commit** + +```bash +git add packages/webview-app/src/screens/account/SecurityScreen.tsx +git commit -m "feat(webview-app): add SecurityScreen wrapper for Euclid 3.0" +``` + +--- + +## Task 2: NotificationPreferencesScreen wrapper + +Manages toggle state locally (persisted via bridge storage in future). + +**Files:** + +- Create: `packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx` + +- [ ] **Step 1: Create NotificationPreferencesScreen wrapper** + +```tsx +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + NotificationPreferencesScreen as EuclidNotificationPreferencesScreen, + LeftArrowIcon, +} from '@selfxyz/euclid-web'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +const defaultToggles = [ + { + key: 'self', + label: 'Allow Self notifications', + description: 'App updates and more', + }, + { + key: 'nova', + label: 'Allow Nova notifications', + description: 'Never miss a mission', + }, + { + key: 'points', + label: 'Allow Self Points notifications', + description: 'Points and rewards', + }, + { + key: 'id_status', + label: 'Allow ID status notifications', + description: 'Document verification updates', + }, +]; + +export const NotificationPreferencesScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic } = useSelfClient(); + const [toggleValues, setToggleValues] = useState>({ + self: true, + nova: true, + points: true, + id_status: false, + }); + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const toggles = defaultToggles.map(t => ({ + label: t.label, + description: t.description, + value: toggleValues[t.key] ?? false, + onToggleChange: (value: boolean) => { + haptic.trigger('selection'); + analytics.trackEvent('notification_toggle_changed', { + key: t.key, + value, + }); + setToggleValues(prev => ({ ...prev, [t.key]: value })); + }, + })); + + return ( + ( + + )} + onBack={onBack} + toggles={toggles} + /> + ); +}; +``` + +- [ ] **Step 2: Verify type-check passes** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: No errors related to NotificationPreferencesScreen + +- [ ] **Step 3: Commit** + +```bash +git add packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx +git commit -m "feat(webview-app): add NotificationPreferencesScreen wrapper for Euclid 3.0" +``` + +--- + +## Task 3: DevModeScreen wrapper + +Manages mock document generation state. More complex — has steppers, dropdowns, toggle, and IDCard display. + +**Files:** + +- Create: `packages/webview-app/src/screens/account/DevModeScreen.tsx` + +- [ ] **Step 1: Create DevModeScreen wrapper** + +```tsx +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + DevModeScreen as EuclidDevModeScreen, + LeftArrowIcon, +} from '@selfxyz/euclid-web'; +import type { IDCardProps } from '@selfxyz/euclid-web'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +const ageOptions = ['18 or older', '21 or older', '25 or older', '30 or older']; +const expiryOptions = ['1 year', '2 years', '5 years', '10 years']; + +export const DevModeScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic } = useSelfClient(); + + const [documentType, setDocumentType] = useState('passport'); + const [nationality, setNationality] = useState('united states of america'); + const [ageIndex, setAgeIndex] = useState(1); + const [expiryIndex, setExpiryIndex] = useState(2); + const [ofacCheck, setOfacCheck] = useState(true); + + const idCard: IDCardProps = { + variant: 'dev-passport', + title: 'Developer Passport', + subtitle: 'Digital credential for developers', + }; + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const onResetAllValues = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('dev_mode_reset'); + setDocumentType('passport'); + setNationality('united states of america'); + setAgeIndex(1); + setExpiryIndex(2); + setOfacCheck(true); + }, [haptic, analytics]); + + const onGenerateMockDocument = useCallback(() => { + haptic.trigger('success'); + analytics.trackEvent('dev_mode_generate_mock', { + documentType, + nationality, + age: ageOptions[ageIndex], + expiresIn: expiryOptions[expiryIndex], + ofacCheck, + }); + navigate('/'); + }, [ + navigate, + haptic, + analytics, + documentType, + nationality, + ageIndex, + expiryIndex, + ofacCheck, + ]); + + return ( + ( + + )} + onBack={onBack} + idCard={idCard} + documentType={documentType} + onDocumentTypePress={() => { + setDocumentType(prev => (prev === 'passport' ? 'id_card' : 'passport')); + }} + nationality={nationality} + onNationalityPress={() => { + setNationality(prev => + prev === 'united states of america' + ? 'germany' + : 'united states of america', + ); + }} + age={ageOptions[ageIndex]} + onAgeIncrement={() => + setAgeIndex(prev => Math.min(prev + 1, ageOptions.length - 1)) + } + onAgeDecrement={() => setAgeIndex(prev => Math.max(prev - 1, 0))} + documentExpiresIn={expiryOptions[expiryIndex]} + onDocumentExpiresIncrement={() => + setExpiryIndex(prev => Math.min(prev + 1, expiryOptions.length - 1)) + } + onDocumentExpiresDecrement={() => + setExpiryIndex(prev => Math.max(prev - 1, 0)) + } + ofacCheck={ofacCheck} + onOfacCheckChange={value => { + haptic.trigger('selection'); + setOfacCheck(value); + }} + onResetAllValues={onResetAllValues} + onGenerateMockDocument={onGenerateMockDocument} + /> + ); +}; +``` + +- [ ] **Step 2: Verify type-check passes** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: No errors related to DevModeScreen + +- [ ] **Step 3: Commit** + +```bash +git add packages/webview-app/src/screens/account/DevModeScreen.tsx +git commit -m "feat(webview-app): add DevModeScreen wrapper for Euclid 3.0" +``` + +--- + +## Task 4: Wire routes in App.tsx + +Add the three new routes under `/settings/*`. + +**Files:** + +- Modify: `packages/webview-app/src/App.tsx` + +- [ ] **Step 1: Add imports and routes** + +Add these imports after the existing `SettingsScreen` import (line 16): + +```tsx +import { SecurityScreen } from './screens/account/SecurityScreen'; +import { NotificationPreferencesScreen } from './screens/account/NotificationPreferencesScreen'; +import { DevModeScreen } from './screens/account/DevModeScreen'; +``` + +Add these routes after the `/settings` route (after line 34): + +```tsx +} /> +} /> +} /> +``` + +- [ ] **Step 2: Verify type-check passes** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: PASS + +- [ ] **Step 3: Commit** + +```bash +git add packages/webview-app/src/App.tsx +git commit -m "feat(webview-app): add settings sub-screen routes" +``` + +--- + +## Task 5: Wire SettingsScreen menu items to real routes + +Replace the five `/coming-soon` navigations with real routes and bridge actions. + +**Files:** + +- Modify: `packages/webview-app/src/screens/account/SettingsScreen.tsx` + +- [ ] **Step 1: Update SettingsScreen menu items** + +Replace the full component with updated navigation wiring. Key changes: + +- "View document info" → `navigate('/coming-soon')` (no Euclid screen for this yet — keep as-is) +- "Recovery phrase" → `navigate('/settings/security')` (Security screen handles this) +- "Cloud backup" → `navigate('/settings/security')` (Security screen handles this) +- "Get support" → call `lifecycle.dismiss()` with support intent (or keep `/coming-soon`) +- "Share Self" → call `lifecycle.dismiss()` with share intent (or keep `/coming-soon`) + +Additionally, add the Settings sub-sections that match the Euclid Storybook stories: + +- **App settings section**: Manage Documents, Security, Notifications +- **Support section**: Support, Send feedback +- **Developer tools section** (conditional): Dev mode + +```tsx +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + SettingsViewScreen, + LeftArrowIcon, + QuestionCircleStrokeIcon, + DocumentDetailsIcon, + LockIcon, + NotificationIcon, + ChatStrokeIcon, + ShareIcon, + CodeIcon, +} from '@selfxyz/euclid-web'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +export const SettingsScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic, lifecycle } = useSelfClient(); + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/'); + }, [navigate, haptic]); + + const onDismiss = useCallback(async () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_dismiss_pressed'); + lifecycle.dismiss(); + }, [haptic, analytics, lifecycle]); + + return ( + ( + + )} + infoIcon={({ size, color }) => ( + + )} + onClose={onBack} + showBackupInfoBox={false} + isBackupEnabled={false} + CTAs={[]} + sections={[ + { + title: 'App settings', + items: [ + { + icon: DocumentDetailsIcon, + label: 'Manage Documents', + description: 'Recovery phrase, passport data', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_manage_documents_pressed'); + navigate('/coming-soon'); + }, + }, + { + icon: LockIcon, + label: 'Security', + description: 'Recovery phrase, passport data', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_security_pressed'); + navigate('/settings/security'); + }, + }, + { + icon: NotificationIcon, + label: 'Notifications', + description: 'Preferences, notification types', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_notifications_pressed'); + navigate('/settings/notifications'); + }, + }, + ], + }, + { + title: 'Support & feedback', + items: [ + { + icon: ChatStrokeIcon, + label: 'Get support', + description: 'Help center & support', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_support_pressed'); + navigate('/coming-soon'); + }, + }, + { + icon: ShareIcon, + label: 'Share Self', + description: 'Share Self with friends', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_share_pressed'); + navigate('/coming-soon'); + }, + }, + ], + }, + { + title: 'Developer tools', + items: [ + { + icon: CodeIcon, + label: 'Dev mode', + description: 'Manage mock IDs, simulate proofs', + onPress: () => { + haptic.trigger('selection'); + analytics.trackEvent('settings_dev_mode_pressed'); + navigate('/settings/dev-mode'); + }, + }, + ], + }, + ]} + connectHeading="" + connectSubheading="" + connectButtons={[]} + bottomSectionItems={[ + { + label: 'Close Self', + onPress: onDismiss, + }, + ]} + /> + ); +}; +``` + +- [ ] **Step 2: Verify type-check passes** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: PASS + +- [ ] **Step 3: Verify Vite build succeeds** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app build` +Expected: Build succeeds, bundle output in `dist/` + +- [ ] **Step 4: Commit** + +```bash +git add packages/webview-app/src/screens/account/SettingsScreen.tsx +git commit -m "feat(webview-app): wire settings menu to Security, Notifications, and DevMode screens" +``` + +--- + +## Task 6: Final validation + +- [ ] **Step 1: Full type-check** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app exec tsc --noEmit` +Expected: PASS with zero errors + +- [ ] **Step 2: Vite production build** + +Run: `cd /Users/evinova-self/Documents/self && yarn workspace @selfxyz/webview-app build` +Expected: Build succeeds. Bundle size should be similar to before (~294 KB, may increase slightly with 3 new screens) + +- [ ] **Step 3: Lint check** + +Run: `cd /Users/evinova-self/Documents/self && yarn lint` +Expected: No new lint errors in changed files + +--- + +## Navigation Flow After Implementation + +``` +/settings (SettingsViewScreen) + ├─ "Manage Documents" → /coming-soon (no screen yet) + ├─ "Security" → /settings/security (SecurityScreen) + │ ├─ "Backup your account" → /coming-soon (future: backup flow) + │ ├─ "Reveal recovery phrase" → /coming-soon (future: bridge to native) + │ ├─ "Restore an account" → /coming-soon (future: restore flow) + │ └─ "Disable backups" → shows dialogue → local state toggle + ├─ "Notifications" → /settings/notifications (NotificationPreferencesScreen) + │ └─ Toggle changes → local state (future: bridge to native prefs) + ├─ "Get support" → /coming-soon (future: external link) + ├─ "Share Self" → /coming-soon (future: share sheet via bridge) + ├─ "Dev mode" → /settings/dev-mode (DevModeScreen) + │ ├─ Steppers/toggles → local state + │ ├─ "Generate mock document" → analytics event + navigate home + │ └─ "Reset all values" → reset local state + └─ "Close Self" → lifecycle.dismiss() +``` + +## Out of Scope + +- Persisting toggle/backup state via bridge storage (marked with `// TODO` comments) +- Recovery phrase reveal flow (requires biometric auth via bridge) +- Cloud backup flow (requires native iCloud/Google backup APIs) +- Restore account flow (requires native wallet restore) +- Support link / Share sheet (requires native intents via bridge) +- Manage Documents screen (no Euclid screen exists yet) +- `@selfxyz/euclid-web` → `@selfxyz/euclid` package rename (separate dependency update task) diff --git a/packages/mobile-sdk-alpha/AGENTS.md b/packages/mobile-sdk-alpha/AGENTS.md index 51035d3c4..0305f1fc0 100644 --- a/packages/mobile-sdk-alpha/AGENTS.md +++ b/packages/mobile-sdk-alpha/AGENTS.md @@ -142,14 +142,14 @@ yarn types # Verify type checking yarn build # Confirm build still works ``` -## SDK Architecture Specs +## SDK Architecture -For architecture context, implementation details, and workstream coordination: +For architecture context: -- **[SDK Overview](../../specs/projects/sdk/OVERVIEW.md)** — System architecture, bridge protocol, decision matrix -- **[SDK Core Spec](../../specs/projects/sdk/workstreams/sdk-core/SPEC.md)** — Implementation chunks for this package (mobile-sdk-alpha) +- **[SDK Overview](../../specs/projects/sdk/OVERVIEW.md)** — System architecture, bridge protocol, decision matrix (read-only reference) +- **Implementation specs** — Live in Linear as documents attached to issues. Check the relevant Linear issue for the current spec. Do not create spec files in `specs/` — that folder is deprecated. -Before implementing SDK work, read `CLAUDE.md` Key Rules and `specs/projects/sdk/workstreams/sdk-core/SPEC.md` for constraints and validation commands. +Before implementing SDK work, read `CLAUDE.md` Key Rules for constraints and validation commands. ## Notes diff --git a/packages/webview-app/package.json b/packages/webview-app/package.json index e8b333d1e..1b744ae7f 100644 --- a/packages/webview-app/package.json +++ b/packages/webview-app/package.json @@ -10,8 +10,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@selfxyz/euclid-core": "^1.0.1", - "@selfxyz/euclid-web": "^1.0.2", + "@selfxyz/euclid": "^1.2.0", + "@selfxyz/euclid-core": "^1.2.0", "@selfxyz/mobile-sdk-alpha": "workspace:^", "@selfxyz/webview-bridge": "workspace:^", "@sumsub/websdk": "^2.0.0", diff --git a/packages/webview-app/src/App.tsx b/packages/webview-app/src/App.tsx index 908007578..fcd4eec4c 100644 --- a/packages/webview-app/src/App.tsx +++ b/packages/webview-app/src/App.tsx @@ -15,7 +15,17 @@ import { HomeScreen } from './screens/home/HomeScreen'; import { ProvingScreen } from './screens/proving/ProvingScreen'; import { VerificationResultScreen } from './screens/proving/VerificationResultScreen'; import { SettingsScreen } from './screens/account/SettingsScreen'; +import { SecurityScreen } from './screens/account/SecurityScreen'; +import { NotificationPreferencesScreen } from './screens/account/NotificationPreferencesScreen'; +import { DevModeScreen } from './screens/account/DevModeScreen'; import { ComingSoonScreen } from './screens/ComingSoonScreen'; +import { TourScreen } from './screens/tunnel/TourScreen'; +import { KycMockScreen } from './screens/tunnel/KycMockScreen'; +import { TunnelCountryPickerScreen } from './screens/tunnel/TunnelCountryPickerScreen'; +import { TunnelIDTypeScreen } from './screens/tunnel/TunnelIDTypeScreen'; +import { TunnelProofReceiptScreen } from './screens/tunnel/TunnelProofReceiptScreen'; +import { TunnelProvingScreen } from './screens/tunnel/TunnelProvingScreen'; +import { TunnelResultScreen } from './screens/tunnel/TunnelResultScreen'; export const App: React.FC = () => ( @@ -40,11 +50,21 @@ export const App: React.FC = () => ( } /> } /> } /> + } /> + } /> + } /> } /> } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> diff --git a/packages/webview-app/src/screens/ComingSoonScreen.tsx b/packages/webview-app/src/screens/ComingSoonScreen.tsx index 90e62fb51..c48d39293 100644 --- a/packages/webview-app/src/screens/ComingSoonScreen.tsx +++ b/packages/webview-app/src/screens/ComingSoonScreen.tsx @@ -4,7 +4,7 @@ import React, { useCallback } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { ComingSoonScreen as EuclidComingSoonScreen } from '@selfxyz/euclid-web'; +import { ComingSoonScreen as EuclidComingSoonScreen } from '@selfxyz/euclid'; import { useSelfClient } from '../providers/SelfClientProvider'; import { getCountryName, renderFlag } from '../utils/countryFlags'; @@ -50,7 +50,7 @@ export const ComingSoonScreen: React.FC = () => { : "We're working to roll out support for this feature." } description="If you'd like to be notified when this becomes available, let us know." - onSignUpPress={onNotifyMe} + onNotifyPress={onNotifyMe} onBack={onDismiss} renderFlag={renderFlag} /> diff --git a/packages/webview-app/src/screens/account/DevModeScreen.tsx b/packages/webview-app/src/screens/account/DevModeScreen.tsx new file mode 100644 index 000000000..2c187424b --- /dev/null +++ b/packages/webview-app/src/screens/account/DevModeScreen.tsx @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + DevModeScreen as EuclidDevModeScreen, + LeftArrowIcon, +} from '@selfxyz/euclid'; +import type { IDCardProps } from '@selfxyz/euclid'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +const ageOptions = ['18 or older', '21 or older', '25 or older', '30 or older']; +const expiryOptions = ['1 year', '2 years', '5 years', '10 years']; + +export const DevModeScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic } = useSelfClient(); + + const [documentType, setDocumentType] = useState('passport'); + const [nationality, setNationality] = useState('united states of america'); + const [ageIndex, setAgeIndex] = useState(1); + const [expiryIndex, setExpiryIndex] = useState(2); + const [ofacCheck, setOfacCheck] = useState(true); + + const idCard: IDCardProps = { + variant: 'dev-passport', + title: 'Developer Passport', + subtitle: 'Digital credential for developers', + }; + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const onResetAllValues = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('dev_mode_reset'); + setDocumentType('passport'); + setNationality('united states of america'); + setAgeIndex(1); + setExpiryIndex(2); + setOfacCheck(true); + }, [haptic, analytics]); + + const onGenerateMockDocument = useCallback(() => { + haptic.trigger('success'); + analytics.trackEvent('dev_mode_generate_mock', { + documentType, + nationality, + age: ageOptions[ageIndex], + expiresIn: expiryOptions[expiryIndex], + ofacCheck, + }); + navigate('/'); + }, [navigate, haptic, analytics, documentType, nationality, ageIndex, expiryIndex, ofacCheck]); + + return ( + ( + + )} + onBack={onBack} + idCard={idCard} + documentType={documentType} + onDocumentTypePress={() => { + setDocumentType(prev => (prev === 'passport' ? 'id_card' : 'passport')); + }} + nationality={nationality} + onNationalityPress={() => { + setNationality(prev => + prev === 'united states of america' ? 'germany' : 'united states of america', + ); + }} + age={ageOptions[ageIndex]} + onAgeIncrement={() => setAgeIndex(prev => Math.min(prev + 1, ageOptions.length - 1))} + onAgeDecrement={() => setAgeIndex(prev => Math.max(prev - 1, 0))} + documentExpiresIn={expiryOptions[expiryIndex]} + onDocumentExpiresIncrement={() => + setExpiryIndex(prev => Math.min(prev + 1, expiryOptions.length - 1)) + } + onDocumentExpiresDecrement={() => setExpiryIndex(prev => Math.max(prev - 1, 0))} + ofacCheck={ofacCheck} + onOfacCheckChange={value => { + haptic.trigger('selection'); + setOfacCheck(value); + }} + onResetAllValues={onResetAllValues} + onGenerateMockDocument={onGenerateMockDocument} + /> + ); +}; diff --git a/packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx b/packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx new file mode 100644 index 000000000..2a8adfa2a --- /dev/null +++ b/packages/webview-app/src/screens/account/NotificationPreferencesScreen.tsx @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + NotificationPreferencesScreen as EuclidNotificationPreferencesScreen, + LeftArrowIcon, +} from '@selfxyz/euclid'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +const defaultToggles = [ + { key: 'self', label: 'Allow Self notifications', description: 'App updates and more' }, + { key: 'nova', label: 'Allow Nova notifications', description: 'Never miss a mission' }, + { key: 'points', label: 'Allow Self Points notifications', description: 'Points and rewards' }, + { key: 'id_status', label: 'Allow ID status notifications', description: 'Document verification updates' }, +]; + +export const NotificationPreferencesScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic } = useSelfClient(); + const [toggleValues, setToggleValues] = useState>({ + self: true, + nova: true, + points: true, + id_status: false, + }); + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const toggles = defaultToggles.map(t => ({ + label: t.label, + description: t.description, + value: toggleValues[t.key] ?? false, + onToggleChange: (value: boolean) => { + haptic.trigger('selection'); + analytics.trackEvent('notification_toggle_changed', { key: t.key, value }); + setToggleValues(prev => ({ ...prev, [t.key]: value })); + }, + })); + + return ( + ( + + )} + onBack={onBack} + toggles={toggles} + /> + ); +}; diff --git a/packages/webview-app/src/screens/account/SecurityScreen.tsx b/packages/webview-app/src/screens/account/SecurityScreen.tsx new file mode 100644 index 000000000..673da5165 --- /dev/null +++ b/packages/webview-app/src/screens/account/SecurityScreen.tsx @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + SecurityScreen as EuclidSecurityScreen, + LeftArrowIcon, + CloudKeyIcon, + LockIcon, + ZapShieldIcon, +} from '@selfxyz/euclid'; + +import { useSelfClient } from '../../providers/SelfClientProvider'; + +export const SecurityScreen: React.FC = () => { + const navigate = useNavigate(); + const { analytics, haptic } = useSelfClient(); + const [isBackupEnabled, setIsBackupEnabled] = useState(false); + const [showDisableDialogue, setShowDisableDialogue] = useState(false); + + const onBack = useCallback(() => { + haptic.trigger('selection'); + navigate('/settings'); + }, [navigate, haptic]); + + const onBackupAccount = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_backup_account_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onRevealRecoveryPhrase = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_reveal_phrase_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onRestoreAccount = useCallback(() => { + haptic.trigger('selection'); + analytics.trackEvent('security_restore_account_pressed'); + navigate('/coming-soon'); + }, [navigate, haptic, analytics]); + + const onDisableBackups = useCallback(() => { + haptic.trigger('warning'); + setShowDisableDialogue(true); + }, [haptic]); + + const onDisableICloudBackups = useCallback(() => { + haptic.trigger('warning'); + analytics.trackEvent('security_backups_disabled'); + setIsBackupEnabled(false); + setShowDisableDialogue(false); + }, [haptic, analytics]); + + const onDismissDialogue = useCallback(() => { + haptic.trigger('selection'); + setShowDisableDialogue(false); + }, [haptic]); + + return ( + ( + + )} + cloudKeyIcon={CloudKeyIcon} + lockIcon={LockIcon} + zapShieldIcon={ZapShieldIcon} + isBackupEnabled={isBackupEnabled} + onBack={onBack} + onBackupAccount={onBackupAccount} + onRevealRecoveryPhrase={onRevealRecoveryPhrase} + onRestoreAccount={onRestoreAccount} + onDisableBackups={onDisableBackups} + showDisableDialogue={showDisableDialogue} + onDisableICloudBackups={onDisableICloudBackups} + onDismissDialogue={onDismissDialogue} + /> + ); +}; diff --git a/packages/webview-app/src/screens/account/SettingsScreen.tsx b/packages/webview-app/src/screens/account/SettingsScreen.tsx index dbc5effbb..add662d6e 100644 --- a/packages/webview-app/src/screens/account/SettingsScreen.tsx +++ b/packages/webview-app/src/screens/account/SettingsScreen.tsx @@ -10,10 +10,11 @@ import { QuestionCircleStrokeIcon, DocumentDetailsIcon, LockIcon, - CloudKeyIcon, + NotificationIcon, ChatStrokeIcon, ShareIcon, -} from '@selfxyz/euclid-web'; + CodeIcon, +} from '@selfxyz/euclid'; import { useSelfClient } from '../../providers/SelfClientProvider'; @@ -47,35 +48,35 @@ export const SettingsScreen: React.FC = () => { CTAs={[]} sections={[ { - title: 'Account', + title: 'App settings', items: [ { icon: DocumentDetailsIcon, - label: 'View document info', - description: 'View your stored document details', + label: 'Manage Documents', + description: 'Recovery phrase, passport data', onPress: () => navigate('/coming-soon'), }, { icon: LockIcon, - label: 'Recovery phrase', - description: 'View your recovery phrase', - onPress: () => navigate('/coming-soon'), + label: 'Security', + description: 'Recovery phrase, passport data', + onPress: () => navigate('/settings/security'), }, { - icon: CloudKeyIcon, - label: 'Cloud backup', - description: 'Manage your cloud backup', - onPress: () => navigate('/coming-soon'), + icon: NotificationIcon, + label: 'Notifications', + description: 'Preferences, notification types', + onPress: () => navigate('/settings/notifications'), }, ], }, { - title: 'Support', + title: 'Support & feedback', items: [ { icon: ChatStrokeIcon, label: 'Get support', - description: 'Contact us for help', + description: 'Help center & support', onPress: () => navigate('/coming-soon'), }, { @@ -86,6 +87,23 @@ export const SettingsScreen: React.FC = () => { }, ], }, + { + title: 'Developer tools', + items: [ + { + icon: CodeIcon, + label: 'Dev mode', + description: 'Manage mock IDs, simulate proofs', + onPress: () => navigate('/settings/dev-mode'), + }, + { + icon: CodeIcon, + label: 'Tunnel flow', + description: 'Demo: register + disclose in one flow', + onPress: () => navigate('/tunnel/tour/1'), + }, + ], + }, ]} connectHeading="" connectSubheading="" diff --git a/packages/webview-app/src/screens/home/HomeScreen.tsx b/packages/webview-app/src/screens/home/HomeScreen.tsx index 119e54af2..ac1418a02 100644 --- a/packages/webview-app/src/screens/home/HomeScreen.tsx +++ b/packages/webview-app/src/screens/home/HomeScreen.tsx @@ -7,8 +7,8 @@ import { useNavigate } from 'react-router-dom'; import { HomeScreen as EuclidHomeScreen, GearIcon, -} from '@selfxyz/euclid-web'; -import type { IDCardVariant } from '@selfxyz/euclid-web'; +} from '@selfxyz/euclid'; +import type { IDCardVariant } from '@selfxyz/euclid'; import { useSelfClient } from '../../providers/SelfClientProvider'; diff --git a/packages/webview-app/src/screens/onboarding/ConfirmIdentificationScreen.tsx b/packages/webview-app/src/screens/onboarding/ConfirmIdentificationScreen.tsx index 7de570edb..61764922d 100644 --- a/packages/webview-app/src/screens/onboarding/ConfirmIdentificationScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/ConfirmIdentificationScreen.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { StatusState, CheckCircleIcon, colors } from '@selfxyz/euclid-web'; +import { StatusState, CheckCircleIcon, colors } from '@selfxyz/euclid'; import type { VerificationResult } from '@selfxyz/webview-bridge'; import { useSelfClient } from '../../providers/SelfClientProvider'; diff --git a/packages/webview-app/src/screens/onboarding/CountryPickerScreen.tsx b/packages/webview-app/src/screens/onboarding/CountryPickerScreen.tsx index d34f88f80..ed67f2170 100644 --- a/packages/webview-app/src/screens/onboarding/CountryPickerScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/CountryPickerScreen.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { CountryPickerScreen as EuclidCountryPickerScreen } from '@selfxyz/euclid-web'; +import { CountryPickerScreen as EuclidCountryPickerScreen } from '@selfxyz/euclid'; import countryDocumentTypes from '../../data/country-document-types.json'; import { useSelfClient } from '../../providers/SelfClientProvider'; diff --git a/packages/webview-app/src/screens/onboarding/IDSelectionScreen.tsx b/packages/webview-app/src/screens/onboarding/IDSelectionScreen.tsx index 0d7b9bd04..fed873834 100644 --- a/packages/webview-app/src/screens/onboarding/IDSelectionScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/IDSelectionScreen.tsx @@ -4,8 +4,8 @@ import React, { useCallback } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { IDTypeScreen } from '@selfxyz/euclid-web'; -import type { IDType } from '@selfxyz/euclid-web'; +import { IDTypeScreen } from '@selfxyz/euclid'; +import type { IDType } from '@selfxyz/euclid'; import { useSelfClient } from '../../providers/SelfClientProvider'; import { getCountryName, renderFlag } from '../../utils/countryFlags'; diff --git a/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx b/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx index f8c0d8e2a..eef360a87 100644 --- a/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { Button, Title, Description, colors, spacing } from '@selfxyz/euclid-web'; +import { Button, Title, Description, colors, spacing } from '@selfxyz/euclid'; import { useSelfClient } from '../../providers/SelfClientProvider'; import { useVerificationRequest } from '../../providers/VerificationRequestProvider'; diff --git a/packages/webview-app/src/screens/onboarding/ProviderResultScreen.tsx b/packages/webview-app/src/screens/onboarding/ProviderResultScreen.tsx index 5ee5be3ec..c05011b58 100644 --- a/packages/webview-app/src/screens/onboarding/ProviderResultScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/ProviderResultScreen.tsx @@ -9,7 +9,7 @@ import { CheckCircleIcon, WarningOctagonIcon, colors, -} from '@selfxyz/euclid-web'; +} from '@selfxyz/euclid'; import { useSelfClient } from '../../providers/SelfClientProvider'; import type { KycProviderResult } from '../../types/kycProvider'; diff --git a/packages/webview-app/src/screens/proving/ProvingScreen.tsx b/packages/webview-app/src/screens/proving/ProvingScreen.tsx index d374df3a0..16473ad90 100644 --- a/packages/webview-app/src/screens/proving/ProvingScreen.tsx +++ b/packages/webview-app/src/screens/proving/ProvingScreen.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { ProofRequestScreen, SelfLogo } from '@selfxyz/euclid-web'; +import { ProofRequestScreen, SelfLogo } from '@selfxyz/euclid'; import type { VerificationResult } from '@selfxyz/webview-bridge'; import { useSelfClient } from '../../providers/SelfClientProvider'; @@ -98,6 +98,8 @@ export const ProvingScreen: React.FC = () => { appEndpoint={appEndpoint} timestamp={timestamp} items={proofItems} + // TODO: hardcoding for now, fetch real value + documentType='passport' /> ); }; diff --git a/packages/webview-app/src/screens/proving/VerificationResultScreen.tsx b/packages/webview-app/src/screens/proving/VerificationResultScreen.tsx index bdb856cfc..9ba92b7ab 100644 --- a/packages/webview-app/src/screens/proving/VerificationResultScreen.tsx +++ b/packages/webview-app/src/screens/proving/VerificationResultScreen.tsx @@ -9,7 +9,7 @@ import { CheckCircleIcon, WarningOctagonIcon, colors, -} from '@selfxyz/euclid-web'; +} from '@selfxyz/euclid'; import type { VerificationResult } from '@selfxyz/webview-bridge'; import { useSelfClient } from '../../providers/SelfClientProvider'; diff --git a/packages/webview-app/src/screens/tunnel/KycMockScreen.tsx b/packages/webview-app/src/screens/tunnel/KycMockScreen.tsx new file mode 100644 index 000000000..757ec92f2 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/KycMockScreen.tsx @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button } from '@selfxyz/euclid'; + +export const KycMockScreen: React.FC = () => { + const navigate = useNavigate(); + + const onContinue = useCallback(() => { + navigate('/tunnel/registration/country'); + }, [navigate]); + + return ( +
+

KYC mock

+
+ ); +}; diff --git a/packages/webview-app/src/screens/tunnel/TourScreen.tsx b/packages/webview-app/src/screens/tunnel/TourScreen.tsx new file mode 100644 index 000000000..4d5c8fad2 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TourScreen.tsx @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useNavigate, useParams, Navigate } from 'react-router-dom'; +import { + LaunchTour1Screen, + LaunchTour2Screen, + LaunchTour3Screen, + LaunchTour4Screen, +} from '@selfxyz/euclid'; + +const insets = { top: 0, bottom: 0 }; + +export const TourScreen: React.FC = () => { + const navigate = useNavigate(); + const { step } = useParams<{ step: string }>(); + const stepNum = parseInt(step ?? '1', 10); + + const onNext = useCallback(() => { + navigate(stepNum < 4 ? `/tunnel/tour/${stepNum + 1}` : '/tunnel/kyc'); + }, [navigate, stepNum]); + + switch (step) { + case '1': + return ; + case '2': + return ; + case '3': + return ; + case '4': + return ; + default: + return ; + } +}; diff --git a/packages/webview-app/src/screens/tunnel/TunnelCountryPickerScreen.tsx b/packages/webview-app/src/screens/tunnel/TunnelCountryPickerScreen.tsx new file mode 100644 index 000000000..3f9c77b80 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TunnelCountryPickerScreen.tsx @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { CountryPickerScreen as EuclidCountryPickerScreen } from '@selfxyz/euclid'; + +import { getCountryName, renderFlag } from '../../utils/countryFlags'; + +const MOCK_COUNTRIES = [ + { countryCode: 'US' }, + { countryCode: 'GB' }, + { countryCode: 'DE' }, + { countryCode: 'PL' }, + { countryCode: 'FR' }, +]; + +const MOCK_DOCUMENT_TYPES: Record = { + US: ['p', 'i'], + GB: ['p'], + DE: ['p', 'i'], + PL: ['p', 'i'], + FR: ['p', 'i'], +}; + +export const TunnelCountryPickerScreen: React.FC = () => { + const navigate = useNavigate(); + const [search, setSearch] = useState(''); + + const onCountrySelect = useCallback( + (countryCode: string) => { + navigate('/tunnel/registration/id-type', { + state: { countryCode, documentTypes: MOCK_DOCUMENT_TYPES[countryCode] ?? ['p'] }, + }); + }, + [navigate], + ); + + return ( + navigate('/tunnel/kyc')} + renderFlag={renderFlag} + getCountryName={getCountryName} + searchValue={search} + onSearchChange={setSearch} + /> + ); +}; diff --git a/packages/webview-app/src/screens/tunnel/TunnelIDTypeScreen.tsx b/packages/webview-app/src/screens/tunnel/TunnelIDTypeScreen.tsx new file mode 100644 index 000000000..93fe20ef5 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TunnelIDTypeScreen.tsx @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { IDTypeScreen } from '@selfxyz/euclid'; +import type { IDType } from '@selfxyz/euclid'; + +import { getCountryName, renderFlag } from '../../utils/countryFlags'; + +const docTypeToIDType = (docType: string): IDType => { + switch (docType) { + case 'p': + return { id: 'p', title: 'Passport', subtitle: 'Verified Biometric Passport' }; + case 'i': + return { id: 'i', title: 'ID Card', subtitle: 'Verified Biometric ID card' }; + default: + return { id: docType, title: 'Unknown Document', subtitle: '' }; + } +}; + +const renderIDTypeIcon = (idType: IDType): React.ReactNode => { + const emoji = idType.id === 'p' ? '🛂' : '🪪'; + return {emoji}; +}; + +export const TunnelIDTypeScreen: React.FC = () => { + const navigate = useNavigate(); + const location = useLocation(); + + const { countryCode = 'US', documentTypes = ['p'] } = + (location.state as { countryCode?: string; documentTypes?: string[] }) || {}; + + const idTypes = documentTypes.map(docTypeToIDType); + + const onIDTypeSelect = useCallback( + (_idType: IDType) => { + navigate('/tunnel/proof/receipt'); + }, + [navigate], + ); + + return ( + navigate(-1)} + renderFlag={renderFlag} + renderIDTypeIcon={renderIDTypeIcon} + /> + ); +}; diff --git a/packages/webview-app/src/screens/tunnel/TunnelProofReceiptScreen.tsx b/packages/webview-app/src/screens/tunnel/TunnelProofReceiptScreen.tsx new file mode 100644 index 000000000..15f58573f --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TunnelProofReceiptScreen.tsx @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { ProofRequestScreen, SelfLogo } from '@selfxyz/euclid'; + +const MOCK_ITEMS = [ + { label: 'Full Name' }, + { label: 'Date of Birth' }, + { label: 'Nationality' }, + { label: 'Age above 18' }, +]; + +export const TunnelProofReceiptScreen: React.FC = () => { + const navigate = useNavigate(); + + const onConfirm = useCallback(() => { + navigate('/tunnel/proof/generating'); + }, [navigate]); + + const onClose = useCallback(() => { + navigate(-1); + }, [navigate]); + + return ( + } + appName="KYC" + appEndpoint="example.com" + documentType="passport" + timestamp={Date.now()} + items={MOCK_ITEMS} + /> + ); +}; diff --git a/packages/webview-app/src/screens/tunnel/TunnelProvingScreen.tsx b/packages/webview-app/src/screens/tunnel/TunnelProvingScreen.tsx new file mode 100644 index 000000000..93f52a879 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TunnelProvingScreen.tsx @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { ProofGenerationScreen } from '@selfxyz/euclid'; + +const MOCK_ID_CARD = { + variant: 'passport' as const, + title: 'Passport', + subtitle: 'Mock Passport', +}; + +export const TunnelProvingScreen: React.FC = () => { + const navigate = useNavigate(); + + useEffect(() => { + const timer = setTimeout(() => { + navigate('/tunnel/proof/result'); + }, 3000); + return () => clearTimeout(timer); + }, [navigate]); + + return ( + + ); +}; diff --git a/packages/webview-app/src/screens/tunnel/TunnelResultScreen.tsx b/packages/webview-app/src/screens/tunnel/TunnelResultScreen.tsx new file mode 100644 index 000000000..f49c2fb09 --- /dev/null +++ b/packages/webview-app/src/screens/tunnel/TunnelResultScreen.tsx @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { StatusState, CheckCircleIcon, colors } from '@selfxyz/euclid'; + +export const TunnelResultScreen: React.FC = () => { + const navigate = useNavigate(); + + const onContinue = useCallback(() => { + navigate('/'); + }, [navigate]); + + return ( + } + /> + ); +}; diff --git a/specs/projects/sdk/INDEX.md b/specs/projects/sdk/INDEX.md index 21c794660..f133e4ac9 100644 --- a/specs/projects/sdk/INDEX.md +++ b/specs/projects/sdk/INDEX.md @@ -12,12 +12,12 @@ Status: Active (WebView-first + native keychain/crypto) ## Active Workstreams -| Workstream | Spec | Focus | -| ---------- | ---- | ----- | -| WebView UI | [WebView Spec](./workstreams/webview/SPEC.md) | Sumsub Web SDK integration, KYC result flow | -| SDK Core | [SDK Core Spec](./workstreams/sdk-core/SPEC.md) | Browser-portable engine | +| Workstream | Spec | Focus | +| -------------------- | ------------------------------------------------------------------- | --------------------------------------------- | +| WebView UI | [WebView Spec](./workstreams/webview/SPEC.md) | Sumsub Web SDK integration, KYC result flow | +| SDK Core | [SDK Core Spec](./workstreams/sdk-core/SPEC.md) | Browser-portable engine | | Native Shells (Lite) | [Native Shells Lite Spec](./workstreams/native-shells-lite/SPEC.md) | Plain Kotlin + Swift for keychain/crypto only | -| Build Pipeline | [Build Pipeline Spec](./workstreams/build-pipeline/SPEC.md) | Bundle webview-app into native shells | +| Build Pipeline | [Build Pipeline Spec](./workstreams/build-pipeline/SPEC.md) | Bundle webview-app into native shells | ## Paused Workstreams diff --git a/specs/projects/sdk/OVERVIEW.md b/specs/projects/sdk/OVERVIEW.md index 0c894ddd2..f5962210e 100644 --- a/specs/projects/sdk/OVERVIEW.md +++ b/specs/projects/sdk/OVERVIEW.md @@ -110,19 +110,19 @@ On **March 20, 2026**, the active SDK delivery target was refined: ## Module Table -| Module | Location | Status | Current Role | Action Needed | -| -------------------- | ----------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| WebView UI | `packages/webview-app/` | Active | Primary product surface, KYC provider handoff, verification UX | Integrate KYC provider Web SDK (WV-05), wire result flow (WV-06) | -| SDK Core | `packages/mobile-sdk-alpha/` | Active | Shared engine for WebView/browser delivery | Keep browser entry clean and request-driven | -| WebView Bridge | `packages/webview-bridge/` | Active | Bridge protocol v1 between WebView and native shells | Stable — no changes needed | -| Android Shell | `packages/native-shell-android/` | New | Thin Kotlin shell: keychain/crypto + WebView host | Build (NSL-01) | -| iOS Shell | `packages/native-shell-ios/` | New | Thin Swift shell: keychain/crypto + WebView host | Build (NSL-02) | -| Test App | `packages/sdk-test-app/` | New | Minimal native apps for end-to-end testing | Adapt from kmp-sdk-test-app (NSL-03) | -| KMP Native Shell | `packages/kmp-sdk/` | Deprecated | Reference for native shell porting, replaced by native-shell-android/ios | Do not advance; use as port reference only | -| Swift Providers | `packages/self-sdk-swift/` | Deprecated | Reference for iOS keychain/crypto porting, replaced by native-shell-ios | Do not advance; use as port reference only | -| RN SDK | `packages/rn-sdk/` | Paused | Retained React Native shell work | Do not advance unless scope reopens | -| Native Consolidation | `app/ios/`, `packages/mobile-sdk-alpha/ios/`, related native code | Paused | Historical native cleanup and parity track | Keep as reference only for now | -| MiniPay Sample | `packages/kmp-minipay-sample/` | Paused | Historical KMP integration example | Resume only if KMP path returns | +| Module | Location | Status | Current Role | Action Needed | +| -------------------- | ----------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- | +| WebView UI | `packages/webview-app/` | Active | Primary product surface, KYC provider handoff, verification UX | Integrate KYC provider Web SDK (WV-05), wire result flow (WV-06) | +| SDK Core | `packages/mobile-sdk-alpha/` | Active | Shared engine for WebView/browser delivery | Keep browser entry clean and request-driven | +| WebView Bridge | `packages/webview-bridge/` | Active | Bridge protocol v1 between WebView and native shells | Stable — no changes needed | +| Android Shell | `packages/native-shell-android/` | New | Thin Kotlin shell: keychain/crypto + WebView host | Build (NSL-01) | +| iOS Shell | `packages/native-shell-ios/` | New | Thin Swift shell: keychain/crypto + WebView host | Build (NSL-02) | +| Test App | `packages/sdk-test-app/` | New | Minimal native apps for end-to-end testing | Adapt from kmp-sdk-test-app (NSL-03) | +| KMP Native Shell | `packages/kmp-sdk/` | Deprecated | Reference for native shell porting, replaced by native-shell-android/ios | Do not advance; use as port reference only | +| Swift Providers | `packages/self-sdk-swift/` | Deprecated | Reference for iOS keychain/crypto porting, replaced by native-shell-ios | Do not advance; use as port reference only | +| RN SDK | `packages/rn-sdk/` | Paused | Retained React Native shell work | Do not advance unless scope reopens | +| Native Consolidation | `app/ios/`, `packages/mobile-sdk-alpha/ios/`, related native code | Paused | Historical native cleanup and parity track | Keep as reference only for now | +| MiniPay Sample | `packages/kmp-minipay-sample/` | Paused | Historical KMP integration example | Resume only if KMP path returns | ## Scope Rules diff --git a/specs/projects/sdk/workstreams/build-pipeline/SPEC.md b/specs/projects/sdk/workstreams/build-pipeline/SPEC.md index 3e96ad502..358f79ce1 100644 --- a/specs/projects/sdk/workstreams/build-pipeline/SPEC.md +++ b/specs/projects/sdk/workstreams/build-pipeline/SPEC.md @@ -32,18 +32,18 @@ ## Dependencies -| Depends On | Type | Status | Notes | -|------------|------|--------|-------| -| `packages/webview-app/` | Upstream | Active | Source of the WebView bundle | -| `packages/native-shell-android/` | Downstream | Ready | Receives assets at `src/main/assets/self-wallet/` | -| `packages/native-shell-ios/` | Downstream | Ready | Receives assets at `Resources/self-sdk-web/` | +| Depends On | Type | Status | Notes | +| -------------------------------- | ---------- | ------ | ------------------------------------------------- | +| `packages/webview-app/` | Upstream | Active | Source of the WebView bundle | +| `packages/native-shell-android/` | Downstream | Ready | Receives assets at `src/main/assets/self-wallet/` | +| `packages/native-shell-ios/` | Downstream | Ready | Receives assets at `Resources/self-sdk-web/` | ## Backlog -| ID | Title | Status | Priority | Depends On | Plan | PR | -|----|-------|--------|----------|------------|------|----| -| BP-01 | WebView bundle build + copy script | Done | Medium | NSL-01, NSL-02 | [plans/BP-01-build-script.md](./plans/BP-01-build-script.md) | Complete on `feat/webview-sdk` | -| BP-02 | Runtime bundle integrity for CDN loading | Deferred | High | — | — | — | +| ID | Title | Status | Priority | Depends On | Plan | PR | +| ----- | ---------------------------------------- | -------- | -------- | -------------- | ------------------------------------------------------------ | ------------------------------ | +| BP-01 | WebView bundle build + copy script | Done | Medium | NSL-01, NSL-02 | [plans/BP-01-build-script.md](./plans/BP-01-build-script.md) | Complete on `feat/webview-sdk` | +| BP-02 | Runtime bundle integrity for CDN loading | Deferred | High | — | — | — | Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` @@ -61,9 +61,9 @@ Trigger: when remote/CDN bundle loading is implemented. ## Active Plans -| Plan | IDs | Status | -|------|-----|--------| -| [plans/BP-01-build-script.md](./plans/BP-01-build-script.md) | BP-01 | Done | +| Plan | IDs | Status | +| ------------------------------------------------------------ | ----- | ------ | +| [plans/BP-01-build-script.md](./plans/BP-01-build-script.md) | BP-01 | Done | ## Completion Checklist @@ -73,8 +73,8 @@ Trigger: when remote/CDN bundle loading is implemented. ## Related Specs -| Spec | Relationship | -|------|-------------| -| [SDK Overview](../../OVERVIEW.md) | Parent architecture | -| [WebView Spec](../webview/SPEC.md) | Upstream — produces the bundle | +| Spec | Relationship | +| --------------------------------------------------- | -------------------------------- | +| [SDK Overview](../../OVERVIEW.md) | Parent architecture | +| [WebView Spec](../webview/SPEC.md) | Upstream — produces the bundle | | [Native Shells Lite](../native-shells-lite/SPEC.md) | Downstream — consumes the bundle | diff --git a/specs/projects/sdk/workstreams/native-shells-lite/SPEC.md b/specs/projects/sdk/workstreams/native-shells-lite/SPEC.md index 607cb6174..640a04388 100644 --- a/specs/projects/sdk/workstreams/native-shells-lite/SPEC.md +++ b/specs/projects/sdk/workstreams/native-shells-lite/SPEC.md @@ -37,39 +37,39 @@ ## Dependencies -| Depends On | Type | Status | Notes | -|------------|------|--------|-------| -| `packages/webview-bridge/` | Upstream (bridge protocol) | Done | Defines message shapes and transport names | -| `packages/webview-app/` | Upstream (WebView bundle) | Active | Native shells load this bundle | -| Build pipeline | Downstream | Ready | Copies webview-app dist into native assets | +| Depends On | Type | Status | Notes | +| -------------------------- | -------------------------- | ------ | ------------------------------------------ | +| `packages/webview-bridge/` | Upstream (bridge protocol) | Done | Defines message shapes and transport names | +| `packages/webview-app/` | Upstream (WebView bundle) | Active | Native shells load this bundle | +| Build pipeline | Downstream | Ready | Copies webview-app dist into native assets | ## Ownership Boundaries -| Area | Owner | Notes | -|------|-------|-------| -| `packages/native-shell-android/` | Native Shells (Lite) | New package | -| `packages/native-shell-ios/` | Native Shells (Lite) | New package | -| `packages/sdk-test-app/` | Native Shells (Lite) | Adapted from kmp-sdk-test-app | -| `packages/kmp-sdk/` | Paused | Reference only, do not modify | -| `packages/self-sdk-swift/` | Paused | Reference only, do not modify | +| Area | Owner | Notes | +| -------------------------------- | -------------------- | ----------------------------- | +| `packages/native-shell-android/` | Native Shells (Lite) | New package | +| `packages/native-shell-ios/` | Native Shells (Lite) | New package | +| `packages/sdk-test-app/` | Native Shells (Lite) | Adapted from kmp-sdk-test-app | +| `packages/kmp-sdk/` | Paused | Reference only, do not modify | +| `packages/self-sdk-swift/` | Paused | Reference only, do not modify | ## Backlog -| ID | Title | Status | Priority | Depends On | Plan | PR | -|----|-------|--------|----------|------------|------|----| -| NSL-01 | Android native shell (plain Kotlin) | In Progress | High | - | [plans/NSL-01-android-shell.md](./plans/NSL-01-android-shell.md) | Code complete on `feat/webview-sdk`, needs testing | -| NSL-02 | iOS native shell (plain Swift) | In Progress | High | - | [plans/NSL-02-ios-shell.md](./plans/NSL-02-ios-shell.md) | Code complete on `feat/webview-sdk`, needs testing | -| NSL-03 | Test apps (adapt from kmp-sdk-test-app) | In Progress | Medium | NSL-01, NSL-02 | [plans/NSL-03-test-apps.md](./plans/NSL-03-test-apps.md) | Code complete on `feat/webview-sdk`, needs build verification | +| ID | Title | Status | Priority | Depends On | Plan | PR | +| ------ | --------------------------------------- | ----------- | -------- | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | +| NSL-01 | Android native shell (plain Kotlin) | In Progress | High | - | [plans/NSL-01-android-shell.md](./plans/NSL-01-android-shell.md) | Code complete on `feat/webview-sdk`, needs testing | +| NSL-02 | iOS native shell (plain Swift) | In Progress | High | - | [plans/NSL-02-ios-shell.md](./plans/NSL-02-ios-shell.md) | Code complete on `feat/webview-sdk`, needs testing | +| NSL-03 | Test apps (adapt from kmp-sdk-test-app) | In Progress | Medium | NSL-01, NSL-02 | [plans/NSL-03-test-apps.md](./plans/NSL-03-test-apps.md) | Code complete on `feat/webview-sdk`, needs build verification | Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` ## Active Plans -| Plan | IDs | Status | -|------|-----|--------| -| [plans/NSL-01-android-shell.md](./plans/NSL-01-android-shell.md) | NSL-01 | In Progress (code complete, needs testing) | -| [plans/NSL-02-ios-shell.md](./plans/NSL-02-ios-shell.md) | NSL-02 | In Progress (code complete, needs testing) | -| [plans/NSL-03-test-apps.md](./plans/NSL-03-test-apps.md) | NSL-03 | In Progress (code complete, needs build verification) | +| Plan | IDs | Status | +| ---------------------------------------------------------------- | ------ | ----------------------------------------------------- | +| [plans/NSL-01-android-shell.md](./plans/NSL-01-android-shell.md) | NSL-01 | In Progress (code complete, needs testing) | +| [plans/NSL-02-ios-shell.md](./plans/NSL-02-ios-shell.md) | NSL-02 | In Progress (code complete, needs testing) | +| [plans/NSL-03-test-apps.md](./plans/NSL-03-test-apps.md) | NSL-03 | In Progress (code complete, needs build verification) | ## Completion Checklist @@ -82,19 +82,19 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` These existing files define the contract and patterns to port: -| What | Source | Notes | -|------|--------|-------| -| Bridge protocol (message shapes) | `packages/webview-bridge/src/types.ts` | Canonical — native must match | -| Bridge transport detection | `packages/webview-bridge/src/bridge.ts` | Android: `SelfNativeAndroid`, iOS: `SelfNativeIOS` | -| Crypto adapter expectations | `packages/webview-bridge/src/adapters/crypto.ts` | Defines request params and response shapes | -| Storage adapter expectations | `packages/webview-bridge/src/adapters/storage.ts` | Defines request params and response shapes | -| KMP MessageRouter | `packages/kmp-sdk/.../commonMain/.../MessageRouter.kt` | Port routing logic | -| Android SecureStorage | `packages/kmp-sdk/.../androidMain/.../SecureStorageBridgeHandler.kt` | Port EncryptedSharedPreferences pattern | -| Android WebView host | `packages/kmp-sdk/.../androidMain/.../AndroidWebViewHost.kt` | Port WebViewAssetLoader + JS interface | -| Android Activity | `packages/kmp-sdk/.../androidMain/.../SelfVerificationActivity.kt` | Port entry point, simplify permissions | -| iOS CryptoProvider | `packages/self-sdk-swift/.../CryptoProviderImpl.swift` | Port EC P-256 signing | -| iOS SecureStorage | `packages/self-sdk-swift/.../SecureStorageProviderImpl.swift` | Port Keychain Services | -| iOS WebView host | `packages/self-sdk-swift/.../WebViewProviderImpl.swift` | Port WKWebView + script message handler | +| What | Source | Notes | +| -------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------- | +| Bridge protocol (message shapes) | `packages/webview-bridge/src/types.ts` | Canonical — native must match | +| Bridge transport detection | `packages/webview-bridge/src/bridge.ts` | Android: `SelfNativeAndroid`, iOS: `SelfNativeIOS` | +| Crypto adapter expectations | `packages/webview-bridge/src/adapters/crypto.ts` | Defines request params and response shapes | +| Storage adapter expectations | `packages/webview-bridge/src/adapters/storage.ts` | Defines request params and response shapes | +| KMP MessageRouter | `packages/kmp-sdk/.../commonMain/.../MessageRouter.kt` | Port routing logic | +| Android SecureStorage | `packages/kmp-sdk/.../androidMain/.../SecureStorageBridgeHandler.kt` | Port EncryptedSharedPreferences pattern | +| Android WebView host | `packages/kmp-sdk/.../androidMain/.../AndroidWebViewHost.kt` | Port WebViewAssetLoader + JS interface | +| Android Activity | `packages/kmp-sdk/.../androidMain/.../SelfVerificationActivity.kt` | Port entry point, simplify permissions | +| iOS CryptoProvider | `packages/self-sdk-swift/.../CryptoProviderImpl.swift` | Port EC P-256 signing | +| iOS SecureStorage | `packages/self-sdk-swift/.../SecureStorageProviderImpl.swift` | Port Keychain Services | +| iOS WebView host | `packages/self-sdk-swift/.../WebViewProviderImpl.swift` | Port WKWebView + script message handler | ## Bridge Domain Contract @@ -102,27 +102,27 @@ Only 3 domains are implemented by the native shells: ### `secureStorage` -| Method | Params | Response | -|--------|--------|----------| -| `get` | `{ key: string }` | `{ value: string \| null }` | -| `set` | `{ key: string, value: string }` | `null` | -| `remove` | `{ key: string }` | `null` | +| Method | Params | Response | +| -------- | -------------------------------- | --------------------------- | +| `get` | `{ key: string }` | `{ value: string \| null }` | +| `set` | `{ key: string, value: string }` | `null` | +| `remove` | `{ key: string }` | `null` | ### `crypto` -| Method | Params | Response | -|--------|--------|----------| -| `generateKey` | `{ keyRef: string }` | `{ keyRef: string, success: true }` | -| `getPublicKey` | `{ keyRef: string }` | `{ publicKey: string }` (base64) | -| `sign` | `{ data: string, keyRef: string }` (data is base64) | `{ signature: string }` (base64) | +| Method | Params | Response | +| -------------- | --------------------------------------------------- | ----------------------------------- | +| `generateKey` | `{ keyRef: string }` | `{ keyRef: string, success: true }` | +| `getPublicKey` | `{ keyRef: string }` | `{ publicKey: string }` (base64) | +| `sign` | `{ data: string, keyRef: string }` (data is base64) | `{ signature: string }` (base64) | ### `lifecycle` -| Method | Params | Response | -|--------|--------|----------| -| `ready` | `{}` | `null` (no-op) | -| `dismiss` | `{ reason?: string }` | `null` (finishes Activity / dismisses VC) | -| `setResult` | `{ success: bool, userId?, verificationId?, error? }` | `null` (forwards to host, then finishes) | +| Method | Params | Response | +| ----------- | ----------------------------------------------------- | ----------------------------------------- | +| `ready` | `{}` | `null` (no-op) | +| `dismiss` | `{ reason?: string }` | `null` (finishes Activity / dismisses VC) | +| `setResult` | `{ success: bool, userId?, verificationId?, error? }` | `null` (forwards to host, then finishes) | Any other domain request returns a `DOMAIN_NOT_FOUND` error response. @@ -144,6 +144,7 @@ When the WebView calls `lifecycle.setResult()`, the native shell forwards the re ### Android → Host `LifecycleHandler` calls `Activity.setResult()` with: + - `RESULT_OK` when `success: true` - `RESULT_CANCELED` when user dismisses - `RESULT_FIRST_USER` on error @@ -153,6 +154,7 @@ Result JSON is included as Intent extra (`xyz.self.sdk.RESULT_DATA`). Host reads ### iOS → Host `LifecycleHandler` invokes the `SelfSdkCallback` protocol: + - `onSuccess(result:)` when `success: true` - `onCancelled()` when user dismisses - `onFailure(error:)` on error @@ -165,10 +167,10 @@ The host never sees raw KYC provider output. The WebView normalizes provider res ## Related Specs -| Spec | Relationship | -|------|-------------| -| [SDK Overview](../../OVERVIEW.md) | Parent architecture | -| [WebView Spec](../webview/SPEC.md) | Sibling — owns Sumsub integration and WebView UX | -| [SDK Core Spec](../sdk-core/SPEC.md) | Sibling — owns mobile-sdk-alpha engine | -| [Build Pipeline Spec](../build-pipeline/SPEC.md) | Downstream — bundles webview-app into native shells | -| [Paused Native Shells](../../paused/native-shells/SPEC.md) | Predecessor — KMP-based, now deprecated | +| Spec | Relationship | +| ---------------------------------------------------------- | --------------------------------------------------- | +| [SDK Overview](../../OVERVIEW.md) | Parent architecture | +| [WebView Spec](../webview/SPEC.md) | Sibling — owns Sumsub integration and WebView UX | +| [SDK Core Spec](../sdk-core/SPEC.md) | Sibling — owns mobile-sdk-alpha engine | +| [Build Pipeline Spec](../build-pipeline/SPEC.md) | Downstream — bundles webview-app into native shells | +| [Paused Native Shells](../../paused/native-shells/SPEC.md) | Predecessor — KMP-based, now deprecated | diff --git a/specs/projects/sdk/workstreams/webview/SPEC.md b/specs/projects/sdk/workstreams/webview/SPEC.md index 06bd41220..213f82a8a 100644 --- a/specs/projects/sdk/workstreams/webview/SPEC.md +++ b/specs/projects/sdk/workstreams/webview/SPEC.md @@ -44,27 +44,27 @@ On **March 11, 2026**, the active SDK scope changed to **WebView only, with no c ## Backlog -| ID | Title | Status | Priority | Depends On | Plan | Notes | -| ----- | ----------------------------------------------------------------------------------------------- | ------ | -------- | ---------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | -| WV-01 | Dynamic proof request items sourced from request context | Done | High | - | [plans/WV-01-dynamic-proof-request-items.md](./plans/WV-01-dynamic-proof-request-items.md) | Existing active follow-up | -| WV-02 | Define the KYC-provider contract for document capture, MRZ/liveness handoff, and result mapping | Done | High | - | [plans/WV-02-kyc-provider-contract.md](./plans/WV-02-kyc-provider-contract.md) | Provider-backed path replaces Self-owned native scan flow; active contract is now documented | -| WV-03 | Remove native NFC and native-scan assumptions from active WebView screens, copy, and docs | Done | High | WV-02 | [plans/WV-03-remove-native-scan-assumptions.md](./plans/WV-03-remove-native-scan-assumptions.md) | Active UX/docs now route to a provider placeholder instead of Self-managed scan screens | -| WV-04 | Define the host callback contract for launch, dismiss, and final result without native modules | Done | Medium | WV-02 | [plans/WV-04-host-callback-contract.md](./plans/WV-04-host-callback-contract.md) | Browser host fallback now uses `postMessage` for iframe/popup embedding while native transports keep their current behavior | -| WV-05 | Integrate KYC provider Web SDK into ProviderLaunchScreen (Sumsub as default) | In Progress | High | WV-02 | [plans/WV-05-sumsub-web-sdk.md](./plans/WV-05-sumsub-web-sdk.md) | Code complete on `feat/webview-sdk`, needs testing | -| WV-06 | Wire KYC result through verification pipeline to host lifecycle callback | Ready | High | WV-05 | [plans/WV-06-kyc-result-flow.md](./plans/WV-06-kyc-result-flow.md) | Sumsub result → kycResultStore → ConfirmIdentificationScreen → lifecycle.setResult() | +| ID | Title | Status | Priority | Depends On | Plan | Notes | +| ----- | ----------------------------------------------------------------------------------------------- | ----------- | -------- | ---------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | +| WV-01 | Dynamic proof request items sourced from request context | Done | High | - | [plans/WV-01-dynamic-proof-request-items.md](./plans/WV-01-dynamic-proof-request-items.md) | Existing active follow-up | +| WV-02 | Define the KYC-provider contract for document capture, MRZ/liveness handoff, and result mapping | Done | High | - | [plans/WV-02-kyc-provider-contract.md](./plans/WV-02-kyc-provider-contract.md) | Provider-backed path replaces Self-owned native scan flow; active contract is now documented | +| WV-03 | Remove native NFC and native-scan assumptions from active WebView screens, copy, and docs | Done | High | WV-02 | [plans/WV-03-remove-native-scan-assumptions.md](./plans/WV-03-remove-native-scan-assumptions.md) | Active UX/docs now route to a provider placeholder instead of Self-managed scan screens | +| WV-04 | Define the host callback contract for launch, dismiss, and final result without native modules | Done | Medium | WV-02 | [plans/WV-04-host-callback-contract.md](./plans/WV-04-host-callback-contract.md) | Browser host fallback now uses `postMessage` for iframe/popup embedding while native transports keep their current behavior | +| WV-05 | Integrate KYC provider Web SDK into ProviderLaunchScreen (Sumsub as default) | In Progress | High | WV-02 | [plans/WV-05-sumsub-web-sdk.md](./plans/WV-05-sumsub-web-sdk.md) | Code complete on `feat/webview-sdk`, needs testing | +| WV-06 | Wire KYC result through verification pipeline to host lifecycle callback | Ready | High | WV-05 | [plans/WV-06-kyc-result-flow.md](./plans/WV-06-kyc-result-flow.md) | Sumsub result → kycResultStore → ConfirmIdentificationScreen → lifecycle.setResult() | Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` ## Active Plans -| Plan | IDs | Status | -| ------------------------------------------------------------------------------------------------ | ----- | ------ | -| [plans/WV-01-dynamic-proof-request-items.md](./plans/WV-01-dynamic-proof-request-items.md) | WV-01 | Done | -| [plans/WV-02-kyc-provider-contract.md](./plans/WV-02-kyc-provider-contract.md) | WV-02 | Done | -| [plans/WV-03-remove-native-scan-assumptions.md](./plans/WV-03-remove-native-scan-assumptions.md) | WV-03 | Done | -| [plans/WV-04-host-callback-contract.md](./plans/WV-04-host-callback-contract.md) | WV-04 | Done | +| Plan | IDs | Status | +| ------------------------------------------------------------------------------------------------ | ----- | ------------------------------------------ | +| [plans/WV-01-dynamic-proof-request-items.md](./plans/WV-01-dynamic-proof-request-items.md) | WV-01 | Done | +| [plans/WV-02-kyc-provider-contract.md](./plans/WV-02-kyc-provider-contract.md) | WV-02 | Done | +| [plans/WV-03-remove-native-scan-assumptions.md](./plans/WV-03-remove-native-scan-assumptions.md) | WV-03 | Done | +| [plans/WV-04-host-callback-contract.md](./plans/WV-04-host-callback-contract.md) | WV-04 | Done | | [plans/WV-05-sumsub-web-sdk.md](./plans/WV-05-sumsub-web-sdk.md) | WV-05 | In Progress (code complete, needs testing) | -| [plans/WV-06-kyc-result-flow.md](./plans/WV-06-kyc-result-flow.md) | WV-06 | Ready | +| [plans/WV-06-kyc-result-flow.md](./plans/WV-06-kyc-result-flow.md) | WV-06 | Ready | ## Completion Checklist diff --git a/yarn.lock b/yarn.lock index 1af651674..36d392ad9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7033,6 +7033,24 @@ __metadata: languageName: node linkType: hard +"@lottiefiles/dotlottie-react@npm:^0.18.4": + version: 0.18.7 + resolution: "@lottiefiles/dotlottie-react@npm:0.18.7" + dependencies: + "@lottiefiles/dotlottie-web": "npm:0.67.0" + peerDependencies: + react: ^17 || ^18 || ^19 + checksum: 10c0/9c940db91112ef9de681c6cf8e7238d852f7f2f772b9fb2eacc214b561b22d77db965ce8dd1d3823f8a0cf69d36be9459a4b0863301ba8725aed7cdc59fd2104 + languageName: node + linkType: hard + +"@lottiefiles/dotlottie-web@npm:0.67.0": + version: 0.67.0 + resolution: "@lottiefiles/dotlottie-web@npm:0.67.0" + checksum: 10c0/462a882b3cba7c68e575c2e1c3479afaae1cd39b16a74111b886d02a86053c1738e845d7379e31d8cc75bb66dd63c01a58b4160caa287aa5fc9a4a54dd6b72e1 + languageName: node + linkType: hard + "@mapbox/node-pre-gyp@npm:^1.0": version: 1.0.11 resolution: "@mapbox/node-pre-gyp@npm:1.0.11" @@ -10638,22 +10656,12 @@ __metadata: languageName: unknown linkType: soft -"@selfxyz/euclid-core@npm:^1.0.0, @selfxyz/euclid-core@npm:^1.0.1": - version: 1.0.1 - resolution: "@selfxyz/euclid-core@npm:1.0.1" - checksum: 10c0/13b704c3fe1c8be99cd39a22a8ab22925e26c97b43f41a04e59ff313068c6806ead84f3417b6945253acaa85e2134815b8008320e9fcc4dcb15e640878a3b40e - languageName: node - linkType: hard - -"@selfxyz/euclid-web@npm:^1.0.2": - version: 1.0.2 - resolution: "@selfxyz/euclid-web@npm:1.0.2" - dependencies: - "@selfxyz/euclid-core": "npm:^1.0.0" +"@selfxyz/euclid-core@npm:^1.2.0": + version: 1.2.0 + resolution: "@selfxyz/euclid-core@npm:1.2.0" peerDependencies: react: ">=18.2.0" - react-dom: ">=18.2.0" - checksum: 10c0/3fb4afe120e69bba9545ad358be1b5405c65030df35fe0c53a75adc96fe2515e79570aefe57b38e328ee653e55c20a8c09094071038e06f071ef7ee5387cfad4 + checksum: 10c0/198bcbd21e674a67ac801eb2b5f6a56cb08ae9fd09904deda78f07999540d3a7ccbfffa56599ab4ced2d8204732d2d7576d63eb14983a076223b546cef31e4b0 languageName: node linkType: hard @@ -10670,6 +10678,20 @@ __metadata: languageName: node linkType: hard +"@selfxyz/euclid@npm:^1.2.0": + version: 1.2.0 + resolution: "@selfxyz/euclid@npm:1.2.0" + dependencies: + "@lottiefiles/dotlottie-react": "npm:^0.18.4" + "@selfxyz/euclid-core": "npm:^1.2.0" + lottie-react: "npm:^2.4.1" + peerDependencies: + react: ">=18.2.0" + react-dom: ">=18.2.0" + checksum: 10c0/73e50bcf8540c0f2d91e3b232016c90f1019587acf24a79d9fd0e1d07cfb977aae5336972769bc824eade8ea02e02b3e583906cffa11853b389a7e2b20a34e58 + languageName: node + linkType: hard + "@selfxyz/kmp-sdk-test-app@workspace:packages/kmp-sdk-test-app": version: 0.0.0-use.local resolution: "@selfxyz/kmp-sdk-test-app@workspace:packages/kmp-sdk-test-app" @@ -11110,8 +11132,8 @@ __metadata: version: 0.0.0-use.local resolution: "@selfxyz/webview-app@workspace:packages/webview-app" dependencies: - "@selfxyz/euclid-core": "npm:^1.0.1" - "@selfxyz/euclid-web": "npm:^1.0.2" + "@selfxyz/euclid": "npm:^1.2.0" + "@selfxyz/euclid-core": "npm:^1.2.0" "@selfxyz/mobile-sdk-alpha": "workspace:^" "@selfxyz/webview-bridge": "workspace:^" "@sumsub/websdk": "npm:^2.0.0"