---
phase: 10-ux-interaction
plan: 06
type: execute
wave: 1
depends_on: [10-02, 10-03, 10-04, 10-05]
files_modified:
  - frontend/src/components/storage/StorageBrowser.vue
  - frontend/src/views/FileManagerView.vue
  - frontend/src/views/CloudFolderView.vue
  - frontend/src/components/folders/FolderBreadcrumb.vue
  - frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js
  - frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js
autonomous: true
requirements: [UX-02, UX-01, UX-10, UX-12]
must_haves:
  truths:
    - "StorageBrowser shows 5+ animated skeleton rows when loading=true (no Loading… text)"
    - "Empty file list renders <EmptyState> with the appropriate icon/headline/subtext per context (root, folder, search)"
    - "BreadcrumbBar replaces FolderBreadcrumb in StorageBrowser; FolderBreadcrumb.vue is deleted in this plan"
    - "FileManagerView maps foldersStore.breadcrumb to [{id, label}] before passing as segments"
    - "CloudFolderView maps its breadcrumb to [{id, label}] before passing as segments"
    - "FileManagerView toast wiring fires on document delete and document move success/failure"
  artifacts:
    - path: "frontend/src/components/storage/StorageBrowser.vue"
      provides: "Updated with skeleton + EmptyState + BreadcrumbBar"
    - path: "frontend/src/views/FileManagerView.vue"
      provides: "Updated breadcrumb mapping + toast call sites"
    - path: "frontend/src/views/CloudFolderView.vue"
      provides: "Updated breadcrumb mapping"
  key_links:
    - from: "StorageBrowser.vue"
      to: "BreadcrumbBar.vue"
      via: "import + <BreadcrumbBar :segments=\"breadcrumb\" />"
      pattern: "import BreadcrumbBar"
    - from: "FileManagerView.vue"
      to: "useToastStore"
      via: "show() called in doMove and doDeleteDoc"
      pattern: "useToastStore"
---

<objective>
Wire the Wave 0 foundation components into `StorageBrowser.vue`, `FileManagerView.vue`, and `CloudFolderView.vue`. This plan completes UX-02 (skeleton rows), the StorageBrowser parts of UX-01 (EmptyState), the file-manager parts of UX-12 (BreadcrumbBar swap and FolderBreadcrumb deletion), and the FileManagerView parts of UX-10 (toast call sites).

Per CLAUDE.md "no dead code", FolderBreadcrumb.vue and its test file are deleted in the SAME commit as the BreadcrumbBar swap.

Output:
- StorageBrowser: 5-row skeleton (UX-02), three EmptyState variants (root/folder/search), import swap FolderBreadcrumb → BreadcrumbBar
- FileManagerView: breadcrumb `{id, name}` → `{id, label}` mapping, toast.show() on doMove + doDeleteDoc success/error
- CloudFolderView: breadcrumb mapping + BreadcrumbBar with rootLabel='Cloud'
- FolderBreadcrumb.vue + FolderBreadcrumb.test.js DELETED
- StorageBrowser.skeleton.test.js stub promoted to real tests
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@CLAUDE.md
@.planning/phases/10-ux-interaction/10-CONTEXT.md
@.planning/phases/10-ux-interaction/10-RESEARCH.md
@.planning/phases/10-ux-interaction/10-PATTERNS.md
@frontend/src/components/storage/StorageBrowser.vue
@frontend/src/views/FileManagerView.vue
@frontend/src/views/CloudFolderView.vue
@frontend/src/components/folders/FolderBreadcrumb.vue
@frontend/src/components/ui/EmptyState.vue
@frontend/src/components/ui/BreadcrumbBar.vue
@frontend/src/stores/toast.js

<interfaces>
**StorageBrowser already uses `<script setup>`** — modifications stay in that style. Skeleton classes from RESEARCH.md §Pitfall 7:
- Container: `grid grid-cols-[2rem_1fr_6rem_8rem_6rem] gap-3 px-4 py-2.5 items-center border-b border-gray-100`
- Col 1: `<div class="w-7 h-7 bg-gray-100 rounded-lg animate-pulse"></div>`
- Col 2: `<div class="h-4 bg-gray-100 rounded animate-pulse w-2/3"></div>`
- Col 3: `<div class="h-3 bg-gray-100 rounded animate-pulse hidden md:block"></div>`
- Col 4: `<div class="h-3 bg-gray-100 rounded animate-pulse hidden sm:block"></div>`
- Col 5: `<div class="w-14 h-3 bg-gray-100 rounded animate-pulse"></div>`

