@benz-ai-x/dsh-md-preview 0.7.2 → 0.10.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
@@ -12,6 +12,14 @@ const DEFAULT_PREVIEW_EXTENSIONS = [
12
12
  ".markdown",
13
13
  ".txt"
14
14
  ];
15
+ /** Default search result cap: far above real agent-workspace document counts
16
+ * (tens to low hundreds observed in DSH sessions) while bounding one answer. */
17
+ const DEFAULT_SEARCH_MAX_RESULTS = 200;
18
+ /** Default search traversal cap in directories: bounded for any deployment
19
+ * workspace; a pathological tree reports `traversal-limit`, never hangs. */
20
+ const DEFAULT_SEARCH_MAX_DIRECTORIES = 2e3;
21
+ /** Default parallel `listDir` width for one search. */
22
+ const DEFAULT_SEARCH_CONCURRENCY = 8;
15
23
  //#endregion
16
24
  //#region src/remote.ts
17
25
  var __runInitializers = function(thisArg, initializers, value) {
@@ -96,12 +104,14 @@ let MdPreviewService = (() => {
96
104
  let _read_decorators;
97
105
  let _write_decorators;
98
106
  let _list_decorators;
107
+ let _search_decorators;
99
108
  return class MdPreviewService extends _classSuper {
100
109
  static {
101
110
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
102
111
  _read_decorators = [Remote];
103
112
  _write_decorators = [Remote];
104
113
  _list_decorators = [Remote];
114
+ _search_decorators = [Remote];
105
115
  __esDecorate(this, null, _read_decorators, {
106
116
  kind: "method",
107
117
  name: "read",
@@ -135,6 +145,17 @@ let MdPreviewService = (() => {
135
145
  },
136
146
  metadata: _metadata
137
147
  }, null, _instanceExtraInitializers);
148
+ __esDecorate(this, null, _search_decorators, {
149
+ kind: "method",
150
+ name: "search",
151
+ static: false,
152
+ private: false,
153
+ access: {
154
+ has: (obj) => "search" in obj,
155
+ get: (obj) => obj.search
156
+ },
157
+ metadata: _metadata
158
+ }, null, _instanceExtraInitializers);
138
159
  if (_metadata) Object.defineProperty(this, Symbol.metadata, {
139
160
  enumerable: true,
140
161
  configurable: true,
@@ -262,6 +283,78 @@ let MdPreviewService = (() => {
262
283
  };
263
284
  }
264
285
  /**
286
+ * Search previewable documents by name across the whole session workspace,
287
+ * including directories the browser face never expanded. First-version
288
+ * semantics: case-insensitive substring match on the entry name. The walk
289
+ * stays inside the session authority (root resolve + containment, visited
290
+ * dedupe so links cannot loop it), never reads file bodies, and reports
291
+ * honestly whether the answer is complete — a permission failure, a
292
+ * traversal bound, or the result cap each mark it incomplete with the
293
+ * matching limit.
294
+ * @param sessionId - owning session; its header cwd roots the walk.
295
+ * @param query - raw search text; matched case-insensitively against names.
296
+ * @param signal - caller cancellation carried through every fs call.
297
+ * @returns the query, its matches with workspace-relative paths, and
298
+ * whether the walk finished whole.
299
+ * @throws RemoteError with a stable MdPreview failure code.
300
+ */
301
+ async search(sessionId, query, signal) {
302
+ const q = query.trim().toLowerCase();
303
+ if (q.length === 0) throw failure("md-preview/bad-request", "mdPreview/search requires a non-empty query");
304
+ const { root } = await this.resolveContainedTarget(sessionId, "", signal, "search");
305
+ const rootPath = root.displayPath;
306
+ const previewable = [...this.config.allowedExtensions, ...this.config.previewExtensions];
307
+ const matches = [];
308
+ const limits = /* @__PURE__ */ new Set();
309
+ const visited = /* @__PURE__ */ new Set([rootPath]);
310
+ let frontier = [root];
311
+ while (frontier.length > 0) {
312
+ if (signal.aborted) throw new DOMException("aborted", "AbortError");
313
+ const batch = frontier.splice(0, this.config.searchConcurrency);
314
+ const listings = await Promise.all(batch.map(async (dir) => {
315
+ try {
316
+ return { entries: await this.ctx.fs.listDir(dir, signal) };
317
+ } catch (error) {
318
+ if (signal.aborted) throw error;
319
+ return { entries: null };
320
+ }
321
+ }));
322
+ for (const { entries } of listings) {
323
+ if (entries === null) {
324
+ limits.add("directory-failure");
325
+ continue;
326
+ }
327
+ for (const entry of entries) {
328
+ if (entry.type === "file" && entry.name.toLowerCase().includes(q) && isAllowedExtension(entry.name, previewable)) matches.push({
329
+ name: entry.name,
330
+ path: workspaceRelative(rootPath, entry.target.displayPath, entry.name)
331
+ });
332
+ if (entry.type !== "directory") continue;
333
+ const child = entry.target.displayPath;
334
+ if (visited.has(child) || !this.ctx.fs.contains(root, entry.target)) continue;
335
+ if (visited.size >= this.config.searchMaxDirectories) {
336
+ limits.add("traversal-limit");
337
+ continue;
338
+ }
339
+ visited.add(child);
340
+ frontier.push(entry.target);
341
+ }
342
+ }
343
+ if (matches.length >= this.config.searchMaxResults) {
344
+ limits.add("result-limit");
345
+ matches.length = this.config.searchMaxResults;
346
+ break;
347
+ }
348
+ }
349
+ matches.sort((a, b) => a.path.localeCompare(b.path));
350
+ return {
351
+ query,
352
+ matches,
353
+ complete: limits.size === 0,
354
+ limits: [...limits]
355
+ };
356
+ }
357
+ /**
265
358
  * The shared authority preamble of the remote methods: resolve a path to a
266
359
  * live, contained, regular workspace file, or reject it with the stable
267
360
  * failure code. One home for the check order and the aborted-rethrow
@@ -329,7 +422,10 @@ let MdPreviewService = (() => {
329
422
  const Config = z.object({
330
423
  maxBytes: z.number().min(1).default(DEFAULT_MAX_BYTES),
331
424
  allowedExtensions: z.array(z.string()).default([...DEFAULT_ALLOWED_EXTENSIONS]),
332
- previewExtensions: z.array(z.string()).default([...DEFAULT_PREVIEW_EXTENSIONS])
425
+ previewExtensions: z.array(z.string()).default([...DEFAULT_PREVIEW_EXTENSIONS]),
426
+ searchMaxResults: z.number().min(1).default(200),
427
+ searchMaxDirectories: z.number().min(1).default(DEFAULT_SEARCH_MAX_DIRECTORIES),
428
+ searchConcurrency: z.number().min(1).default(8)
333
429
  });
334
430
  //#endregion
335
431
  //#region src/protocol.ts
@@ -363,11 +459,14 @@ const inject = [
363
459
  function apply(ctx, config = {
364
460
  maxBytes: DEFAULT_MAX_BYTES,
365
461
  allowedExtensions: [...DEFAULT_ALLOWED_EXTENSIONS],
366
- previewExtensions: [...DEFAULT_PREVIEW_EXTENSIONS]
462
+ previewExtensions: [...DEFAULT_PREVIEW_EXTENSIONS],
463
+ searchMaxResults: 200,
464
+ searchMaxDirectories: DEFAULT_SEARCH_MAX_DIRECTORIES,
465
+ searchConcurrency: 8
367
466
  }) {
368
467
  ctx.plugin(MdPreviewService, config);
369
468
  }
370
469
  //#endregion
371
- export { Config, DEFAULT_ALLOWED_EXTENSIONS, DEFAULT_MAX_BYTES, DEFAULT_PREVIEW_EXTENSIONS, MD_PREVIEW_FAILURE_CODES, MdPreviewService, apply, inject, isAllowedExtension, name };
470
+ export { Config, DEFAULT_ALLOWED_EXTENSIONS, DEFAULT_MAX_BYTES, DEFAULT_PREVIEW_EXTENSIONS, DEFAULT_SEARCH_CONCURRENCY, DEFAULT_SEARCH_MAX_DIRECTORIES, DEFAULT_SEARCH_MAX_RESULTS, MD_PREVIEW_FAILURE_CODES, MdPreviewService, apply, inject, isAllowedExtension, name };
372
471
 
373
472
  //# sourceMappingURL=index.js.map
@@ -38,6 +38,21 @@ const listResult$schema = z.object({
38
38
  path: z.string(),
39
39
  entries: z.array(listEntry$schema)
40
40
  });
41
+ const searchQuery$schema = z.string().min(1);
42
+ const searchMatch$schema = z.object({
43
+ name: z.string(),
44
+ path: z.string()
45
+ });
46
+ const searchResult$schema = z.object({
47
+ query: z.string(),
48
+ matches: z.array(searchMatch$schema),
49
+ complete: z.boolean(),
50
+ limits: z.array(z.enum([
51
+ "directory-failure",
52
+ "traversal-limit",
53
+ "result-limit"
54
+ ]))
55
+ });
41
56
  /** The MdPreview contribution mounted by this package's browser entry. */
42
57
  const TYPERT_REMOTE = {
43
58
  package: "@benz-ai-x/dsh-md-preview",
@@ -170,6 +185,38 @@ const TYPERT_REMOTE = {
170
185
  typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/list:result",
171
186
  schema: listResult$schema
172
187
  }
188
+ },
189
+ {
190
+ id: "@benz-ai-x/dsh-md-preview#mdPreview/search",
191
+ service: "mdPreview",
192
+ namespace: "mdPreview",
193
+ method: "search",
194
+ invocation: { kind: "direct" },
195
+ parameters: [{
196
+ name: "sessionId",
197
+ wire: "sessionId",
198
+ source: "json",
199
+ codec: {
200
+ mode: "strict",
201
+ typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
202
+ schema: readSessionId$schema
203
+ }
204
+ }, {
205
+ name: "query",
206
+ wire: "query",
207
+ source: "json",
208
+ codec: {
209
+ mode: "strict",
210
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/search:query",
211
+ schema: searchQuery$schema
212
+ }
213
+ }],
214
+ cancellation: { parameter: "signal" },
215
+ result: {
216
+ mode: "strict",
217
+ typeSymbol: "@benz-ai-x/dsh-md-preview#mdPreview/search:result",
218
+ schema: searchResult$schema
219
+ }
173
220
  }
174
221
  ]
175
222
  };
@@ -0,0 +1,7 @@
1
+ /** One 16px icon family for document identity, tree rows and quick entries. */
2
+ export declare function DocumentIcon({ kind, expanded, className }: {
3
+ kind?: 'directory' | 'markdown' | 'image' | 'text' | 'file';
4
+ expanded?: boolean;
5
+ className?: string;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=DocumentIcon.d.ts.map
@@ -0,0 +1,10 @@
1
+ /** The panel relationship shared by the Session Header entry and its close control. */
2
+ export declare const PREVIEW_PANEL_ID = "dsh-md-preview-panel";
3
+ /** Distinct native glyphs for the document entry and close action, with localized next-action labels. */
4
+ export declare function PanelToggle({ open, label, onClick, entry }: {
5
+ open: boolean;
6
+ label: string;
7
+ onClick(): void;
8
+ entry?: boolean;
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=PanelToggle.d.ts.map
@@ -1,14 +1,19 @@
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
3
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
4
- import type { MdPreviewFile, MdPreviewListResult, MdPreviewWriteResult } from '../protocol.ts';
4
+ import type { MdPreviewFile, MdPreviewListResult, MdPreviewSearchResult, MdPreviewWriteResult } from '../protocol.ts';
5
5
  import type { MdPreviewState, MdPreviewTarget } from './preview-state.ts';
6
+ import type { LeaveIntentSeat } from './leave-intent.ts';
7
+ import type { ReadingStore } from './reading.ts';
8
+ import type { PanelPreferenceStore } from './preferences.ts';
6
9
  /** Read/write/list RPCs and panel dismissal, created in the plugin's apply world. */
7
10
  export interface PreviewOverlayInjected {
8
11
  hooks: {
9
12
  /** Current preview target; null while the panel is closed. */
10
13
  previewTarget: SnapshotStore<MdPreviewState>;
11
14
  };
15
+ /** The common leave-intent entry every plugin outlet shares (#21). */
16
+ leave: LeaveIntentSeat;
12
17
  /** Dismiss the panel and drop the target. */
13
18
  close(): void;
14
19
  /** Set the preview target (the browser face's file-open handoff). */
@@ -19,13 +24,25 @@ export interface PreviewOverlayInjected {
19
24
  write(sessionId: SessionId, path: string, content: string, fingerprint: string | undefined, force: boolean, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewWriteResult>>;
20
25
  /** One workspace directory listing; blank path lists the root. */
21
26
  list(sessionId: SessionId, path: string, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewListResult>>;
27
+ /** One workspace document search; the transport carries the AbortSignal. */
28
+ search(sessionId: SessionId, query: string, signal: AbortSignal): Promise<import('@deepseek-ai/dsh-typert-protocol').RemoteResult<MdPreviewSearchResult>>;
29
+ /** The reading record store (#25): per-(session, path) positions. */
30
+ reading: ReadingStore;
31
+ /** The current-turn outputs seat (#31): the session header's published
32
+ * view of the newest turn's produced documents. */
33
+ turnOutputs?: SnapshotStore<{
34
+ readonly sessionId: SessionId;
35
+ readonly paths: readonly string[];
36
+ } | null>;
37
+ /** The panel preference record (#26): manual geometry and navigation choices. */
38
+ preferences: PanelPreferenceStore;
22
39
  }
23
40
  /** Full composed panel props. */
24
41
  export type PreviewOverlayProps = PropsRuntime<'shell.overlay'> & InjectFace<PreviewOverlayInjected> & PropsLocale<'md-preview'>;
25
42
  /**
26
43
  * Render the preview panel for the current target.
27
- * @param props - target hook, read/write RPCs, dismissal, and the locale seat.
28
- * @returns the docked panel, or null while closed.
44
+ * @param props - target hook, leave seat, read/write RPCs, dismissal, and the locale seat.
45
+ * @returns the docked panel and its hidden shell anchor.
29
46
  */
30
- export declare function PreviewOverlay({ usePreviewTarget, close, setTarget, read, write, list, t }: PreviewOverlayProps): import("react").JSX.Element | null;
47
+ export declare function PreviewOverlay({ usePreviewTarget, useSessions, leave, close, setTarget, read, write, list, search, reading, turnOutputs, preferences, t }: PreviewOverlayProps): import("react").JSX.Element;
31
48
  //# sourceMappingURL=PreviewOverlay.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
2
2
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
3
- import type { MdPreviewEntry, MdPreviewListResult } from '../protocol.ts';
4
- /** Listing RPC and the file-open handoff, created in the plugin's apply world. */
3
+ import type { MdPreviewListResult, MdPreviewSearchResult } from '../protocol.ts';
4
+ /** Listing RPC, the search RPC, and the file-open handoff, created in the plugin's apply world. */
5
5
  export interface WorkspaceBrowserProps {
6
6
  /** Owning session whose workspace the tree roots at. */
7
7
  sessionId: SessionId;
@@ -9,28 +9,41 @@ export interface WorkspaceBrowserProps {
9
9
  active: boolean;
10
10
  /** One directory listing; the transport carries the AbortSignal. */
11
11
  list(sessionId: SessionId, path: string, signal: AbortSignal): Promise<RemoteResult<MdPreviewListResult>>;
12
+ /** One workspace document search; the transport carries the AbortSignal. */
13
+ search(sessionId: SessionId, query: string, signal: AbortSignal): Promise<RemoteResult<MdPreviewSearchResult>>;
12
14
  /** Open one file as the panel's preview target. */
13
15
  onOpenFile(path: string): void;
16
+ /** Previewable documents the session's newest turn produced (#31); null
17
+ * while no owning-session facts were published, empty when it produced
18
+ * none — the section hides either way. */
19
+ turnOutputs?: readonly string[] | null;
20
+ /** The session's recently read documents, recency-first (#31). */
21
+ recentReads?: ReadonlyArray<{
22
+ readonly path: string;
23
+ }>;
14
24
  /** Workspace-relative path of the current preview target, if any. */
15
25
  currentPath: string | null;
26
+ /** Owning session's cwd, supplied by the platform selector, for display only. */
27
+ workspaceRoot?: string | undefined;
28
+ /** The session's continue-reading target, or null without a record (#28). */
29
+ continueTarget?: {
30
+ readonly path: string;
31
+ } | null;
32
+ /** Open the continue-reading target through the unified leave entry (#28). */
33
+ onContinue?: () => void;
16
34
  /** Locale seat. */
17
35
  t(key: string): string;
18
36
  }
37
+ /** How long the search box waits for typing to settle before one RPC (#30). */
38
+ export declare const SEARCH_DEBOUNCE_MS = 200;
39
+ /** Quick entries stay few (#31): the caps each section shows. */
40
+ export declare const QUICK_TURN_MAX = 5;
41
+ export declare const QUICK_RECENT_MAX = 3;
19
42
  /**
20
- * Entries visible under a filter query (#13): a name match, or a loaded
21
- * descendant match, keeps an entry. A name-matched directory keeps its
22
- * whole subtree (the caller renders children unfiltered from there).
23
- * @param entries - one directory level, in listing order.
24
- * @param query - the raw filter text; blank shows everything.
25
- * @param childrenOf - resolves a loaded directory's entries (undefined
26
- * while unloaded or failed).
27
- * @returns the entries this level should render.
43
+ * Render the workspace tree (and, while a query is live, the search results)
44
+ * for one session.
45
+ * @param props - session identity, listing/search RPCs, file-open handoff, locale seat.
46
+ * @returns the browse area's tree/search elements.
28
47
  */
29
- export declare function filterEntries(entries: readonly MdPreviewEntry[], query: string, childrenOf: (path: string) => readonly MdPreviewEntry[] | undefined): readonly MdPreviewEntry[];
30
- /**
31
- * Render the workspace tree for one session.
32
- * @param props - session identity, listing RPC, file-open handoff, locale seat.
33
- * @returns the tree element.
34
- */
35
- export declare function WorkspaceBrowser({ sessionId, active, list, onOpenFile, currentPath, t }: WorkspaceBrowserProps): import("react").JSX.Element;
48
+ export declare function WorkspaceBrowser({ sessionId, active, list, search, onOpenFile, turnOutputs, recentReads, currentPath, workspaceRoot, continueTarget, onContinue, t }: WorkspaceBrowserProps): import("react").JSX.Element;
36
49
  //# sourceMappingURL=WorkspaceBrowser.d.ts.map
@@ -1,23 +1,35 @@
1
1
  /**
2
- * The header browse action: a file-tree button in the conversation header
3
- * that opens the right preview panel on its browse face — the workspace
4
- * tree shows first, and opening a file reads it in the document face.
2
+ * The document-sidebar toggle: a Session Header utility sitting next to
3
+ * the shipped Session-log download capsule. One entry with one meaning
4
+ * (#21): closed enters workspace browsing (the tree face), open requests the
5
+ * collapse — both through the common leave-intent entry, so a dirty draft is
6
+ * asked about before the panel folds. The panel owns docking and carries
7
+ * an X close control while the paperclip header entry stays mounted.
5
8
  */
6
- import type { ReactElement } from 'react';
9
+ import { type ReactElement } from 'react';
10
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
7
11
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
8
12
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
9
- /** The composed props of the header action: slot kit + inject face + locale. */
10
- export type WorkspaceDocsActionProps = PropsRuntime<'conversation.session.header.actions'> & InjectFace<{
11
- setTarget(target: {
12
- sessionId: SessionId;
13
- path: string;
14
- face: 'browse';
15
- }): void;
16
- }> & PropsLocale<'md-preview'>;
13
+ import type { MdPreviewState } from './preview-state.ts';
14
+ import type { LeaveIntentSeat } from './leave-intent.ts';
15
+ import { type TurnOutputsSnapshot } from './turn-files.ts';
16
+ /** The composed props of the header utility: slot kit + inject face + locale. */
17
+ export type WorkspaceDocsActionProps = PropsRuntime<'conversation.session.header.utilities'> & InjectFace<{
18
+ hooks: {
19
+ previewTarget: SnapshotStore<MdPreviewState>;
20
+ };
21
+ leave: LeaveIntentSeat;
22
+ /** Publish the session's current-turn outputs into the shared seat (#31). */
23
+ publishTurnOutputs?(sessionId: SessionId, paths: readonly string[]): void;
24
+ }> & PropsLocale<'md-preview'> & {
25
+ /** The session binding's chat selector hook (ui-chat's standard source);
26
+ * absent when that plugin is not composed — nothing publishes then. */
27
+ useChat?: <T>(selector: (snapshot: TurnOutputsSnapshot) => T) => T;
28
+ };
17
29
  /**
18
- * Render the header browse button for one session.
30
+ * Render the document sidebar toggle for the showing Session.
19
31
  * @param props - the composed action props.
20
- * @returns the header button.
32
+ * @returns the header utility button.
21
33
  */
22
- export declare function WorkspaceDocsAction(props: WorkspaceDocsActionProps): ReactElement;
34
+ export declare function WorkspaceDocsAction({ sessionId, usePreviewTarget, leave, publishTurnOutputs, useChat, t }: WorkspaceDocsActionProps): ReactElement;
23
35
  //# sourceMappingURL=WorkspaceDocsAction.d.ts.map
@@ -0,0 +1,6 @@
1
+ /** Display-only paths. Opening, saving and reading records keep the original identity. */
2
+ /** Remove the owning session's known workspace prefix, never an inferred root. */
3
+ export declare function workspaceDisplayPath(path: string, workspaceRoot?: string): string;
4
+ /** Parent context without repeating the document name on the second line. */
5
+ export declare function documentParent(path: string): string;
6
+ //# sourceMappingURL=document-path.d.ts.map
@@ -0,0 +1,31 @@
1
+ import type { MdPreviewTarget } from './preview-state.ts';
2
+ /** One requested way out of the current preview session. */
3
+ export type MdPreviewLeaveIntent = {
4
+ readonly kind: 'close';
5
+ } | {
6
+ readonly kind: 'switchFace';
7
+ } | {
8
+ readonly kind: 'open';
9
+ readonly target: MdPreviewTarget;
10
+ };
11
+ /** The leave-intent seat shared by every entry and the guarding panel. */
12
+ export interface LeaveIntentSeat {
13
+ /**
14
+ * Request one leave. Ignored while an earlier intent still waits behind
15
+ * the guard — the pending target cannot be silently replaced.
16
+ * @param intent - the requested close, face switch, or open.
17
+ */
18
+ request(intent: MdPreviewLeaveIntent): void;
19
+ /** Drop the pending intent (「继续编辑」, or after execution). */
20
+ clear(): void;
21
+ /** Snapshot-store subscription face for the panel's binding. */
22
+ subscribe(listener: () => void): () => void;
23
+ /** The pending intent, or null while none waits. */
24
+ getSnapshot(): MdPreviewLeaveIntent | null;
25
+ }
26
+ /**
27
+ * Create the leave-intent seat for one plugin mount.
28
+ * @returns the seat to inject into the entries and the preview panel.
29
+ */
30
+ export declare function createLeaveIntentSeat(): LeaveIntentSeat;
31
+ //# sourceMappingURL=leave-intent.d.ts.map
@@ -5,11 +5,15 @@ export declare const NS = "md-preview";
5
5
  export declare const zh: {
6
6
  'panel.title': string;
7
7
  'panel.close': string;
8
+ 'panel.maximize': string;
9
+ 'panel.restore': string;
10
+ 'panel.pickFile': string;
8
11
  'panel.loading': string;
9
12
  'panel.empty': string;
10
13
  'panel.error': string;
11
14
  'panel.unsupported': string;
12
15
  'panel.retry': string;
16
+ 'panel.failBrowse': string;
13
17
  'panel.edit': string;
14
18
  'panel.view': string;
15
19
  'panel.face': string;
@@ -20,9 +24,12 @@ export declare const zh: {
20
24
  'panel.redo': string;
21
25
  'status.chars': string;
22
26
  'status.saved': string;
27
+ 'status.saving': string;
28
+ 'status.saveFailed': string;
23
29
  'status.unsaved': string;
24
30
  'status.clean': string;
25
31
  'panel.keys': string;
32
+ 'panel.more': string;
26
33
  'keys.bold': string;
27
34
  'keys.italic': string;
28
35
  'keys.link': string;
@@ -37,8 +44,10 @@ export declare const zh: {
37
44
  'warn.dismiss': string;
38
45
  'panel.saved': string;
39
46
  'panel.saveError': string;
47
+ 'panel.saved.readFailed': string;
40
48
  'panel.save.retry': string;
41
49
  'panel.conflict.title': string;
50
+ 'panel.conflict.hint': string;
42
51
  'panel.conflict.reload': string;
43
52
  'panel.conflict.force': string;
44
53
  'panel.unsaved.title': string;
@@ -51,11 +60,25 @@ export declare const zh: {
51
60
  'browse.error': string;
52
61
  'browse.retry': string;
53
62
  'browse.refresh': string;
54
- 'browse.filter': string;
55
- 'browse.noMatch': string;
63
+ 'browse.search': string;
64
+ 'search.label': string;
65
+ 'search.clear': string;
66
+ 'search.results': string;
67
+ 'search.searching': string;
68
+ 'search.none': string;
69
+ 'search.failed': string;
70
+ 'search.retry': string;
71
+ 'search.incomplete': string;
72
+ 'search.limit.directory-failure': string;
73
+ 'search.limit.traversal-limit': string;
74
+ 'search.limit.result-limit': string;
56
75
  'rail.files': string;
57
76
  'rail.outline': string;
58
77
  'rail.noHeadings': string;
78
+ 'continue.read': string;
79
+ 'quick.turn': string;
80
+ 'quick.recent': string;
81
+ 'quick.open': string;
59
82
  'outline.open': string;
60
83
  'find.status': string;
61
84
  'find.phrases.find': string;
@@ -73,6 +96,7 @@ export declare const zh: {
73
96
  'chip.open': string;
74
97
  'action.label': string;
75
98
  'dock.browse': string;
99
+ 'dock.expand': string;
76
100
  'action.open': string;
77
101
  copy: string;
78
102
  copied: string;
@@ -17,6 +17,8 @@ export interface OutlineEntry {
17
17
  * @returns ATX headings in order; fenced code is not scanned.
18
18
  */
19
19
  export declare function extractOutline(content: string): readonly OutlineEntry[];
20
+ /** Whitespace-collapsed comparable form of a rendered heading's text. */
21
+ export declare function comparable(text: string): string;
20
22
  /**
21
23
  * Resolve the rendered DOM heading of one outline entry.
22
24
  * @param container - the rendered document container.
@@ -0,0 +1,32 @@
1
+ /** Reversible layout adapter for the pinned shell.overlay DOM (ADR-0004). */
2
+ export interface PanelDockRequest {
3
+ readonly open: boolean;
4
+ readonly width: number;
5
+ readonly maximized: boolean;
6
+ }
7
+ export interface PanelDockBounds {
8
+ readonly left: number;
9
+ readonly top: number;
10
+ readonly height: number;
11
+ readonly width: number;
12
+ readonly reserved: number;
13
+ }
14
+ /** Resolve presentation geometry without changing the reader's width preference. */
15
+ export declare function panelDockGeometry(available: number, requested: number, open: boolean, maximized: boolean): {
16
+ width: number;
17
+ reserved: number;
18
+ };
19
+ /** Ownership handle: frame style, geometry observations, and scheduled work share one lifetime. */
20
+ export interface PanelDock {
21
+ /** Outside the clipping frame, inside the host root's inert/modal boundary. */
22
+ readonly container: HTMLElement;
23
+ update(request: PanelDockRequest): void;
24
+ dispose(): void;
25
+ }
26
+ /**
27
+ * Locate only the frame enclosing our own shell contribution. No host DOM is
28
+ * moved and no feature store or slot occupant is replaced. An unknown mount
29
+ * keeps the original overlay presentation instead of guessing a frame.
30
+ */
31
+ export declare function attachPanelDock(anchor: HTMLElement, report: (bounds: PanelDockBounds) => void): PanelDock | null;
32
+ //# sourceMappingURL=panel-dock.d.ts.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The panel preference record (#26): what lets the reader's own reading
3
+ * space survive closes and revisits. UI-local viewing preferences only —
4
+ * the dragged panel width, the rail width, the rail collapse choice, and
5
+ * the per-session files/outline choice; never file content, drafts, or
6
+ * session facts. The envelope is versioned, every loaded value validated,
7
+ * remembered sessions bounded, and every failing storage call degrades to
8
+ * the in-memory copy — a first open never lands on a bogus zero width.
9
+ */
10
+ import type { StorageLike } from './reading.ts';
11
+ /** Envelope format version; an unknown version means an old, unreadable record. */
12
+ export declare const PANEL_PREFERENCE_VERSION = 1;
13
+ /** Panel width support range (the overlay's drag bounds). */
14
+ export declare const PANEL_MIN_WIDTH = 360;
15
+ export declare const PANEL_MAX_WIDTH = 1200;
16
+ export declare const PANEL_DEFAULT_WIDTH = 720;
17
+ /** Rail width support range (the rail's drag bounds). */
18
+ export declare const RAIL_MIN_WIDTH = 120;
19
+ export declare const RAIL_MAX_WIDTH = 320;
20
+ export declare const RAIL_DEFAULT_WIDTH = 220;
21
+ /** Which rail mini-tab the panel shows. */
22
+ export type RailTab = 'files' | 'outline';
23
+ /** The geometry facts a reader may have manually chosen (null = never chosen). */
24
+ export interface PanelGeometryPreferences {
25
+ readonly panelWidth: number | null;
26
+ readonly railWidth: number | null;
27
+ readonly railCollapsed: boolean | null;
28
+ }
29
+ /** The panel-facing preference store. */
30
+ export interface PanelPreferenceStore {
31
+ /** The remembered geometry (validated; nulls where nothing valid lives). */
32
+ geometry(): PanelGeometryPreferences;
33
+ /** Record one or more manual geometry choices (later values win per field). */
34
+ recordGeometry(partial: Partial<PanelGeometryPreferences>): void;
35
+ /** The remembered files/outline choice of one session, or null. */
36
+ railTab(sessionId: string): RailTab | null;
37
+ /** Record one session's files/outline choice. */
38
+ recordRailTab(sessionId: string, tab: RailTab): void;
39
+ }
40
+ /** An in-memory StorageLike for tests and for browsers without storage. */
41
+ export declare function createMemoryStorage(): StorageLike;
42
+ /** The browser's localStorage when reachable, else null (memory fallback). */
43
+ export declare function browserStorage(): StorageLike | null;
44
+ /**
45
+ * Create the panel preference store over one storage backend.
46
+ * @param storage - the persistence face; failing calls degrade to memory.
47
+ */
48
+ export declare function createPanelPreferenceStore(storage: StorageLike): PanelPreferenceStore;
49
+ /**
50
+ * The width to open the panel at: the stored manual width when one exists,
51
+ * else the default preset — both clamped into the support range and the
52
+ * live viewport, so a shrunken screen never overflows and an absent value
53
+ * never lands on a bogus minimum.
54
+ * @param stored - the remembered manual width, or null when never chosen.
55
+ * @param viewportWidth - the live viewport width (0 = unknown).
56
+ */
57
+ export declare function clampPanelWidth(stored: number | null, viewportWidth: number): number;
58
+ /**
59
+ * The rail width to open at (the same treatment on the rail's own range).
60
+ * @param stored - the remembered manual rail width, or null.
61
+ */
62
+ export declare function clampRailWidth(stored: number | null): number;
63
+ //# sourceMappingURL=preferences.d.ts.map