From 80e53ff252dac1c53e4debf2cc20c5b2c386f4f0 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Wed, 1 Jul 2026 02:46:40 +0800 Subject: [PATCH] Improve workspace search and responsive shell --- docs/UX_UI_REFACTOR_PLAN.md | 87 +++++++++++++++++++++ frontend/e2e/ux-followup.spec.js | 18 +++++ frontend/src/App.svelte | 12 ++- frontend/src/lib/shell/Sidebar.svelte | 27 ++++++- frontend/src/lib/shell/WorkspaceHost.svelte | 40 +++++++++- 5 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 docs/UX_UI_REFACTOR_PLAN.md diff --git a/docs/UX_UI_REFACTOR_PLAN.md b/docs/UX_UI_REFACTOR_PLAN.md new file mode 100644 index 0000000..9de5b41 --- /dev/null +++ b/docs/UX_UI_REFACTOR_PLAN.md @@ -0,0 +1,87 @@ +# UX/UI Refactor Plan + +## Assumptions + +- `verstak-desktop` is the core platform and UI shell. Notes, files, editor, activity, journal, browser inbox, and search behavior stay plugin-owned. +- The old `~/git/verstak` UI is a visual and interaction reference, not an architecture source. +- The v2 shell should improve orientation, density, keyboard/mouse ergonomics, and responsive behavior without reintroducing the v1 monolith. + +## Reference Rules + +Keep from v1: + +- compact dark workbench rhythm; +- clear title/header zones; +- dense rows and tabs for repeated work; +- action controls that appear where the user is working; +- custom, in-app interaction surfaces instead of browser-default dialogs where the flow is important. + +Avoid from v1: + +- putting user business workflows into `App.svelte`; +- direct coupling between shell and notes/files/editor internals; +- global mutable UI state that plugins must know about; +- moving plugin behavior into core for visual convenience. + +## Mimo Delegation Model + +Use `~/bin/mimo.sh run --dir ""` only for bounded junior tasks. + +Good tasks: + +- compare two small components and write a short report to `/tmp`; +- draft CSS for a named component within existing tokens; +- inspect one test file and suggest missing assertions; +- make a one-component mechanical change after the target behavior is already specified. + +Do not delegate: + +- architecture decisions; +- plugin/core boundary decisions; +- final diff review; +- verification claims; +- commits or pushes. + +Every mimo result must be reviewed with `git diff` and verified independently. + +## Work Plan + +1. Shell orientation + - Move persistent search and workspace context into the workspace header. + - Keep exactly one global search entry visible at a time. + - Preserve search availability in global plugin views. + +2. Responsive shell + - Make narrow viewports usable by stacking sidebar above workspace content. + - Ensure tabs and workspace cards do not force horizontal page overflow. + - Add Playwright coverage for mobile geometry. + +3. Today surface + - Make Today feel like a work-resume surface, not a static card grid. + - Keep data loading from plugin settings/contributions; do not add business logic to core. + - Improve empty states and quick actions based on available workspace tools. + +4. Plugin manager polish + - Improve scanning, status density, and permission readability. + - Keep enable/disable/status behavior unchanged. + - Verify degraded/failed/disabled plugin paths. + +5. Files/workbench ergonomics + - Use the existing files plugin comparison report as input. + - Prefer plugin-local improvements: context menu, keyboard navigation, selection, and custom confirmation. + - Do not add file-manager logic to the shell. + +## Verification Gates + +Before commit: + +- `npm run build` +- focused Playwright suite for the touched flow +- full `npm run test:e2e` when App, Sidebar, WorkspaceHost, PluginManager, or shared shell layout changes +- desktop and mobile screenshots inspected manually + +Before push: + +- re-run the relevant verification from a clean current worktree state; +- inspect `git status` and `git diff --stat`; +- push only after tests and visual smoke have current evidence. diff --git a/frontend/e2e/ux-followup.spec.js b/frontend/e2e/ux-followup.spec.js index 10636ac..9297916 100644 --- a/frontend/e2e/ux-followup.spec.js +++ b/frontend/e2e/ux-followup.spec.js @@ -11,10 +11,12 @@ test.describe('UX follow-up fixes', () => { test('global search stays available after opening tool sidebar views', async ({ page }) => { const search = page.locator('[data-global-search-input]'); await expect(search).toBeVisible(); + await expect(page.locator('.workspace-header [data-global-search-input]')).toBeVisible(); await page.locator('.sidebar .nav-item').filter({ hasText: 'Activity' }).click(); await expect(page.locator('.activity-root')).toBeVisible({ timeout: 10000 }); await expect(search).toBeVisible(); + await expect(page.locator('.sidebar [data-global-search-input]')).toBeVisible(); await page.locator('.sidebar .nav-item').filter({ hasText: 'Browser Inbox' }).click(); await expect(page.locator('.browser-inbox-root')).toBeVisible({ timeout: 10000 }); @@ -87,6 +89,22 @@ test.describe('UX follow-up fixes', () => { await expect(searchInput).toHaveValue('pr'); }); + test('mobile workspace layout gives content full width below the sidebar', async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + await page.reload(); + await waitForAppReady(page); + + const workspaceBox = await page.locator('.workspace-host').boundingBox(); + const sidebarBox = await page.locator('.sidebar').boundingBox(); + expect(workspaceBox.width).toBeGreaterThan(340); + expect(workspaceBox.y).toBeGreaterThan(sidebarBox.y + sidebarBox.height - 1); + await expect(page.locator('.workspace-header [data-global-search-input]')).toBeVisible(); + await expect(page.getByRole('tab', { name: 'Today' })).toBeVisible(); + + const hasHorizontalOverflow = await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth); + expect(hasHorizontalOverflow).toBe(false); + }); + test('plugin settings modal gives complex panels enough space', async ({ page }) => { await openPluginManager(page); await page.locator('.plugin-card').filter({ hasText: 'verstak.platform-test' }).getByRole('button', { name: 'Settings' }).click(); diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index c87abd9..6357562 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -385,7 +385,7 @@ {:else}
- +
@@ -576,4 +576,14 @@ flex-direction: column; padding: clamp(1rem, 2vw, 1.5rem); } + + @media (max-width: 720px) { + main { + flex-direction: column; + } + + .content { + padding: 0.75rem; + } + } diff --git a/frontend/src/lib/shell/Sidebar.svelte b/frontend/src/lib/shell/Sidebar.svelte index f539559..0e28991 100644 --- a/frontend/src/lib/shell/Sidebar.svelte +++ b/frontend/src/lib/shell/Sidebar.svelte @@ -6,6 +6,8 @@ import Icon from '../ui/Icon.svelte'; import { debug } from '../log/debug.js'; + export let showGlobalSearch = true; + function flog(msg) { App.WriteFrontendLog('Sidebar', msg); } @@ -77,7 +79,7 @@ Verstak - {#if vaultOpen} + {#if vaultOpen && showGlobalSearch} {/if} @@ -223,4 +225,27 @@ :global(.sidebar-error-icon) { color: #e94560; } + + @media (max-width: 720px) { + .sidebar { + width: 100%; + min-width: 0; + max-height: 14rem; + border-right: 0; + border-bottom: 1px solid #0f3460; + } + + .sidebar-header { + padding: 0.75rem 1rem; + } + + .sidebar-section { + max-height: 5.5rem; + overflow: auto; + } + + .sidebar-footer { + display: none; + } + } diff --git a/frontend/src/lib/shell/WorkspaceHost.svelte b/frontend/src/lib/shell/WorkspaceHost.svelte index beb60b8..7b3a69f 100644 --- a/frontend/src/lib/shell/WorkspaceHost.svelte +++ b/frontend/src/lib/shell/WorkspaceHost.svelte @@ -1,5 +1,6 @@