Make connected accounts and durable cloud metadata feel like the local file manager by extending the one shared `StorageBrowser` and breadcrumb system. Render provider capabilities and freshness without duplicating a cloud grid or implementing Phase 13 actions.
@.planning/phases/12-cloud-resource-foundation/12-02-SUMMARY.md
@.planning/phases/12-cloud-resource-foundation/12-CONTEXT.md
@.planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md
@.planning/phases/12-cloud-resource-foundation/12-PATTERNS.md
@.planning/phases/12-cloud-resource-foundation/12-VALIDATION.md
Task 1: Route and store cloud navigation by connection identity
frontend/src/api/cloud.js, frontend/src/stores/cloudConnections.js, frontend/src/router/index.js, frontend/src/views/CloudStorageView.vue, frontend/src/views/CloudFolderView.vue, frontend/src/components/settings/SettingsCloudTab.vue, frontend/src/stores/__tests__/cloudConnections.test.js, frontend/src/views/__tests__/CloudStorageView.test.js, frontend/src/views/__tests__/CloudFolderView.test.js, frontend/src/components/settings/__tests__/SettingsCloudTab.test.js
- frontend/src/api/cloud.js — current provider-keyed folder URL
- frontend/src/stores/cloudConnections.js — existing Pinia fetch/disconnect pattern
- frontend/src/router/index.js — current `/cloud/:provider/:folderId` route
- frontend/src/views/CloudStorageView.vue — connection list currently bypasses StorageBrowser
- frontend/src/views/CloudFolderView.vue — thin-view boundary and provider-keyed browse
- frontend/src/components/settings/SettingsCloudTab.vue — existing connection maintenance UI where display-name editing belongs
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — root list and session navigation contracts
Change the canonical cloud route to `/cloud/:connectionId/:folderId(.*)` and call the connection-ID browse endpoint through `api/cloud.js`. Add `renameCloudConnection(id, displayName)` and a Pinia `rename` action that updates only the returned connection after the API succeeds. Add accessible display-name editing to the existing SettingsCloudTab connection maintenance surface; default names remain plain provider labels and duplicate defaults show a four-character connection UUID suffix in the cloud root until customized. Extend the Pinia store with selected connection, browse result, capabilities, freshness, last refresh, and safe error state while keeping API calls out of smart components. Render all connection roots through `StorageBrowser` data rather than a parallel row grid. `CloudFolderView` passes normalized props/events only. Save the last folder in `sessionStorage` under a namespaced key containing connection UUID; entering from the root may resume during the same session, while absent session state starts at root. Do not store JWTs, credentials, cloud item content, or persistent navigation in browser storage. Add tests for rename validation/success/failure, two same-provider roots, connection UUID URLs/API calls, root reset/fresh session, same-session resume, and thin-view behavior.
- no frontend browse request or route selects a connection by provider slug alone
- duplicate provider connections render as separate roots, default-name suffixes are deterministic, and Settings can customize each name independently
- only folder navigation state is stored in sessionStorage; no token or credential data is stored
- CloudStorageView and CloudFolderView contain no file-grid markup
- focused store/view tests pass
cd frontend && npm test -- --run cloudConnections CloudStorageView CloudFolderView SettingsCloudTab
Connection roots, custom names, connection-ID routing, and session-only folder memory pass store/view/settings tests.
Task 2: Replace mode-gated actions with accessible capability rendering
frontend/src/components/storage/StorageBrowser.vue, frontend/src/components/ui/AppIcon.vue, frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js
- frontend/src/components/storage/StorageBrowser.vue — all current `mode === 'local'` gates, emits, and touch targets
- frontend/src/components/ui/AppIcon.vue — centralized icon registry
- frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js — shared browser mount/event pattern
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — exact capability colors, props, interaction, copy, accessibility
Add typed/defaulted props for capabilities, connectionRoot, folderFreshness, lastRefreshedAt, and byteAvailability. Supply a full-supported local capability set so local and cloud use one action-rendering path. Keep action controls in stable locations. Supported controls emit existing generic events; unsupported controls render gray with `aria-disabled="true"`; temporarily unavailable controls render amber; neither uses native `disabled`; Enter, Space, pointer click, and touch tap suppress the action and surface the capability message through a shared explanation event/state. Use tooltips for hover/focus and an inline dismissible notice for touch per UI-SPEC. Do not expose Phase 13 mutation handlers: controls may be present but capability-disabled. Add a compact cached-byte icon and exact quota tooltip only when availability is `cached`; cloud_only renders no marker. Add AppIcon entries only if existing names are insufficient. Tests cover all three states, exact message/remedy, click/keyboard/touch action suppression, active event emission, touch target classes, cached marker, no cloud-only marker, and unchanged local drag/move/share/delete behavior.
- no action visibility block depends solely on `mode === 'local'`
- `aria-disabled` controls remain focusable and never emit the underlying action
- unsupported and temporary states use gray and amber UI-SPEC tokens respectively
- local capability defaults preserve all pre-Phase-12 local actions
- all StorageBrowser tests pass
cd frontend && npm test -- --run StorageBrowser
One shared browser renders and safely explains every capability state while preserving local behavior.
Task 3: Add connection breadcrumb freshness and finish UI plan protocol
frontend/src/components/ui/BreadcrumbBar.vue, frontend/src/components/ui/__tests__/BreadcrumbBar.test.js, frontend/src/utils/formatters.js, frontend/src/views/CloudFolderView.vue, backend/main.py, frontend/package.json, frontend/package-lock.json, AGENTS.md, README.md
- frontend/src/components/ui/BreadcrumbBar.vue — existing generic segment/root API
- frontend/src/components/ui/__tests__/BreadcrumbBar.test.js — current test conventions
- frontend/src/utils/formatters.js — single source for date/size/provider formatting
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — sync indicator states, copy, typography, spacing
- AGENTS.md — documentation and version protocol
Extend BreadcrumbBar generically with optional connection root icon/name and freshness metadata. Render refreshing spinner with accessible label, fresh checkmark that fades after three seconds without layout shift, and persistent warning indicator/banner with exact UI-SPEC copy and relative last-update time. Keep cached rows interactive. Use shared formatter utilities; add one reusable relative-time formatter there only if absent. Ensure no repeated provider badge appears on rows within a connection. Add fake-timer tests for fade behavior plus labels/tooltips/warning copy. Apply plan protocol: bump versions from 0.1.5 to 0.1.6 in backend main, package.json, and package-lock root/package entry; update AGENTS current state/shared component map if contracts changed; update README feature text for unified connection-root browsing and capability explanations.
- breadcrumb root is connection display name, with no redundant `Cloud` segment
- freshness exposes exact accessible labels for refreshing, up to date, and failed
- fresh indicator fades without resizing the breadcrumb; warning persists until state changes
- versions are exactly 0.1.6 across backend/frontend lock sources
- focused frontend suite and production build pass
cd frontend && npm test -- --run BreadcrumbBar StorageBrowser CloudFolderView && npm run build
Breadcrumb freshness, cached-byte status, documentation, version 0.1.6, focused tests, and production build are complete.
<threat_model>
Threat ID
Threat
Mitigation and evidence
T-12-01
Client route IDOR
Backend remains authority; frontend uses opaque connection UUID and tests never infer owner/provider access
T-12-08
Stored XSS via filename/provider message
Vue text interpolation only; no v-html; controlled capability message schema
T-12-10
Disabled action executes on keyboard/touch
aria-disabled guard suppresses click/Enter/Space and tests assert no action emit