mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Sidebar session list grows the figma 239-10458 feature set and the
workspace/session browsing region moves wholesale into ui-workspace:
- Group-by menu (WorkSpace / In one list): flat mode lists every session
top-level, strictly newest-first; the choice persists across reloads.
- Session rows get a 500ms hover detail card (title / relative time /
status line) and a ... menu (Rename / Fork session / Delete session,
visual-only for now); workspace headers get ... with Rename (wired) and
Delete workspace (visual-only).
- workspace.rename RPC: trims, rejects duplicate titles on the create
chain (workspace-name-conflict), no-op on same title; modal dialog with
client-side duplicate pre-check.
- workspace.insertSessionBefore RPC (DOM-insertBefore semantics, omitted
anchor appends): HTML5 drag reorder of root sessions inside a workspace
group; order truth stays host-side, the view refreshes from the
response/changed frame.
- Activity pinning removed: the session/event touchSession chain is gone;
workspace accounts are manually owned (new sessions prepend, explicit
reordering only). Contracts and tests updated, api catalog regenerated.
- ui-sidebar reduced to the column shell (brand, fold state machine, New
Session, Settings) exposing one sidebar.workspaces hole with a two-fact
owner share {wide, expandSidebar}; ui-workspace owns the whole region
(header, search, grouped/flat lists, dialogs, drag) plus the picker via
a shared WorkspaceCreateFlow. The old sidebar.workspace picker slot and
its deferral indirection are gone.
- ui-primitives: Menu gains label entries, danger rows, and
closeOnPointerLeave; new HoverCard (portaled, open-delay, disabled
guard). Hover card and row menu never coexist.
82 lines
3.6 KiB
TypeScript
82 lines
3.6 KiB
TypeScript
/**
|
|
* workspace domain contract. Wire projection of the host-side workspace
|
|
* entity (@deepseek-ai/dsh-workspace): a stable id over a directory path,
|
|
* a display title, and the ordered session account. Method signatures are the
|
|
* source of truth, same as the sessions domain.
|
|
*/
|
|
|
|
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
|
import type { Branded } from '@deepseek-ai/dsh-brand'
|
|
import type { RpcRequest, RpcResponse } from './rpc.ts'
|
|
|
|
/**
|
|
* Wire-side workspace id brand. Deliberately re-declared here rather than
|
|
* imported from dsh-workspace: api/ must stay browser-importable with zero
|
|
* host-package dependencies, and the brand string matches, so both sides
|
|
* agree structurally.
|
|
*/
|
|
export type WorkspaceId = Branded<'WorkspaceId'>
|
|
|
|
/** One workspace row: the record projection every workspace.* value carries. */
|
|
export interface WorkspaceView {
|
|
workspaceId: WorkspaceId
|
|
/** Canonical directory path (host-side realpath canon). */
|
|
path: string
|
|
/** Unique display title (defaults to the path basename at create). */
|
|
title: string
|
|
/**
|
|
* Sessions accounted under this workspace, in manually owned order
|
|
* (attach prepends, insertSessionBefore reorders; activity never does).
|
|
*/
|
|
sessionIds: SessionId[]
|
|
/** ISO-8601 creation instant. */
|
|
createdAt: string
|
|
/** ISO-8601 last-mutation instant. */
|
|
updatedAt: string
|
|
}
|
|
|
|
/** Workspace-domain unary methods (the map keys workspace.* of RpcMethodMap). */
|
|
export interface WorkspaceApi {
|
|
/** Lists all workspaces in the registry's durable display order. */
|
|
list(request: RpcRequest<{}>): Promise<RpcResponse<{ items: WorkspaceView[] }>>
|
|
|
|
/**
|
|
* Creates (or idempotently resolves) a workspace. Exactly one of `path` /
|
|
* `name` (schema-enforced): `path` registers an EXISTING directory (no
|
|
* mkdir — a missing or non-directory path fails with `workspace-invalid-path`);
|
|
* `name` is a single path segment the host mkdirs under its default project
|
|
* root before registering. Either spelling resolving to a directory already
|
|
* owned by a workspace returns that workspace (`created: false`) for the
|
|
* existing-folder spelling. Create-by-name rejects an existing title with
|
|
* `workspace-name-conflict`; a new path whose basename duplicates another
|
|
* Workspace title is rejected by the registry with the same code.
|
|
* A new name-created workspace uses `name` as both directory name and title;
|
|
* a path-created workspace uses the registry's basename title default.
|
|
*/
|
|
create(request: RpcRequest<{ path?: string; name?: string }>):
|
|
Promise<RpcResponse<{ workspace: WorkspaceView; created: boolean }>>
|
|
|
|
/**
|
|
* Renames a workspace. `title` is trimmed and must be non-empty
|
|
* (schema-enforced). An unknown id fails with `workspace-not-found`; a
|
|
* title equal to another workspace's fails with `workspace-name-conflict`.
|
|
* Renaming to the current title is a no-op success (no durable write).
|
|
*/
|
|
rename(request: RpcRequest<{ workspaceId: WorkspaceId; title: string }>):
|
|
Promise<RpcResponse<{ workspace: WorkspaceView }>>
|
|
|
|
/**
|
|
* Moves an accounted session within its workspace's manual order,
|
|
* DOM-insertBefore-like: with `beforeSessionId` the session is inserted
|
|
* before that anchor; omitted appends to the end. An unknown workspace
|
|
* fails with `workspace-not-found`; a session or anchor not accounted by
|
|
* the workspace fails with `workspace-move-invalid`. A move to the current
|
|
* position is a no-op success.
|
|
*/
|
|
insertSessionBefore(request: RpcRequest<{
|
|
workspaceId: WorkspaceId
|
|
sessionId: SessionId
|
|
beforeSessionId?: SessionId
|
|
}>): Promise<RpcResponse<{ workspace: WorkspaceView }>>
|
|
}
|