**EmptyState contexts in StorageBrowser** (from RESEARCH.md §Component Inventory §3):
| Condition | icon | headline | subtext |
|-----------|------|----------|---------|
| Root: !currentFolderId && lists empty && !searchQuery | folder | "Nothing here yet" | "Create a folder or upload your first file to get started." |
| In folder: currentFolderId && lists empty && !searchQuery | document | "This folder is empty" | "Upload files above or create a sub-folder." |
| Search: searchQuery && lists empty | search | `No results for "{{ searchQuery }}"` | "Try a different search term or clear the filter." |

StorageBrowser does not know `currentFolderId` directly — keep using the existing `emptyMessage` and `emptyHint` props, but change FileManagerView to pass per-context strings, OR replace the inline empty divs with three explicit conditionals inside StorageBrowser using `breadcrumb.length > 0` as a proxy for "in folder".

**Decision:** keep the props (emptyMessage, emptyHint) but render via `<EmptyState>` block. Use `breadcrumb.length === 0` (root) vs `> 0` (in folder) as the discriminator inside StorageBrowser for the icon name (folder vs document). For search no-results, the searchQuery check overrides.

**BreadcrumbBar wiring:**
- StorageBrowser passes through `:segments="breadcrumb"` — parents must already map name→label
- FileManagerView template: `:breadcrumb="mappedBreadcrumb"` where `mappedBreadcrumb` = computed mapping `foldersStore.breadcrumb` to `[{id: f.id, label: f.name}]`
- CloudFolderView: existing `breadcrumb` computed already returns `{id, name}` — add a `mappedBreadcrumb` computed
- Pass `rootLabel="Home"` for local mode and `rootLabel="Cloud"` for cloud mode (handled by passing through a prop or hardcoding in StorageBrowser based on `mode`)

**Toast call sites in FileManagerView (per RESEARCH.md §Toast System):**
- `doMove(docId, folderId)`: on success → `useToastStore().show('Document moved', 'success')`; on catch → `useToastStore().show('Move failed: ' + (e.message || 'unknown error'), 'error')`
- `doDeleteDoc(docId)`: on success → `useToastStore().show('Document deleted', 'success')`; on catch → `useToastStore().show('Delete failed: ' + (e.message || 'unknown error'), 'error')`
- (Upload toasts are handled in plan 10-07 to keep file ownership clean? — NO, FileManagerView owns upload too. Defer upload toast wiring to this plan as well.)
- `onFilesSelected({files, autoClassify})`: after `Promise.allSettled`, count items with `done=true` and items with errors, and show one toast: `show(`${successCount} of ${files.length} file(s) uploaded`, successCount === files.length ? 'success' : 'warning')` and for each item with `item.error` set, show `show('Upload failed: ' + item.error, 'error')`.

**FolderBreadcrumb.vue deletion:**
- Delete `frontend/src/components/folders/FolderBreadcrumb.vue`
- Delete `frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js`
- Grep verify no remaining import: `grep -r "FolderBreadcrumb" frontend/src/` returns 0 matches after edits
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Promote StorageBrowser.skeleton.test.js stubs to real tests</name>
  <files>frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js</files>
  <read_first>
    - frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js (Wave 0 stub from plan 10-05)
    - frontend/src/components/storage/StorageBrowser.vue (current state)
    - .planning/phases/10-ux-interaction/10-PATTERNS.md §"5-column StorageBrowser skeleton"
  </read_first>
  <behavior>
    Replace the `.todo` entries (UX-02 group) with real assertions:
    - Test 1: `renders 5 skeleton rows when loading=true and lists empty` — mount StorageBrowser with loading=true, folders=[], files=[], stub BreadcrumbBar/SearchBar/SortControls/DropZone/UploadProgress/TopicBadge. Assert `wrapper.findAll('.animate-pulse').length >= 5` (or assert a count of skeleton row containers >= 5).
    - Test 2: `Loading… text is absent when loading=true` — mount loading=true, assert `wrapper.text()` does NOT include 'Loading…'.
    - Test 3: `skeleton rows are NOT rendered when loading=false` — mount loading=false, folders=[], files=[], assert `wrapper.findAll('.animate-pulse').length === 0`.
    - Test 4: `skeleton row grid matches grid-cols-[2rem_1fr_6rem_8rem_6rem]` — mount loading=true, assert at least one element with class `grid-cols-[2rem_1fr_6rem_8rem_6rem]` exists in the skeleton block.

    Keep the UX-13 `it.todo` stubs as-is (deferred to plan 10-12).
  </behavior>
  <action>
    Modify `frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js`. Replace ONLY the UX-02 `describe` block's `it.todo` entries with the 4 real tests above. Keep the UX-13 describe block untouched (its tests are promoted in plan 10-12). Import `StorageBrowser` from `../StorageBrowser.vue` and use `import { mount } from '@vue/test-utils'`. Stub child components via `global.stubs: { BreadcrumbBar: true, SearchBar: true, SortControls: true, DropZone: true, UploadProgress: true, TopicBadge: true }`. Create a fresh Pinia instance per test using `setActivePinia(createPinia())` if needed.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run StorageBrowser.skeleton</automated>
    Expected: 4 UX-02 tests FAIL (RED — StorageBrowser still shows Loading… text); 3 UX-13 .todo entries stay pending.
  </verify>
  <acceptance_criteria>
    - File still exists with both `describe('UX-02: ...')` and `describe('UX-13: ...')` blocks
    - The UX-02 describe block contains 4 real `it(...)` tests (no `.todo` for UX-02)
    - The UX-13 describe block still contains `.todo` entries
    - Running `cd frontend && npm run test -- --run StorageBrowser.skeleton` shows 4 failing UX-02 tests
  </acceptance_criteria>
  <done>4 RED tests in place for UX-02 skeleton behavior.</done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Update StorageBrowser.vue — skeleton, EmptyState, BreadcrumbBar swap</name>
  <files>frontend/src/components/storage/StorageBrowser.vue</files>
  <read_first>
    - frontend/src/components/storage/StorageBrowser.vue (current state — uses <script setup>)
    - frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js (failing tests)
    - frontend/src/components/ui/BreadcrumbBar.vue (interface)
    - frontend/src/components/ui/EmptyState.vue (interface)
    - .planning/phases/10-ux-interaction/10-PATTERNS.md §"StorageBrowser.vue" + §"5-column StorageBrowser skeleton"
    - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §3"
  </read_first>
  <behavior>
    1. Import swap: replace `import FolderBreadcrumb from '../folders/FolderBreadcrumb.vue'` with `import BreadcrumbBar from '../ui/BreadcrumbBar.vue'`; also import `EmptyState`.
    2. Template: replace `<FolderBreadcrumb :segments="breadcrumb" ...>` with `<BreadcrumbBar :segments="breadcrumb" :root-label="mode === 'cloud' ? 'Cloud' : 'Home'" @navigate="$emit('breadcrumb-navigate', $event)" />`.
    3. Replace the three inline empty-state divs (lines 226-241 of current file) with `<EmptyState>` blocks based on the discriminators:
       - Search no-results: `<EmptyState v-else-if="!loading && searchQuery && (folders.length + files.length) === 0" icon="search" :headline="'No results for ' + JSON.stringify(searchQuery)" subtext="Try a different search term or clear the filter."><template #cta><button @click="$emit('search-change', '')" class="mt-3 text-sm text-indigo-600 hover:underline">Clear search</button></template></EmptyState>`
       - In-folder empty (breadcrumb non-empty): `<EmptyState v-else-if="!loading && breadcrumb.length > 0 && folders.length === 0 && files.length === 0 && !showNewFolderInput" icon="document" :headline="emptyMessage" :subtext="emptyHint" />`
       - Root empty: `<EmptyState v-else-if="!loading && breadcrumb.length === 0 && folders.length === 0 && files.length === 0 && !showNewFolderInput" icon="folder" :headline="emptyMessage" :subtext="emptyHint" />`
       (Use the `emptyMessage` / `emptyHint` props for headline/subtext so existing parent prop-driven configuration still works.)
    4. Replace the `<div v-if="loading" ...>Loading…</div>` element (current line 244) with a `<template v-if="loading">` block containing 5 skeleton row divs matching the grid:
       ```
       <template v-if="loading">
         <div v-for="n in 5" :key="`sk-${n}`" class="px-4 py-2.5 grid grid-cols-[2rem_1fr_6rem_8rem_6rem] gap-3 items-center border-b border-gray-100">
           <div class="w-7 h-7 bg-gray-100 rounded-lg animate-pulse"></div>
           <div class="h-4 bg-gray-100 rounded animate-pulse w-2/3"></div>
           <div class="h-3 bg-gray-100 rounded animate-pulse hidden md:block"></div>
           <div class="h-3 bg-gray-100 rounded animate-pulse hidden sm:block"></div>
           <div class="w-14 h-3 bg-gray-100 rounded animate-pulse"></div>
         </div>
       </template>
       ```
       Ensure the empty-state blocks use `v-else-if` so they only render when `!loading`.
  </behavior>
  <action>
    Edit `frontend/src/components/storage/StorageBrowser.vue`:

    **Step 1 — Imports (in `<script setup>` block):**
    Replace `import FolderBreadcrumb from '../folders/FolderBreadcrumb.vue'` with `import BreadcrumbBar from '../ui/BreadcrumbBar.vue'` and add `import EmptyState from '../ui/EmptyState.vue'`.

    **Step 2 — Template breadcrumb usage:**
    Replace lines 7-10 (the `<FolderBreadcrumb>` block) with:
    ```
    <BreadcrumbBar
      :segments="breadcrumb"
      :root-label="mode === 'cloud' ? 'Cloud' : 'Home'"
      @navigate="$emit('breadcrumb-navigate', $event)"
    />
    ```

    **Step 3 — Replace empty state and loading blocks (current lines 226-244):**
    Replace the existing three blocks (lines 226-244) with:
    ```
    <template v-if="loading">
      <div
        v-for="n in 5"
        :key="`sk-${n}`"
        class="px-4 py-2.5 grid grid-cols-[2rem_1fr_6rem_8rem_6rem] gap-3 items-center border-b border-gray-100"
      >
        <div class="w-7 h-7 bg-gray-100 rounded-lg animate-pulse"></div>
        <div class="h-4 bg-gray-100 rounded animate-pulse w-2/3"></div>
        <div class="h-3 bg-gray-100 rounded animate-pulse hidden md:block"></div>
        <div class="h-3 bg-gray-100 rounded animate-pulse hidden sm:block"></div>
        <div class="w-14 h-3 bg-gray-100 rounded animate-pulse"></div>
      </div>
    </template>

    <EmptyState
      v-else-if="searchQuery && folders.length === 0 && files.length === 0"
      icon="search"
      :headline="`No results for &quot;${searchQuery}&quot;`"
      subtext="Try a different search term or clear the filter."
    >
      <template #cta>
        <button @click="$emit('search-change', '')" class="mt-3 text-sm text-indigo-600 hover:underline">
          Clear search
        </button>
      </template>
    </EmptyState>

    <EmptyState
      v-else-if="breadcrumb.length > 0 && folders.length === 0 && files.length === 0 && !showNewFolderInput"
      icon="document"
      :headline="emptyMessage"
      :subtext="emptyHint"
    />

    <EmptyState
      v-else-if="folders.length === 0 && files.length === 0 && !showNewFolderInput"
      icon="folder"
      :headline="emptyMessage"
      :subtext="emptyHint"
    />
    ```

    No comments. Keep the rest of StorageBrowser.vue unchanged.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run StorageBrowser.skeleton</automated>
    Expected: 4 UX-02 tests PASS.
  </verify>
  <acceptance_criteria>
    - `grep -E "import BreadcrumbBar from '../ui/BreadcrumbBar.vue'" frontend/src/components/storage/StorageBrowser.vue` returns 1
    - `grep -E "import EmptyState from '../ui/EmptyState.vue'" frontend/src/components/storage/StorageBrowser.vue` returns 1
    - `grep -E "import FolderBreadcrumb" frontend/src/components/storage/StorageBrowser.vue` returns 0
    - `grep -E "<FolderBreadcrumb" frontend/src/components/storage/StorageBrowser.vue` returns 0
    - `grep -E "<BreadcrumbBar" frontend/src/components/storage/StorageBrowser.vue` returns 1
    - `grep -E "<EmptyState" frontend/src/components/storage/StorageBrowser.vue` returns 3 (root, folder, search)
    - `grep -v '^#' frontend/src/components/storage/StorageBrowser.vue | grep -c "Loading…"` returns 0
    - `grep -E "animate-pulse" frontend/src/components/storage/StorageBrowser.vue` returns ≥ 5
    - `cd frontend && npm run test -- --run StorageBrowser.skeleton` exits 0 (UX-02 GREEN)
  </acceptance_criteria>
  <done>StorageBrowser has skeleton, EmptyState, and BreadcrumbBar wired; UX-02 tests green.</done>
