@qilin-agent/client-ui-sidebar-files 0.0.0-stage → 3.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,148 @@
1
+ /**
2
+ * The tree's row mutations: creating a file or a directory, renaming one entry,
3
+ * and deleting one — plus how the tabs already open on a moved or removed path
4
+ * are reconciled with it.
5
+ *
6
+ * The three gestures share one shape: validate the typed name, call one Remote
7
+ * on the `workspaceFiles` namespace, and on success re-list the directory that
8
+ * held the entry, so the row is read again rather than guessed at. An entry a
9
+ * move or a removal touched is also reconciled with the open tabs: a tab whose
10
+ * address names the old path is retargeted to the new one, and a tab whose path
11
+ * is gone is closed, because its next read or save would fail against a path
12
+ * nothing holds.
13
+ *
14
+ * Everything here is either a pure path or failure decision — directly testable
15
+ * on its own — or the thin binding of one Remote to the shape the tree's face
16
+ * calls.
17
+ * @module
18
+ */
19
+ import type { ClientRemote, RemoteFailure, RemoteResult } from '@qilin-agent/api-remotes/client';
20
+ import type { TabId } from '@qilin-agent/client-ui-dockkit';
21
+ import type { TranslateNS } from '@qilin-agent/client-locale/client';
22
+ import type { SessionId } from '@qilin-agent/session/types';
23
+ /** The two entry kinds a create gesture makes. */
24
+ export type EntryKind = 'file' | 'directory';
25
+ /**
26
+ * The one name a typed text may become, or `undefined` when it may not.
27
+ *
28
+ * A name is a single path segment: surrounding spaces are dropped, and a blank
29
+ * text, a dot segment, or anything carrying a separator is refused. The caller
30
+ * keeps its box open on `undefined` so nothing is written under a name the
31
+ * reader did not mean.
32
+ * @param text - the box's text, exactly as typed.
33
+ * @returns the trimmed name, or `undefined` when it cannot be one.
34
+ */
35
+ export declare function normalizedEntryName(text: string): string | undefined;
36
+ /**
37
+ * The directory holding one path, in that path's own spelling.
38
+ * @param path - an absolute path.
39
+ * @returns the parent directory; its root (`/`, `C:\`) when the path is one
40
+ * level below a root, and the empty string for a path with no separator.
41
+ */
42
+ export declare function parentDirectoryOf(path: string): string;
43
+ /**
44
+ * The absolute path of one named entry inside a directory.
45
+ *
46
+ * The child joins with `/` whatever the parent's own separators: the Host
47
+ * resolves mixed separators, and the tree only needs one stable key.
48
+ * @param directory - absolute path of the directory the entry sits in.
49
+ * @param name - the entry's basename.
50
+ * @returns the entry's absolute path.
51
+ */
52
+ export declare function joinEntryPath(directory: string, name: string): string;
53
+ /**
54
+ * Whether one path is a target itself or lives under it.
55
+ *
56
+ * Both sides are compared with `/` separators and without a trailing separator,
57
+ * so the same directory spelled with either separator, with or without its
58
+ * final slash, matches.
59
+ * @param candidate - the path to classify.
60
+ * @param target - the entry a move or a removal touched.
61
+ * @returns whether the candidate is the target or one of its descendants.
62
+ */
63
+ export declare function pathAtOrUnder(candidate: string, target: string): boolean;
64
+ /**
65
+ * Say why a row mutation failed, in terms of the entry rather than of the
66
+ * transport. Codes this tree does not name — the filesystem's own `FS_*`
67
+ * pass-throughs among them — reach the reader as the carrier's message.
68
+ * @param t - namespace-bound translate.
69
+ * @param failure - the settled Remote failure.
70
+ * @returns the line to show in the tree's mutation strip.
71
+ */
72
+ export declare function mutationFailureLine(t: TranslateNS<'sidebarFiles'>, failure: RemoteFailure): string;
73
+ /**
74
+ * The `workspaceFiles` calls one row mutation is made of, as the tree's face
75
+ * calls them. Each resolves with the endpoint's own result; the caller only
76
+ * branches on `ok`.
77
+ */
78
+ export interface WorkspaceFileMutations {
79
+ /** Write one new, empty file: a `write` with no `baseVersion` is unconditional. */
80
+ readonly createFile: (sessionId: SessionId, path: string, signal: AbortSignal) => Promise<RemoteResult<unknown>>;
81
+ /** Create one directory; its parent must already exist. */
82
+ readonly createDirectory: (sessionId: SessionId, path: string, signal: AbortSignal) => Promise<RemoteResult<unknown>>;
83
+ /** Move or rename one entry; an existing destination is refused. */
84
+ readonly move: (sessionId: SessionId, from: string, to: string, signal: AbortSignal) => Promise<RemoteResult<unknown>>;
85
+ /** Delete one file, or one directory whole with `recursive`. */
86
+ readonly remove: (sessionId: SessionId, path: string, recursive: boolean, signal: AbortSignal) => Promise<RemoteResult<unknown>>;
87
+ }
88
+ /**
89
+ * The slice of the Client Remote face this package mutates entries through,
90
+ * exactly as the Host's generated client declares it.
91
+ */
92
+ export type WorkspaceFilesMutationRemote = {
93
+ readonly workspaceFiles: Pick<ClientRemote['workspaceFiles'], 'write' | 'createDirectory' | 'move' | 'delete'>;
94
+ };
95
+ /**
96
+ * Bind the four row mutations to one Remote face, keeping each call's shape.
97
+ * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.
98
+ * @returns the mutations the tree's face performs.
99
+ */
100
+ export declare function createMutations(remote: WorkspaceFilesMutationRemote): WorkspaceFileMutations;
101
+ /** One open tab, as far as a path mutation cares: the address it was opened at. */
102
+ export interface OpenTabRef {
103
+ /** The tab's own id, the handle a close or a replacement names. */
104
+ readonly id: TabId;
105
+ /** The resource address the tab was opened at. */
106
+ readonly contentId: string;
107
+ }
108
+ /**
109
+ * The Sidebar slice open tabs are reconciled through.
110
+ *
111
+ * Structural rather than the controller's own type: this package needs three
112
+ * calls, and the injected service is a superset of them.
113
+ */
114
+ export interface SidebarTabReconcile {
115
+ /** The committed tabs of one Session, or an empty list before adoption. */
116
+ readonly tabsIn: (sessionId: SessionId) => readonly OpenTabRef[];
117
+ /** Open a resource in one Session, optionally in the replaced tab's own place. */
118
+ readonly openResourceIn: (sessionId: SessionId, address: string, options: {
119
+ readonly replaceTab: TabId;
120
+ }) => void;
121
+ /** Close one tab of one Session. */
122
+ readonly closeIn: (sessionId: SessionId, tabId: TabId) => void;
123
+ }
124
+ /** How one touched path is settled against the tabs already open on it. */
125
+ export interface TabReconcile {
126
+ /**
127
+ * Retarget every tab open at or under a path that moved.
128
+ * @param sessionId - the Session whose tabs are read.
129
+ * @param root - the Session's workspace root, which the addresses are relative to.
130
+ * @param from - the entry's path before the move.
131
+ * @param to - the entry's path after it.
132
+ */
133
+ readonly moved: (sessionId: SessionId, root: string, from: string, to: string) => void;
134
+ /**
135
+ * Close every tab open at or under a path that is gone.
136
+ * @param sessionId - the Session whose tabs are read.
137
+ * @param root - the Session's workspace root, which the addresses are relative to.
138
+ * @param path - the removed entry's path.
139
+ */
140
+ readonly removed: (sessionId: SessionId, root: string, path: string) => void;
141
+ }
142
+ /**
143
+ * Bind the tab reconciliation to one Sidebar controller.
144
+ * @param sidebar - the right Sidebar's tab paths.
145
+ * @returns the reconciliation a mutation performs on success.
146
+ */
147
+ export declare function createTabReconcile(sidebar: SidebarTabReconcile): TabReconcile;
148
+ //# sourceMappingURL=file-mutations.d.ts.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Whole-file reading for the editor: one paged walk over `workspaceFiles.read`
3
+ * assembled into the exact disk text, bounded by the editor's own byte cap.
4
+ *
5
+ * A page's lines join by `\n` with no terminator after the last, and a final
6
+ * `\n` on disk terminates the last line rather than starting an empty one, so
7
+ * the page stream alone cannot say whether the file ends with a newline. The
8
+ * complete file's byte size rides every page: an assembled text exactly one
9
+ * UTF-8 byte short of it had that terminator, and it is restored so a save
10
+ * round-trips the file byte for byte.
11
+ * @module
12
+ */
13
+ import type { RemoteResult } from '@qilin-agent/api-remotes/client';
14
+ import type { SessionId } from '@qilin-agent/session/types';
15
+ import type { WorkspaceFileText } from '@qilin-agent/api-workspace-files/types';
16
+ /** Largest file the editor opens, in bytes; the Host's page cap bounds each read. */
17
+ export declare const MAX_EDIT_BYTES: number;
18
+ /** One page of lines, as the endpoint returns it. */
19
+ export type ReadWorkspaceFilePage = (sessionId: SessionId, path: string, offset: number, signal: AbortSignal) => Promise<RemoteResult<WorkspaceFileText>>;
20
+ /** A whole file, or the failure that stopped the walk. */
21
+ export type WholeFileResult = RemoteResult<{
22
+ readonly text: string;
23
+ readonly version: string;
24
+ }>;
25
+ /**
26
+ * Assemble whole text from a complete page walk.
27
+ *
28
+ * Pages join by `\n`; a file whose byte size is exactly one more than the
29
+ * assembly's UTF-8 length ends with a newline on disk, and it is appended.
30
+ * A size that matches neither is reported as-is rather than guessed at.
31
+ * @param pages - the walk's pages, in order.
32
+ * @returns the file's text as the disk holds it.
33
+ */
34
+ export declare function reassemblePages(pages: readonly WorkspaceFileText[]): string;
35
+ /**
36
+ * Bind the whole-file walk to one paged read.
37
+ * @param readPage - the bound `workspaceFiles.read` call.
38
+ * @returns the read the editor's face performs.
39
+ */
40
+ export declare function createReadWhole(readPage: ReadWorkspaceFilePage): (sessionId: SessionId, path: string, signal: AbortSignal) => Promise<WholeFileResult>;
41
+ //# sourceMappingURL=file-pages.d.ts.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The routes to the preview viewer: the editor toolbar's targeted open of the
3
+ * tab's own address, and the open options the workspace trees hand their rows.
4
+ *
5
+ * The toolbar cannot use the tab's own `openResource` action for this: that
6
+ * action opens through the registry's ranking, and this type outranks the
7
+ * viewer, so the ranking would land back here. Naming the kind is the one way
8
+ * past it; the Sidebar controller places the open on the session on screen.
9
+ * @module
10
+ */
11
+ import type { ISidebarRight, SidebarRightTabActions } from '@qilin-agent/client-ui-sidebar-right/client';
12
+ import type { SessionId } from '@qilin-agent/session/types';
13
+ /** The preview navigation the body receives. */
14
+ export interface FilePreviewInjected {
15
+ /**
16
+ * Open this address in the preview viewer.
17
+ * @param sessionId - the session whose preview toolbar asked; reserved by the
18
+ * controller's session-scoped open, which this face does not need.
19
+ * @param address - the tab's own `qilin-resource://file/` address.
20
+ */
21
+ readonly openPreview: (sessionId: SessionId, address: string) => void;
22
+ }
23
+ /**
24
+ * Bind the preview navigation to the Sidebar controller.
25
+ * @param sidebarRight - the right-Sidebar navigation controller.
26
+ * @returns the Slot `inject` factory carrying the callback.
27
+ */
28
+ export declare function filePreviewFace(sidebarRight: ISidebarRight): () => FilePreviewInjected;
29
+ /**
30
+ * Open one tree row's file: a renderable document names the `text` viewer so
31
+ * the first open is the rendered document rather than this package's code
32
+ * view; the viewer's edit affordance opens the editor in return. Everything
33
+ * else takes the ranked claim unchanged.
34
+ * @param actions - the acting tab's action face, carrying `openResource`.
35
+ * @param address - the row's session file address.
36
+ * @param path - the row's workspace-relative path, for the renderable check.
37
+ */
38
+ export declare function openFileRow(actions: Pick<SidebarRightTabActions, 'openResource'>, address: string, path: string): void;
39
+ //# sourceMappingURL=file-preview.d.ts.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Browser half: register this package's two right-Sidebar tab types.
3
+ *
4
+ * The `files` page — the workspace tree as an empty-state explorer — and the
5
+ * `file` editor workbench, which claims session file addresses with editable
6
+ * extensions. Each type goes through the public two-stage path: the type into
7
+ * `ctx.sidebarRightTabs`, its body into the keyed `sidebar.right.pane.tab`
8
+ * seat and its chip title into the keyed `sidebar.right.pane.tab.title` seat,
9
+ * all under the type's own `id`.
10
+ *
11
+ * The file split is this package's layering: what each type IS
12
+ * (`definition.tsx`, `file-definition.ts`), what it keeps (`store.ts`), how it
13
+ * lists, mutates, and saves (`face.ts`, `file-mutations.ts`, `file-face.ts`,
14
+ * `file-pages.ts`), how it hands a
15
+ * file to the preview viewer (`file-preview.ts`), what it draws
16
+ * (`FilesBody.tsx`, `FilesTitle.tsx`, `FileBody.tsx`, `FileTitle.tsx`,
17
+ * `FileTree.tsx`), the editor surface behind the adapter (`file-editor.ts`),
18
+ * what it says (`locales.ts`), and this module, which only wires them
19
+ * together.
20
+ */
21
+ import type { Context as ClientContext } from '@qilin-agent/kylin';
22
+ import type { ClientRemote } from '@qilin-agent/api-remotes/client';
23
+ import type { WriteWorkspaceFile } from './file-face.ts';
24
+ import type { ReadWorkspaceFilePage } from './file-pages.ts';
25
+ declare module '@qilin-agent/client-ui-slots' {
26
+ interface SlotMap {
27
+ /** Workspace directory actions after the file tree's reload control. */
28
+ 'sidebar.right.tab.files.actions': {
29
+ kind: 'list';
30
+ scope: 'session';
31
+ owner: {
32
+ /** Absolute directory path displayed by the file tree. */
33
+ readonly absolutePath: string;
34
+ };
35
+ };
36
+ }
37
+ }
38
+ export type { SidebarFilesKey } from './locales.ts';
39
+ export type { DirLevel, FileEditState, FilesState, FilesTabState, LevelState } from './store.ts';
40
+ export type { FilesInjected, ListWorkspaceDirectory, SearchWorkspaceFileNames, WorkspaceFilesTreeRemote } from './face.ts';
41
+ export type { FilePreviewInjected } from './file-preview.ts';
42
+ export type { FilesBodyProps } from './FilesBody.tsx';
43
+ /**
44
+ * The slice of the Client Remote face this package calls: the `workspaceFiles`
45
+ * namespace's `write`, exactly as the Host's generated client declares it.
46
+ */
47
+ export interface WorkspaceFilesWriteRemote {
48
+ readonly workspaceFiles: {
49
+ /** Save one complete UTF-8 text file. */
50
+ write: WriteWorkspaceFile;
51
+ };
52
+ }
53
+ /**
54
+ * Bind the write to one Remote face, keeping the call's shape.
55
+ * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.
56
+ * @returns the write the editor's face performs.
57
+ */
58
+ export declare function createWriteFile(remote: WorkspaceFilesWriteRemote): WriteWorkspaceFile;
59
+ /**
60
+ * Bind the paged read to one Remote face. The page length is the Host's
61
+ * configured cap, so no `limit` travels; the walk starts at line 1.
62
+ * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.
63
+ * @returns the read the whole-file walk performs.
64
+ */
65
+ export declare function createReadPage(remote: {
66
+ readonly workspaceFiles: Pick<ClientRemote['workspaceFiles'], 'read'>;
67
+ }): ReadWorkspaceFilePage;
68
+ /**
69
+ * Required browser services: the tab registry, the keyed seat, the Sidebar
70
+ * controller (the preview open), the Remote carrier and its namespace, and copy.
71
+ */
72
+ export declare const inject: string[];
73
+ /**
74
+ * Client plugin body: register both types, their dictionaries, and each body
75
+ * and chip title under its own id.
76
+ * @param ctx - client root context carrying the registry, the slots, and the Remote face.
77
+ */
78
+ export declare function apply(ctx: ClientContext): void;
79
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,157 @@
1
+ declare module '@qilin-agent/client-ui-slots' {
2
+ interface LocaleNamespaceMap {
3
+ /** File-tree and file-editor type names, guide entry, row states, and failure lines. */
4
+ sidebarFiles: SidebarFilesKey;
5
+ }
6
+ }
7
+ /** Simplified Chinese dictionary and key-set source of truth. */
8
+ export declare const zh: {
9
+ 'type.label': string;
10
+ 'guide.title': string;
11
+ 'guide.description': string;
12
+ loading: string;
13
+ empty: string;
14
+ truncated: string;
15
+ noWorkspace: string;
16
+ 'upload.dropHint': string;
17
+ 'upload.uploading': string;
18
+ 'upload.done': string;
19
+ 'upload.failed': string;
20
+ reload: string;
21
+ 'search.label': string;
22
+ 'search.placeholder': string;
23
+ 'search.clear': string;
24
+ 'search.running': string;
25
+ 'search.empty': string;
26
+ 'search.truncated': string;
27
+ 'entry.other': string;
28
+ 'entry.symlink': string;
29
+ 'menu.open': string;
30
+ 'menu.copyRelative': string;
31
+ 'menu.copyAbsolute': string;
32
+ 'menu.newFile': string;
33
+ 'menu.newFolder': string;
34
+ 'menu.rename': string;
35
+ 'menu.delete': string;
36
+ 'create.fileTitle': string;
37
+ 'create.directoryTitle': string;
38
+ 'create.name': string;
39
+ 'create.placeholder': string;
40
+ 'create.inDirectory': string;
41
+ 'create.confirm': string;
42
+ 'delete.title': string;
43
+ 'delete.fileBody': string;
44
+ 'delete.directoryBody': string;
45
+ 'delete.confirm': string;
46
+ cancel: string;
47
+ 'name.invalid': string;
48
+ mutating: string;
49
+ 'error.exists': string;
50
+ 'error.gone': string;
51
+ 'error.notEmpty': string;
52
+ 'error.notRegular': string;
53
+ 'error.mutationFailed': string;
54
+ 'error.dismiss': string;
55
+ 'error.notFound': string;
56
+ 'error.outsideWorkspace': string;
57
+ 'error.notDirectory': string;
58
+ 'error.unavailable': string;
59
+ 'file.type.label': string;
60
+ 'file.save': string;
61
+ 'file.saving': string;
62
+ 'file.saved': string;
63
+ 'file.unsaved': string;
64
+ 'file.reload': string;
65
+ 'file.treeToggle': string;
66
+ 'file.wrap': string;
67
+ 'file.preview': string;
68
+ 'file.reloadDirty': string;
69
+ 'file.reloadDirty.confirm': string;
70
+ 'file.reloadDirty.cancel': string;
71
+ 'file.conflict': string;
72
+ 'file.conflict.reload': string;
73
+ 'file.conflict.overwrite': string;
74
+ 'file.error.notFound': string;
75
+ 'file.error.tooLarge': string;
76
+ 'file.error.notText': string;
77
+ 'file.error.notRegularFile': string;
78
+ 'file.error.outsideWorkspace': string;
79
+ 'file.error.unavailable': string;
80
+ };
81
+ /** Files dictionary key union. */
82
+ export type SidebarFilesKey = keyof typeof zh;
83
+ /** English dictionary, checked against the Chinese key set. */
84
+ export declare const en: {
85
+ 'upload.dropHint': string;
86
+ 'upload.uploading': string;
87
+ 'upload.done': string;
88
+ 'upload.failed': string;
89
+ 'type.label': string;
90
+ 'guide.title': string;
91
+ 'guide.description': string;
92
+ loading: string;
93
+ empty: string;
94
+ truncated: string;
95
+ noWorkspace: string;
96
+ reload: string;
97
+ 'search.label': string;
98
+ 'search.placeholder': string;
99
+ 'search.clear': string;
100
+ 'search.running': string;
101
+ 'search.empty': string;
102
+ 'search.truncated': string;
103
+ 'entry.other': string;
104
+ 'entry.symlink': string;
105
+ 'menu.open': string;
106
+ 'menu.copyRelative': string;
107
+ 'menu.copyAbsolute': string;
108
+ 'menu.newFile': string;
109
+ 'menu.newFolder': string;
110
+ 'menu.rename': string;
111
+ 'menu.delete': string;
112
+ 'create.fileTitle': string;
113
+ 'create.directoryTitle': string;
114
+ 'create.name': string;
115
+ 'create.placeholder': string;
116
+ 'create.inDirectory': string;
117
+ 'create.confirm': string;
118
+ 'delete.title': string;
119
+ 'delete.fileBody': string;
120
+ 'delete.directoryBody': string;
121
+ 'delete.confirm': string;
122
+ cancel: string;
123
+ 'name.invalid': string;
124
+ mutating: string;
125
+ 'error.exists': string;
126
+ 'error.gone': string;
127
+ 'error.notEmpty': string;
128
+ 'error.notRegular': string;
129
+ 'error.mutationFailed': string;
130
+ 'error.dismiss': string;
131
+ 'error.notFound': string;
132
+ 'error.outsideWorkspace': string;
133
+ 'error.notDirectory': string;
134
+ 'error.unavailable': string;
135
+ 'file.type.label': string;
136
+ 'file.save': string;
137
+ 'file.saving': string;
138
+ 'file.saved': string;
139
+ 'file.unsaved': string;
140
+ 'file.reload': string;
141
+ 'file.treeToggle': string;
142
+ 'file.wrap': string;
143
+ 'file.preview': string;
144
+ 'file.reloadDirty': string;
145
+ 'file.reloadDirty.confirm': string;
146
+ 'file.reloadDirty.cancel': string;
147
+ 'file.conflict': string;
148
+ 'file.conflict.reload': string;
149
+ 'file.conflict.overwrite': string;
150
+ 'file.error.notFound': string;
151
+ 'file.error.tooLarge': string;
152
+ 'file.error.notText': string;
153
+ 'file.error.notRegularFile': string;
154
+ 'file.error.outsideWorkspace': string;
155
+ 'file.error.unavailable': string;
156
+ };
157
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,189 @@
1
+ /**
2
+ * The two view states this package owns, one store per session, bucketed by
3
+ * tab id because two tabs in one session are independent:
4
+ *
5
+ * - the file tree: which directories are expanded, what each loaded level
6
+ * contains, and the filename search beside it. Shared by the `files` page and
7
+ * the `file` editor's tree pane.
8
+ * - the file editor: one open file's load state, draft, and save state.
9
+ *
10
+ * Writers run between `start` and `forget` (tree) or the editor face's abort
11
+ * listener (editor): the owner's `signal` is what ends a bucket's life, and
12
+ * the faces stop dispatching once it aborts.
13
+ */
14
+ import { type EngineStoreHandle } from '@qilin-agent/client-store';
15
+ import type { RemoteFailure } from '@qilin-agent/api-remotes/client';
16
+ import type { TabId } from '@qilin-agent/client-ui-dockkit';
17
+ import type { WorkspaceDirectoryEntry, WorkspaceFileNameMatch } from '@qilin-agent/api-workspace-files/types';
18
+ /**
19
+ * One directory's contents, as one expanded level of the tree.
20
+ *
21
+ * The endpoint's listing also names the directory as a workspace-relative path;
22
+ * the tree keys every level by absolute path instead, so the adapter drops it.
23
+ */
24
+ export interface DirLevel {
25
+ /** The directory's entries, in the endpoint's order. */
26
+ readonly entries: readonly WorkspaceDirectoryEntry[];
27
+ /** The listing hit the endpoint's entry cap, so entries are missing. */
28
+ readonly truncated: boolean;
29
+ }
30
+ /** What one directory level is doing right now. */
31
+ export type LevelState = {
32
+ readonly kind: 'loading';
33
+ } | {
34
+ readonly kind: 'ready';
35
+ readonly level: DirLevel;
36
+ } | {
37
+ readonly kind: 'failed';
38
+ readonly failure: RemoteFailure;
39
+ };
40
+ /**
41
+ * What one tab's filename search is showing, carrying the box's own text so the
42
+ * input is restored with its result after a remount.
43
+ *
44
+ * `idle` is the box's content with nothing asked of the Host yet — including the
45
+ * empty box — and it is also where typing lands: the previous answer describes a
46
+ * different query, so it is dropped the moment the text changes.
47
+ */
48
+ export type FileSearchState = {
49
+ readonly kind: 'idle';
50
+ readonly query: string;
51
+ } | {
52
+ readonly kind: 'running';
53
+ readonly query: string;
54
+ } | {
55
+ readonly kind: 'ready';
56
+ readonly query: string;
57
+ readonly matches: readonly WorkspaceFileNameMatch[];
58
+ readonly truncated: boolean;
59
+ } | {
60
+ readonly kind: 'failed';
61
+ readonly query: string;
62
+ readonly failure: RemoteFailure;
63
+ };
64
+ /**
65
+ * What one tab's last row mutation is doing: the create, rename, or delete
66
+ * gesture the tree asked for and has not heard back about, or the failure it
67
+ * ended in. One gesture is in force at a time, so a newer one replaces the
68
+ * record rather than queueing behind it.
69
+ */
70
+ export type TreeMutationState = {
71
+ readonly kind: 'idle';
72
+ } | {
73
+ readonly kind: 'running';
74
+ } | {
75
+ readonly kind: 'failed';
76
+ readonly failure: RemoteFailure;
77
+ };
78
+ /**
79
+ * One tab's tree: its root, the levels it has asked for, what is open, the
80
+ * filename search beside it, and the last row mutation.
81
+ *
82
+ * Every path here is absolute: the root is the session's working directory as
83
+ * the Host reports it, and a child is the parent joined with the entry name.
84
+ */
85
+ export interface FilesTabState {
86
+ /** Absolute path of the workspace root this tree is rooted at. */
87
+ root: string;
88
+ /** Level state by absolute directory path; a path absent here was never asked for. */
89
+ levels: Record<string, LevelState>;
90
+ /** Expanded absolute directory paths, root included. */
91
+ expanded: string[];
92
+ /** The body's scroll offset in px, so a remounted tree comes back where the reader was. */
93
+ scrollTop: number;
94
+ /** What the search box holds and has last heard back. */
95
+ search: FileSearchState;
96
+ /** How the last create, rename, or delete gesture ended, for the tree's strip. */
97
+ mutation: TreeMutationState;
98
+ }
99
+ /** What one file editor is doing with its file right now. */
100
+ export type FileEditPhase = {
101
+ readonly kind: 'reading';
102
+ } | {
103
+ readonly kind: 'ready';
104
+ } | {
105
+ readonly kind: 'failed';
106
+ readonly failure: RemoteFailure;
107
+ };
108
+ /** How the last finished save ended, for the toolbar status. */
109
+ export type FileSaveState = 'idle' | 'saved' | 'failed';
110
+ /**
111
+ * One tab's editor: the file as the disk holds it, the unsaved draft on top
112
+ * of it, and the save state between them.
113
+ *
114
+ * `version` is the opaque equality token every save writes against; `draft`
115
+ * carries the editor document while it differs from the disk text, so a body
116
+ * remount restores it. The state survives across a body's unmounts — that is
117
+ * why it is a declared store and not component state.
118
+ */
119
+ export interface FileEditState {
120
+ phase: FileEditPhase;
121
+ /** The text as last read from or written to the disk. */
122
+ text: string;
123
+ /** The freshness token of `text`; every save's `baseVersion`. */
124
+ version: string;
125
+ /** Whether the editor holds unsaved changes. */
126
+ dirty: boolean;
127
+ /** The editor document while dirty; absent when it matches `text`. */
128
+ draft: string | undefined;
129
+ /** Whether a save is in flight. */
130
+ saving: boolean;
131
+ /** How the last finished save ended. */
132
+ saveState: FileSaveState;
133
+ /** The settled save failure, for its line. */
134
+ saveFailure: RemoteFailure | undefined;
135
+ /** A save was refused because the disk moved on; the draft is intact. */
136
+ conflict: boolean;
137
+ /** Whether the editor wraps lines. */
138
+ wrap: boolean;
139
+ /** Bumped by every load that should remount the editor surface. */
140
+ loadSeq: number;
141
+ /** The navigation revision whose line target has been answered. */
142
+ answered: number;
143
+ }
144
+ /** Every tab's tree, keyed by tab id. */
145
+ export interface FilesState {
146
+ byTab: Record<TabId, FilesTabState>;
147
+ /** The `file` editor's buckets; the `files` page never seeds one. */
148
+ edits: Record<TabId, FileEditState>;
149
+ }
150
+ /** The store's write set; every action names the tab it writes. */
151
+ type FilesActions = {
152
+ start: (draft: FilesState, tabId: TabId, root: string) => void;
153
+ loading: (draft: FilesState, tabId: TabId, path: string) => void;
154
+ loaded: (draft: FilesState, tabId: TabId, path: string, level: DirLevel) => void;
155
+ failed: (draft: FilesState, tabId: TabId, path: string, failure: RemoteFailure) => void;
156
+ toggled: (draft: FilesState, tabId: TabId, path: string) => void;
157
+ scrolled: (draft: FilesState, tabId: TabId, scrollTop: number) => void;
158
+ searchTyped: (draft: FilesState, tabId: TabId, query: string) => void;
159
+ searchRunning: (draft: FilesState, tabId: TabId, query: string) => void;
160
+ searchSettled: (draft: FilesState, tabId: TabId, query: string, matches: readonly WorkspaceFileNameMatch[], truncated: boolean) => void;
161
+ searchFailed: (draft: FilesState, tabId: TabId, query: string, failure: RemoteFailure) => void;
162
+ searchCleared: (draft: FilesState, tabId: TabId) => void;
163
+ mutationStarted: (draft: FilesState, tabId: TabId) => void;
164
+ mutationFailed: (draft: FilesState, tabId: TabId, failure: RemoteFailure) => void;
165
+ mutationCleared: (draft: FilesState, tabId: TabId) => void;
166
+ reset: (draft: FilesState, tabId: TabId) => void;
167
+ forget: (draft: FilesState, tabId: TabId) => void;
168
+ editRead: (draft: FilesState, tabId: TabId) => void;
169
+ editLoaded: (draft: FilesState, tabId: TabId, text: string, version: string) => void;
170
+ editFailed: (draft: FilesState, tabId: TabId, failure: RemoteFailure) => void;
171
+ editDraft: (draft: FilesState, tabId: TabId, text: string) => void;
172
+ editSaving: (draft: FilesState, tabId: TabId) => void;
173
+ editSaved: (draft: FilesState, tabId: TabId, text: string, version: string) => void;
174
+ editSaveFailed: (draft: FilesState, tabId: TabId, failure: RemoteFailure) => void;
175
+ editWrap: (draft: FilesState, tabId: TabId, wrap: boolean) => void;
176
+ editAnswered: (draft: FilesState, tabId: TabId, revision: number) => void;
177
+ editForget: (draft: FilesState, tabId: TabId) => void;
178
+ };
179
+ /**
180
+ * Declare the tree and editor store.
181
+ *
182
+ * A factory rather than a shared handle: the registrations declare it as an
183
+ * exclusive store, so the framework mints one instance per session, shared by
184
+ * the `files` and `file` seats that receive it.
185
+ * @returns the store handle to declare on the registrations.
186
+ */
187
+ export declare function createFilesStore(): EngineStoreHandle<FilesState, FilesActions>;
188
+ export {};
189
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Pure host half; the whole tab type lives in the browser export. */
2
+ /** Host plugin body: the file tree contributes nothing to the host tree. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map