@benz-ai-x/dsh-md-preview 0.2.7 → 0.4.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/lib/index.js CHANGED
@@ -6,6 +6,12 @@ import z from "@deepseek-ai/schemastery";
6
6
  const DEFAULT_MAX_BYTES = 1048576;
7
7
  /** Markdown documents are the previewable vocabulary; deployments may widen it. */
8
8
  const DEFAULT_ALLOWED_EXTENSIONS = [".md", ".markdown"];
9
+ /** The preview union: readable (and rendered) even though only markdown edits. */
10
+ const DEFAULT_PREVIEW_EXTENSIONS = [
11
+ ".md",
12
+ ".markdown",
13
+ ".txt"
14
+ ];
9
15
  //#endregion
10
16
  //#region src/remote.ts
11
17
  var __runInitializers = function(thisArg, initializers, value) {
@@ -64,6 +70,12 @@ function fsErrorCode(error) {
64
70
  if (typeof code === "string") return code;
65
71
  }
66
72
  }
73
+ /** Workspace-relative form of an entry target, falling back to its name. */
74
+ function workspaceRelative(rootPath, targetPath, name) {
75
+ if (targetPath === rootPath) return name;
76
+ const prefix = rootPath.endsWith("/") ? rootPath : `${rootPath}/`;
77
+ return targetPath.startsWith(prefix) ? targetPath.slice(prefix.length) : name;
78
+ }
67
79
  /** Lowercased dot-prefixed extension of a path, or the empty string. */
