Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

351 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @tutti-os/workspace-file-manager contract
Status: agreed package boundary (direction). Open questions are listed under
[Open questions](#open-questions) when any remain.
This document is the authoritative ownership contract for
`packages/workspace/file-manager`. Prefer it over chat history when planning
or reviewing changes. The package `README.md` remains a short usage overview
and must stay consistent with this contract.
Sibling preview ownership lives in
[`@tutti-os/workspace-file-preview` CONTRACT.md](../file-preview/CONTRACT.md).
When preview vs open/activation ownership is in doubt, that document wins for
preview concerns; this document wins for file-manager session and host concerns.
## Purpose
This package owns a **host-neutral workspace file manager surface**:
1. Session state and interaction orchestration for one workspace-scoped
file-manager instance (navigation, selection, locations, search projection,
mutation busy/dialogs).
2. The `WorkspaceFileManagerHost` contract that hosts implement for listing,
mutation, activation, open-with, reveal, and preview bytes.
3. File-manager-specific activation-target shaping, view-model derivation, and
capability projection from optional host methods.
4. Optional React UI that binds to the shared session and delegates context-menu
actions to the host through `resolveContextMenu`.
It does **not** own:
- Daemon / VM / preload transport, absolute host paths, or product globals
- Import / export / upload / download pipelines, transfer-center tasks, or
progress UI
- Share / exposure / product collaboration flows
- Full-content editors, dirty/save, or workbench framing outside this surface
- A second preview kind taxonomy (that belongs to
`@tutti-os/workspace-file-preview`)
## File manager vs preview vs host product
| Concern | Owner |
| -------------------------------------------------------------- | ---------------------------------------- |
| Browse / navigate / select / search projection / locations | This package (+ host listing/search) |
| Side-pane preview loading + built-in preview shells | `@tutti-os/workspace-file-preview` |
| File-manager preview projection into session `previewState` | This package |
| Open / reveal / open-with / browser / system activation | This package orchestrates; host executes |
| Import / export / upload / download / drag-to-import UX | Host (toolbar slot / menus / overlays) |
| Multi-step upload UX, transfer progress, gitignore pickers | Host |
| Share / link / exposure / room collaboration | Host |
| Context-menu action lists for blank / directory / file targets | Host via `resolveContextMenu` |
| Logical workspace paths in shared state | This package |
| Absolute / VM / OS paths and path projection | Host adapter |
| User-visible copy overrides and product chrome | Host |
**Decided route:** this package is a reusable frontend workspace-domain
surface, not only a transport-agnostic data kernel. Shared session
orchestration and React-facing interaction state may live here when they are
part of the reusable file-manager experience across hosts.
## Ownership layers
### 1. Paths and identity
Shared state uses **logical workspace paths** (for example
`/workspace/src/index.ts`). The package does not depend on tuttid, TSH, TACH,
VM roots, or host absolute paths.
**Workspace identity (decided):** session input and host methods use
`workspaceID`. Hosts that natively key workspaces differently (for example TSH
`roomId`) map at the adapter edge. Do not rename the public field for a single
host.
**Entry shape (decided for now):** keep the current
`WorkspaceFileEntry` fields (`kind`, `name`, `path`, `hasChildren`, sizes,
timestamps). Do **not** add MIME / MIME-source fields to the shared entry
contract for classification or preview routing; preview classification stays on
the preview package’s extension-based model. Hosts may keep richer metadata
locally without pushing it into this package.
### 2. Host contract and capabilities
Hosts integrate primarily by implementing `WorkspaceFileManagerHost` and
calling `createWorkspaceFileManagerService().createSession(...)`.
**Host shape (decided):** keep one flat `WorkspaceFileManagerHost` interface.
Do not split into TSH-style listing/mutation/share ports inside this package
unless a later contract revision explicitly does so. Optional methods remain
optional; the package derives `WorkspaceFileManagerCapabilities` from method
presence (for example `search` → `canSearch`, `moveEntry` → `canMove`).
**Minimum host obligation (decided):**
- Required: `listDirectory`
- Everything else is optional and gated by capabilities / UI flags
Practical capability notes (not additional required methods):
- Side-pane preview bytes need `readPreviewFile`
- Double-click / Open need `activateFile` and/or the relevant `openFile*` /
`revealEntry` methods the host wants to expose
- Search UI needs `search`
- Recent location content needs `listRecentEntries` when the host supplies a
recent location
Import, export, upload, and download stay **outside** this package. Hosts that
need those flows implement them in host adapters and expose them through host
context-menu DI rather than package-owned import/export host methods.
Hosts must not reimplement a second file-manager session stack for the same
product job when they already consume this package; extend this contract or
keep truly product-specific flows outside the shared session.
### 3. Session orchestration
`WorkspaceFileManagerSession` owns, for one workspace-scoped instance:
- Directory listing and navigation stacks
- Selection, context menu anchor state, create/delete/rename dialogs
- Location selection (`selectedLocationId`) and host-supplied
`locationSections`
- Search query/results projection when `canSearch`
- Search request lifecycle: starting a replacement search, clearing search
state, or disposing the session aborts the active request. The session passes
that lifecycle through `AbortSignal` on `search`; recent-location filtering
passes the same signal to `listRecentEntries`.
- Mutation busy state and shared error projection helpers
- Entry-name validation for create and rename operations before host mutation;
one name segment is limited to 255 UTF-8 bytes
- Preview projection into `previewState` via the preview package controller
- Activation orchestration that calls host `activateFile` / open / reveal APIs
- Persisted navigation/location snapshot via
`WorkspaceFileManagerPersistedState`
**Locations (decided):** the package owns the location model
(`directory` / `external` / `recent` and location sections). Hosts inject
`locationSections` (and optional `defaultLocationId`). A host may supply a
minimal single-root section set; Tutti-shaped project/home/recent sections are
host policy, not a required package shape.
**External locations (decided):** browsing content for `external` locations is
host-owned UI injected through React props such as
`renderExternalLocationContent`. This package does not depend on
`@tutti-os/workspace-file-reference` for that content. Reference pickers may
reuse this package’s entry types, icons, and arrange helpers without inverting
ownership.
### 4. Preview integration
Preview **classification and loading lifecycle** are owned by
`@tutti-os/workspace-file-preview`. This package:
- Shapes file-manager activation targets for host open policy
- Wires `host.readPreviewFile` into the shared preview controller
- Projects controller state into session `previewState` and optional UI
Open / reveal / open-with / browser activation remain file-manager + host
concerns, consistent with the preview contract. Open-with advisories such as
`isWorkspaceFileBrowserOpenable` and
`shouldFilterVideoPlayersForOpenWith` live in this package’s `/services`
export, not in preview.
### 5. Context menu DI
**Context menu contract (decided):** the package owns only the menu anchor and
interaction shell (`contextMenu`, `currentDirectoryPath`, busy/loading flags).
Menu items are host-owned.
When the user opens a blank-area, directory, or file context menu, the React
surface calls the required `resolveContextMenu(request)` prop. The request
includes:
- `target`: `"blank"`, `"directory"`, or `"file"` (via
`resolveWorkspaceFileManagerContextMenuTarget`)
- Current directory path, selected location, search/external/recent flags, and
busy state
The host returns the menu item tree (`WorkspaceFileManagerContextMenuItem[]`).
Import, export, upload, create, delete, open-with, and other product actions
belong in that host resolver (or in host wrappers outside the shared menu),
not in package-owned menu visibility flags or package host import/export
methods.
### 6. Activation
When the user opens a file, the session orchestrates activation and calls host
hooks. Typical Tutti policy (host-owned, not package law): try in-app preview
presentation, then fall back to system open / unsupported dialogs.
`activateFile` results may be `handled`, `fallback`, or `unsupported`, with
optional host-provided fallback actions. The package owns dialog/shell
presentation of those results; the host owns what the actions do.
Default opener hints may be supplied through session input
`resolveFileDefaultOpener` (`appBrowser` / `defaultBrowser` / `fileViewer` /
`system`) without moving transport into the package.
### 7. Persistence
**Persisted state (decided):** the package owns
`WorkspaceFileManagerPersistedState` and its `schemaVersion`. Current fields:
- `currentDirectoryPath`
- `navigationBackStack` / `navigationForwardStack`
- `selectedLocationId`
- `schemaVersion`
Hosts that historically persisted other fields (selected file focus tokens,
cached listings, product-specific focus request IDs) must map at the adapter
or workbench edge. Do not widen the shared schema for one host’s focus
protocol without a contract revision.
Device-local UI chrome widths (locations sidebar / details pane) may persist
in the optional React surface; that is presentation chrome, not workbench
node external state.
### 8. Optional React UI and `/services`
Public entrypoints:
- `@tutti-os/workspace-file-manager` — UI + common types/helpers
- `@tutti-os/workspace-file-manager/services` — service/session/host contracts
without requiring the React tree
- `@tutti-os/workspace-file-manager/i18n` — default resources / namespace helpers
**Consumption modes (decided):**
- `/services` alone is a valid integration (host brings its own UI)
- Consuming the package React UI implies accepting its peer dependencies
(including `valtio`) and session-store binding
UI injection points that remain host-owned presentation hooks include, among
others: `resolveContextMenu`, `renderExternalLocationContent`,
`renderToolbarTrailingActions`, `resolveEntryIconUrl`, `showPreviewPanel`,
`showLocationSidebar`, `showInternalOpenWithActions`, open-with icon
overrides, and analytics callbacks. These do **not** become a general
product-action plugin system in this phase.
**Toolbar trailing actions (decided):** hosts may inject a narrow React node
cluster through `renderToolbarTrailingActions`. The package places it in the
toolbar trailing cluster after Refresh and before Search, and passes the
current directory path plus busy/loading/mutating flags. Product primary
affordances such as upload belong here (or in host chrome outside the shared
surface); multi-step transfer UX stays host-owned.
**Published UI assets (decided):** folder/archive fallback glyphs ship as
inlined `data:` URLs in the published `dist` (tsup `loader: { ".png": "dataurl" }`).
Do not rely on `new URL(..., import.meta.url)` against loose files next to
`dist` — Electron hosts with strict `img-src` (no `file:`) will show broken
images. Hosts that alias this package to source for local development still
get Vite-rewritten asset URLs; published consumers must keep working without
that alias.
### 9. i18n
The package owns default file-manager copy under its i18n namespace. Hosts merge
`workspaceFileManagerI18nResources` into an app-level runtime and scope with
`createWorkspaceFileManagerI18nRuntime(...)`. Product-specific wording overrides
belong in the host runtime, not as forks of package message files.
`resolveRevealInFolderLabel(copy, platform)` remains available for host menu
builders that need platform-specific reveal labels.
## Host responsibilities
Hosts own:
- Transport: daemon/VM/local reads and writes, uploads/downloads, auth
- Mapping host identity (`roomId`, and similar) onto `workspaceID`
- Mapping absolute/VM paths onto logical workspace paths (and the reverse at
the OS edge)
- Implementing required `listDirectory` plus whichever optional Host methods
they want capabilities for
- Forwarding the session-provided search `AbortSignal` through the host
transport so superseded work can stop promptly; hosts must still tolerate a
request completing after cancellation
- Import / export / upload / download pipelines, including any transfer-center
integration
- Context-menu action trees through `resolveContextMenu`
- Share / exposure / collaboration product flows
- In-app preview canvas / workbench presentation policy beyond the shared
side-pane projection
- Product chrome outside this surface (toolbars, dock, room shell)
- App-level i18n overrides and host-only analytics
## Non-goals
- Embedding tuttid, TSH desktopd, preload bridges, or product globals
- Owning share-link / exposure / agent-session binding UI or state
- Owning multi-phase transfer-center protocols or progress UI
- Owning package-level import/export host methods or drag-to-import overlays
- Becoming a general document editor platform
- Requiring every host to implement Tutti’s full capability set
- Inventing a parallel preview taxonomy or open-with advisory stack in hosts
that already consume the workspace file packages
- Promising a general host menu/action contribution API for arbitrary product
commands in this phase (hosts use `resolveContextMenu`,
`renderToolbarTrailingActions`, or wrappers outside the shared surface)
## Relationship to other workspace packages
| Package | Relationship |
| ------------------------------------- | ------------------------------------------------------------------------- |
| `@tutti-os/workspace-file-preview` | Classification + preview loading; consumed by this package |
| `@tutti-os/workspace-file-reference` | May reuse types/icons/UI helpers; external location body is host-injected |
| Host adapters (Tutti desktop, TSH, …) | Implement `WorkspaceFileManagerHost` and product-only flows |
## Keep as-is
- Flat `WorkspaceFileManagerHost` + capabilities-from-optional-methods
- Logical paths and `workspaceID` naming
- Session-owned shared interaction state with optional React UI
- Preview package as the only preview taxonomy owner
- Open-with advisories on `/services`
- Location model with host-supplied sections
- Persisted navigation/location schema versioning in this package
- Host-owned context menus through `resolveContextMenu`
## Current shipped shape vs this contract
The Tutti desktop adapter and package implementation are aligned with the
sections above:
- Desktop feature code implements `WorkspaceFileManagerHost` and keeps tuttid /
preload / analytics / reference external panes outside the package
- Tutti desktop does not ship file-manager import/export/upload at all (no
package host methods and no desktop `resolveContextMenu` actions for them).
Other hosts that need those product flows must own them entirely outside
this package
- Desktop wires `resolveContextMenu` for blank / directory / file targets
(open / open-with / create / rename / copy / reveal / delete)
- Side-pane preview uses `@tutti-os/workspace-file-preview`; activation policy
for canvas vs system open stays in the desktop adapter
- Other hosts (for example TSH) should adapt onto this contract rather than
growing a second shared file-manager session for the same job
## Open questions
None right now. New questions should be added here when they appear, then moved
into the main sections once decided.
## Change policy
- Contract changes that alter host obligations, session ownership, persistence
schema meaning, activation/preview boundaries, or context-menu DI expectations
require updating this file in the same change.
- Prefer extending optional host methods or UI injection props over inventing
parallel file-manager stacks in hosts.
- When an open question is decided, move the decision into the main sections and
remove it from [Open questions](#open-questions).