@bendyline/docblocks 2.7.5 → 2.7.7

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.
@@ -14,7 +14,7 @@ This notice is included in the published npm tarball. Transitive packages instal
14
14
 
15
15
  | Package | Version | License | Source |
16
16
  | --- | --- | --- | --- |
17
- | @bendyline/squisq | 2.13.0 | MIT | https://github.com/bendyline/squisq |
17
+ | @bendyline/squisq | 2.13.3 | MIT | https://github.com/bendyline/squisq |
18
18
  | @bendyline/squisq-graph | 2.13.0 | MIT | https://github.com/bendyline/squisq |
19
19
  | @types/debug | 4.1.12 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped |
20
20
  | @types/hast | 3.0.4 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped |
@@ -133,7 +133,7 @@ This notice is included in the published npm tarball. Transitive packages instal
133
133
  Package-local license, copying, and notice files are reproduced below. Where a published package omits its repository license, the reviewed aggregate or README identified in `Source files` supplies the retained material.
134
134
 
135
135
  ==============================================================================
136
- Components: @bendyline/squisq-graph@2.13.0, @bendyline/squisq@2.13.0
136
+ Components: @bendyline/squisq-graph@2.13.0, @bendyline/squisq@2.13.3
137
137
  Source files: node_modules/@bendyline/squisq-graph/LICENSE, node_modules/@bendyline/squisq/LICENSE
138
138
  ==============================================================================
139
139
  MIT License
@@ -189,7 +189,7 @@ repository license, the pinned upstream copy is vendored and identified below.
189
189
  COMPONENTS
190
190
 
191
191
  ==============================================================================
192
- Components: @bendyline/squisq@2.13.0
192
+ Components: @bendyline/squisq@2.13.3
193
193
  Source files: node_modules/@bendyline/squisq/NOTICE.md
194
194
  ==============================================================================
195
195
  # Third-Party Notices for @bendyline/squisq
@@ -220,7 +220,7 @@ third-party license texts for bundled code and data are shipped in
220
220
  THIRD_PARTY_LICENSES.txt.
221
221
 
222
222
  ==============================================================================
223
- Components: @bendyline/squisq@2.13.0
223
+ Components: @bendyline/squisq@2.13.3
224
224
  Source files: node_modules/@bendyline/squisq/THIRD_PARTY_LICENSES.txt
225
225
  ==============================================================================
226
226
  THIRD-PARTY LICENSES FOR @bendyline/squisq
@@ -216,6 +216,17 @@ interface AiChatRequest {
216
216
  readonly maxTokens?: number;
217
217
  readonly purpose: AiChatPurpose;
218
218
  readonly responseFormat?: AiResponseFormat;
219
+ /**
220
+ * What to search knowledge catalogs for. Without it a provider searches with
221
+ * the last user message, which for a templated prompt is mostly document.
222
+ */
223
+ readonly knowledgeQuery?: string;
224
+ /**
225
+ * Number knowledge passages and ask the model to cite them as `[n]`. Only a
226
+ * caller that checks and renders citations should: the markers otherwise
227
+ * end up in its output.
228
+ */
229
+ readonly knowledgeCitations?: boolean;
219
230
  }
