@punica/editor 1.12.0 → 1.13.0
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.
- package/dist/index.bundle.esm.js +2 -2
- package/dist/index.bundle.esm.js.map +1 -1
- package/dist/index.bundle.umd.js +2 -2
- package/dist/index.bundle.umd.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +1 -0
- package/types/punica.module.runtime.api.d.ts +6 -0
- package/types/punica.module.runtime.fileOperations.d.ts +141 -0
- package/types/punica.module.shell.contentTabs.d.ts +57 -3
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
/// <reference path="punica.module.extensions.settings.d.ts" />
|
|
26
26
|
/// <reference path="punica.module.extensions.i18n.d.ts" />
|
|
27
27
|
/// <reference path="punica.module.runtime.fs.d.ts" />
|
|
28
|
+
/// <reference path="punica.module.runtime.fileOperations.d.ts" />
|
|
28
29
|
/// <reference path="punica.module.runtime.search.d.ts" />
|
|
29
30
|
/// <reference path="punica.module.runtime.vcs.d.ts" />
|
|
30
31
|
/// <reference path="punica.module.runtime.workspace.d.ts" />
|
|
@@ -70,6 +70,12 @@ declare module 'punica' {
|
|
|
70
70
|
lifecyclePhase?: LifecyclePhase;
|
|
71
71
|
fs: FileSystemProvider;
|
|
72
72
|
fileOpeners: FileOpenersApi;
|
|
73
|
+
/**
|
|
74
|
+
* Rename/move and delete, announced to the layers that key state by
|
|
75
|
+
* path (content tabs, editors). Prefer this over calling the
|
|
76
|
+
* equivalent `fs` methods directly.
|
|
77
|
+
*/
|
|
78
|
+
fileOperations: FileOperationsApi;
|
|
73
79
|
search: SearchApi;
|
|
74
80
|
vcs: VcsApi;
|
|
75
81
|
terminals: TerminalApi;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
declare module 'punica' {
|
|
2
|
+
export namespace runtime {
|
|
3
|
+
/**
|
|
4
|
+
* A rename (or move — the same thing at the filesystem level) that is
|
|
5
|
+
* about to happen, or has just happened. Both paths are
|
|
6
|
+
* workspace-relative, the same shape `FileSystemProvider` takes.
|
|
7
|
+
*/
|
|
8
|
+
export interface FileRenameEvent {
|
|
9
|
+
oldPath: string;
|
|
10
|
+
newPath: string;
|
|
11
|
+
isDirectory: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** A delete that is about to happen, or has just happened. */
|
|
15
|
+
export interface FileDeleteEvent {
|
|
16
|
+
path: string;
|
|
17
|
+
isDirectory: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A `will*` hook's refusal. Returning this aborts the operation before
|
|
22
|
+
* the filesystem is touched; `reason` becomes the thrown error's message,
|
|
23
|
+
* so it is read by a human and should say what the user can do about it.
|
|
24
|
+
*/
|
|
25
|
+
export interface ParticipantVeto {
|
|
26
|
+
veto: true;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A layer that needs to know about — or object to — file operations.
|
|
32
|
+
*
|
|
33
|
+
* This exists because a path is an identity, not just an argument. The
|
|
34
|
+
* shell keys open content tabs by path, the code editor keys its Monaco
|
|
35
|
+
* instances by path, notebook controllers key their models by path. When
|
|
36
|
+
* a file is renamed underneath them they are all holding a key that no
|
|
37
|
+
* longer names anything, and the failure is silent: a save writes to the
|
|
38
|
+
* old name, or finds no editor and writes nothing at all.
|
|
39
|
+
*
|
|
40
|
+
* So the operation cannot just be `fs.renameFile`. It has to be a
|
|
41
|
+
* transaction the interested layers take part in:
|
|
42
|
+
*
|
|
43
|
+
* - `will*` runs BEFORE the filesystem call and may veto (returning
|
|
44
|
+
* `{ veto: true }` or throwing). Use it for "this must not happen
|
|
45
|
+
* yet" — not for cleanup.
|
|
46
|
+
* - `did*` runs AFTER the filesystem call succeeded, awaited and in
|
|
47
|
+
* registration order. Use it to re-key state. A throwing `did*` hook
|
|
48
|
+
* is logged and skipped; it does not fail the operation, because the
|
|
49
|
+
* file has already moved and reporting failure would be a lie.
|
|
50
|
+
*
|
|
51
|
+
* Participants are host/shell territory. A headless boot registers none,
|
|
52
|
+
* and then an operation is exactly the filesystem call plus its event.
|
|
53
|
+
*/
|
|
54
|
+
export interface FileOperationParticipant {
|
|
55
|
+
/** Stable id, used in logs and to make double-registration visible. */
|
|
56
|
+
id: string;
|
|
57
|
+
willRename?(
|
|
58
|
+
event: FileRenameEvent
|
|
59
|
+
): Promise<void | ParticipantVeto> | void | ParticipantVeto;
|
|
60
|
+
didRename?(event: FileRenameEvent): Promise<void> | void;
|
|
61
|
+
willDelete?(
|
|
62
|
+
event: FileDeleteEvent
|
|
63
|
+
): Promise<void | ParticipantVeto> | void | ParticipantVeto;
|
|
64
|
+
didDelete?(event: FileDeleteEvent): Promise<void> | void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Options for `FileOperationsApi.rename`. */
|
|
68
|
+
export interface FileRenameOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Whether the target is a directory. Callers that already know (the
|
|
71
|
+
* explorer reads it off its tree) should pass it; otherwise it is
|
|
72
|
+
* resolved via `fs.stat` and, on providers without `stat`, assumed to
|
|
73
|
+
* be a file.
|
|
74
|
+
*/
|
|
75
|
+
isDirectory?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Options for `FileOperationsApi.delete`. */
|
|
79
|
+
export interface FileDeleteOperationOptions {
|
|
80
|
+
isDirectory?: boolean;
|
|
81
|
+
/** Passed through to `FileSystemProvider.deleteFile` for idempotency. */
|
|
82
|
+
ifExists?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The single seam every file mutation that changes a path should go
|
|
87
|
+
* through: rename/move and delete.
|
|
88
|
+
*
|
|
89
|
+
* `runtime.fs` stays the raw filesystem surface and is still the right
|
|
90
|
+
* call for reads and for content writes, where the path is unchanged and
|
|
91
|
+
* nobody's key breaks. Creating and deleting a path, or moving one, is
|
|
92
|
+
* different: it invalidates state held elsewhere in the app, and the
|
|
93
|
+
* whole point of routing it here is that the invalidation is announced
|
|
94
|
+
* instead of discovered.
|
|
95
|
+
*
|
|
96
|
+
* Two consumers, one code path: the explorer's context menu and an agent
|
|
97
|
+
* invoking the `file-explorer.rename` capability both land here, so tab
|
|
98
|
+
* state stays correct whether a human or a model moved the file.
|
|
99
|
+
*
|
|
100
|
+
* ## Dirty (unsaved) tracking
|
|
101
|
+
*
|
|
102
|
+
* `setDirty` / `isDirty` / `getDirtyPaths` are a working-copy registry.
|
|
103
|
+
* The shell mirrors its content tabs into it, which lets a caller ask
|
|
104
|
+
* "would deleting this lose unsaved work?" without knowing that content
|
|
105
|
+
* tabs exist — the explorer's delete confirmation reads it to say so in
|
|
106
|
+
* the dialog, in one prompt rather than two.
|
|
107
|
+
*/
|
|
108
|
+
export interface FileOperationsApi {
|
|
109
|
+
/**
|
|
110
|
+
* Add a participant. Registering the same `id` twice replaces the
|
|
111
|
+
* first. Dispose to remove it — a shell that is torn down must not
|
|
112
|
+
* leave a hook pointing at dead state.
|
|
113
|
+
*/
|
|
114
|
+
registerParticipant(participant: FileOperationParticipant): {
|
|
115
|
+
dispose(): void;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Record (or clear) unsaved-changes state for a path. */
|
|
119
|
+
setDirty(path: string, dirty: boolean): void;
|
|
120
|
+
isDirty(path: string): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Every path with unsaved changes; with `prefix`, only that path and
|
|
123
|
+
* its descendants (so a folder delete can name what it would lose).
|
|
124
|
+
*/
|
|
125
|
+
getDirtyPaths(prefix?: string): string[];
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Move `oldPath` to `newPath`, announcing it. Rejects if a participant
|
|
129
|
+
* vetoes or the filesystem call fails; in both cases nothing has moved.
|
|
130
|
+
*/
|
|
131
|
+
rename(
|
|
132
|
+
oldPath: string,
|
|
133
|
+
newPath: string,
|
|
134
|
+
opts?: FileRenameOptions
|
|
135
|
+
): Promise<void>;
|
|
136
|
+
|
|
137
|
+
/** Delete `path`, announcing it. Same failure contract as `rename`. */
|
|
138
|
+
delete(path: string, opts?: FileDeleteOperationOptions): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
declare module 'punica' {
|
|
2
2
|
export namespace shell {
|
|
3
|
+
/** One open content tab, as reported by `ContentTabsApi.list`. */
|
|
4
|
+
export interface ContentTabInfo {
|
|
5
|
+
/** Workspace-relative file path, or a scheme URI for command-only views. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Label shown on the tab (without the unsaved-changes marker). */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Set by whichever extension opened it (e.g. "code", "markdown"). */
|
|
10
|
+
viewType?: string;
|
|
11
|
+
/** Whether the tab holds unsaved edits. */
|
|
12
|
+
dirty: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* Content tab management capability for the main editor area.
|
|
17
|
+
*
|
|
18
|
+
* Tabs are identified by `path` — the same workspace-relative path the
|
|
19
|
+
* filesystem uses — which is what makes `rename` necessary rather than
|
|
20
|
+
* cosmetic: after a file moves, every tab keyed by the old path is
|
|
21
|
+
* holding an identity that no longer exists.
|
|
5
22
|
*/
|
|
6
23
|
export interface ContentTabsApi {
|
|
7
24
|
open(options: {
|
|
@@ -9,9 +26,18 @@ declare module 'punica' {
|
|
|
9
26
|
title?: string;
|
|
10
27
|
viewType?: string;
|
|
11
28
|
}): HTMLElement | null;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Close the tab at `path`, prompting first when it has unsaved changes
|
|
31
|
+
* (Save / Don't Save / Cancel). Resolves when the tab is gone or the
|
|
32
|
+
* user cancelled — check `list()` if you need to know which.
|
|
33
|
+
*/
|
|
34
|
+
close(path: string): Promise<void>;
|
|
35
|
+
/** As `close`, addressed by tab id rather than path. */
|
|
36
|
+
closeById(id: string): Promise<void>;
|
|
37
|
+
closeOthers(path: string): Promise<void>;
|
|
38
|
+
/** As `closeOthers`, addressed by tab id rather than path. */
|
|
39
|
+
closeOthersById(id: string): Promise<void>;
|
|
40
|
+
closeAll(): Promise<void>;
|
|
15
41
|
setDirty(path: string, dirty: boolean): void;
|
|
16
42
|
/**
|
|
17
43
|
* Returns the path of the currently active content tab, or null if none.
|
|
@@ -23,6 +49,34 @@ declare module 'punica' {
|
|
|
23
49
|
* (e.g. "code", "flow-book", "markdown").
|
|
24
50
|
*/
|
|
25
51
|
getActiveViewType(): string | null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Re-key the tab at `oldPath` to `newPath`, keeping its content,
|
|
55
|
+
* dirty marker and selection. When `oldPath` names a directory, every
|
|
56
|
+
* tab beneath it moves too — renaming `src/` has to carry
|
|
57
|
+
* `src/app.ts` with it.
|
|
58
|
+
*
|
|
59
|
+
* This does not touch the filesystem; it is the tab half of a rename
|
|
60
|
+
* that already happened. Callers should not invoke it directly —
|
|
61
|
+
* `runtime.fileOperations.rename` drives it through the shell's
|
|
62
|
+
* participant, so an agent-initiated rename syncs the same way a
|
|
63
|
+
* context-menu one does.
|
|
64
|
+
*/
|
|
65
|
+
rename(oldPath: string, newPath: string): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Close the tab at `path` (and, for a directory, everything beneath it)
|
|
69
|
+
* without prompting. For when the file is already gone: there is
|
|
70
|
+
* nothing left to save, so the unsaved-changes dialog would offer a
|
|
71
|
+
* choice that cannot be honored.
|
|
72
|
+
*/
|
|
73
|
+
forceClose(path: string): void;
|
|
74
|
+
|
|
75
|
+
/** Whether the tab at `path` holds unsaved edits. False when no such tab. */
|
|
76
|
+
isDirty(path: string): boolean;
|
|
77
|
+
|
|
78
|
+
/** Every open tab, in the order they appear in the tab strip. */
|
|
79
|
+
list(): readonly ContentTabInfo[];
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
/**
|