68
80
  function extensionOf(path) {
69
81
  const name = path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
@@ -83,11 +95,13 @@ let MdPreviewService = (() => {
83
95
  let _instanceExtraInitializers = [];
84
96
  let _read_decorators;
85
97
  let _write_decorators;
98
+ let _list_decorators;
86
99
  return class MdPreviewService extends _classSuper {
87
100
  static {
88
101
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
89
102
  _read_decorators = [Remote];
90
103
  _write_decorators = [Remote];
104
+ _list_decorators = [Remote];
91
105
  __esDecorate(this, null, _read_decorators, {
92
106
  kind: "method",
93
107
  name: "read",
@@ -110,6 +124,17 @@ let MdPreviewService = (() => {
110
124
  },
111
125
  metadata: _metadata
112
126
  }, null, _instanceExtraInitializers);
127
+ __esDecorate(this, null, _list_decorators, {
128
+ kind: "method",
129
+ name: "list",
130
+ static: false,
131
+ private: false,
132
+ access: {
133
+ has: (obj) => "list" in obj,
134
+ get: (obj) => obj.list
135
+ },
136
+ metadata: _metadata
137
+ }, null, _instanceExtraInitializers);
113
138
  if (_metadata) Object.defineProperty(this, Symbol.metadata, {
114
139
  enumerable: true,
115
140
  configurable: true,
@@ -146,7 +171,7 @@ let MdPreviewService = (() => {
146
171
  * @throws RemoteError with a stable MdPreview failure code.
147
172
  */
148
173
  async read(sessionId, path, signal) {
149
- const { target, info } = await this.resolveWorkspaceTarget(sessionId, path, signal, "read");
174
+ const { target, info } = await this.resolveWorkspaceTarget(sessionId, path, signal, "read", [...this.config.allowedExtensions, ...this.config.previewExtensions]);
150
175
  if (info.size !== void 0 && info.size > this.config.maxBytes) throw failure("md-preview/too-large", `mdPreview/read refuses "${path}" above the configured byte cap`);
151
176
  let content;
152
177
  try {
@@ -175,7 +200,7 @@ let MdPreviewService = (() => {
175
200
  * @throws RemoteError with a stable MdPreview failure code.
176
201
  */
177
202
  async write(sessionId, path, content, fingerprint, force, signal) {
178
- const { target, cwd } = await this.resolveWorkspaceTarget(sessionId, path, signal, "write");
203
+ const { target, cwd } = await this.resolveWorkspaceTarget(sessionId, path, signal, "write", this.config.allowedExtensions);
179
204
  if (content.length > this.config.maxBytes) throw failure("md-preview/too-large", `mdPreview/write refuses "${path}" above the configured byte cap`);
180
205
  if (fingerprint === void 0 && !force) throw failure("md-preview/bad-request", "mdPreview/write requires a fingerprint or force");
181
206
  const expected = fingerprint === void 0 ? void 0 : {
@@ -204,6 +229,39 @@ let MdPreviewService = (() => {
204
229
  };
205
230
  }
206
231
  /**
232
+ * List one workspace directory for the browser face; folders sort first,
233
+ * then entries alphabetize by name. A blank path lists the workspace root.
234
+ * @param sessionId - owning session; its header cwd roots the listing.
235
+ * @param path - directory path as the tree spelled it; blank = root.
236
+ * @param signal - caller cancellation carried through every fs call.
237
+ * @returns the requested path with its workspace-relative entries.
238
+ * @throws RemoteError with a stable MdPreview failure code.
239
+ */
240
+ async list(sessionId, path, signal) {
241
+ const { root, target } = await this.resolveContainedTarget(sessionId, path, signal, "list");
242
+ const info = await this.ctx.fs.stat(target, signal);
243
+ if (info === void 0) throw failure("md-preview/not-found", `mdPreview/list cannot find "${path}"`);
244
+ if (info.type !== "directory") throw failure("md-preview/bad-request", `mdPreview/list requires a directory path, not "${path}"`);
245
+ let raw;
246
+ try {
247
+ raw = await this.ctx.fs.listDir(target, signal);
248
+ } catch (error) {
249
+ if (signal.aborted) throw error;
250
+ throw failure("md-preview/unavailable", `mdPreview/list failed to list "${path}": ${error instanceof Error ? error.message : String(error)}`);
251
+ }
252
+ const rootPath = root.displayPath;
253
+ const entries = raw.map((entry) => ({
254
+ name: entry.name,
255
+ type: entry.type,
256
+ path: workspaceRelative(rootPath, entry.target.displayPath, entry.name)
257
+ }));
258
+ entries.sort((a, b) => a.type === b.type || a.type !== "directory" && b.type !== "directory" ? a.name.localeCompare(b.name) : a.type === "directory" ? -1 : 1);
259
+ return {
260
+ path: path.trim().length === 0 ? "" : path,
261
+ entries
262
+ };
263
+ }
264
+ /**
207
265
  * The shared authority preamble of the remote methods: resolve a path to a
208
266
  * live, contained, regular workspace file, or reject it with the stable
209
267
  * failure code. One home for the check order and the aborted-rethrow
@@ -215,16 +273,38 @@ let MdPreviewService = (() => {
215
273
  * @returns the resolved target, its stat info, and the workspace cwd.
216
274
  * @throws RemoteError with a stable MdPreview failure code.
217
275
  */
218
- async resolveWorkspaceTarget(sessionId, path, signal, op) {
276
+ async resolveWorkspaceTarget(sessionId, path, signal, op, extensions) {
219
277
  if (path.trim().length === 0) throw failure("md-preview/bad-request", `mdPreview/${op} requires a non-empty path`);
220
- if (!isAllowedExtension(path, this.config.allowedExtensions)) throw failure("md-preview/unsupported-extension", `mdPreview/${op} refuses non-previewable path "${path}"`);
278
+ if (!isAllowedExtension(path, extensions)) throw failure("md-preview/unsupported-extension", `mdPreview/${op} refuses non-previewable path "${path}"`);
279
+ const contained = await this.resolveContainedTarget(sessionId, path, signal, op);
280
+ const info = await this.ctx.fs.stat(contained.target, signal);
281
+ if (info === void 0) throw failure("md-preview/not-found", `mdPreview/${op} cannot find "${path}"`);
282
+ if (info.type !== "file") throw failure("md-preview/unsupported-extension", `mdPreview/${op} target "${path}" is not a regular file`);
283
+ return {
284
+ ...contained,
285
+ info
286
+ };
287
+ }
288
+ /**
289
+ * The authority middle shared by every method: session → workspace cwd →
290
+ * root resolve → target resolve (a blank path means the root itself) →
291
+ * containment. Extension and type checks belong to the callers.
292
+ * @param sessionId - owning session; its header cwd roots the resolution.
293
+ * @param path - requested path; blank spells the workspace root.
294
+ * @param signal - caller cancellation carried through every fs call.
295
+ * @param op - calling method name for diagnostic messages.
296
+ * @returns the resolved root, target, and the workspace cwd.
297
+ * @throws RemoteError with a stable MdPreview failure code.
298
+ */
299
+ async resolveContainedTarget(sessionId, path, signal, op) {
221
300
  const session = this.ctx.sessions.get(sessionId);
222
301
  if (session === void 0) throw failure("md-preview/unknown-session", `mdPreview/${op} cannot resolve session "${sessionId}"`);
223
302
  const cwd = session.header.cwd;
224
303
  if (cwd === void 0) throw failure("md-preview/no-workspace", `mdPreview/${op} session "${sessionId}" has no working directory`);
225
304
  const root = await this.ctx.fs.resolve(cwd, { signal });
226
305
  let target;
227
- try {
306
+ if (path.trim().length === 0) target = root;
307
+ else try {
228
308
  target = await this.ctx.fs.resolve(path, {
229
309
  cwd,
230
310
  signal
@@ -234,12 +314,9 @@ let MdPreviewService = (() => {
234
314
  throw failure("md-preview/not-found", `mdPreview/${op} cannot resolve path "${path}"`);
235
315
  }
236
316
  if (!this.ctx.fs.contains(root, target)) throw failure("md-preview/forbidden", `mdPreview/${op} refuses paths outside the session workspace`);
237
- const info = await this.ctx.fs.stat(target, signal);
238
- if (info === void 0) throw failure("md-preview/not-found", `mdPreview/${op} cannot find "${path}"`);
239
- if (info.type !== "file") throw failure("md-preview/unsupported-extension", `mdPreview/${op} target "${path}" is not a regular file`);
240
317
  return {
318
+ root,
241
319
  target,
242
- info,
243
320
  cwd
244
321
  };
245
322
  }
@@ -251,7 +328,8 @@ let MdPreviewService = (() => {
251
328
  /** Standard Schema twin of {@link Config}; defaults live here, not in code. */
252
329
  const Config = z.object({
253
330
  maxBytes: z.number().min(1).default(DEFAULT_MAX_BYTES),
254
- allowedExtensions: z.array(z.string()).default([...DEFAULT_ALLOWED_EXTENSIONS])
331
+ allowedExtensions: z.array(z.string()).default([...DEFAULT_ALLOWED_EXTENSIONS]),
332
+ previewExtensions: z.array(z.string()).default([...DEFAULT_PREVIEW_EXTENSIONS])
255
333
  });
256
334
  //#endregion
257
335
  //#region src/protocol.ts
@@ -284,11 +362,12 @@ const inject = [
284
362
  */
285
363
  function apply(ctx, config = {
286
364
  maxBytes: DEFAULT_MAX_BYTES,
287
- allowedExtensions: [...DEFAULT_ALLOWED_EXTENSIONS]
365
+ allowedExtensions: [...DEFAULT_ALLOWED_EXTENSIONS],
366
+ previewExtensions: [...DEFAULT_PREVIEW_EXTENSIONS]
288
367
  }) {
289
368
  ctx.plugin(MdPreviewService, config);
290
369
  }
291
370
  //#endregion
292
- export { Config, DEFAULT_ALLOWED_EXTENSIONS, DEFAULT_MAX_BYTES, MD_PREVIEW_FAILURE_CODES, MdPreviewService, apply, inject, isAllowedExtension, name };
371
+ export { Config, DEFAULT_ALLOWED_EXTENSIONS, DEFAULT_MAX_BYTES, DEFAULT_PREVIEW_EXTENSIONS, MD_PREVIEW_FAILURE_CODES, MdPreviewService, apply, inject, isAllowedExtension, name };
293
372
 
294
373
  //# sourceMappingURL=index.js.map
@@ -24,48 +24,31 @@ const writeResult$schema = z.object({
24
24
  path: z.string(),
25
25
  fingerprint: z.string()
26
26
  });
27
+ const listPath$schema = z.string();
28
+ const listEntry$schema = z.object({
29
+ name: z.string(),
30
+ type: z.enum([
31
+ "file",
32
+ "directory",
33
+ "other"
34
+ ]),
35
+ path: z.string()
36
+ });
37
+ const listResult$schema = z.object({
38
+ path: z.string(),
39
+ entries: z.array(listEntry$schema)
40
+ });
27
41
  /** The MdPreview contribution mounted by this package's browser entry. */
28
42
  const TYPERT_REMOTE = {
29
43
  package: "@benz-ai-x/dsh-md-preview",
30
- descriptors: [{
31
- id: "@benz-ai-x/dsh-md-preview#mdPreview/read",
32
- service: "mdPreview",
33
- namespace: "mdPreview",
34
- method: "read",
35
- invocation: { kind: "direct" },
36
- parameters: [{
37
- name: "sessionId",
38
- wire: "sessionId",
39
- source: "json",
40
- codec: {
41
- mode: "strict",
42
- typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
43
- schema: readSessionId$schema
44
- }
45
- }, {
46
- name: "path",
47
- wire: "path",
48
- source: "json",
49
- codec: {
50
- mode: "strict",
51
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/read:path",
52
- schema: readPath$schema
53
- }
54
- }],
55
- cancellation: { parameter: "signal" },
56
- result: {
57
- mode: "strict",
58
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/read:result",
59
- schema: readResult$schema
60
- }
61
- }, {
62
- id: "@benz-ai-x/dsh-md-preview#mdPreview/write",
63
- service: "mdPreview",
64
- namespace: "mdPreview",
65
- method: "write",
66
- invocation: { kind: "direct" },
67
- parameters: [
68
- {
44
+ descriptors: [
45
+ {
46
+ id: "@benz-ai-x/dsh-md-preview#mdPreview/read",
47
+ service: "mdPreview",
48
+ namespace: "mdPreview",
49
+ method: "read",
50
+ invocation: { kind: "direct" },
51
+ parameters: [{
69
52
  name: "sessionId",
70
53
  wire: "sessionId",
71
54
  source: "json",
@@ -74,55 +57,121 @@ const TYPERT_REMOTE = {
74
57
  typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
75
58
  schema: readSessionId$schema
76
59
  }
77
- },
78
- {
60
+ }, {
79
61
  name: "path",
80
62
  wire: "path",
81
63
  source: "json",
82
64
  codec: {
83
65
  mode: "strict",
84
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:path",
66
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/read:path",
85
67
  schema: readPath$schema
86
68
  }
87
- },
88
- {
89
- name: "content",
90
- wire: "content",
91
- source: "json",
92
- codec: {
93
- mode: "strict",
94
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:content",
95
- schema: writeContent$schema
69
+ }],
70
+ cancellation: { parameter: "signal" },
71
+ result: {
72
+ mode: "strict",
73
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/read:result",
74
+ schema: readResult$schema
75
+ }
76
+ },
77
+ {
78
+ id: "@benz-ai-x/dsh-md-preview#mdPreview/write",
79
+ service: "mdPreview",
80
+ namespace: "mdPreview",
81
+ method: "write",
82
+ invocation: { kind: "direct" },
83
+ parameters: [
84
+ {
85
+ name: "sessionId",
86
+ wire: "sessionId",
87
+ source: "json",
88
+ codec: {
89
+ mode: "strict",
90
+ typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
91
+ schema: readSessionId$schema
92
+ }
93
+ },
94
+ {
95
+ name: "path",
96
+ wire: "path",
97
+ source: "json",
98
+ codec: {
99
+ mode: "strict",
100
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:path",
101
+ schema: readPath$schema
102
+ }
103
+ },
104
+ {
105
+ name: "content",
106
+ wire: "content",
107
+ source: "json",
108
+ codec: {
109
+ mode: "strict",
110
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:content",
111
+ schema: writeContent$schema
112
+ }
113
+ },
114
+ {
115
+ name: "fingerprint",
116
+ wire: "fingerprint",
117
+ source: "json",
118
+ codec: {
119
+ mode: "strict",
120
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:fingerprint",
121
+ schema: writeFingerprint$schema
122
+ }
123
+ },
124
+ {
125
+ name: "force",
126
+ wire: "force",
127
+ source: "json",
128
+ codec: {
129
+ mode: "strict",
130
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:force",
131
+ schema: writeForce$schema
132
+ }
96
133
  }
97
- },
98
- {
99
- name: "fingerprint",
100
- wire: "fingerprint",
134
+ ],
135
+ cancellation: { parameter: "signal" },
136
+ result: {
137
+ mode: "strict",
138
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:result",
139
+ schema: writeResult$schema
140
+ }
141
+ },
142
+ {
143
+ id: "@benz-ai-x/dsh-md-preview#mdPreview/list",
144
+ service: "mdPreview",
145
+ namespace: "mdPreview",
146
+ method: "list",
147
+ invocation: { kind: "direct" },
148
+ parameters: [{
149
+ name: "sessionId",
150
+ wire: "sessionId",
101
151
  source: "json",
102
152
  codec: {
103
153
  mode: "strict",
104
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:fingerprint",
105
- schema: writeFingerprint$schema
154
+ typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
155
+ schema: readSessionId$schema
106
156
  }
107
- },
108
- {
109
- name: "force",
110
- wire: "force",
157
+ }, {
158
+ name: "path",
159
+ wire: "path",
111
160
  source: "json",
112
161
  codec: {
113
162
  mode: "strict",
114
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:force",
115
- schema: writeForce$schema
163
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/list:path",
164
+ schema: listPath$schema
116
165
  }
166
+ }],
167
+ cancellation: { parameter: "signal" },
168
+ result: {
169
+ mode: "strict",
170
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/list:result",
171
+ schema: listResult$schema
117
172
  }
118
- ],
119
- cancellation: { parameter: "signal" },
120
- result: {
121
- mode: "strict",
122
- typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/write:result",
123
- schema: writeResult$schema
124
173
  }
125
- }]
174
+ ]
126
175
  };
127
176
  //#endregion
128
177
  export { TYPERT_REMOTE, TYPERT_REMOTE as default };
@@ -1,10 +1,9 @@
1
1
  import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
2
2
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
- import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
4
3
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
5
- import type { MdPreviewFile, MdPreviewWriteResult } from '../protocol.ts';
6
- import type { MdPreviewState } from './preview-state.ts';
7
- /** Read/write RPCs and panel dismissal, created in the plugin's apply world. */
4
+ import type { MdPreviewFile, MdPreviewListResult, MdPreviewWriteResult } from '../protocol.ts';
5
+ import type { MdPreviewState, MdPreviewTarget } from './preview-state.ts';
6
+ /** Read/write/list RPCs and panel dismissal, created in the plugin's apply world. */
8
7
  export interface PreviewOverlayInjected {
9
8
  hooks: {
10
9
  /** Current preview target; null while the panel is closed. */
@@ -12,17 +11,21 @@ export interface PreviewOverlayInjected {
12
11
  };
13
12
  /** Dismiss the panel and drop the target. */
14
13
  close(): void;
14
+ /** Set the preview target (the browser face's file-open handoff). */
15
+ setTarget(target: MdPreviewTarget | null): void;
15
16
  /** One bounded read; the transport carries the AbortSignal. */
16
- read(sessionId: SessionId, path: string, signal: AbortSignal): Promise<RemoteResult<MdPreviewFile>>;
17
+ read(sessionId: SessionId, path: string, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewFile>>;
17
18
  /** One guarded write; `fingerprint` comes from the backing read. */
18
- write(sessionId: SessionId, path: string, content: string, fingerprint: string | undefined, force: boolean, signal: AbortSignal): Promise<RemoteResult<MdPreviewWriteResult>>;
19
+ write(sessionId: SessionId, path: string, content: string, fingerprint: string | undefined, force: boolean, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewWriteResult>>;
20
+ /** One workspace directory listing; blank path lists the root. */
21
+ list(sessionId: SessionId, path: string, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewListResult>>;
19
22
  }
20
23
  /** Full composed panel props. */
21
24
  export type PreviewOverlayProps = PropsRuntime<'shell.overlay'> & InjectFace<PreviewOverlayInjected> & PropsLocale<'md-preview'>;
22
25
  /**
23
26
  * Render the preview panel for the current target.
24
- * @param props - target hook, read RPC, dismissal, and the locale seat.
27
+ * @param props - target hook, read/write RPCs, dismissal, and the locale seat.
25
28
  * @returns the docked panel, or null while closed.
26
29
  */
27
- export declare function PreviewOverlay({ usePreviewTarget, close, read, write, t }: PreviewOverlayProps): import("react").JSX.Element | null;
30
+ export declare function PreviewOverlay({ usePreviewTarget, close, setTarget, read, write, list, t }: PreviewOverlayProps): import("react").JSX.Element | null;
28
31
  //# sourceMappingURL=PreviewOverlay.d.ts.map
@@ -0,0 +1,23 @@
1
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
2
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
3
+ import type { MdPreviewListResult } from '../protocol.ts';
4
+ /** Listing RPC and the file-open handoff, created in the plugin's apply world. */
5
+ export interface WorkspaceBrowserProps {
6
+ /** Owning session whose workspace the tree roots at. */
7
+ sessionId: SessionId;
8
+ /** One directory listing; the transport carries the AbortSignal. */
9
+ list(sessionId: SessionId, path: string, signal: AbortSignal): Promise<RemoteResult<MdPreviewListResult>>;
10
+ /** Open one file as the panel's preview target. */
11
+ onOpenFile(path: string): void;
12
+ /** Workspace-relative path of the current preview target, if any. */
13
+ currentPath: string | null;
14
+ /** Locale seat. */
15
+ t(key: string): string;
16
+ }
17
+ /**
18
+ * Render the workspace tree for one session.
19
+ * @param props - session identity, listing RPC, file-open handoff, locale seat.
20
+ * @returns the tree element.
21
+ */
22
+ export declare function WorkspaceBrowser({ sessionId, list, onOpenFile, currentPath, t }: WorkspaceBrowserProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=WorkspaceBrowser.d.ts.map
@@ -8,6 +8,7 @@ export declare const zh: {
8
8
  'panel.loading': string;
9
9
  'panel.empty': string;
10
10
  'panel.error': string;
11
+ 'panel.unsupported': string;
11
12
  'panel.retry': string;
12
13
  'panel.edit': string;
13
14
  'panel.save': string;
@@ -21,6 +22,12 @@ export declare const zh: {
21
22
  'panel.unsaved.title': string;
22
23
  'panel.unsaved.discard': string;
23
24
  'panel.unsaved.keep': string;
25
+ 'browse.open': string;
26
+ 'browse.back': string;
27
+ 'browse.loading': string;
28
+ 'browse.empty': string;
29
+ 'browse.error': string;
30
+ 'browse.retry': string;
24
31
  'chip.preview': string;
25
32
  'chip.open': string;
26
33
  'action.label': string;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The PreviewSession machine: the pure state algebra of one preview target's
3
+ * lifecycle — content read, edit session, guarded save, prompts. The React
4
+ * adapter lives beside the panel (use-preview-session.ts); effects and RPC
5
+ * never enter here. `READ_STARTED` is the single reset point: a new read
6
+ * begins only when the previous document's whole session is over.
7
+ */
8
+ import type { MdPreviewFile, MdPreviewWriteResult } from '../protocol.ts';
9
+ /** Content lifecycle of one preview target. */
10
+ export type PreviewSessionContent = {
11
+ readonly state: 'loading';
12
+ } | {
13
+ readonly state: 'ready';
14
+ readonly file: MdPreviewFile;
15
+ } | {
16
+ readonly state: 'failed';
17
+ readonly code: string;
18
+ readonly message: string;
19
+ };
20
+ /** Whole machine state for the panel's current preview target. */
21
+ export interface PreviewSessionState {
22
+ /** Read lifecycle of the current target. */
23
+ readonly content: PreviewSessionContent;
24
+ /** Panel face: rendering the document or editing a draft of it. */
25
+ readonly face: 'view' | 'edit';
26
+ /** The edit session's draft; meaningful only while `face` is edit. */
27
+ readonly draft: string;
28
+ /** A save is in flight. */
29
+ readonly saving: boolean;
30
+ /** The last save met a changed file; the conflict bar is up. */
31
+ readonly conflicted: boolean;
32
+ /** The last save failed for a non-conflict reason; the error bar is up. */
33
+ readonly saveError: {
34
+ readonly code: string;
35
+ readonly message: string;
36
+ } | null;
37
+ /** The unsaved guard is asking before a close. */
38
+ readonly unsavedPrompt: boolean;
39
+ /** The saved toast is showing. */
40
+ readonly toast: boolean;
41
+ /** The session asked the shell to close the panel; the adapter acts once. */
42
+ readonly closeRequested: boolean;
43
+ }
44
+ /** One machine transition. */
45
+ export type PreviewSessionAction = {
46
+ readonly type: 'READ_STARTED';
47
+ } | {
48
+ readonly type: 'RETRY_READ';
49
+ } | {
50
+ readonly type: 'READ_RESOLVED';
51
+ readonly file: MdPreviewFile;
52
+ } | {
53
+ readonly type: 'READ_FAILED';
54
+ readonly code: string;
55
+ readonly message: string;
56
+ } | {
57
+ readonly type: 'ENTER_EDIT';
58
+ } | {
59
+ readonly type: 'EDIT';
60
+ readonly draft: string;
61
+ } | {
62
+ readonly type: 'SAVE_STARTED';
63
+ } | {
64
+ readonly type: 'SAVE_RESOLVED';
65
+ readonly result: MdPreviewWriteResult;
66
+ } | {
67
+ readonly type: 'SAVE_CONFLICT';
68
+ } | {
69
+ readonly type: 'SAVE_FAILED';
70
+ readonly code: string;
71
+ readonly message: string;
72
+ } | {
73
+ readonly type: 'CANCEL_EDIT';
74
+ } | {
75
+ readonly type: 'REQUEST_CLOSE';
76
+ } | {
77
+ readonly type: 'DISCARD';
78
+ } | {
79
+ readonly type: 'KEEP_EDITING';
80
+ } | {
81
+ readonly type: 'TOAST_EXPIRED';
82
+ };
83
+ /** The pristine state every session starts (and resets) from. */
84
+ export declare function initialPreviewSession(): PreviewSessionState;
85
+ /** Whether the draft differs from the loaded document. */
86
+ export declare function isDirty(state: PreviewSessionState): boolean;
87
+ /** The save-enable policy: something to save (dirty or conflicted), no save in flight. */
88
+ export declare function canSave(state: PreviewSessionState): boolean;
89
+ /**
90
+ * Apply one transition.
91
+ * @param state - current machine state.
92
+ * @param action - the transition.
93
+ * @returns the next state (transitions are total; guards decline no-ops).
94
+ */
95
+ export declare function transition(state: PreviewSessionState, action: PreviewSessionAction): PreviewSessionState;
96
+ //# sourceMappingURL=preview-session.d.ts.map
@@ -13,6 +13,8 @@ export declare function createPreviewStore(): import("@deepseek-ai/dsh-client-st
13
13
  export declare function basename(path: string): string;
14
14
  /** Whether the client treats a produced path as a previewable markdown document. */
15
15
  export declare function isPreviewable(path: string): boolean;
16
+ /** Whether a path may enter an edit session (the markdown-only editable set). */
17
+ export declare function isEditable(path: string): boolean;
16
18
  /** Produced paths of one turn split into previewable and externally-opened groups, first-seen order preserved. */
17
19
  export declare function splitPreviewable(paths: readonly string[]): {
18
20
  previewable: string[];
@@ -0,0 +1,36 @@
1
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
2
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
3
+ import type { MdPreviewFile, MdPreviewWriteResult } from '../protocol.ts';
4
+ import type { MdPreviewTarget } from './preview-state.ts';
5
+ import { initialPreviewSession } from './preview-session.ts';
6
+ /** The adapter's inputs: the RPCs and dismissal from the plugin's apply world. */
7
+ export interface PanelDocumentSessionDeps {
8
+ readonly read: (sessionId: SessionId, path: string, signal: AbortSignal) => Promise<RemoteResult<MdPreviewFile>>;
9
+ readonly write: (sessionId: SessionId, path: string, content: string, fingerprint: string | undefined, force: boolean, signal: AbortSignal) => Promise<RemoteResult<MdPreviewWriteResult>>;
10
+ readonly close: () => void;
11
+ }
12
+ /** The panel-facing surface: machine state plus intent actions. */
13
+ export interface PanelDocumentSession {
14
+ readonly state: ReturnType<typeof initialPreviewSession>;
15
+ readonly canSave: boolean;
16
+ readonly dirty: boolean;
17
+ readonly actions: {
18
+ enterEdit(): void;
19
+ edit(draft: string): void;
20
+ save(force: boolean): void;
21
+ cancelEdit(): void;
22
+ reload(): void;
23
+ retryRead(): void;
24
+ requestClose(): void;
25
+ discard(): void;
26
+ keepEditing(): void;
27
+ };
28
+ }
29
+ /**
30
+ * Run one PreviewSession for the current preview target.
31
+ * @param deps - read/write RPCs and the panel dismissal.
32
+ * @param target - the current preview target; null while the panel is closed.
33
+ * @returns machine state with derived flags and intent actions.
34
+ */
35
+ export declare function usePanelDocumentSession(deps: PanelDocumentSessionDeps, target: MdPreviewTarget | null): PanelDocumentSession;
36
+ //# sourceMappingURL=use-preview-session.d.ts.map
@@ -4,8 +4,11 @@ import z from '@deepseek-ai/schemastery';
4
4
  export interface Config {
5
5
  /** Largest file size the service will read and return, in bytes. */
6
6
  maxBytes: number;
7
- /** Extensions (lowercased, dot-prefixed) eligible for preview. */
7
+ /** Extensions (lowercased, dot-prefixed) eligible for editing. */
8
8
  allowedExtensions: string[];
9
+ /** Extensions (lowercased, dot-prefixed) eligible for preview (a superset
10
+ * of the editable set — plain-text members render but never edit). */
11
+ previewExtensions: string[];
9
12
  }
10
13
  /** Standard Schema twin of {@link Config}; defaults live here, not in code. */
11
14
  export declare const Config: z<Config>;
@@ -3,4 +3,6 @@
3
3
  export declare const DEFAULT_MAX_BYTES = 1048576;
4
4
  /** Markdown documents are the previewable vocabulary; deployments may widen it. */
5
5
  export declare const DEFAULT_ALLOWED_EXTENSIONS: readonly [".md", ".markdown"];
6
+ /** The preview union: readable (and rendered) even though only markdown edits. */
7
+ export declare const DEFAULT_PREVIEW_EXTENSIONS: readonly [".md", ".markdown", ".txt"];
6
8
  //# sourceMappingURL=constants.d.ts.map