220
231
  interface AiChatCompletion {
221
232
  readonly text: string;
@@ -235,7 +246,46 @@ interface AiChatProgress {
235
246
  readonly outputTokens: number | null;
236
247
  readonly tokensPerSecond: number | null;
237
248
  }
249
+ /** One reference passage the model was given; its citation number is its position from 1. */
250
+ interface AiChatEvidence {
251
+ readonly title: string;
252
+ readonly catalogId: string;
253
+ readonly text: string;
254
+ }
255
+ /** One knowledge catalog that contributed reference passages to a request. */
256
+ interface AiChatKnowledgeSource {
257
+ readonly catalogId: string;
258
+ /** The catalog's display name, or its id when the provider did not name it. */
259
+ readonly name: string;
260
+ /** Distinct titles of the cited articles, best first. */
261
+ readonly titles: readonly string[];
262
+ }
263
+ /**
264
+ * What the person's enabled knowledge catalogs added to one request. A
265
+ * provider sends this at most once, before any text, and only when at least
266
+ * one catalog is enabled.
267
+ */
268
+ type AiChatKnowledge = {
269
+ readonly outcome: 'used';
270
+ readonly sources: readonly AiChatKnowledgeSource[];
271
+ /**
272
+ * The passages the model was given, in citation order: it was asked to
273
+ * cite `evidence[0]` as `[1]`. Its output can be checked against them.
274
+ */
275
+ readonly evidence: readonly AiChatEvidence[];
276
+ }
277
+ /** The catalogs were searched and nothing relevant came back. */
278
+ | {
279
+ readonly outcome: 'none';
280
+ }
281
+ /** The catalogs could not be consulted: retrieval failed or the prompt left no room. */
282
+ | {
283
+ readonly outcome: 'skipped';
284
+ };
238
285
  type AiChatEvent = {
286
+ readonly kind: 'knowledge';
287
+ readonly knowledge: AiChatKnowledge;
288
+ } | {
239
289
  readonly kind: 'progress';
240
290
  readonly progress: AiChatProgress;
241
291
  } | {
@@ -248,6 +298,40 @@ type AiChatEvent = {
248
298
  readonly kind: 'error';
249
299
  readonly error: AiError;
250
300
  };
301
+ /** Check AI output for specific details that nothing the model was shown supports. */
302
+ interface AiFactCheckRequest {
303
+ /** The output, as Markdown. */
304
+ readonly text: string;
305
+ /** Reference passage texts the model was given, `[1]` first. */
306
+ readonly evidence: readonly string[];
307
+ /**
308
+ * The model was asked to cite passages by number, and its numbers match
309
+ * `evidence`: check each sentence against what it cites. Otherwise each
310
+ * sentence is checked against all of the evidence and markers are ignored.
311
+ */
312
+ readonly citations: boolean;
313
+ /** What the person supplied: their instructions and document. Details found here need no source. */
314
+ readonly given: string;
315
+ }
316
+ interface AiFactCheckSentence {
317
+ /** The sentence without citation markers, possibly shortened. */
318
+ readonly text: string;
319
+ /** `bad-citation`: it cites a passage number the model was never given. */
320
+ readonly problem: 'unsourced' | 'bad-citation';
321
+ /** The names, dates, numbers or quotations no source shows. */
322
+ readonly missing: readonly string[];
323
+ /**
324
+ * A missing detail is a year, number, month or quotation. Names alone are a
325
+ * weaker signal: capitalized words are the noisiest part of the check.
326
+ */
327
+ readonly strong: boolean;
328
+ }
329
+ interface AiFactCheck {
330
+ /** Sentences that state something checkable. */
331
+ readonly checked: number;
332
+ /** Sentences stating details no source shows, strongest first. */
333
+ readonly unsourced: readonly AiFactCheckSentence[];
334
+ }
251
335
  interface AiChatHandle {
252
336
  readonly done: Promise<AiResult<AiChatCompletion>>;
253
337
  cancel(): void;
@@ -352,6 +436,11 @@ interface DocBlocksHostAiAPI {
352
436
  models(): Promise<AiResult<readonly AiModelInfo[]>>;
353
437
  /** Catalog models this host can download. Optional for older providers. */
354
438
  availableModels?(): Promise<AiResult<readonly AiModelDownloadInfo[]>>;
439
+ /**
440
+ * Find sentences whose names, dates, numbers or quotations appear in none of
441
+ * the evidence or the person's own text. Mechanical: no model call.
442
+ */
443
+ checkFacts?(request: AiFactCheckRequest): Promise<AiResult<AiFactCheck>>;
355
444
  /** Start a model download from an explicit user gesture. */
356
445
  installModel?(modelId: string, onProgress?: (progress: AiProgress) => void): AiModelInstallHandle;
357
446
  /**
@@ -887,7 +976,10 @@ interface DocBlocksHostShellAPI {
887
976
  revealInFolder?(workspaceId: string, workspacePath?: string): Promise<void>;
888
977
  /** Open a registered workspace root in Finder or the platform file manager. */
889
978
  openWorkspaceFolder?(workspaceId: string): Promise<void>;
890
- /** Open a URL in the default browser. */
979
+ /**
980
+ * Open an HTTP(S) URL in the default browser, or a `mailto:` URL to support
981
+ * in the default mail app. See `parseOpenExternalUrl`.
982
+ */
891
983
  openExternal(url: string): Promise<void>;
892
984
  }
893
985
  /** Native clipboard operations exposed through the trusted host boundary. */
@@ -1059,6 +1151,14 @@ interface DocBlocksHostExternalAPI {
1059
1151
  writeBinary(resourceId: string, data: ArrayBuffer | Uint8Array): Promise<void>;
1060
1152
  commitText(resourceId: string, content: string, expectedContent: string | null): Promise<FileCommitResult>;
1061
1153
  commitBinary(resourceId: string, data: ArrayBuffer | Uint8Array, expectedVersion: string | null): Promise<ExternalBinaryCommitResult>;
1154
+ /**
1155
+ * Read, never write, a media file inside the folder of a loose markdown
1156
+ * grant, so the document's relative image paths render. `relativePath` is a
1157
+ * canonical `WorkspacePath` from that folder naming an image, audio, or video
1158
+ * file; it cannot climb out or follow a symbolic link. Resolves null when
1159
+ * the file does not exist. Bundle grants carry their media and refuse this.
1160
+ */
1161
+ readRelativeMedia?(resourceId: string, relativePath: string): Promise<ArrayBuffer | null>;
1062
1162
  /** Revoke one external resource when its transient document is closed. */
1063
1163
  revoke(resourceId: string): Promise<void>;
1064
1164
  }
@@ -1213,6 +1313,8 @@ interface HostCapabilities {
1213
1313
  readonly exportRememberedTargets: boolean;
1214
1314
  readonly openSavedExports: boolean;
1215
1315
  readonly externalResources: boolean;
1316
+ /** A loose OS-opened file's relative images can be read from beside it. */
1317
+ readonly externalRelativeMedia: boolean;
1216
1318
  readonly openRequests: boolean;
1217
1319
  readonly menuCommands: boolean;
1218
1320
  readonly pinnedDocumentMirror: boolean;
@@ -1276,4 +1378,4 @@ declare function parseHostEnvironment(value: unknown): HostEnvironment | null;
1276
1378
  */
1277
1379
  declare function deriveHostCapabilities(host: unknown): HostCapabilities;
1278
1380
 
1279
- export { type ExternalBinaryCommitResult as $, type AiChatCompletion as A, type AiResult as B, type AiReviewMode as C, type AiSearchHit as D, type AiSearchQuery as E, type AiSearchResult as F, type AiSpeechAudio as G, type AiStatus as H, type AiSynthesizeRequest as I, type AiTranscribeRequest as J, type AiTranscript as K, type AiUnavailableReason as L, type DocBlocksHostAPI as M, type DocBlocksHostAiAPI as N, type DocBlocksHostClipboardAPI as O, type DocBlocksHostExportAPI as P, type DocBlocksHostExternalAPI as Q, type DocBlocksHostFfmpegAPI as R, type DocBlocksHostFsAPI as S, type DocBlocksHostGitAPI as T, type DocBlocksHostLifecycleAPI as U, type DocBlocksHostMenuAPI as V, type DocBlocksHostShellAPI as W, type DocBlocksHostSpeechAPI as X, type DocBlocksHostUpdaterAPI as Y, type DocBlocksHostWorkspacesAPI as Z, type ElectronWorkspaceInfo as _, type AiChatEvent as a, type GitBranchInfo as a0, type GitCapabilities as a1, type GitCloneHandle as a2, type GitCloneProgress as a3, type GitError as a4, type GitErrorCode as a5, type GitFileAtRevision as a6, type GitFileChange as a7, type GitFileStatusCode as a8, type GitLogEntry as a9, type SpeechModelInfo as aA, type SpeechModelKind as aB, type SpeechPreferences as aC, type SpeechPreferencesPatch as aD, type SpeechProgress as aE, type SpeechReadiness as aF, type SpeechReadinessState as aG, type SpeechResult as aH, type SpeechSegment as aI, type SpeechStatus as aJ, type SpeechSynthesisHandle as aK, type SpeechSynthesisSummary as aL, type SpeechSynthesizeEvent as aM, type SpeechSynthesizeRequest as aN, type SpeechTranscribeRequest as aO, type SpeechTranscript as aP, type SpeechVoiceInfo as aQ, type SpeechWordTiming as aR, type UpdateCheckResult as aS, type UpdateInstallResult as aT, type UpdaterStatus as aU, deriveHostCapabilities as aV, parseAiKnowledgeAction as aW, parseAiKnowledgeState as aX, parseHostEnvironment as aY, type GitLogOptions as aa, type GitRemoteInfo as ab, type GitRepoDetection as ac, type GitResult as ad, type GitRevision as ae, type GitStatus as af, HOST_PLATFORMS as ag, HOST_SURFACE_KINDS as ah, type HostCapabilities as ai, type HostCloseReason as aj, type HostEnvironment as ak, type HostExportTargetGrant as al, type HostPinnedDocument as am, type HostPlatform as an, type HostPrepareCloseRequest as ao, type HostPrepareCloseResult as ap, type HostSurfaceKind as aq, type MenuCommand as ar, NO_HOST_CAPABILITIES as as, type OpenRequest as at, type SpeechAudioChunk as au, type SpeechCatalog as av, type SpeechError as aw, type SpeechErrorCode as ax, type SpeechInstallEvent as ay, type SpeechInstallHandle as az, type AiChatHandle as b, type AiChatMessage as c, type AiChatProgress as d, type AiChatPurpose as e, type AiChatRequest as f, type AiConnectionStep as g, type AiEnsureWorkspaceHandle as h, type AiError as i, type AiErrorCode as j, type AiImageRequest as k, type AiImageResult as l, type AiKnowledgeAPI as m, type AiKnowledgeAction as n, type AiKnowledgeCatalog as o, type AiKnowledgeState as p, type AiModelDownloadInfo as q, type AiModelFit as r, type AiModelInfo as s, type AiModelInstallEvent as t, type AiModelInstallHandle as u, type AiPreferences as v, type AiPreferencesPatch as w, type AiProgress as x, type AiProviderInfo as y, type AiResponseFormat as z };
1381
+ export { type DocBlocksHostMenuAPI as $, type AiChatCompletion as A, type AiModelInstallHandle as B, type AiPreferences as C, type AiPreferencesPatch as D, type AiProgress as E, type AiProviderInfo as F, type AiResponseFormat as G, type AiResult as H, type AiReviewMode as I, type AiSearchHit as J, type AiSearchQuery as K, type AiSearchResult as L, type AiSpeechAudio as M, type AiStatus as N, type AiSynthesizeRequest as O, type AiTranscribeRequest as P, type AiTranscript as Q, type AiUnavailableReason as R, type DocBlocksHostAPI as S, type DocBlocksHostAiAPI as T, type DocBlocksHostClipboardAPI as U, type DocBlocksHostExportAPI as V, type DocBlocksHostExternalAPI as W, type DocBlocksHostFfmpegAPI as X, type DocBlocksHostFsAPI as Y, type DocBlocksHostGitAPI as Z, type DocBlocksHostLifecycleAPI as _, type AiChatEvent as a, deriveHostCapabilities as a$, type DocBlocksHostShellAPI as a0, type DocBlocksHostSpeechAPI as a1, type DocBlocksHostUpdaterAPI as a2, type DocBlocksHostWorkspacesAPI as a3, type ElectronWorkspaceInfo as a4, type ExternalBinaryCommitResult as a5, type GitBranchInfo as a6, type GitCapabilities as a7, type GitCloneHandle as a8, type GitCloneProgress as a9, type SpeechAudioChunk as aA, type SpeechCatalog as aB, type SpeechError as aC, type SpeechErrorCode as aD, type SpeechInstallEvent as aE, type SpeechInstallHandle as aF, type SpeechModelInfo as aG, type SpeechModelKind as aH, type SpeechPreferences as aI, type SpeechPreferencesPatch as aJ, type SpeechProgress as aK, type SpeechReadiness as aL, type SpeechReadinessState as aM, type SpeechResult as aN, type SpeechSegment as aO, type SpeechStatus as aP, type SpeechSynthesisHandle as aQ, type SpeechSynthesisSummary as aR, type SpeechSynthesizeEvent as aS, type SpeechSynthesizeRequest as aT, type SpeechTranscribeRequest as aU, type SpeechTranscript as aV, type SpeechVoiceInfo as aW, type SpeechWordTiming as aX, type UpdateCheckResult as aY, type UpdateInstallResult as aZ, type UpdaterStatus as a_, type GitError as aa, type GitErrorCode as ab, type GitFileAtRevision as ac, type GitFileChange as ad, type GitFileStatusCode as ae, type GitLogEntry as af, type GitLogOptions as ag, type GitRemoteInfo as ah, type GitRepoDetection as ai, type GitResult as aj, type GitRevision as ak, type GitStatus as al, HOST_PLATFORMS as am, HOST_SURFACE_KINDS as an, type HostCapabilities as ao, type HostCloseReason as ap, type HostEnvironment as aq, type HostExportTargetGrant as ar, type HostPinnedDocument as as, type HostPlatform as at, type HostPrepareCloseRequest as au, type HostPrepareCloseResult as av, type HostSurfaceKind as aw, type MenuCommand as ax, NO_HOST_CAPABILITIES as ay, type OpenRequest as az, type AiChatEvidence as b, parseAiKnowledgeAction as b0, parseAiKnowledgeState as b1, parseHostEnvironment as b2, type AiChatHandle as c, type AiChatKnowledge as d, type AiChatKnowledgeSource as e, type AiChatMessage as f, type AiChatProgress as g, type AiChatPurpose as h, type AiChatRequest as i, type AiConnectionStep as j, type AiEnsureWorkspaceHandle as k, type AiError as l, type AiErrorCode as m, type AiFactCheck as n, type AiFactCheckRequest as o, type AiFactCheckSentence as p, type AiImageRequest as q, type AiImageResult as r, type AiKnowledgeAPI as s, type AiKnowledgeAction as t, type AiKnowledgeCatalog as u, type AiKnowledgeState as v, type AiModelDownloadInfo as w, type AiModelFit as x, type AiModelInfo as y, type AiModelInstallEvent as z };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RawIndexedDBFileSystemStore,
3
3
  indexedDBWorkspaceDatabaseName
4
- } from "./chunk-DAPFSEKY.js";
4
+ } from "./chunk-OPLU2CYA.js";
5
5
  import {
6
6
  withFileCommitLock
7
7
  } from "./chunk-DZB666V7.js";
@@ -1616,11 +1616,23 @@ var EXTENSION_MIME_MAP = {
1616
1616
  ".svg": "image/svg+xml",
1617
1617
  ".webp": "image/webp",
1618
1618
  ".avif": "image/avif",
1619
+ // Video and audio match the extensions Squisq inserts as players, so a file
1620
+ // dropped into a document lists as the same media it was inserted as.
1619
1621
  ".mp4": "video/mp4",
1622
+ ".m4v": "video/mp4",
1623
+ ".mov": "video/quicktime",
1620
1624
  ".webm": "video/webm",
1625
+ ".ogv": "video/ogg",
1626
+ ".mkv": "video/x-matroska",
1621
1627
  ".mp3": "audio/mpeg",
1628
+ ".m4a": "audio/mp4",
1629
+ ".aac": "audio/aac",
1622
1630
  ".wav": "audio/wav",
1623
- ".ogg": "audio/ogg"
1631
+ ".ogg": "audio/ogg",
1632
+ ".oga": "audio/ogg",
1633
+ ".opus": "audio/opus",
1634
+ ".flac": "audio/flac",
1635
+ ".weba": "audio/webm"
1624
1636
  };
1625
1637
  function guessMimeType(path) {
1626
1638
  const dot = path.lastIndexOf(".");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guessMimeType
3
- } from "./chunk-Q3OFZWJS.js";
3
+ } from "./chunk-3HUKGWDK.js";
4
4
  import {
5
5
  bytesEqual,
6
6
  compareSnapshotsByName,
@@ -26,6 +26,7 @@ import {
26
26
  WORKSPACE_ROOT,
27
27
  fsErrorFromUnknown,
28
28
  parseWorkspacePath,
29
+ tryParseWorkspacePath,
29
30
  workspacePathBasename,
30
31
  workspacePathContains,
31
32
  workspacePathDirname,
@@ -1111,7 +1112,7 @@ var FileSystemContentContainer = class {
1111
1112
  function stripExt(name) {
1112
1113
  return name.replace(/\.[^.]+$/, "");
1113
1114
  }
1114
- function createFileMediaProvider(container, markdownBasename) {
1115
+ function createFileMediaProvider(container, markdownBasename, options = {}) {
1115
1116
  const folder = stripExt(markdownBasename) + "_files";
1116
1117
  const prefix = folder + "/";
1117
1118
  const blobUrlCache = /* @__PURE__ */ new Map();
@@ -1122,35 +1123,63 @@ function createFileMediaProvider(container, markdownBasename) {
1122
1123
  const clean = ref.replace(/^\/+/, "");
1123
1124
  return clean.startsWith(prefix) ? clean : prefix + clean;
1124
1125
  }
1126
+ function fallbackSlot(key) {
1127
+ return "\0" + key;
1128
+ }
1125
1129
  function invalidate(key) {
1126
1130
  generations.set(key, (generations.get(key) ?? 0) + 1);
1127
- pending.delete(key);
1128
- const cached = blobUrlCache.get(key);
1129
- if (cached) URL.revokeObjectURL(cached);
1130
- blobUrlCache.delete(key);
1131
+ for (const slot of [key, fallbackSlot(key)]) {
1132
+ pending.delete(slot);
1133
+ const cached = blobUrlCache.get(slot);
1134
+ if (cached) URL.revokeObjectURL(cached);
1135
+ blobUrlCache.delete(slot);
1136
+ }
1131
1137
  }
1132
1138
  function resolveUrl(ref) {
1133
1139
  if (disposed) return Promise.resolve(ref);
1134
1140
  const key = toKey(ref);
1135
- const cached = blobUrlCache.get(key);
1141
+ const readDocumentRelative = options.readDocumentRelative;
1142
+ if (!readDocumentRelative || ref.startsWith("/") || ref.startsWith(prefix)) {
1143
+ return resolveSlot(ref, key, key, () => container.readFile(key));
1144
+ }
1145
+ const found = blobUrlCache.get(key) ?? blobUrlCache.get(fallbackSlot(key));
1146
+ if (found) return Promise.resolve(found);
1147
+ const resolveFallback = () => resolveSlot(
1148
+ ref,
1149
+ key,
1150
+ fallbackSlot(key),
1151
+ () => (
1152
+ // The fallback can only add resolutions, never new failures.
1153
+ readDocumentRelative(ref).catch(() => null)
1154
+ )
1155
+ );
1156
+ if (key.split("/").some((segment) => segment === "." || segment === "..")) {
1157
+ return resolveFallback();
1158
+ }
1159
+ return resolveSlot(ref, key, key, () => container.readFile(key)).then(
1160
+ (url) => url !== ref || disposed ? url : resolveFallback()
1161
+ );
1162
+ }
1163
+ function resolveSlot(ref, key, slot, read) {
1164
+ const cached = blobUrlCache.get(slot);
1136
1165
  if (cached) return Promise.resolve(cached);
1137
- const existing = pending.get(key);
1166
+ const existing = pending.get(slot);
1138
1167
  if (existing) {
1139
1168
  return existing.promise.then((url) => url === existing.ref ? ref : url);
1140
1169
  }
1141
1170
  const generation = generations.get(key) ?? 0;
1142
1171
  const work = (async () => {
1143
- const data = await container.readFile(key);
1172
+ const data = await read();
1144
1173
  if (disposed) return ref;
1145
1174
  if ((generations.get(key) ?? 0) !== generation) return resolveUrl(ref);
1146
1175
  if (!data) return ref;
1147
1176
  const url = URL.createObjectURL(new Blob([data], { type: guessMimeType(key) }));
1148
- blobUrlCache.set(key, url);
1177
+ blobUrlCache.set(slot, url);
1149
1178
  return url;
1150
1179
  })().finally(() => {
1151
- if (pending.get(key)?.promise === work) pending.delete(key);
1180
+ if (pending.get(slot)?.promise === work) pending.delete(slot);
1152
1181
  });
1153
- pending.set(key, { ref, promise: work });
1182
+ pending.set(slot, { ref, promise: work });
1154
1183
  return work;
1155
1184
  }
1156
1185
  return {
@@ -1194,6 +1223,35 @@ function createFileMediaProvider(container, markdownBasename) {
1194
1223
  };
1195
1224
  }
1196
1225
 
1226
+ // src/filesystem/document-relative-media.ts
1227
+ function isDocumentMediaPath(path) {
1228
+ return /^(?:image|audio|video)\//.test(guessMimeType(path));
1229
+ }
1230
+ function resolveDocumentRelativeMediaPath(documentDirectory, ref) {
1231
+ const directory = tryParseWorkspacePath(documentDirectory);
1232
+ if (directory === null) return null;
1233
+ const queryAt = ref.search(/[?#]/);
1234
+ const raw = (queryAt === -1 ? ref : ref.slice(0, queryAt)).trim();
1235
+ if (!raw || /^[a-z][a-z0-9+.-]*:/i.test(raw) || /^[\\/]/.test(raw)) return null;
1236
+ let decoded = raw;
1237
+ try {
1238
+ decoded = decodeURIComponent(raw);
1239
+ } catch {
1240
+ }
1241
+ const segments = directory ? directory.split("/") : [];
1242
+ for (const segment of decoded.replace(/\\/g, "/").split("/")) {
1243
+ if (segment === "" || segment === ".") continue;
1244
+ if (segment === "..") {
1245
+ if (segments.length === 0) return null;
1246
+ segments.pop();
1247
+ continue;
1248
+ }
1249
+ segments.push(segment);
1250
+ }
1251
+ const resolved = tryParseWorkspacePath(segments.join("/"));
1252
+ return resolved && isDocumentMediaPath(resolved) ? resolved : null;
1253
+ }
1254
+
1197
1255
  export {
1198
1256
  suggestWikiArticles,
1199
1257
  resolveWikiArticlePath,
@@ -1207,5 +1265,7 @@ export {
1207
1265
  createDbkWorkspaceSnapshot,
1208
1266
  replaceMemoryWorkspaceFromDbk,
1209
1267
  FileSystemContentContainer,
1210
- createFileMediaProvider
1268
+ createFileMediaProvider,
1269
+ isDocumentMediaPath,
1270
+ resolveDocumentRelativeMediaPath
1211
1271
  };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  HostFileSystemProviderV2
3
- } from "./chunk-EFGQUOTL.js";
3
+ } from "./chunk-NECMAJHS.js";
4
4
  import {
5
5
  ELECTRON_FILE_SYSTEM_V2_CAPABILITIES,
6
6
  maybeGetDocBlocksHost
7
- } from "./chunk-64733GFL.js";
7
+ } from "./chunk-RFEB4N5Q.js";
8
8
 
9
9
  // src/filesystem/electron-provider-v2.ts
10
10
  var ElectronFileSystemProviderV2 = class extends HostFileSystemProviderV2 {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RawIndexedDBFileSystemStore
3
- } from "./chunk-DAPFSEKY.js";
3
+ } from "./chunk-OPLU2CYA.js";
4
4
  import {
5
5
  getFileSystemProviderV2
6
6
  } from "./chunk-XAMILHQP.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FILE_SYSTEM_TRANSFER_LIMITS,
3
3
  maybeGetDocBlocksHost
4
- } from "./chunk-64733GFL.js";
4
+ } from "./chunk-RFEB4N5Q.js";
5
5
  import {
6
6
  parseFileSystemCapabilities
7
7
  } from "./chunk-245W57QP.js";
@@ -9,7 +9,7 @@ import {
9
9
  HOST_WIRE_LIMITS,
10
10
  isBoundedBytePayload,
11
11
  isBoundedString
12
- } from "./chunk-46AGYHDY.js";
12
+ } from "./chunk-V5SWLUZY.js";
13
13
  import {
14
14
  isRecord
15
15
  } from "./chunk-73OOROXH.js";
@@ -24,8 +24,7 @@ var RawIndexedDBFileSystemStore = class {
24
24
  this.closed = false;
25
25
  }
26
26
  async transaction(mode, operation) {
27
- const database = await this.getDatabase();
28
- const nativeTransaction = database.transaction(this.storeName, mode);
27
+ const nativeTransaction = await this.beginTransaction(mode);
29
28
  const completion = waitForTransaction(nativeTransaction);
30
29
  const transaction = new RawIndexedDBFileSystemTransaction(
31
30
  nativeTransaction.objectStore(this.storeName)
@@ -51,6 +50,30 @@ var RawIndexedDBFileSystemStore = class {
51
50
  this.database = null;
52
51
  this.opening = null;
53
52
  }
53
+ /**
54
+ * Start a native transaction, reconnecting once if the cached connection
55
+ * was closed underneath this store. Browsers close IndexedDB connections on
56
+ * their own (after the device sleeps, when site data is cleared, or when
57
+ * the storage backend restarts), and a closed connection rejects every
58
+ * later `transaction()` call with InvalidStateError ("The database
59
+ * connection is closing"). No work has run at that point, so reopening and
60
+ * starting again is safe.
61
+ */
62
+ async beginTransaction(mode) {
63
+ const database = await this.getDatabase();
64
+ try {
65
+ return database.transaction(this.storeName, mode);
66
+ } catch (error) {
67
+ if (!isClosedConnectionError(error)) throw error;
68
+ this.forgetDatabase(database);
69
+ const reopened = await this.getDatabase();
70
+ return reopened.transaction(this.storeName, mode);
71
+ }
72
+ }
73
+ forgetDatabase(database) {
74
+ if (this.database === database) this.database = null;
75
+ database.close();
76
+ }
54
77
  getDatabase() {
55
78
  if (this.closed) {
56
79
  return Promise.reject(new Error("The IndexedDB filesystem store is closed."));
@@ -67,10 +90,8 @@ var RawIndexedDBFileSystemStore = class {
67
90
  }
68
91
  this.database = database;
69
92
  this.opening = null;
70
- database.onversionchange = () => {
71
- database.close();
72
- if (this.database === database) this.database = null;
73
- };
93
+ database.onversionchange = () => this.forgetDatabase(database);
94
+ database.onclose = () => this.forgetDatabase(database);
74
95
  return database;
75
96
  }).catch((error) => {
76
97
  this.opening = null;
@@ -182,6 +203,9 @@ function openDatabase(databaseName, version, upgrade) {
182
203
  };
183
204
  });
184
205
  }
206
+ function isClosedConnectionError(error) {
207
+ return typeof error === "object" && error !== null && error.name === "InvalidStateError";
208
+ }
185
209
  function requestResult(request) {
186
210
  return new Promise((resolve, reject) => {
187
211
  request.onsuccess = () => resolve(request.result);