# Workspace Improvements Implementation Plan > **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:** Polish the workspace by removing logo/login button clutter, adding a history toggle, enforcing mandatory login after 3 guest uploads, and adding email verification code to the registration flow. **Architecture:** Five independent changes across the workspace layer. History toggle state lives in `WorkspacePage` and is passed into `LeftSidebar`. Email verification adds a two-step flow inside `AuthModal` only — no new files needed. All i18n strings go through `translations.ts`. **Tech Stack:** React 18, TypeScript, Tailwind CSS (utility classes), Lucide React icons, existing `http` client in `lib/api.ts` --- ## File Map | File | Change | |------|--------| | `src/components/layout/AppNavbar.tsx` | Remove logo icon + text + divider | | `src/components/LeftSidebar.tsx` | Remove login button; add history toggle prop | | `src/pages/WorkspacePage.tsx` | Add `historyEnabled` state; pass to sidebar; force mandatory modal after 3rd upload | | `src/components/AuthModal.tsx` | Add send-code button, countdown, verification code field to signup tab | | `src/lib/authService.ts` | Add `sendEmailCode()` method; update `register()` to send `code` | | `src/types/api.ts` | Add `SendEmailCodeRequest`; add `code` to `RegisterRequest` | | `src/lib/translations.ts` | Add new i18n keys for history toggle, verification code flow | --- ## Task 1: Add i18n strings for new features **Files:** - Modify: `src/lib/translations.ts` - [ ] **Step 1: Add new keys to both `en` and `zh` auth sections and sidebar section** In `src/lib/translations.ts`, find the `en.auth` block and add after `oauthFailed`: ```ts sendCode: 'Send Code', resendCode: 'Resend', codeSent: 'Code sent', verificationCode: 'Verification Code', verificationCodePlaceholder: 'Enter 6-digit code', verificationCodeRequired: 'Please enter the verification code.', verificationCodeHint: 'Check your inbox for the 6-digit code.', sendCodeFailed: 'Failed to send verification code, please retry.', ``` In `en.sidebar`, add after `historyHeader`: ```ts historyToggle: 'Show History', historyLoginRequired: 'Login to enable history', ``` In `zh.auth`, add after `oauthFailed`: ```ts sendCode: '发送验证码', resendCode: '重新发送', codeSent: '验证码已发送', verificationCode: '验证码', verificationCodePlaceholder: '请输入 6 位验证码', verificationCodeRequired: '请输入验证码。', verificationCodeHint: '请查收邮箱中的 6 位验证码。', sendCodeFailed: '发送验证码失败,请重试。', ``` In `zh.sidebar`, add after `historyHeader`: ```ts historyToggle: '显示历史', historyLoginRequired: '登录后开启历史记录', ``` - [ ] **Step 2: Commit** ```bash git add src/lib/translations.ts git commit -m "feat: add i18n keys for history toggle and email verification" ``` --- ## Task 2: Remove logo from AppNavbar **Files:** - Modify: `src/components/layout/AppNavbar.tsx:38-50` - [ ] **Step 1: Remove logo image, text, and divider — keep only the Home icon link** Replace the entire `{/* Left: Logo + Home link */}` div (lines 38–51) with: ```tsx {/* Left: Home link */}
{t.marketing.nav.home}
``` - [ ] **Step 2: Verify dev server renders correctly** ```bash npm run dev ``` Navigate to `/app`. Confirm the navbar no longer shows the TexPixel icon or text, only the Home link on the left. - [ ] **Step 3: Commit** ```bash git add src/components/layout/AppNavbar.tsx git commit -m "feat: remove logo from workspace navbar" ``` --- ## Task 3: Remove login button from LeftSidebar and add history toggle prop **Files:** - Modify: `src/components/LeftSidebar.tsx` - [ ] **Step 1: Add `historyEnabled` and `onToggleHistory` to the props interface** Replace the existing `LeftSidebarProps` interface with: ```ts interface LeftSidebarProps { files: FileRecord[]; selectedFileId: string | null; onFileSelect: (fileId: string) => void; onUploadClick: () => void; canUploadAnonymously: boolean; onRequireAuth: () => void; isCollapsed: boolean; onToggleCollapse: () => void; onUploadFiles: (files: File[]) => void; hasMore: boolean; loadingMore: boolean; onLoadMore: () => void; historyEnabled: boolean; onToggleHistory: () => void; } ``` - [ ] **Step 2: Destructure the two new props in the function signature** Replace the destructuring block (the function params) to add `historyEnabled` and `onToggleHistory`: ```ts export default function LeftSidebar({ files, selectedFileId, onFileSelect, onUploadClick, canUploadAnonymously, onRequireAuth, isCollapsed, onToggleCollapse, onUploadFiles, hasMore, loadingMore, onLoadMore, historyEnabled, onToggleHistory, }: LeftSidebarProps) { ``` - [ ] **Step 3: Add `Toggle` icon to lucide imports** Change the import line at the top from: ```ts import { Upload, LogIn, LogOut, FileText, Clock, ChevronLeft, ChevronRight, History, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react'; ``` to: ```ts import { Upload, LogOut, FileText, Clock, ChevronLeft, ChevronRight, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react'; ``` (Remove `LogIn`, `History` — no longer used in expanded view) - [ ] **Step 4: Remove the `showAuthModal` state and the `AuthModal` import/usage from LeftSidebar** Remove these lines near the top of the function body: ```ts const [showAuthModal, setShowAuthModal] = useState(false); ``` and the `useEffect` that sets `showAuthModal` to false on auth: ```ts useEffect(() => { if (isAuthenticated) { setShowAuthModal(false); } }, [isAuthenticated]); ``` Also remove `import AuthModal from './AuthModal';` at the top of the file, and at the bottom remove the `{showAuthModal && setShowAuthModal(false)} />}` JSX. - [ ] **Step 5: Replace the history header section with a toggle switch** Find the `{/* Middle Area: History */}` block. Replace the header div (the one with `Clock` icon and `historyHeader` text) with: ```tsx
{t.sidebar.historyHeader}
``` - [ ] **Step 6: Gate the history list behind `historyEnabled`** Replace the entire `
` scrollable list with: ```tsx
{!historyEnabled ? (
{t.sidebar.historyToggle}
) : !user ? (
{t.sidebar.historyLoginRequired}
) : files.length === 0 ? (
{t.sidebar.noHistory}
) : ( <> {files.map((file) => ( ))} {loadingMore && (
{t.common.loading}
)} {!hasMore && files.length > 0 && (
{t.sidebar.noMore}
)} )}
``` - [ ] **Step 7: Replace the bottom user/login area — remove login button, keep only logged-in user view** Replace the entire `{/* Bottom Area: User/Login */}` div with: ```tsx {/* Bottom Area: User info (only shown when logged in) */} {user && (

{displayName}

)} ``` - [ ] **Step 8: Fix collapsed view — remove LogIn icon button** In the `if (isCollapsed)` return block, remove the bottom ` ``` Replace with nothing (delete that button entirely). The `isAuthenticated` import can stay for now. - [ ] **Step 9: Commit** ```bash git add src/components/LeftSidebar.tsx git commit -m "feat: remove login button from sidebar, add history toggle" ``` --- ## Task 4: Wire history toggle state and mandatory auth in WorkspacePage **Files:** - Modify: `src/pages/WorkspacePage.tsx` - [ ] **Step 1: Add `historyEnabled` state** After the existing `const [loadingMore, setLoadingMore] = useState(false);` line, add: ```ts const [historyEnabled, setHistoryEnabled] = useState(false); ``` - [ ] **Step 2: Add `handleToggleHistory` callback** After the `openAuthModal` callback, add: ```ts const handleToggleHistory = useCallback(() => { if (!historyEnabled) { // Turning on if (!user) { openAuthModal(); return; } setHistoryEnabled(true); if (!hasLoadedFiles.current) { hasLoadedFiles.current = true; loadFiles(); } } else { setHistoryEnabled(false); } }, [historyEnabled, user, openAuthModal]); ``` - [ ] **Step 3: Remove auto-load on auth — history is now opt-in** Find the `useEffect` that auto-calls `loadFiles()` when `user` becomes available: ```ts useEffect(() => { if (!initializing && user && !hasLoadedFiles.current) { hasLoadedFiles.current = true; loadFiles(); } if (!user) { hasLoadedFiles.current = false; setFiles([]); setSelectedFileId(null); setCurrentPage(1); setHasMore(false); } }, [initializing, user]); ``` Replace with (keep the reset on logout, remove the auto-load): ```ts useEffect(() => { if (!user) { hasLoadedFiles.current = false; setHistoryEnabled(false); setFiles([]); setSelectedFileId(null); setCurrentPage(1); setHasMore(false); } }, [user]); ``` - [ ] **Step 4: Make the auth modal mandatory after 3rd upload** Add a new state after `showAuthModal`: ```ts const [authModalMandatory, setAuthModalMandatory] = useState(false); ``` Change `openAuthModal` to accept an optional `mandatory` parameter: ```ts const openAuthModal = useCallback((mandatory = false) => { setAuthModalMandatory(mandatory); setShowAuthModal(true); }, []); ``` In `handleUpload`, after `incrementGuestUsage()`, add a mandatory modal trigger when the new count hits the limit: ```ts if (!user && successfulUploads > 0) { incrementGuestUsage(); // Force login after hitting the limit setGuestUsageCount(prev => { const next = prev + 1; if (next >= GUEST_USAGE_LIMIT) { openAuthModal(true); } return next; }); } ``` Wait — `incrementGuestUsage` already increments. We need to check the new count after increment. Replace the `if (!user && successfulUploads > 0)` block at the end of `handleUpload` with: ```ts if (!user && successfulUploads > 0) { const nextCount = guestUsageCount + successfulUploads; const newCount = Math.min(nextCount, GUEST_USAGE_LIMIT + 10); localStorage.setItem(GUEST_USAGE_COUNT_KEY, String(newCount)); setGuestUsageCount(newCount); if (newCount >= GUEST_USAGE_LIMIT) { openAuthModal(true); } } ``` And remove the separate `incrementGuestUsage` call entirely (delete the `incrementGuestUsage` callback too). - [ ] **Step 5: Update the two places that check upload limit to pass `mandatory=false` (keep non-mandatory for pre-upload checks)** The `onUploadClick` handler and paste handler already call `openAuthModal()` with no arg (defaults to `false`) — that stays as-is. - [ ] **Step 6: Update AuthModal JSX to pass `mandatory` prop and update close handler** Find `{showAuthModal && ( setShowAuthModal(false)} />)}` and replace with: ```tsx {showAuthModal && ( { setShowAuthModal(false); setAuthModalMandatory(false); }} mandatory={authModalMandatory} /> )} ``` - [ ] **Step 7: Pass `historyEnabled` and `onToggleHistory` to LeftSidebar** Find the ` { if (user && !hasLoadedFiles.current && historyEnabled) { // user logged in while history was already toggled on hasLoadedFiles.current = true; loadFiles(); } if (!user) { hasLoadedFiles.current = false; setHistoryEnabled(false); setFiles([]); setSelectedFileId(null); setCurrentPage(1); setHasMore(false); } }, [user]); ``` - [ ] **Step 9: Commit** ```bash git add src/pages/WorkspacePage.tsx git commit -m "feat: history toggle state, mandatory auth after 3 guest uploads" ``` --- ## Task 5: Add email verification to API types and authService **Files:** - Modify: `src/types/api.ts` - Modify: `src/lib/authService.ts` - [ ] **Step 1: Update `RegisterRequest` and add `SendEmailCodeRequest` in `src/types/api.ts`** Find the existing `RegisterRequest` interface: ```ts export interface RegisterRequest { email: string; password: string; } ``` Replace with: ```ts export interface RegisterRequest { email: string; password: string; code: string; } export interface SendEmailCodeRequest { email: string; } ``` - [ ] **Step 2: Add `sendEmailCode()` to `authService` in `src/lib/authService.ts`** Add the import of `SendEmailCodeRequest` — it will be used below. Then add a new method inside `export const authService = { ... }` after the `login` method: ```ts async sendEmailCode(email: string): Promise { await http.post('/user/email/code', { email } satisfies SendEmailCodeRequest, { skipAuth: true }); }, ``` Also update the `register` method signature to accept `code`: ```ts async register(credentials: RegisterRequest): Promise<{ user: UserInfo; token: string; expiresAt: number }> { const response = await http.post('/user/register', credentials, { skipAuth: true }); if (!response.data) { throw new ApiError(-1, '注册失败,请重试'); } return buildSession(response.data, credentials.email); }, ``` (Signature stays the same — just ensuring `credentials` now includes `code` via the updated type.) - [ ] **Step 3: Update the import in `authService.ts` to include `SendEmailCodeRequest`** Find: ```ts import type { AuthData, GoogleAuthUrlData, GoogleOAuthCallbackRequest, LoginRequest, RegisterRequest, UserInfoData, UserInfo, } from '../types/api'; ``` Replace with: ```ts import type { AuthData, GoogleAuthUrlData, GoogleOAuthCallbackRequest, LoginRequest, RegisterRequest, SendEmailCodeRequest, UserInfoData, UserInfo, } from '../types/api'; ``` - [ ] **Step 4: Commit** ```bash git add src/types/api.ts src/lib/authService.ts git commit -m "feat: add sendEmailCode API and code field to RegisterRequest" ``` --- ## Task 6: Update AuthContext to pass `code` through signUp **Files:** - Modify: `src/contexts/AuthContext.tsx` - [ ] **Step 1: Update `signUp` to accept and forward `code`** Find the `signUp` function/action in `AuthContext.tsx`. It currently calls `authService.register({ email, password })`. Update the `signUp` signature to accept a third `code` parameter and pass it: ```ts signUp: async (email: string, password: string, code: string) => { ... } ``` Inside, change: ```ts await authService.register({ email, password, code }); ``` Also update the `AuthContextValue` interface (or wherever `signUp` type is declared) to: ```ts signUp: (email: string, password: string, code: string) => Promise<{ error: string | null }>; ``` - [ ] **Step 2: Commit** ```bash git add src/contexts/AuthContext.tsx git commit -m "feat: thread verification code through AuthContext signUp" ``` --- ## Task 7: Add email verification UI to AuthModal **Files:** - Modify: `src/components/AuthModal.tsx` - [ ] **Step 1: Add verification code state variables** After the existing `useState` declarations in `AuthModal`, add: ```ts const [verificationCode, setVerificationCode] = useState(''); const [codeSent, setCodeSent] = useState(false); const [codeCountdown, setCodeCountdown] = useState(0); const [sendingCode, setSendingCode] = useState(false); const countdownRef = useRef(null); ``` Add `useRef` to the React import if not already there. Also import `authService` at the top: ```ts import { authService } from '../lib/authService'; ``` - [ ] **Step 2: Add `sendCode` handler** After the `handleGoogleOAuth` function, add: ```ts const handleSendCode = async () => { const normalizedEmail = email.trim(); if (!normalizedEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) { setFieldErrors(prev => ({ ...prev, email: t.auth.emailInvalid })); return; } setSendingCode(true); try { await authService.sendEmailCode(normalizedEmail); setCodeSent(true); setCodeCountdown(60); countdownRef.current = setInterval(() => { setCodeCountdown(prev => { if (prev <= 1) { clearInterval(countdownRef.current!); return 0; } return prev - 1; }); }, 1000); } catch { setLocalError(t.auth.sendCodeFailed); } finally { setSendingCode(false); } }; ``` Add a cleanup `useEffect` for the countdown interval: ```ts useEffect(() => { return () => { if (countdownRef.current) clearInterval(countdownRef.current); }; }, []); ``` - [ ] **Step 3: Update `handleSubmit` to validate code and pass it to `signUp`** In the `handleSubmit` function, inside the `if (mode === 'signup')` validation block, add a check for the verification code: ```ts if (!verificationCode.trim()) { nextFieldErrors.verificationCode = t.auth.verificationCodeRequired; } ``` Update `fieldErrors` type to include `verificationCode`: ```ts const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string; confirmPassword?: string; verificationCode?: string; }>({}); ``` Update the `signUp` call to pass the code: ```ts const result = mode === 'signup' ? await signUp(normalizedEmail, password, verificationCode.trim()) : await signIn(normalizedEmail, password); ``` - [ ] **Step 4: Add Send Code button inline with the email field (signup mode only)** In the JSX, find the email `
` block. Replace it with a version that adds the send-code button when in signup mode: ```tsx
{ setEmail(e.target.value); if (fieldErrors.email) setFieldErrors((prev) => ({ ...prev, email: undefined })); }} style={fieldErrors.email ? { ...s.input, ...s.inputError } : s.input} placeholder="your@email.com" required disabled={isBusy} /> {fieldErrors.email &&

{fieldErrors.email}

} {mode === 'signup' &&

{t.auth.emailHint}

}
{mode === 'signup' && ( )}
``` - [ ] **Step 5: Add the verification code input field (signup mode, after confirm password)** After the confirm-password `{mode === 'signup' && (...)}` block, add: ```tsx {mode === 'signup' && (
{ setVerificationCode(e.target.value.replace(/\D/g, '')); if (fieldErrors.verificationCode) setFieldErrors(prev => ({ ...prev, verificationCode: undefined })); }} style={fieldErrors.verificationCode ? { ...s.input, ...s.inputError } : s.input} placeholder={t.auth.verificationCodePlaceholder} disabled={isBusy} /> {fieldErrors.verificationCode ?

{fieldErrors.verificationCode}

:

{t.auth.verificationCodeHint}

}
)} ``` - [ ] **Step 6: Reset code state when switching to signin tab** In the `setMode('signin')` onClick handler, add resets: ```ts onClick={() => { setMode('signin'); setFieldErrors({}); setLocalError(''); setVerificationCode(''); setCodeSent(false); setCodeCountdown(0); if (countdownRef.current) clearInterval(countdownRef.current); }} ``` - [ ] **Step 7: Verify in browser — register flow should now require sending code first** ```bash npm run dev ``` Open `/app`, click register. Confirm: 1. Email field has "Send Code" button 2. Clicking it (with valid email) triggers send and starts countdown 3. Verification code field appears below confirm-password 4. Submitting without code shows validation error 5. Login tab does not show the code field - [ ] **Step 8: Commit** ```bash git add src/components/AuthModal.tsx git commit -m "feat: add email verification code step to registration flow" ``` --- ## Task 8: Final smoke test - [ ] **Step 1: Run type check** ```bash npm run typecheck ``` Expected: no errors - [ ] **Step 2: Run unit tests** ```bash npm run test ``` Expected: all pass (existing tests should still pass) - [ ] **Step 3: Manual end-to-end check** ```bash npm run dev ``` Verify: 1. Workspace navbar has no logo — only Home link on left 2. Sidebar bottom shows no login button when logged out 3. History toggle is off by default, shows placeholder text 4. Toggling history on while logged out opens auth modal 5. After 3 guest uploads, mandatory auth modal appears (no close button) 6. Register tab: Send Code button appears, countdown works, code field required 7. Sign-in tab: no code field visible 8. Logged-in user: history toggle on loads history; logout resets toggle to off