@tutti-os/workspace-file-preview 0.0.15 → 0.0.17

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.
@@ -208,6 +208,16 @@ function resolveWorkspaceImageMimeType(pathOrName) {
208
208
  return null;
209
209
  }
210
210
  }
211
+ function resolveWorkspaceVideoMimeType(pathOrName) {
212
+ switch (resolveWorkspaceFileExtension(pathOrName)) {
213
+ case "mp4":
214
+ return "video/mp4";
215
+ case "webm":
216
+ return "video/webm";
217
+ default:
218
+ return null;
219
+ }
220
+ }
211
221
  function classifyWorkspaceFilePreviewKind(entry) {
212
222
  if (entry.kind !== "file") {
213
223
  return null;
@@ -216,6 +226,9 @@ function classifyWorkspaceFilePreviewKind(entry) {
216
226
  if (resolveWorkspaceImageMimeType(name) !== null) {
217
227
  return "image";
218
228
  }
229
+ if (resolveWorkspaceVideoMimeType(name) !== null) {
230
+ return "video";
231
+ }
219
232
  const normalizedName = name.trim().toLowerCase();
220
233
  const extension = resolveWorkspaceFileExtension(name);
221
234
  if (textExtensions.has(extension) || textFileNames.has(normalizedName)) {
@@ -286,6 +299,14 @@ function createWorkspaceFilePreviewLoadedState(input) {
286
299
  status: "image"
287
300
  };
288
301
  }
302
+ if (input.target.fileKind === "video") {
303
+ return {
304
+ bytes: copyWorkspaceFilePreviewBytes(input.bytes),
305
+ contentType: input.contentType ?? resolveWorkspaceVideoMimeType(input.target.name) ?? "application/octet-stream",
306
+ entry: input.target,
307
+ status: "video"
308
+ };
309
+ }
289
310
  try {
290
311
  const content = decodeWorkspaceTextFile(input.bytes);
291
312
  if (looksLikeBinaryText(content)) {
@@ -295,6 +316,13 @@ function createWorkspaceFilePreviewLoadedState(input) {
295
316
  status: "readonly"
296
317
  };
297
318
  }
319
+ if (input.renderHtml && isWorkspaceHtmlFileName(input.target.name)) {
320
+ return {
321
+ content,
322
+ entry: input.target,
323
+ status: "html"
324
+ };
325
+ }
298
326
  return {
299
327
  content,
300
328
  entry: input.target,
@@ -308,6 +336,11 @@ function createWorkspaceFilePreviewLoadedState(input) {
308
336
  };
309
337
  }
310
338
  }
339
+ function isWorkspaceHtmlFileName(pathOrName) {
340
+ return browserOpenableHtmlExtensions.has(
341
+ resolveWorkspaceFileExtension(pathOrName)
342
+ );
343
+ }
311
344
  function resolveWorkspaceFilePreviewName(entry) {
312
345
  return entry.name?.trim() || entry.displayName?.trim() || entry.path.split("/").pop()?.trim() || entry.path;
313
346
  }
@@ -374,6 +407,7 @@ export {
374
407
  isWorkspaceFileBrowserOpenable,
375
408
  shouldFilterVideoPlayersForOpenWith,
376
409
  resolveWorkspaceImageMimeType,
410
+ resolveWorkspaceVideoMimeType,
377
411
  classifyWorkspaceFilePreviewKind,
378
412
  resolveWorkspaceFileActivationTarget,
379
413
  resolveWorkspaceFilePreviewReadiness,
@@ -387,4 +421,4 @@ export {
387
421
  looksLikeBinaryText,
388
422
  formatWorkspacePreviewByteLimit
389
423
  };
390
- //# sourceMappingURL=chunk-MAJHX2MV.js.map
424
+ //# sourceMappingURL=chunk-FYAXIZ3F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/workspaceFilePreview.ts"],"sourcesContent":["export type WorkspaceFilePreviewEntryKind =\n | \"file\"\n | \"directory\"\n | \"folder\"\n | \"unknown\"\n | (string & {});\n\nexport type WorkspaceFilePreviewKind = \"image\" | \"text\" | \"video\";\n\nexport type WorkspaceFileVisualKind =\n | \"binary\"\n | \"code\"\n | \"directory\"\n | \"document\"\n | \"image\"\n | \"markdown\"\n | \"video\";\n\nexport interface WorkspaceFilePreviewEntry {\n displayName?: string;\n kind: WorkspaceFilePreviewEntryKind;\n mtimeMs?: number | null;\n name?: string;\n path: string;\n sizeBytes?: number | null;\n}\n\nexport interface WorkspaceFilePreviewActivationTarget {\n fileKind: WorkspaceFilePreviewKind;\n mtimeMs?: number | null;\n name: string;\n path: string;\n sizeBytes?: number | null;\n}\n\nexport type WorkspaceFilePreviewReadonlyReason =\n | \"binary\"\n | \"decode_failed\"\n | \"file_too_large\"\n | \"text_too_large\";\n\nexport type WorkspaceFilePreviewReadiness<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget =\n WorkspaceFilePreviewActivationTarget\n> =\n | { entry: TEntry; status: \"directory\" }\n | {\n entry: TEntry;\n maxSizeBytes: number;\n reason: Extract<\n WorkspaceFilePreviewReadonlyReason,\n \"file_too_large\" | \"text_too_large\"\n >;\n status: \"readonly\";\n }\n | { entry: TEntry; status: \"unsupported\" }\n | { entry: TEntry; status: \"ready\"; target: TTarget };\n\nexport type WorkspaceFilePreviewLoadedState<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget\n> =\n | { content: string; entry: TTarget; status: \"text\" }\n | { content: string; entry: TTarget; status: \"html\" }\n | {\n bytes: Uint8Array<ArrayBuffer>;\n contentType: string;\n entry: TTarget;\n status: \"image\";\n }\n | {\n bytes: Uint8Array<ArrayBuffer>;\n contentType: string;\n entry: TTarget;\n status: \"video\";\n }\n | {\n entry: TEntry;\n maxSizeBytes?: number;\n reason: WorkspaceFilePreviewReadonlyReason;\n status: \"readonly\";\n };\n\nconst imageExtensions = new Set([\n \"avif\",\n \"gif\",\n \"jpeg\",\n \"jpg\",\n \"png\",\n \"svg\",\n \"webp\"\n]);\nconst browserOpenableHtmlExtensions = new Set([\n \"htm\",\n \"html\",\n \"shtml\",\n \"xhtml\"\n]);\nconst browserOpenableVideoExtensions = new Set([\"mp4\", \"webm\"]);\n\nconst videoExtensions = new Set([\n \"avi\",\n \"m2ts\",\n \"mkv\",\n \"mov\",\n \"mp4\",\n \"mpeg\",\n \"mpg\",\n \"mts\",\n \"webm\",\n \"wmv\"\n]);\n/**\n * Extensions where macOS Launch Services may register video handlers even when\n * the workspace file is source code (UTI / uniform type collisions).\n */\nexport const workspaceFileVideoHandlerCollisionExtensions = new Set([\"ts\"]);\nconst markdownExtensions = new Set([\"md\", \"mdx\"]);\nconst codeExtensions = new Set([\n \"bash\",\n \"c\",\n \"cc\",\n \"conf\",\n \"cpp\",\n \"cs\",\n \"css\",\n \"go\",\n \"h\",\n \"hpp\",\n \"html\",\n \"java\",\n \"js\",\n \"jsx\",\n \"json\",\n \"lua\",\n \"m\",\n \"mm\",\n \"php\",\n \"plist\",\n \"proto\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"swift\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"xml\",\n \"yaml\",\n \"yml\",\n \"zsh\"\n]);\nconst documentExtensions = new Set([\n \"csv\",\n \"doc\",\n \"docx\",\n \"log\",\n \"pdf\",\n \"rtf\",\n \"txt\",\n \"xls\",\n \"xlsx\"\n]);\nconst textExtensions = new Set([\n \"bash\",\n \"c\",\n \"cc\",\n \"conf\",\n \"cpp\",\n \"cs\",\n \"css\",\n \"csv\",\n \"env\",\n \"go\",\n \"h\",\n \"hpp\",\n \"html\",\n \"ini\",\n \"java\",\n \"js\",\n \"json\",\n \"jsx\",\n \"log\",\n \"lua\",\n \"m\",\n \"md\",\n \"mdx\",\n \"mm\",\n \"php\",\n \"plist\",\n \"proto\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"swift\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"txt\",\n \"xml\",\n \"yaml\",\n \"yml\",\n \"zsh\"\n]);\nconst textFileNames = new Set([\n \".gitignore\",\n \".npmrc\",\n \".nvmrc\",\n \"dockerfile\",\n \"makefile\",\n \"readme\"\n]);\n\nexport const workspaceFileTextMaxBytes = 1024 * 1024;\nexport const workspaceFilePreviewMaxBytes = 20 * 1024 * 1024;\n\nexport function resolveWorkspaceFileVisualKind(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): WorkspaceFileVisualKind {\n if (entry.kind === \"directory\" || entry.kind === \"folder\") {\n return \"directory\";\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (imageExtensions.has(extension)) {\n return \"image\";\n }\n if (videoExtensions.has(extension)) {\n return \"video\";\n }\n if (markdownExtensions.has(extension)) {\n return \"markdown\";\n }\n if (codeExtensions.has(extension)) {\n return \"code\";\n }\n if (documentExtensions.has(extension)) {\n return \"document\";\n }\n return \"binary\";\n}\n\nexport function resolveWorkspaceFileExtension(pathOrName: string): string {\n const name = pathOrName.split(\"/\").pop()?.trim().toLowerCase() ?? \"\";\n const dotIndex = name.lastIndexOf(\".\");\n return dotIndex > 0 ? name.slice(dotIndex + 1) : \"\";\n}\n\nexport function isWorkspaceFileBrowserOpenable(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): boolean {\n if (entry.kind !== \"file\") {\n return false;\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (\n extension === \"pdf\" ||\n browserOpenableHtmlExtensions.has(extension) ||\n imageExtensions.has(extension) ||\n browserOpenableVideoExtensions.has(extension)\n ) {\n return true;\n }\n\n return classifyWorkspaceFilePreviewKind(entry) === \"text\";\n}\n\nexport function shouldFilterVideoPlayersForOpenWith(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): boolean {\n if (entry.kind !== \"file\") {\n return false;\n }\n\n const visualKind = resolveWorkspaceFileVisualKind(entry);\n if (visualKind === \"video\") {\n return false;\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (workspaceFileVideoHandlerCollisionExtensions.has(extension)) {\n return true;\n }\n\n if (visualKind === \"code\" || visualKind === \"markdown\") {\n return true;\n }\n\n return classifyWorkspaceFilePreviewKind(entry) === \"text\";\n}\n\nexport function resolveWorkspaceImageMimeType(\n pathOrName: string\n): string | null {\n switch (resolveWorkspaceFileExtension(pathOrName)) {\n case \"avif\":\n return \"image/avif\";\n case \"gif\":\n return \"image/gif\";\n case \"jpeg\":\n case \"jpg\":\n return \"image/jpeg\";\n case \"png\":\n return \"image/png\";\n case \"svg\":\n return \"image/svg+xml\";\n case \"webp\":\n return \"image/webp\";\n default:\n return null;\n }\n}\n\nexport function resolveWorkspaceVideoMimeType(\n pathOrName: string\n): string | null {\n switch (resolveWorkspaceFileExtension(pathOrName)) {\n case \"mp4\":\n return \"video/mp4\";\n case \"webm\":\n return \"video/webm\";\n default:\n return null;\n }\n}\n\nexport function classifyWorkspaceFilePreviewKind(\n entry: Pick<\n WorkspaceFilePreviewEntry,\n \"displayName\" | \"kind\" | \"name\" | \"path\"\n >\n): WorkspaceFilePreviewKind | null {\n if (entry.kind !== \"file\") {\n return null;\n }\n\n const name = resolveWorkspaceFilePreviewName(entry);\n if (resolveWorkspaceImageMimeType(name) !== null) {\n return \"image\";\n }\n if (resolveWorkspaceVideoMimeType(name) !== null) {\n return \"video\";\n }\n\n const normalizedName = name.trim().toLowerCase();\n const extension = resolveWorkspaceFileExtension(name);\n if (textExtensions.has(extension) || textFileNames.has(normalizedName)) {\n return \"text\";\n }\n\n return null;\n}\n\nexport function resolveWorkspaceFileActivationTarget(\n entry: WorkspaceFilePreviewEntry\n): WorkspaceFilePreviewActivationTarget | null {\n const fileKind = classifyWorkspaceFilePreviewKind(entry);\n if (!fileKind) {\n return null;\n }\n\n const target: WorkspaceFilePreviewActivationTarget = {\n fileKind,\n name: resolveWorkspaceFilePreviewName(entry),\n path: entry.path\n };\n if (entry.mtimeMs !== undefined) {\n target.mtimeMs = entry.mtimeMs;\n }\n if (entry.sizeBytes !== undefined) {\n target.sizeBytes = entry.sizeBytes;\n }\n return target;\n}\n\nexport function resolveWorkspaceFilePreviewReadiness<\n TEntry extends WorkspaceFilePreviewEntry\n>(entry: TEntry): WorkspaceFilePreviewReadiness<TEntry> {\n if (entry.kind === \"directory\" || entry.kind === \"folder\") {\n return {\n entry,\n status: \"directory\"\n };\n }\n\n const target = resolveWorkspaceFileActivationTarget(entry);\n if (!target) {\n return {\n entry,\n status: \"unsupported\"\n };\n }\n\n if (\n target.fileKind === \"text\" &&\n isWorkspaceTextFileTooLarge(entry.sizeBytes)\n ) {\n return {\n entry,\n maxSizeBytes: workspaceFileTextMaxBytes,\n reason: \"text_too_large\",\n status: \"readonly\"\n };\n }\n\n if (isWorkspacePreviewFileTooLarge(entry.sizeBytes)) {\n return {\n entry,\n maxSizeBytes: workspaceFilePreviewMaxBytes,\n reason: \"file_too_large\",\n status: \"readonly\"\n };\n }\n\n return {\n entry,\n status: \"ready\",\n target\n };\n}\n\nexport function createWorkspaceFilePreviewLoadedState<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget\n>(input: {\n bytes: Uint8Array | ArrayBuffer;\n contentType?: string | null;\n entry: TEntry;\n renderHtml?: boolean;\n target: TTarget;\n}): WorkspaceFilePreviewLoadedState<TEntry, TTarget> {\n if (input.target.fileKind === \"image\") {\n return {\n bytes: copyWorkspaceFilePreviewBytes(input.bytes),\n contentType:\n input.contentType ??\n resolveWorkspaceImageMimeType(input.target.name) ??\n \"application/octet-stream\",\n entry: input.target,\n status: \"image\"\n };\n }\n if (input.target.fileKind === \"video\") {\n return {\n bytes: copyWorkspaceFilePreviewBytes(input.bytes),\n contentType:\n input.contentType ??\n resolveWorkspaceVideoMimeType(input.target.name) ??\n \"application/octet-stream\",\n entry: input.target,\n status: \"video\"\n };\n }\n\n try {\n const content = decodeWorkspaceTextFile(input.bytes);\n if (looksLikeBinaryText(content)) {\n return {\n entry: input.entry,\n reason: \"binary\",\n status: \"readonly\"\n };\n }\n if (input.renderHtml && isWorkspaceHtmlFileName(input.target.name)) {\n return {\n content,\n entry: input.target,\n status: \"html\"\n };\n }\n return {\n content,\n entry: input.target,\n status: \"text\"\n };\n } catch {\n return {\n entry: input.entry,\n reason: \"decode_failed\",\n status: \"readonly\"\n };\n }\n}\n\nfunction isWorkspaceHtmlFileName(pathOrName: string): boolean {\n return browserOpenableHtmlExtensions.has(\n resolveWorkspaceFileExtension(pathOrName)\n );\n}\n\nexport function resolveWorkspaceFilePreviewName(\n entry: Pick<WorkspaceFilePreviewEntry, \"displayName\" | \"name\" | \"path\">\n): string {\n return (\n entry.name?.trim() ||\n entry.displayName?.trim() ||\n entry.path.split(\"/\").pop()?.trim() ||\n entry.path\n );\n}\n\nexport function isWorkspaceTextFileTooLarge(\n sizeBytes?: number | null\n): boolean {\n return (\n typeof sizeBytes === \"number\" &&\n Number.isFinite(sizeBytes) &&\n sizeBytes > workspaceFileTextMaxBytes\n );\n}\n\nexport function isWorkspacePreviewFileTooLarge(\n sizeBytes?: number | null\n): boolean {\n return (\n typeof sizeBytes === \"number\" &&\n Number.isFinite(sizeBytes) &&\n sizeBytes > workspaceFilePreviewMaxBytes\n );\n}\n\nexport function decodeWorkspaceTextFile(\n bytes: Uint8Array | ArrayBuffer\n): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(\n normalizeWorkspaceFilePreviewBytes(bytes)\n );\n}\n\nexport function normalizeWorkspaceFilePreviewBytes(\n value: Uint8Array | ArrayBuffer\n): Uint8Array {\n if (value instanceof Uint8Array) {\n return value;\n }\n return new Uint8Array(value);\n}\n\nexport function copyWorkspaceFilePreviewBytes(\n bytes: Uint8Array | ArrayBuffer\n): Uint8Array<ArrayBuffer> {\n const normalized = normalizeWorkspaceFilePreviewBytes(bytes);\n const buffer = new ArrayBuffer(normalized.byteLength);\n const copy = new Uint8Array(buffer);\n copy.set(normalized);\n return copy;\n}\n\nexport function looksLikeBinaryText(content: string): boolean {\n if (content.length === 0) {\n return false;\n }\n\n const sample = content.slice(0, 4096);\n if (sample.includes(\"\\u0000\")) {\n return true;\n }\n\n let suspiciousControlChars = 0;\n for (let index = 0; index < sample.length; index += 1) {\n const code = sample.charCodeAt(index);\n const isAllowedWhitespace = code === 9 || code === 10 || code === 13;\n const isControlChar = code < 32 || (code >= 127 && code <= 159);\n if (isControlChar && !isAllowedWhitespace) {\n suspiciousControlChars += 1;\n }\n }\n\n return suspiciousControlChars / sample.length > 0.12;\n}\n\nexport function formatWorkspacePreviewByteLimit(sizeBytes: number): string {\n if (sizeBytes < 1024) {\n return `${sizeBytes} B`;\n }\n\n const mebibytes = sizeBytes / (1024 * 1024);\n if (Number.isInteger(mebibytes)) {\n return `${mebibytes} MiB`;\n }\n return `${mebibytes.toFixed(1)} MiB`;\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  type WorkspaceFilePreviewEntryKind = "file" | "directory" | "folder" | "unknown" | (string & {});
2
- type WorkspaceFilePreviewKind = "image" | "text";
2
+ type WorkspaceFilePreviewKind = "image" | "text" | "video";
3
3
  type WorkspaceFileVisualKind = "binary" | "code" | "directory" | "document" | "image" | "markdown" | "video";
4
4
  interface WorkspaceFilePreviewEntry {
5
5
  displayName?: string;
@@ -37,11 +37,20 @@ type WorkspaceFilePreviewLoadedState<TEntry extends WorkspaceFilePreviewEntry, T
37
37
  content: string;
38
38
  entry: TTarget;
39
39
  status: "text";
40
+ } | {
41
+ content: string;
42
+ entry: TTarget;
43
+ status: "html";
40
44
  } | {
41
45
  bytes: Uint8Array<ArrayBuffer>;
42
46
  contentType: string;
43
47
  entry: TTarget;
44
48
  status: "image";
49
+ } | {
50
+ bytes: Uint8Array<ArrayBuffer>;
51
+ contentType: string;
52
+ entry: TTarget;
53
+ status: "video";
45
54
  } | {
46
55
  entry: TEntry;
47
56
  maxSizeBytes?: number;
@@ -60,6 +69,7 @@ declare function resolveWorkspaceFileExtension(pathOrName: string): string;
60
69
  declare function isWorkspaceFileBrowserOpenable(entry: Pick<WorkspaceFilePreviewEntry, "kind" | "name" | "path">): boolean;
61
70
  declare function shouldFilterVideoPlayersForOpenWith(entry: Pick<WorkspaceFilePreviewEntry, "kind" | "name" | "path">): boolean;
62
71
  declare function resolveWorkspaceImageMimeType(pathOrName: string): string | null;
72
+ declare function resolveWorkspaceVideoMimeType(pathOrName: string): string | null;
63
73
  declare function classifyWorkspaceFilePreviewKind(entry: Pick<WorkspaceFilePreviewEntry, "displayName" | "kind" | "name" | "path">): WorkspaceFilePreviewKind | null;
64
74
  declare function resolveWorkspaceFileActivationTarget(entry: WorkspaceFilePreviewEntry): WorkspaceFilePreviewActivationTarget | null;
65
75
  declare function resolveWorkspaceFilePreviewReadiness<TEntry extends WorkspaceFilePreviewEntry>(entry: TEntry): WorkspaceFilePreviewReadiness<TEntry>;
@@ -67,6 +77,7 @@ declare function createWorkspaceFilePreviewLoadedState<TEntry extends WorkspaceF
67
77
  bytes: Uint8Array | ArrayBuffer;
68
78
  contentType?: string | null;
69
79
  entry: TEntry;
80
+ renderHtml?: boolean;
70
81
  target: TTarget;
71
82
  }): WorkspaceFilePreviewLoadedState<TEntry, TTarget>;
72
83
  declare function resolveWorkspaceFilePreviewName(entry: Pick<WorkspaceFilePreviewEntry, "displayName" | "name" | "path">): string;
@@ -78,4 +89,4 @@ declare function copyWorkspaceFilePreviewBytes(bytes: Uint8Array | ArrayBuffer):
78
89
  declare function looksLikeBinaryText(content: string): boolean;
79
90
  declare function formatWorkspacePreviewByteLimit(sizeBytes: number): string;
80
91
 
81
- export { type WorkspaceFilePreviewActivationTarget, type WorkspaceFilePreviewEntry, type WorkspaceFilePreviewEntryKind, type WorkspaceFilePreviewKind, type WorkspaceFilePreviewLoadedState, type WorkspaceFilePreviewReadiness, type WorkspaceFilePreviewReadonlyReason, type WorkspaceFileVisualKind, classifyWorkspaceFilePreviewKind, copyWorkspaceFilePreviewBytes, createWorkspaceFilePreviewLoadedState, decodeWorkspaceTextFile, formatWorkspacePreviewByteLimit, isWorkspaceFileBrowserOpenable, isWorkspacePreviewFileTooLarge, isWorkspaceTextFileTooLarge, looksLikeBinaryText, normalizeWorkspaceFilePreviewBytes, resolveWorkspaceFileActivationTarget, resolveWorkspaceFileExtension, resolveWorkspaceFilePreviewName, resolveWorkspaceFilePreviewReadiness, resolveWorkspaceFileVisualKind, resolveWorkspaceImageMimeType, shouldFilterVideoPlayersForOpenWith, workspaceFilePreviewMaxBytes, workspaceFileTextMaxBytes, workspaceFileVideoHandlerCollisionExtensions };
92
+ export { type WorkspaceFilePreviewActivationTarget, type WorkspaceFilePreviewEntry, type WorkspaceFilePreviewEntryKind, type WorkspaceFilePreviewKind, type WorkspaceFilePreviewLoadedState, type WorkspaceFilePreviewReadiness, type WorkspaceFilePreviewReadonlyReason, type WorkspaceFileVisualKind, classifyWorkspaceFilePreviewKind, copyWorkspaceFilePreviewBytes, createWorkspaceFilePreviewLoadedState, decodeWorkspaceTextFile, formatWorkspacePreviewByteLimit, isWorkspaceFileBrowserOpenable, isWorkspacePreviewFileTooLarge, isWorkspaceTextFileTooLarge, looksLikeBinaryText, normalizeWorkspaceFilePreviewBytes, resolveWorkspaceFileActivationTarget, resolveWorkspaceFileExtension, resolveWorkspaceFilePreviewName, resolveWorkspaceFilePreviewReadiness, resolveWorkspaceFileVisualKind, resolveWorkspaceImageMimeType, resolveWorkspaceVideoMimeType, shouldFilterVideoPlayersForOpenWith, workspaceFilePreviewMaxBytes, workspaceFileTextMaxBytes, workspaceFileVideoHandlerCollisionExtensions };
@@ -15,11 +15,12 @@ import {
15
15
  resolveWorkspaceFilePreviewReadiness,
16
16
  resolveWorkspaceFileVisualKind,
17
17
  resolveWorkspaceImageMimeType,
18
+ resolveWorkspaceVideoMimeType,
18
19
  shouldFilterVideoPlayersForOpenWith,
19
20
  workspaceFilePreviewMaxBytes,
20
21
  workspaceFileTextMaxBytes,
21
22
  workspaceFileVideoHandlerCollisionExtensions
22
- } from "../chunk-MAJHX2MV.js";
23
+ } from "../chunk-FYAXIZ3F.js";
23
24
  export {
24
25
  classifyWorkspaceFilePreviewKind,
25
26
  copyWorkspaceFilePreviewBytes,
@@ -37,6 +38,7 @@ export {
37
38
  resolveWorkspaceFilePreviewReadiness,
38
39
  resolveWorkspaceFileVisualKind,
39
40
  resolveWorkspaceImageMimeType,
41
+ resolveWorkspaceVideoMimeType,
40
42
  shouldFilterVideoPlayersForOpenWith,
41
43
  workspaceFilePreviewMaxBytes,
42
44
  workspaceFileTextMaxBytes,
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { WorkspaceFilePreviewActivationTarget, WorkspaceFilePreviewEntry, WorkspaceFilePreviewEntryKind, WorkspaceFilePreviewKind, WorkspaceFilePreviewLoadedState, WorkspaceFilePreviewReadiness, WorkspaceFilePreviewReadonlyReason, WorkspaceFileVisualKind, classifyWorkspaceFilePreviewKind, copyWorkspaceFilePreviewBytes, createWorkspaceFilePreviewLoadedState, decodeWorkspaceTextFile, formatWorkspacePreviewByteLimit, isWorkspaceFileBrowserOpenable, isWorkspacePreviewFileTooLarge, isWorkspaceTextFileTooLarge, looksLikeBinaryText, normalizeWorkspaceFilePreviewBytes, resolveWorkspaceFileActivationTarget, resolveWorkspaceFileExtension, resolveWorkspaceFilePreviewName, resolveWorkspaceFilePreviewReadiness, resolveWorkspaceFileVisualKind, resolveWorkspaceImageMimeType, shouldFilterVideoPlayersForOpenWith, workspaceFilePreviewMaxBytes, workspaceFileTextMaxBytes, workspaceFileVideoHandlerCollisionExtensions } from './core/index.js';
1
+ export { WorkspaceFilePreviewActivationTarget, WorkspaceFilePreviewEntry, WorkspaceFilePreviewEntryKind, WorkspaceFilePreviewKind, WorkspaceFilePreviewLoadedState, WorkspaceFilePreviewReadiness, WorkspaceFilePreviewReadonlyReason, WorkspaceFileVisualKind, classifyWorkspaceFilePreviewKind, copyWorkspaceFilePreviewBytes, createWorkspaceFilePreviewLoadedState, decodeWorkspaceTextFile, formatWorkspacePreviewByteLimit, isWorkspaceFileBrowserOpenable, isWorkspacePreviewFileTooLarge, isWorkspaceTextFileTooLarge, looksLikeBinaryText, normalizeWorkspaceFilePreviewBytes, resolveWorkspaceFileActivationTarget, resolveWorkspaceFileExtension, resolveWorkspaceFilePreviewName, resolveWorkspaceFilePreviewReadiness, resolveWorkspaceFileVisualKind, resolveWorkspaceImageMimeType, resolveWorkspaceVideoMimeType, shouldFilterVideoPlayersForOpenWith, workspaceFilePreviewMaxBytes, workspaceFileTextMaxBytes, workspaceFileVideoHandlerCollisionExtensions } from './core/index.js';
package/dist/index.js CHANGED
@@ -15,11 +15,12 @@ import {
15
15
  resolveWorkspaceFilePreviewReadiness,
16
16
  resolveWorkspaceFileVisualKind,
17
17
  resolveWorkspaceImageMimeType,
18
+ resolveWorkspaceVideoMimeType,
18
19
  shouldFilterVideoPlayersForOpenWith,
19
20
  workspaceFilePreviewMaxBytes,
20
21
  workspaceFileTextMaxBytes,
21
22
  workspaceFileVideoHandlerCollisionExtensions
22
- } from "./chunk-MAJHX2MV.js";
23
+ } from "./chunk-FYAXIZ3F.js";
23
24
  export {
24
25
  classifyWorkspaceFilePreviewKind,
25
26
  copyWorkspaceFilePreviewBytes,
@@ -37,6 +38,7 @@ export {
37
38
  resolveWorkspaceFilePreviewReadiness,
38
39
  resolveWorkspaceFileVisualKind,
39
40
  resolveWorkspaceImageMimeType,
41
+ resolveWorkspaceVideoMimeType,
40
42
  shouldFilterVideoPlayersForOpenWith,
41
43
  workspaceFilePreviewMaxBytes,
42
44
  workspaceFileTextMaxBytes,
@@ -12,10 +12,18 @@ type WorkspaceFilePreviewSurfaceState<TEntry> = {
12
12
  content: string;
13
13
  entry: TEntry;
14
14
  status: "text";
15
+ } | {
16
+ content: string;
17
+ entry: TEntry;
18
+ status: "html";
15
19
  } | {
16
20
  entry: TEntry;
17
21
  objectUrl: string;
18
22
  status: "image";
23
+ } | {
24
+ entry: TEntry;
25
+ objectUrl: string;
26
+ status: "video";
19
27
  } | {
20
28
  entry: TEntry;
21
29
  message: string;
@@ -33,6 +41,9 @@ interface WorkspaceFilePreviewSurfaceProps<TEntry> {
33
41
  directoryMessage: string;
34
42
  emptyMessage: string;
35
43
  frameClassName: string;
44
+ htmlClassName?: string;
45
+ htmlFrameClassName?: string;
46
+ htmlTitle?: (entry: TEntry) => string;
36
47
  imageAlt: (entry: TEntry) => string;
37
48
  imageFrameClassName?: string;
38
49
  imageClassName?: string;
@@ -43,7 +54,9 @@ interface WorkspaceFilePreviewSurfaceProps<TEntry> {
43
54
  state: WorkspaceFilePreviewSurfaceState<TEntry>;
44
55
  textClassName?: string;
45
56
  textFrameClassName?: string;
57
+ videoClassName?: string;
58
+ videoFrameClassName?: string;
46
59
  }
47
- declare function WorkspaceFilePreviewSurface<TEntry>({ directoryMessage, emptyMessage, frameClassName, imageAlt, imageClassName, imageFrameClassName, loadingIndicator, loadingMessage, messageClassName, renderIcon, state, textClassName, textFrameClassName }: WorkspaceFilePreviewSurfaceProps<TEntry>): ReactElement;
60
+ declare function WorkspaceFilePreviewSurface<TEntry>({ directoryMessage, emptyMessage, frameClassName, htmlClassName, htmlFrameClassName, htmlTitle, imageAlt, imageClassName, imageFrameClassName, loadingIndicator, loadingMessage, messageClassName, renderIcon, state, textClassName, textFrameClassName, videoClassName, videoFrameClassName }: WorkspaceFilePreviewSurfaceProps<TEntry>): ReactElement;
48
61
 
49
62
  export { WorkspaceFilePreviewSurface, type WorkspaceFilePreviewSurfaceProps, type WorkspaceFilePreviewSurfaceState };
@@ -4,6 +4,9 @@ function WorkspaceFilePreviewSurface({
4
4
  directoryMessage,
5
5
  emptyMessage,
6
6
  frameClassName,
7
+ htmlClassName = "h-full w-full border-0 bg-white",
8
+ htmlFrameClassName,
9
+ htmlTitle,
7
10
  imageAlt,
8
11
  imageClassName = "max-h-full max-w-full rounded-[6px] object-contain",
9
12
  imageFrameClassName,
@@ -13,7 +16,9 @@ function WorkspaceFilePreviewSurface({
13
16
  renderIcon,
14
17
  state,
15
18
  textClassName = "h-full overflow-auto p-4 text-[11px] leading-5 whitespace-pre-wrap break-words text-[var(--text-primary)]",
16
- textFrameClassName
19
+ textFrameClassName,
20
+ videoClassName = "block max-h-full max-w-full rounded-[6px] object-contain",
21
+ videoFrameClassName
17
22
  }) {
18
23
  switch (state.status) {
19
24
  case "directory":
@@ -49,6 +54,46 @@ function WorkspaceFilePreviewSurface({
49
54
  children: /* @__PURE__ */ jsx("pre", { className: textClassName, children: state.content })
50
55
  }
51
56
  );
57
+ case "html":
58
+ return /* @__PURE__ */ jsx(
59
+ WorkspaceFilePreviewFrame,
60
+ {
61
+ className: joinClassNames(frameClassName, htmlFrameClassName),
62
+ children: /* @__PURE__ */ jsx(
63
+ "iframe",
64
+ {
65
+ className: htmlClassName,
66
+ sandbox: "allow-forms allow-scripts",
67
+ srcDoc: state.content,
68
+ title: htmlTitle?.(state.entry) ?? "HTML preview"
69
+ }
70
+ )
71
+ }
72
+ );
73
+ case "video":
74
+ return /* @__PURE__ */ jsx(
75
+ WorkspaceFilePreviewFrame,
76
+ {
77
+ className: joinClassNames(frameClassName, videoFrameClassName),
78
+ children: /* @__PURE__ */ jsx(
79
+ "video",
80
+ {
81
+ "aria-label": imageAlt(state.entry),
82
+ className: videoClassName,
83
+ muted: true,
84
+ playsInline: true,
85
+ preload: "metadata",
86
+ src: state.objectUrl,
87
+ onLoadedMetadata: (event) => {
88
+ const video = event.currentTarget;
89
+ if (video.duration > 0 && video.currentTime === 0) {
90
+ video.currentTime = Math.min(0.1, video.duration / 2);
91
+ }
92
+ }
93
+ }
94
+ )
95
+ }
96
+ );
52
97
  case "readonly":
53
98
  case "unsupported":
54
99
  case "error":
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/workspaceFilePreviewSurface.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\";\n\nexport type WorkspaceFilePreviewSurfaceState<TEntry> =\n | { status: \"empty\" }\n | { entry: TEntry; status: \"directory\" }\n | { entry: TEntry; status: \"loading\" }\n | { content: string; entry: TEntry; status: \"text\" }\n | { entry: TEntry; objectUrl: string; status: \"image\" }\n | { entry: TEntry; message: string; status: \"readonly\" }\n | { entry: TEntry; message: string; status: \"unsupported\" }\n | { entry: TEntry; message: string; status: \"error\" };\n\nexport interface WorkspaceFilePreviewSurfaceProps<TEntry> {\n directoryMessage: string;\n emptyMessage: string;\n frameClassName: string;\n imageAlt: (entry: TEntry) => string;\n imageFrameClassName?: string;\n imageClassName?: string;\n loadingIndicator: ReactNode;\n loadingMessage: string;\n messageClassName?: string;\n renderIcon: (entry: TEntry) => ReactNode;\n state: WorkspaceFilePreviewSurfaceState<TEntry>;\n textClassName?: string;\n textFrameClassName?: string;\n}\n\nexport function WorkspaceFilePreviewSurface<TEntry>({\n directoryMessage,\n emptyMessage,\n frameClassName,\n imageAlt,\n imageClassName = \"max-h-full max-w-full rounded-[6px] object-contain\",\n imageFrameClassName,\n loadingIndicator,\n loadingMessage,\n messageClassName = \"max-w-[24ch] text-center text-[13px] leading-5 text-[var(--text-tertiary)] [overflow-wrap:anywhere]\",\n renderIcon,\n state,\n textClassName = \"h-full overflow-auto p-4 text-[11px] leading-5 whitespace-pre-wrap break-words text-[var(--text-primary)]\",\n textFrameClassName\n}: WorkspaceFilePreviewSurfaceProps<TEntry>): ReactElement {\n switch (state.status) {\n case \"directory\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"flex flex-col items-center justify-center gap-2.5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]\">\n {renderIcon(state.entry)}\n <span>{directoryMessage}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"loading\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"space-y-3 px-4 text-center text-[13px] text-[var(--text-tertiary)]\">\n {loadingIndicator}\n <span>{loadingMessage}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"image\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, imageFrameClassName)}\n >\n <img\n alt={imageAlt(state.entry)}\n className={imageClassName}\n src={state.objectUrl}\n />\n </WorkspaceFilePreviewFrame>\n );\n case \"text\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, textFrameClassName)}\n >\n <pre className={textClassName}>{state.content}</pre>\n </WorkspaceFilePreviewFrame>\n );\n case \"readonly\":\n case \"unsupported\":\n case \"error\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 text-center text-[13px] text-[var(--text-tertiary)]\">\n {renderIcon(state.entry)}\n <span className={messageClassName}>{state.message}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"empty\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <span className={messageClassName}>{emptyMessage}</span>\n </WorkspaceFilePreviewFrame>\n );\n }\n}\n\nfunction WorkspaceFilePreviewFrame({\n children,\n className\n}: {\n children: ReactNode;\n className: string;\n}): ReactElement {\n return <div className={className}>{children}</div>;\n}\n\nfunction joinClassNames(\n ...classNames: Array<string | false | null | undefined>\n): string {\n return classNames.filter(Boolean).join(\" \");\n}\n"],"mappings":";AA+CU,SAEE,KAFF;AAnBH,SAAS,4BAAoC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACF,GAA2D;AACzD,UAAQ,MAAM,QAAQ;AAAA,IACpB,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,mHACZ;AAAA,mBAAW,MAAM,KAAK;AAAA,QACvB,oBAAC,UAAM,4BAAiB;AAAA,SAC1B,GACF;AAAA,IAEJ,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,sEACZ;AAAA;AAAA,QACD,oBAAC,UAAM,0BAAe;AAAA,SACxB,GACF;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,mBAAmB;AAAA,UAE7D;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,SAAS,MAAM,KAAK;AAAA,cACzB,WAAW;AAAA,cACX,KAAK,MAAM;AAAA;AAAA,UACb;AAAA;AAAA,MACF;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,kBAAkB;AAAA,UAE5D,8BAAC,SAAI,WAAW,eAAgB,gBAAM,SAAQ;AAAA;AAAA,MAChD;AAAA,IAEJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,4GACZ;AAAA,mBAAW,MAAM,KAAK;AAAA,QACvB,oBAAC,UAAK,WAAW,kBAAmB,gBAAM,SAAQ;AAAA,SACpD,GACF;AAAA,IAEJ,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,8BAAC,UAAK,WAAW,kBAAmB,wBAAa,GACnD;AAAA,EAEN;AACF;AAEA,SAAS,0BAA0B;AAAA,EACjC;AAAA,EACA;AACF,GAGiB;AACf,SAAO,oBAAC,SAAI,WAAuB,UAAS;AAC9C;AAEA,SAAS,kBACJ,YACK;AACR,SAAO,WAAW,OAAO,OAAO,EAAE,KAAK,GAAG;AAC5C;","names":[]}
1
+ {"version":3,"sources":["../../src/react/workspaceFilePreviewSurface.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\";\n\nexport type WorkspaceFilePreviewSurfaceState<TEntry> =\n | { status: \"empty\" }\n | { entry: TEntry; status: \"directory\" }\n | { entry: TEntry; status: \"loading\" }\n | { content: string; entry: TEntry; status: \"text\" }\n | { content: string; entry: TEntry; status: \"html\" }\n | { entry: TEntry; objectUrl: string; status: \"image\" }\n | { entry: TEntry; objectUrl: string; status: \"video\" }\n | { entry: TEntry; message: string; status: \"readonly\" }\n | { entry: TEntry; message: string; status: \"unsupported\" }\n | { entry: TEntry; message: string; status: \"error\" };\n\nexport interface WorkspaceFilePreviewSurfaceProps<TEntry> {\n directoryMessage: string;\n emptyMessage: string;\n frameClassName: string;\n htmlClassName?: string;\n htmlFrameClassName?: string;\n htmlTitle?: (entry: TEntry) => string;\n imageAlt: (entry: TEntry) => string;\n imageFrameClassName?: string;\n imageClassName?: string;\n loadingIndicator: ReactNode;\n loadingMessage: string;\n messageClassName?: string;\n renderIcon: (entry: TEntry) => ReactNode;\n state: WorkspaceFilePreviewSurfaceState<TEntry>;\n textClassName?: string;\n textFrameClassName?: string;\n videoClassName?: string;\n videoFrameClassName?: string;\n}\n\nexport function WorkspaceFilePreviewSurface<TEntry>({\n directoryMessage,\n emptyMessage,\n frameClassName,\n htmlClassName = \"h-full w-full border-0 bg-white\",\n htmlFrameClassName,\n htmlTitle,\n imageAlt,\n imageClassName = \"max-h-full max-w-full rounded-[6px] object-contain\",\n imageFrameClassName,\n loadingIndicator,\n loadingMessage,\n messageClassName = \"max-w-[24ch] text-center text-[13px] leading-5 text-[var(--text-tertiary)] [overflow-wrap:anywhere]\",\n renderIcon,\n state,\n textClassName = \"h-full overflow-auto p-4 text-[11px] leading-5 whitespace-pre-wrap break-words text-[var(--text-primary)]\",\n textFrameClassName,\n videoClassName = \"block max-h-full max-w-full rounded-[6px] object-contain\",\n videoFrameClassName\n}: WorkspaceFilePreviewSurfaceProps<TEntry>): ReactElement {\n switch (state.status) {\n case \"directory\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"flex flex-col items-center justify-center gap-2.5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]\">\n {renderIcon(state.entry)}\n <span>{directoryMessage}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"loading\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"space-y-3 px-4 text-center text-[13px] text-[var(--text-tertiary)]\">\n {loadingIndicator}\n <span>{loadingMessage}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"image\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, imageFrameClassName)}\n >\n <img\n alt={imageAlt(state.entry)}\n className={imageClassName}\n src={state.objectUrl}\n />\n </WorkspaceFilePreviewFrame>\n );\n case \"text\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, textFrameClassName)}\n >\n <pre className={textClassName}>{state.content}</pre>\n </WorkspaceFilePreviewFrame>\n );\n case \"html\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, htmlFrameClassName)}\n >\n <iframe\n className={htmlClassName}\n sandbox=\"allow-forms allow-scripts\"\n srcDoc={state.content}\n title={htmlTitle?.(state.entry) ?? \"HTML preview\"}\n />\n </WorkspaceFilePreviewFrame>\n );\n case \"video\":\n return (\n <WorkspaceFilePreviewFrame\n className={joinClassNames(frameClassName, videoFrameClassName)}\n >\n <video\n aria-label={imageAlt(state.entry)}\n className={videoClassName}\n muted\n playsInline\n preload=\"metadata\"\n src={state.objectUrl}\n onLoadedMetadata={(event) => {\n const video = event.currentTarget;\n if (video.duration > 0 && video.currentTime === 0) {\n video.currentTime = Math.min(0.1, video.duration / 2);\n }\n }}\n />\n </WorkspaceFilePreviewFrame>\n );\n case \"readonly\":\n case \"unsupported\":\n case \"error\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 text-center text-[13px] text-[var(--text-tertiary)]\">\n {renderIcon(state.entry)}\n <span className={messageClassName}>{state.message}</span>\n </div>\n </WorkspaceFilePreviewFrame>\n );\n case \"empty\":\n return (\n <WorkspaceFilePreviewFrame className={frameClassName}>\n <span className={messageClassName}>{emptyMessage}</span>\n </WorkspaceFilePreviewFrame>\n );\n }\n}\n\nfunction WorkspaceFilePreviewFrame({\n children,\n className\n}: {\n children: ReactNode;\n className: string;\n}): ReactElement {\n return <div className={className}>{children}</div>;\n}\n\nfunction joinClassNames(\n ...classNames: Array<string | false | null | undefined>\n): string {\n return classNames.filter(Boolean).join(\" \");\n}\n"],"mappings":";AA2DU,SAEE,KAFF;AAxBH,SAAS,4BAAoC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,iBAAiB;AAAA,EACjB;AACF,GAA2D;AACzD,UAAQ,MAAM,QAAQ;AAAA,IACpB,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,mHACZ;AAAA,mBAAW,MAAM,KAAK;AAAA,QACvB,oBAAC,UAAM,4BAAiB;AAAA,SAC1B,GACF;AAAA,IAEJ,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,sEACZ;AAAA;AAAA,QACD,oBAAC,UAAM,0BAAe;AAAA,SACxB,GACF;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,mBAAmB;AAAA,UAE7D;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,SAAS,MAAM,KAAK;AAAA,cACzB,WAAW;AAAA,cACX,KAAK,MAAM;AAAA;AAAA,UACb;AAAA;AAAA,MACF;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,kBAAkB;AAAA,UAE5D,8BAAC,SAAI,WAAW,eAAgB,gBAAM,SAAQ;AAAA;AAAA,MAChD;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,kBAAkB;AAAA,UAE5D;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,cACX,SAAQ;AAAA,cACR,QAAQ,MAAM;AAAA,cACd,OAAO,YAAY,MAAM,KAAK,KAAK;AAAA;AAAA,UACrC;AAAA;AAAA,MACF;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,eAAe,gBAAgB,mBAAmB;AAAA,UAE7D;AAAA,YAAC;AAAA;AAAA,cACC,cAAY,SAAS,MAAM,KAAK;AAAA,cAChC,WAAW;AAAA,cACX,OAAK;AAAA,cACL,aAAW;AAAA,cACX,SAAQ;AAAA,cACR,KAAK,MAAM;AAAA,cACX,kBAAkB,CAAC,UAAU;AAC3B,sBAAM,QAAQ,MAAM;AACpB,oBAAI,MAAM,WAAW,KAAK,MAAM,gBAAgB,GAAG;AACjD,wBAAM,cAAc,KAAK,IAAI,KAAK,MAAM,WAAW,CAAC;AAAA,gBACtD;AAAA,cACF;AAAA;AAAA,UACF;AAAA;AAAA,MACF;AAAA,IAEJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,+BAAC,SAAI,WAAU,4GACZ;AAAA,mBAAW,MAAM,KAAK;AAAA,QACvB,oBAAC,UAAK,WAAW,kBAAmB,gBAAM,SAAQ;AAAA,SACpD,GACF;AAAA,IAEJ,KAAK;AACH,aACE,oBAAC,6BAA0B,WAAW,gBACpC,8BAAC,UAAK,WAAW,kBAAmB,wBAAa,GACnD;AAAA,EAEN;AACF;AAEA,SAAS,0BAA0B;AAAA,EACjC;AAAA,EACA;AACF,GAGiB;AACf,SAAO,oBAAC,SAAI,WAAuB,UAAS;AAC9C;AAEA,SAAS,kBACJ,YACK;AACR,SAAO,WAAW,OAAO,OAAO,EAAE,KAAK,GAAG;AAC5C;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutti-os/workspace-file-preview",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/workspaceFilePreview.ts"],"sourcesContent":["export type WorkspaceFilePreviewEntryKind =\n | \"file\"\n | \"directory\"\n | \"folder\"\n | \"unknown\"\n | (string & {});\n\nexport type WorkspaceFilePreviewKind = \"image\" | \"text\";\n\nexport type WorkspaceFileVisualKind =\n | \"binary\"\n | \"code\"\n | \"directory\"\n | \"document\"\n | \"image\"\n | \"markdown\"\n | \"video\";\n\nexport interface WorkspaceFilePreviewEntry {\n displayName?: string;\n kind: WorkspaceFilePreviewEntryKind;\n mtimeMs?: number | null;\n name?: string;\n path: string;\n sizeBytes?: number | null;\n}\n\nexport interface WorkspaceFilePreviewActivationTarget {\n fileKind: WorkspaceFilePreviewKind;\n mtimeMs?: number | null;\n name: string;\n path: string;\n sizeBytes?: number | null;\n}\n\nexport type WorkspaceFilePreviewReadonlyReason =\n | \"binary\"\n | \"decode_failed\"\n | \"file_too_large\"\n | \"text_too_large\";\n\nexport type WorkspaceFilePreviewReadiness<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget =\n WorkspaceFilePreviewActivationTarget\n> =\n | { entry: TEntry; status: \"directory\" }\n | {\n entry: TEntry;\n maxSizeBytes: number;\n reason: Extract<\n WorkspaceFilePreviewReadonlyReason,\n \"file_too_large\" | \"text_too_large\"\n >;\n status: \"readonly\";\n }\n | { entry: TEntry; status: \"unsupported\" }\n | { entry: TEntry; status: \"ready\"; target: TTarget };\n\nexport type WorkspaceFilePreviewLoadedState<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget\n> =\n | { content: string; entry: TTarget; status: \"text\" }\n | {\n bytes: Uint8Array<ArrayBuffer>;\n contentType: string;\n entry: TTarget;\n status: \"image\";\n }\n | {\n entry: TEntry;\n maxSizeBytes?: number;\n reason: WorkspaceFilePreviewReadonlyReason;\n status: \"readonly\";\n };\n\nconst imageExtensions = new Set([\n \"avif\",\n \"gif\",\n \"jpeg\",\n \"jpg\",\n \"png\",\n \"svg\",\n \"webp\"\n]);\nconst browserOpenableHtmlExtensions = new Set([\n \"htm\",\n \"html\",\n \"shtml\",\n \"xhtml\"\n]);\nconst browserOpenableVideoExtensions = new Set([\"mp4\", \"webm\"]);\n\nconst videoExtensions = new Set([\n \"avi\",\n \"m2ts\",\n \"mkv\",\n \"mov\",\n \"mp4\",\n \"mpeg\",\n \"mpg\",\n \"mts\",\n \"webm\",\n \"wmv\"\n]);\n/**\n * Extensions where macOS Launch Services may register video handlers even when\n * the workspace file is source code (UTI / uniform type collisions).\n */\nexport const workspaceFileVideoHandlerCollisionExtensions = new Set([\"ts\"]);\nconst markdownExtensions = new Set([\"md\", \"mdx\"]);\nconst codeExtensions = new Set([\n \"bash\",\n \"c\",\n \"cc\",\n \"conf\",\n \"cpp\",\n \"cs\",\n \"css\",\n \"go\",\n \"h\",\n \"hpp\",\n \"html\",\n \"java\",\n \"js\",\n \"jsx\",\n \"json\",\n \"lua\",\n \"m\",\n \"mm\",\n \"php\",\n \"plist\",\n \"proto\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"swift\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"xml\",\n \"yaml\",\n \"yml\",\n \"zsh\"\n]);\nconst documentExtensions = new Set([\n \"csv\",\n \"doc\",\n \"docx\",\n \"log\",\n \"pdf\",\n \"rtf\",\n \"txt\",\n \"xls\",\n \"xlsx\"\n]);\nconst textExtensions = new Set([\n \"bash\",\n \"c\",\n \"cc\",\n \"conf\",\n \"cpp\",\n \"cs\",\n \"css\",\n \"csv\",\n \"env\",\n \"go\",\n \"h\",\n \"hpp\",\n \"html\",\n \"ini\",\n \"java\",\n \"js\",\n \"json\",\n \"jsx\",\n \"log\",\n \"lua\",\n \"m\",\n \"md\",\n \"mdx\",\n \"mm\",\n \"php\",\n \"plist\",\n \"proto\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"swift\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"txt\",\n \"xml\",\n \"yaml\",\n \"yml\",\n \"zsh\"\n]);\nconst textFileNames = new Set([\n \".gitignore\",\n \".npmrc\",\n \".nvmrc\",\n \"dockerfile\",\n \"makefile\",\n \"readme\"\n]);\n\nexport const workspaceFileTextMaxBytes = 1024 * 1024;\nexport const workspaceFilePreviewMaxBytes = 20 * 1024 * 1024;\n\nexport function resolveWorkspaceFileVisualKind(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): WorkspaceFileVisualKind {\n if (entry.kind === \"directory\" || entry.kind === \"folder\") {\n return \"directory\";\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (imageExtensions.has(extension)) {\n return \"image\";\n }\n if (videoExtensions.has(extension)) {\n return \"video\";\n }\n if (markdownExtensions.has(extension)) {\n return \"markdown\";\n }\n if (codeExtensions.has(extension)) {\n return \"code\";\n }\n if (documentExtensions.has(extension)) {\n return \"document\";\n }\n return \"binary\";\n}\n\nexport function resolveWorkspaceFileExtension(pathOrName: string): string {\n const name = pathOrName.split(\"/\").pop()?.trim().toLowerCase() ?? \"\";\n const dotIndex = name.lastIndexOf(\".\");\n return dotIndex > 0 ? name.slice(dotIndex + 1) : \"\";\n}\n\nexport function isWorkspaceFileBrowserOpenable(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): boolean {\n if (entry.kind !== \"file\") {\n return false;\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (\n extension === \"pdf\" ||\n browserOpenableHtmlExtensions.has(extension) ||\n imageExtensions.has(extension) ||\n browserOpenableVideoExtensions.has(extension)\n ) {\n return true;\n }\n\n return classifyWorkspaceFilePreviewKind(entry) === \"text\";\n}\n\nexport function shouldFilterVideoPlayersForOpenWith(\n entry: Pick<WorkspaceFilePreviewEntry, \"kind\" | \"name\" | \"path\">\n): boolean {\n if (entry.kind !== \"file\") {\n return false;\n }\n\n const visualKind = resolveWorkspaceFileVisualKind(entry);\n if (visualKind === \"video\") {\n return false;\n }\n\n const extension = resolveWorkspaceFileExtension(\n entry.path || entry.name || \"\"\n );\n if (workspaceFileVideoHandlerCollisionExtensions.has(extension)) {\n return true;\n }\n\n if (visualKind === \"code\" || visualKind === \"markdown\") {\n return true;\n }\n\n return classifyWorkspaceFilePreviewKind(entry) === \"text\";\n}\n\nexport function resolveWorkspaceImageMimeType(\n pathOrName: string\n): string | null {\n switch (resolveWorkspaceFileExtension(pathOrName)) {\n case \"avif\":\n return \"image/avif\";\n case \"gif\":\n return \"image/gif\";\n case \"jpeg\":\n case \"jpg\":\n return \"image/jpeg\";\n case \"png\":\n return \"image/png\";\n case \"svg\":\n return \"image/svg+xml\";\n case \"webp\":\n return \"image/webp\";\n default:\n return null;\n }\n}\n\nexport function classifyWorkspaceFilePreviewKind(\n entry: Pick<\n WorkspaceFilePreviewEntry,\n \"displayName\" | \"kind\" | \"name\" | \"path\"\n >\n): WorkspaceFilePreviewKind | null {\n if (entry.kind !== \"file\") {\n return null;\n }\n\n const name = resolveWorkspaceFilePreviewName(entry);\n if (resolveWorkspaceImageMimeType(name) !== null) {\n return \"image\";\n }\n\n const normalizedName = name.trim().toLowerCase();\n const extension = resolveWorkspaceFileExtension(name);\n if (textExtensions.has(extension) || textFileNames.has(normalizedName)) {\n return \"text\";\n }\n\n return null;\n}\n\nexport function resolveWorkspaceFileActivationTarget(\n entry: WorkspaceFilePreviewEntry\n): WorkspaceFilePreviewActivationTarget | null {\n const fileKind = classifyWorkspaceFilePreviewKind(entry);\n if (!fileKind) {\n return null;\n }\n\n const target: WorkspaceFilePreviewActivationTarget = {\n fileKind,\n name: resolveWorkspaceFilePreviewName(entry),\n path: entry.path\n };\n if (entry.mtimeMs !== undefined) {\n target.mtimeMs = entry.mtimeMs;\n }\n if (entry.sizeBytes !== undefined) {\n target.sizeBytes = entry.sizeBytes;\n }\n return target;\n}\n\nexport function resolveWorkspaceFilePreviewReadiness<\n TEntry extends WorkspaceFilePreviewEntry\n>(entry: TEntry): WorkspaceFilePreviewReadiness<TEntry> {\n if (entry.kind === \"directory\" || entry.kind === \"folder\") {\n return {\n entry,\n status: \"directory\"\n };\n }\n\n const target = resolveWorkspaceFileActivationTarget(entry);\n if (!target) {\n return {\n entry,\n status: \"unsupported\"\n };\n }\n\n if (\n target.fileKind === \"text\" &&\n isWorkspaceTextFileTooLarge(entry.sizeBytes)\n ) {\n return {\n entry,\n maxSizeBytes: workspaceFileTextMaxBytes,\n reason: \"text_too_large\",\n status: \"readonly\"\n };\n }\n\n if (isWorkspacePreviewFileTooLarge(entry.sizeBytes)) {\n return {\n entry,\n maxSizeBytes: workspaceFilePreviewMaxBytes,\n reason: \"file_too_large\",\n status: \"readonly\"\n };\n }\n\n return {\n entry,\n status: \"ready\",\n target\n };\n}\n\nexport function createWorkspaceFilePreviewLoadedState<\n TEntry extends WorkspaceFilePreviewEntry,\n TTarget extends WorkspaceFilePreviewActivationTarget\n>(input: {\n bytes: Uint8Array | ArrayBuffer;\n contentType?: string | null;\n entry: TEntry;\n target: TTarget;\n}): WorkspaceFilePreviewLoadedState<TEntry, TTarget> {\n if (input.target.fileKind === \"image\") {\n return {\n bytes: copyWorkspaceFilePreviewBytes(input.bytes),\n contentType:\n input.contentType ??\n resolveWorkspaceImageMimeType(input.target.name) ??\n \"application/octet-stream\",\n entry: input.target,\n status: \"image\"\n };\n }\n\n try {\n const content = decodeWorkspaceTextFile(input.bytes);\n if (looksLikeBinaryText(content)) {\n return {\n entry: input.entry,\n reason: \"binary\",\n status: \"readonly\"\n };\n }\n return {\n content,\n entry: input.target,\n status: \"text\"\n };\n } catch {\n return {\n entry: input.entry,\n reason: \"decode_failed\",\n status: \"readonly\"\n };\n }\n}\n\nexport function resolveWorkspaceFilePreviewName(\n entry: Pick<WorkspaceFilePreviewEntry, \"displayName\" | \"name\" | \"path\">\n): string {\n return (\n entry.name?.trim() ||\n entry.displayName?.trim() ||\n entry.path.split(\"/\").pop()?.trim() ||\n entry.path\n );\n}\n\nexport function isWorkspaceTextFileTooLarge(\n sizeBytes?: number | null\n): boolean {\n return (\n typeof sizeBytes === \"number\" &&\n Number.isFinite(sizeBytes) &&\n sizeBytes > workspaceFileTextMaxBytes\n );\n}\n\nexport function isWorkspacePreviewFileTooLarge(\n sizeBytes?: number | null\n): boolean {\n return (\n typeof sizeBytes === \"number\" &&\n Number.isFinite(sizeBytes) &&\n sizeBytes > workspaceFilePreviewMaxBytes\n );\n}\n\nexport function decodeWorkspaceTextFile(\n bytes: Uint8Array | ArrayBuffer\n): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(\n normalizeWorkspaceFilePreviewBytes(bytes)\n );\n}\n\nexport function normalizeWorkspaceFilePreviewBytes(\n value: Uint8Array | ArrayBuffer\n): Uint8Array {\n if (value instanceof Uint8Array) {\n return value;\n }\n return new Uint8Array(value);\n}\n\nexport function copyWorkspaceFilePreviewBytes(\n bytes: Uint8Array | ArrayBuffer\n): Uint8Array<ArrayBuffer> {\n const normalized = normalizeWorkspaceFilePreviewBytes(bytes);\n const buffer = new ArrayBuffer(normalized.byteLength);\n const copy = new Uint8Array(buffer);\n copy.set(normalized);\n return copy;\n}\n\nexport function looksLikeBinaryText(content: string): boolean {\n if (content.length === 0) {\n return false;\n }\n\n const sample = content.slice(0, 4096);\n if (sample.includes(\"\\u0000\")) {\n return true;\n }\n\n let suspiciousControlChars = 0;\n for (let index = 0; index < sample.length; index += 1) {\n const code = sample.charCodeAt(index);\n const isAllowedWhitespace = code === 9 || code === 10 || code === 13;\n const isControlChar = code < 32 || (code >= 127 && code <= 159);\n if (isControlChar && !isAllowedWhitespace) {\n suspiciousControlChars += 1;\n }\n }\n\n return suspiciousControlChars / sample.length > 0.12;\n}\n\nexport function formatWorkspacePreviewByteLimit(sizeBytes: number): string {\n if (sizeBytes < 1024) {\n return `${sizeBytes} B`;\n }\n\n const mebibytes = sizeBytes / (1024 * 1024);\n if (Number.isInteger(mebibytes)) {\n return `${mebibytes} MiB`;\n }\n return `${mebibytes.toFixed(1)} MiB`;\n}\n"],"mappings":"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","names":[]}