feat(desktop): information architecture overhaul — home, records, meetings first
Wave 2 of the 2026-09-28 design overhaul ("records are the main
character, a quiet instrument"). Decisions: design.md.
- Shell: sidebar with primary Home / Records / Meetings (Ctrl+1..3) and a
secondary "Tools" group; every item is a button with aria-current.
The status bar is removed — its "100% LOCAL PRIVACY · ZERO CLOUD SYNC"
claim contradicted device sync; the sidebar shows only queried state.
- Navigation context (routes, params, back, useRouteRequest) so Home can
open a specific meeting or record.
- Home replaces the dashboard: dictation key, start actions, only real
problems, one-line summary, recent records and meetings, file
transcription, free-tier limits.
- Settings become a full-screen route with vertical tabs (tabs no longer
clip) and a shared row grammar; long-standing bugs fixed: team and
enterprise tiers crashed the license tab (currentTier undefined),
first-run onboarding could not be left, Pro+ saw "upgrade to Pro+".
- Records: date-grouped single list with in-place expansion, search and
tags together, mode/favorite filters, load more past 50, undoable
delete. Meetings: list with search/rename/delete, summary-first detail
(summary | transcript | documents | my notes), notes persisted locally
instead of being lost. Tools pages cleaned up; stale-closure example
chips and a 1550x mouse-distance bug fixed.
- Shared pieces: PageHeader, EmptyStateCard, SearchInput ("/" works),
ConfirmDialog, notify with undo; display-labels maps domain values to
typed i18n keys so missing keys fail to compile.
- Mono font stack falls back to Pretendard for Hangul (Korean labels no
longer render spaced out).
- i18n: new ko/en keys via scripts/i18n-add-keys.mjs (one read-merge-
write so parallel editors cannot clobber locale files).
Some renderer files also carried pre-existing in-flight changes (error
recovery deep links, meeting document hooks); they are included as-is.
This commit is contained in:
parent
ba9ef9741e
commit
f064c93197
96 changed files with 12886 additions and 8924 deletions
|
|
@ -59,20 +59,23 @@ test.describe('History Page E2E', () => {
|
|||
});
|
||||
|
||||
test('should clear all history', async () => {
|
||||
// Wait for Dashboard to load first
|
||||
await expect(window.locator('text=/^(Dashboard|대시보드)$/').first()).toBeVisible({ timeout: 10000 });
|
||||
// Navigate to Records (sidebar label "기록"/"Records")
|
||||
await window.locator('text=/^(Records|기록)$/').first().click();
|
||||
await expect(window.getByRole('heading', { level: 1, name: /^(Records|기록)$/ })).toBeVisible();
|
||||
|
||||
// Navigate to History Tab
|
||||
await window.locator('text=/^(History|히스토리|기록)$/').first().click();
|
||||
await expect(window.locator('text=/^(History|히스토리|변환 기록)$/').first()).toBeVisible();
|
||||
|
||||
// Look for a Clear All button (this should fail in TDD RED)
|
||||
// 전체 삭제는 더보기 메뉴 안의 위험 행동 → 확인 대화상자를 거친다
|
||||
await window.getByTestId('records-more-button').click();
|
||||
const clearButton = window.getByTestId('clear-all-history-button');
|
||||
await expect(clearButton).toBeVisible();
|
||||
|
||||
// Click it and confirm
|
||||
await clearButton.click();
|
||||
|
||||
// 기록이 있으면 확인 대화상자가 뜬다 — "모두 삭제"로 확정
|
||||
const confirm = window.getByRole('button', { name: /^(Delete all|모두 삭제)$/ });
|
||||
await confirm
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
.then(() => confirm.click())
|
||||
.catch(() => undefined);
|
||||
|
||||
// Expect empty state to appear
|
||||
await expect(window.getByTestId('empty-state-card')).toBeVisible();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ test.describe('Launch Readiness E2E & Visual Verification', () => {
|
|||
|
||||
test('03. Should expose the web billing upgrade link without an in-app checkout modal', async () => {
|
||||
// "Remove ads with Pro" opens billingUrl({ tier }) in the external browser (LICENSE.OPEN_BILLING).
|
||||
const removeAdsLink = window.locator('text=/Remove ads with Pro/').first();
|
||||
const removeAdsLink = window.locator('text=/Remove ads with Pro|Pro로 광고 없애기/').first();
|
||||
if (await removeAdsLink.isVisible()) {
|
||||
await expect(window.locator('text=/D3RO Voice Secure Checkout/')).toHaveCount(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,47 +138,47 @@ test.describe.serial('Extreme Red Team - Cycle 5: Rapid Route Thrashing, Onboard
|
|||
await window.waitForTimeout(1000);
|
||||
|
||||
// 2. Verify OnboardingModal is visible
|
||||
await expect(window.getByText(/D3RO Voice 시작 가이드/i)).toBeVisible({ timeout: 10000 });
|
||||
await expect(window.getByText(/로컬 AI 모드/i).first()).toBeVisible();
|
||||
await expect(window.getByText(/온라인 클라우드 모드/i).first()).toBeVisible();
|
||||
await expect(window.getByText(/D3RO Voice 시작하기/i)).toBeVisible({ timeout: 10000 });
|
||||
await expect(window.getByText(/로컬 모드/i).first()).toBeVisible();
|
||||
await expect(window.getByText(/온라인 모드/i).first()).toBeVisible();
|
||||
|
||||
await window.screenshot({
|
||||
path: path.join(SCREENSHOT_DIR, 'rt17_01_onboarding_select_mode.png'),
|
||||
});
|
||||
|
||||
// 3. Test Local Mode Selection
|
||||
await window.getByRole('button', { name: /로컬 AI 모드로 시작/i }).click();
|
||||
await window.getByRole('button', { name: /로컬 모드로 시작/i }).click();
|
||||
await window.waitForTimeout(600);
|
||||
|
||||
// Verify transitions to local_ollama_setup phase
|
||||
await expect(window.getByText(/Ollama 로컬 엔진/i).first()).toBeVisible({ timeout: 5000 });
|
||||
await expect(window.getByText(/Ollama에 연결/i).first()).toBeVisible({ timeout: 5000 });
|
||||
|
||||
await window.screenshot({
|
||||
path: path.join(SCREENSHOT_DIR, 'rt17_02_onboarding_local_setup.png'),
|
||||
});
|
||||
|
||||
// Test back button
|
||||
const backBtn = window.getByText('뒤로 가기', { exact: true });
|
||||
const backBtn = window.getByRole('button', { name: '뒤로', exact: true });
|
||||
await expect(backBtn).toBeVisible();
|
||||
await backBtn.click();
|
||||
await window.waitForTimeout(500);
|
||||
|
||||
// Verify back to select_mode
|
||||
await expect(window.getByText(/로컬 AI 모드/i).first()).toBeVisible();
|
||||
await expect(window.getByText(/로컬 모드/i).first()).toBeVisible();
|
||||
|
||||
// 4. Test Online Cloud Mode Selection
|
||||
await window.getByRole('button', { name: /온라인 계정 로그인/i }).click();
|
||||
await window.getByRole('button', { name: /계정으로 로그인/i }).click();
|
||||
await window.waitForTimeout(600);
|
||||
|
||||
// Verify transitions to online_auth phase
|
||||
await expect(window.getByText(/온라인 서비스를 이용하기 위해/i).first()).toBeVisible({ timeout: 5000 });
|
||||
await expect(window.getByText(/D3RO 계정으로 로그인하면 온라인 모드로/i).first()).toBeVisible({ timeout: 5000 });
|
||||
|
||||
await window.screenshot({
|
||||
path: path.join(SCREENSHOT_DIR, 'rt17_03_onboarding_online_auth.png'),
|
||||
});
|
||||
|
||||
// Go back again
|
||||
await window.getByText('뒤로 가기', { exact: true }).click();
|
||||
await window.getByRole('button', { name: '뒤로', exact: true }).click();
|
||||
await window.waitForTimeout(500);
|
||||
|
||||
// 5. Restore onboardingCompleted
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue