Skip to content

ux(canvas/files): "Files not available" banner for external runtimes (#2999 PR-B) - #3003

Merged
HongmingWang-Rabbit merged 1 commit into
stagingfrom
ux/files-tab-external-not-available
May 6, 2026
Merged

ux(canvas/files): "Files not available" banner for external runtimes (#2999 PR-B)#3003
HongmingWang-Rabbit merged 1 commit into
stagingfrom
ux/files-tab-external-not-available

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Why

Reported by user (issue #2999): external workspaces (mac laptop, mac mini, hermes-on-home-server — runtime="external") render the FilesTab identically to the SaaS empty-listing bug, showing "0 files / No config files yet" even though the platform doesn't actually own the filesystem of these workspaces. Visually indistinguishable from the broken state, reads as a bug.

Fix

Mirror the affordance TerminalTab adopted in PR #2830 for runtimes without a TTY:

  1. New NotAvailablePanel in canvas/src/components/tabs/FilesTab/ — folder-with-slash icon + "Files not available" headline + body text that names the runtime and points the user at Chat.
  2. FilesTab now takes optional data?: WorkspaceNodeData. When data.runtime is in RUNTIMES_WITHOUT_FILES (currently just "external"), early-return the placeholder before mounting the useFilesApi hook.
  3. SidePanel passes node.data to FilesTab (matches existing pattern for ChatTab / TerminalTab).

Test coverage

FilesTab.notAvailable.test.tsx (4 tests):

  • external runtime → banner renders with runtime name + Chat-tab guidance copy.
  • external runtime → NO /files API request fires (asserted by inspecting the mocked api.get call log — discriminating: would fail without the early-return).
  • claude-code runtime → no banner, normal mount proceeds.
  • data prop omitted → falls through to normal mount (back-compat with legacy callers).

Three weakest spots (hostile self-review)

  1. RUNTIMES_WITHOUT_FILES is hardcoded. Reviewed alternative: pull from a shared runtime-capabilities registry (same shape as TerminalTab's RUNTIMES_WITHOUT_TERMINAL). Chose parallel pattern; consolidating into a shared registry can land if/when a third tab grows the same gate (rule of three).
  2. Static panel — no retry, no "report bug" link. Same as TerminalTab's; acceptable because the absence is intentional, not transient.
  3. Chat-tab guidance is hardcoded English. Matches the rest of canvas; will move with i18n migration.

Verification

  • npx tsc --noEmit clean
  • 54/54 canvas tab + SidePanel tests pass
  • Will be live-verified on staging post-merge: open Files tab on an external workspace → expect placeholder; on a platform-owned workspace → expect normal tree.

Pairing

Pairs with PR #3002 (PR-A, backend EIC fix). Without PR-A the platform-owned path still shows "0 files" because the backend never returns rows. Both can merge in either order; PR-B alone fixes the external-workspace UX, PR-A alone fixes the SaaS listing.

Refs #2999.

🤖 Generated with Claude Code

…2999 PR-B)

## Why

Reported by user (issue #2999): external workspaces (mac laptop, mac
mini, hermes-on-home-server — runtime="external") render the FilesTab
identically to the SaaS empty-listing bug, showing "0 files / No
config files yet" even though the platform doesn't actually own the
filesystem of these workspaces. Visually indistinguishable from the
broken state, reads as a bug.

## Fix

Mirror the affordance TerminalTab adopted in PR #2830 for runtimes
without a TTY:

1. New `NotAvailablePanel` in `canvas/src/components/tabs/FilesTab/`
   — folder-with-slash icon + "Files not available" headline + body
   text that names the runtime and points the user at Chat.

2. `FilesTab` now takes optional `data?: WorkspaceNodeData`. When
   `data.runtime` is in `RUNTIMES_WITHOUT_FILES` (currently just
   "external"), early-return the placeholder before mounting the
   useFilesApi hook. Mirrors TerminalTab's prop shape exactly so the
   review pattern is uniform across tabs.

3. SidePanel passes `node.data` to FilesTab (matches existing pattern
   for ChatTab / TerminalTab).

## Test coverage

`FilesTab.notAvailable.test.tsx` (4 tests):

- external runtime → banner renders with runtime name + Chat-tab
  guidance copy.
- external runtime → NO `/files` API request fires (asserted by
  inspecting the mocked api.get call log).
- claude-code runtime → no banner, normal mount proceeds (toolbar's
  root selector is the discriminator).
- data prop omitted → falls through to normal mount (back-compat
  with any caller that doesn't thread data through, e.g. legacy
  tests).

Each branch is independent and discriminating — none would pass on
a code-deleted version of the early-return.

## Three weakest spots (hostile self-review)

1. `RUNTIMES_WITHOUT_FILES` is a hardcoded set in this file. If a
   future runtime joins (e.g. a "byok-claude" that runs on user
   hardware), someone has to remember to add it here. Reviewed
   alternatives: pull from a runtime-capabilities registry — same
   shape as `RUNTIMES_WITHOUT_TERMINAL` already in TerminalTab. We
   chose the parallel pattern over a new abstraction; consolidating
   into a shared registry can land if/when a third tab grows the
   same gate (rule of three). Documented inline.

2. The placeholder is a static panel — no retry, no "report bug"
   link. Same as TerminalTab's. Acceptable because the absence is
   intentional, not transient.

3. Chat-tab guidance is hardcoded English. No i18n in canvas yet;
   matches the rest of the codebase. Will move with the i18n
   migration when that lands.

## Verification

- `npx tsc --noEmit` clean
- 54/54 canvas tab + SidePanel tests pass
- Will be live-verified on staging post-merge: open Files tab on an
  external workspace (mac laptop) → expect placeholder; open on a
  platform-owned workspace (Hongming Personal Brand Agent) → expect
  normal tree (assuming PR-A also lands).

Refs #2999. Pairs with PR-A (backend EIC fix) — without PR-A the
platform-owned path still shows "0 files" because the backend never
returns rows.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue May 6, 2026
Merged via the queue into staging with commit 2e6bed7 May 6, 2026
23 of 24 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the ux/files-tab-external-not-available branch May 6, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant