4126cf698424

make create-vault an explicit onboarding choice and guard final-card click from silently creating

time
2026-08-18 04:16 UTC · 2 months ago
machine
koh2
agent
deepseek-v4-flash-free
session
onboarding create-vault choice
parent
7a2a18dfa865
3 files src/popup/styles/popup.css+10 −0src/popup/views/AdoptVaultView.test.tsx+20 −0src/popup/views/Onboarding.tsx+22 −1
src/popup/styles/popup.css+10 −0
@@ -7124,6 +7124,16 @@71247124   background: color-mix(in srgb, var(--peach-accent) 13%, transparent);71257125 }71267126 7127+.peach-onboarding-choice--create {7128+  border-color: var(--peach-accent);7129+  background: var(--peach-accent);7130+  color: rgba(0, 0, 0, 0.92);7131+}7132+7133+.peach-onboarding-choice--create .peach-onboarding-choice-detail {7134+  color: rgba(0, 0, 0, 0.72);7135+}7136+71277137 .peach-adopt-vault {71287138   position: relative;71297139   min-height: 100%;

view file

src/popup/views/AdoptVaultView.test.tsx+20 −0
@@ -69,6 +69,26 @@6969     expect(onAdopt).toHaveBeenCalledTimes(1);7070   });7171 72+  it("T-A21b: create-vault is an explicit choice; a final-card tap does not silently create", () => {73+    vi.useFakeTimers();74+    const onComplete = vi.fn();75+    const view = mounted(() => <Onboarding onComplete={onComplete} onRestore={() => {}} onAdopt={() => {}} />);76+77+    button(view.container, "next").click();78+    vi.advanceTimersByTime(220);79+    button(view.container, "next").click();80+    vi.advanceTimersByTime(220);81+82+    expect(view.container.textContent).toContain("Create a new vault");83+84+    (view.container.querySelector(".peach-onboarding-card") as HTMLElement).click();85+    expect(onComplete).not.toHaveBeenCalled();86+87+    button(view.container, "Create a new vault").click();88+    vi.advanceTimersByTime(250);89+    expect(onComplete).toHaveBeenCalledTimes(1);90+  });91+7292   it("T-A22: password screen uses the same minimum-length gate as create-vault", () => {7393     const view = mounted(() => <AdoptVaultView onCancel={() => {}} onDone={() => {}} />);7494     const continueButton = button(view.container, "Continue");

view file

src/popup/views/Onboarding.tsx+22 −1
@@ -95,11 +95,17 @@9595       <div9696         class="peach-onboarding-card"9797         classList={{ "is-exiting": exiting() }}98-        onClick={advance}98+        onClick={() => {99+          // Final card: creation is an explicit choice; a stray card-body tap100+          // must not silently start vault creation.101+          if (card() === CARDS.length - 1) return;102+          advance();103+        }}99104         role="button"100105         tabIndex={0}101106         onKeyDown={(e) => {102107           if (e.key === "Enter" || e.key === " ") {108+            if (card() === CARDS.length - 1) return;103109             e.preventDefault();104110             advance();105111           }@@ -119,6 +125,21 @@119125           </p>120126         </Show>121127         <Show when={card() === CARDS.length - 1}>128+          <div class="peach-onboarding-choice-row">129+            <button130+              type="button"131+              class="peach-onboarding-choice peach-onboarding-choice--create"132+              onClick={(e) => {133+                e.stopPropagation();134+                void advance();135+              }}136+            >137+              <span class="peach-onboarding-choice-label">Create a new vault</span>138+              <span class="peach-onboarding-choice-detail">139+                Set your master password and start saving.140+              </span>141+            </button>142+          </div>122143           <p class="peach-onboarding-choice-heading">Already using Peach?</p>123144           <div class="peach-onboarding-choice-row">124145             <button

view file