</task>

<task type="auto">
  <name>Task 3: Update FileManagerView + CloudFolderView for breadcrumb mapping + toast wiring; delete FolderBreadcrumb</name>
  <files>
    frontend/src/views/FileManagerView.vue,
    frontend/src/views/CloudFolderView.vue,
    frontend/src/components/folders/FolderBreadcrumb.vue,
    frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js
  </files>
  <read_first>
    - frontend/src/views/FileManagerView.vue (current state — uses <script setup>)
    - frontend/src/views/CloudFolderView.vue (current breadcrumb computed)
    - frontend/src/stores/folders.js (foldersStore.breadcrumb shape)
    - frontend/src/stores/toast.js (useToastStore signature)
    - .planning/phases/10-ux-interaction/10-PATTERNS.md §"FileManagerView.vue"
  </read_first>
  <action>
    **Step A — Modify `frontend/src/views/FileManagerView.vue`:**

    Add `import { useToastStore } from '../stores/toast.js'` near the other imports.

    Add a `computed` for `mappedBreadcrumb`:
    ```js
    const mappedBreadcrumb = computed(() =>
      (foldersStore.breadcrumb || []).map(f => ({ id: f.id, label: f.name }))
    )
    ```

    Change the template `:breadcrumb="foldersStore.breadcrumb"` to `:breadcrumb="mappedBreadcrumb"` on the `<StorageBrowser>` element.

    Update `doMove`:
    ```js
    async function doMove(docId, folderId) {
      const toast = useToastStore()
      try {
        await docsStore.moveToFolder(docId, folderId)
        toast.show('Document moved', 'success')
      } catch (e) {
        toast.show('Move failed: ' + (e.message || 'unknown error'), 'error')
      }
    }
    ```

    Update `doDeleteDoc`:
    ```js
    async function doDeleteDoc(docId) {
      const toast = useToastStore()
      try {
        await docsStore.remove(docId)
        toast.show('Document deleted', 'success')
      } catch (e) {
        toast.show('Delete failed: ' + (e.message || 'unknown error'), 'error')
      }
    }
    ```

    Update `onFilesSelected` to fire a summary toast at the end:
    ```js
    async function onFilesSelected({ files, autoClassify }) {
      const folderId = currentFolderId.value
      const toast = useToastStore()
      const promises = files.map(file => {
        const item = reactive({ name: file.name, done: false, error: null, quotaError: null, topics: null })
        uploadQueue.value.unshift(item)
        return docsStore.upload(file, autoClassify, folderId)
          .then(({ doc }) => { item.done = true; item.topics = doc.topics ?? [] })
          .catch(e => {
            if (e.status === 413 && e.payload) item.quotaError = e.payload
            else item.error = e.message
          })
      })
      await Promise.allSettled(promises)
      await topicsStore.fetchTopics()
      const succeeded = uploadQueue.value.slice(0, files.length).filter(i => i.done).length
      if (succeeded === files.length) {
        toast.show(`${succeeded} file(s) uploaded`, 'success')
      } else if (succeeded > 0) {
        toast.show(`${succeeded} of ${files.length} file(s) uploaded`, 'warning')
      } else {
        toast.show('Upload failed', 'error')
      }
    }
    ```
    (Important: the upload error per-item already populates `item.error`; UploadProgress already renders these. The summary toast is in addition.)

    **Step B — Modify `frontend/src/views/CloudFolderView.vue`:**

    Add a `mappedBreadcrumb` computed mapping `breadcrumb.value` (or the existing computed) to `[{id, label: name}]`. Pass `:breadcrumb="mappedBreadcrumb"` instead of `:breadcrumb="breadcrumb"`. Leave everything else unchanged.

    **Step C — Delete FolderBreadcrumb files:**

    Delete both files:
    - `frontend/src/components/folders/FolderBreadcrumb.vue`
    - `frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js`

    Use the file deletion tool or `rm` via Bash. Confirm with `ls frontend/src/components/folders/`.

    No comments added. No `console.error` removed from doMove/doDeleteDoc — leave existing behavior intact aside from the toast addition. Actually: REMOVE the `console.error(e.message)` lines because the toast now communicates the error to the user.

    Per D-11/D-13: the `mappedBreadcrumb` computed is also used in any test for FileManagerView — update those tests in the next step.

    Final grep verification: `grep -r "FolderBreadcrumb" frontend/src/` returns 0 matches.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run FileManagerView &amp;&amp; cd frontend && npm run test -- --run StorageBrowser.skeleton &amp;&amp; cd frontend && npm run test -- --run BreadcrumbBar &amp;&amp; cd frontend && npm run test -- --run toast</automated>
    Expected: all relevant test suites pass. FolderBreadcrumb tests no longer collected (file deleted).
  </verify>
  <acceptance_criteria>
    - File `frontend/src/components/folders/FolderBreadcrumb.vue` no longer exists
    - File `frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js` no longer exists
    - `grep -r "FolderBreadcrumb" frontend/src/` returns 0 lines
    - `grep -E "useToastStore" frontend/src/views/FileManagerView.vue` returns ≥ 4 occurrences (import + 3 action handlers minimum)
    - `grep -E "toast\\.show\\('Document moved'" frontend/src/views/FileManagerView.vue` returns 1 match
    - `grep -E "toast\\.show\\('Document deleted'" frontend/src/views/FileManagerView.vue` returns 1 match
    - `grep -E "mappedBreadcrumb" frontend/src/views/FileManagerView.vue` returns ≥ 2 matches
    - `grep -E "mappedBreadcrumb" frontend/src/views/CloudFolderView.vue` returns ≥ 2 matches
    - `grep -E "console\\.error" frontend/src/views/FileManagerView.vue` returns ≤ 1 (or 0 if previously only present in doMove/doDeleteDoc)
    - Suites pass: FileManagerView, StorageBrowser.skeleton, BreadcrumbBar, toast all exit 0
  </acceptance_criteria>
  <done>FileManagerView + CloudFolderView wired; FolderBreadcrumb deleted; toast call sites live; all tests green.</done>
</task>

</tasks>

<verification>
- `cd frontend && npm run test -- --run StorageBrowser.skeleton` exits 0
- `cd frontend && npm run test -- --run FileManagerView` exits 0
- `cd frontend && npm run test -- --run BreadcrumbBar` exits 0 (regression)
- `cd frontend && npm run test -- --run toast` exits 0 (regression)
- `grep -r "FolderBreadcrumb" frontend/src/` returns 0 lines (dead code removed)
- `cd frontend && npm run test -- --run` (full suite) exits 0 — no other suite broke
- StorageBrowser.vue contains exactly 3 `<EmptyState>` blocks and exactly 1 `<BreadcrumbBar>` block
- StorageBrowser.vue contains 5 skeleton `<div>`s with `animate-pulse`
- Loading… text removed (grep returns 0 in StorageBrowser.vue)
</verification>

<success_criteria>
Loading the file manager with `loading=true` shows 5 animated skeleton rows. After load completes:
- Empty root folder → "Nothing here yet" with folder icon
- Empty sub-folder → "This folder is empty" with document icon
- Search with no matches → "No results for {query}" with search icon and Clear button
Breadcrumbs use the shared BreadcrumbBar with rootLabel="Home"/"Cloud" per mode. Document move/delete/upload actions fire toasts.
</success_criteria>

<output>
Create `.planning/phases/10-ux-interaction/10-06-SUMMARY.md` when done. List which behaviors became GREEN and the exact line ranges modified.
</output>
