@ai-matrx/media 0.8.0 → 0.9.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/CHANGELOG.md +15 -0
- package/dist/files/engine/api/assets.d.ts +223 -0
- package/dist/files/engine/api/assets.js +124 -0
- package/dist/files/engine/api/assets.js.map +1 -0
- package/dist/files/engine/api/direct.d.ts +43 -0
- package/dist/files/engine/api/direct.js +73 -0
- package/dist/files/engine/api/direct.js.map +1 -0
- package/dist/files/engine/api/fileOrganization.d.ts +34 -0
- package/dist/files/engine/api/fileOrganization.js +38 -0
- package/dist/files/engine/api/fileOrganization.js.map +1 -0
- package/dist/files/engine/api/files.d.ts +249 -0
- package/dist/files/engine/api/files.js +231 -0
- package/dist/files/engine/api/files.js.map +1 -0
- package/dist/files/engine/api/folders.d.ts +63 -0
- package/dist/files/engine/api/folders.js +56 -0
- package/dist/files/engine/api/folders.js.map +1 -0
- package/dist/files/engine/api/office.d.ts +27 -0
- package/dist/files/engine/api/office.js +19 -0
- package/dist/files/engine/api/office.js.map +1 -0
- package/dist/files/engine/api/permissions.d.ts +37 -0
- package/dist/files/engine/api/permissions.js +109 -0
- package/dist/files/engine/api/permissions.js.map +1 -0
- package/dist/files/engine/api/versions.d.ts +26 -0
- package/dist/files/engine/api/versions.js +37 -0
- package/dist/files/engine/api/versions.js.map +1 -0
- package/dist/files/engine/cache/idb-store.d.ts +95 -0
- package/dist/files/engine/cache/idb-store.js +172 -0
- package/dist/files/engine/cache/idb-store.js.map +1 -0
- package/dist/files/engine/cache/policy.d.ts +14 -0
- package/dist/files/engine/cache/policy.js +40 -0
- package/dist/files/engine/cache/policy.js.map +1 -0
- package/dist/files/engine/cache/register-service-worker.d.ts +57 -0
- package/dist/files/engine/cache/register-service-worker.js +80 -0
- package/dist/files/engine/cache/register-service-worker.js.map +1 -0
- package/dist/files/engine/db-types.d.ts +48865 -0
- package/dist/files/engine/db-types.js +1 -0
- package/dist/files/engine/db-types.js.map +1 -0
- package/dist/files/engine/filesDb.d.ts +1913 -0
- package/dist/files/engine/filesDb.js +28 -0
- package/dist/files/engine/filesDb.js.map +1 -0
- package/dist/files/engine/handler/handler.d.ts +82 -0
- package/dist/files/engine/handler/handler.js +124 -0
- package/dist/files/engine/handler/handler.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFile.d.ts +17 -0
- package/dist/files/engine/handler/hooks/useFile.js +75 -0
- package/dist/files/engine/handler/hooks/useFile.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFileUpload.d.ts +76 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js +67 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js.map +1 -0
- package/dist/files/engine/handler/input/normalize.d.ts +14 -0
- package/dist/files/engine/handler/input/normalize.js +365 -0
- package/dist/files/engine/handler/input/normalize.js.map +1 -0
- package/dist/files/engine/handler/intelligence/access.d.ts +35 -0
- package/dist/files/engine/handler/intelligence/access.js +85 -0
- package/dist/files/engine/handler/intelligence/access.js.map +1 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.d.ts +21 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js +67 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js.map +1 -0
- package/dist/files/engine/handler/output/target.d.ts +19 -0
- package/dist/files/engine/handler/output/target.js +222 -0
- package/dist/files/engine/handler/output/target.js.map +1 -0
- package/dist/files/engine/handler/resolver.d.ts +34 -0
- package/dist/files/engine/handler/resolver.js +133 -0
- package/dist/files/engine/handler/resolver.js.map +1 -0
- package/dist/files/engine/handler/types.d.ts +367 -0
- package/dist/files/engine/handler/types.js +1 -0
- package/dist/files/engine/handler/types.js.map +1 -0
- package/dist/files/engine/handler/upload.d.ts +32 -0
- package/dist/files/engine/handler/upload.js +295 -0
- package/dist/files/engine/handler/upload.js.map +1 -0
- package/dist/files/engine/handler/utils/classify.d.ts +14 -0
- package/dist/files/engine/handler/utils/classify.js +27 -0
- package/dist/files/engine/handler/utils/classify.js.map +1 -0
- package/dist/files/engine/handler/utils/prefer-locator.d.ts +32 -0
- package/dist/files/engine/handler/utils/prefer-locator.js +34 -0
- package/dist/files/engine/handler/utils/prefer-locator.js.map +1 -0
- package/dist/files/engine/handler/utils/python-base.d.ts +129 -0
- package/dist/files/engine/handler/utils/python-base.js +60 -0
- package/dist/files/engine/handler/utils/python-base.js.map +1 -0
- package/dist/files/engine/hooks/blob-cache.d.ts +130 -0
- package/dist/files/engine/hooks/blob-cache.js +162 -0
- package/dist/files/engine/hooks/blob-cache.js.map +1 -0
- package/dist/files/engine/hooks/office-extraction-cache.d.ts +28 -0
- package/dist/files/engine/hooks/office-extraction-cache.js +67 -0
- package/dist/files/engine/hooks/office-extraction-cache.js.map +1 -0
- package/dist/files/engine/host/configure.d.ts +166 -0
- package/dist/files/engine/host/configure.js +30 -0
- package/dist/files/engine/host/configure.js.map +1 -0
- package/dist/files/engine/host/org.d.ts +8 -0
- package/dist/files/engine/host/org.js +16 -0
- package/dist/files/engine/host/org.js.map +1 -0
- package/dist/files/engine/host/python-client.d.ts +60 -0
- package/dist/files/engine/host/python-client.js +46 -0
- package/dist/files/engine/host/python-client.js.map +1 -0
- package/dist/files/engine/host/share-links.d.ts +20 -0
- package/dist/files/engine/host/share-links.js +32 -0
- package/dist/files/engine/host/share-links.js.map +1 -0
- package/dist/files/engine/host/store.d.ts +24 -0
- package/dist/files/engine/host/store.js +23 -0
- package/dist/files/engine/host/store.js.map +1 -0
- package/dist/files/engine/host/supabase.d.ts +3808 -0
- package/dist/files/engine/host/supabase.js +33 -0
- package/dist/files/engine/host/supabase.js.map +1 -0
- package/dist/files/engine/host/toast.d.ts +14 -0
- package/dist/files/engine/host/toast.js +20 -0
- package/dist/files/engine/host/toast.js.map +1 -0
- package/dist/files/engine/host/typed-client.d.ts +136 -0
- package/dist/files/engine/host/typed-client.js +66 -0
- package/dist/files/engine/host/typed-client.js.map +1 -0
- package/dist/files/engine/index.d.ts +22 -0
- package/dist/files/engine/index.js +49 -0
- package/dist/files/engine/index.js.map +1 -0
- package/dist/files/engine/media/our-file-sources.d.ts +39 -0
- package/dist/files/engine/media/our-file-sources.js +83 -0
- package/dist/files/engine/media/our-file-sources.js.map +1 -0
- package/dist/files/engine/media/signed-url.d.ts +1 -0
- package/dist/files/engine/media/signed-url.js +6 -0
- package/dist/files/engine/media/signed-url.js.map +1 -0
- package/dist/files/engine/redux/converters.d.ts +73 -0
- package/dist/files/engine/redux/converters.js +286 -0
- package/dist/files/engine/redux/converters.js.map +1 -0
- package/dist/files/engine/redux/file-hydration.d.ts +15 -0
- package/dist/files/engine/redux/file-hydration.js +46 -0
- package/dist/files/engine/redux/file-hydration.js.map +1 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.d.ts +5 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js +28 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js.map +1 -0
- package/dist/files/engine/redux/file-tree-timeout.d.ts +32 -0
- package/dist/files/engine/redux/file-tree-timeout.js +73 -0
- package/dist/files/engine/redux/file-tree-timeout.js.map +1 -0
- package/dist/files/engine/redux/mutation-toast-middleware.d.ts +27 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js +103 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js.map +1 -0
- package/dist/files/engine/redux/realtime-middleware.d.ts +61 -0
- package/dist/files/engine/redux/realtime-middleware.js +431 -0
- package/dist/files/engine/redux/realtime-middleware.js.map +1 -0
- package/dist/files/engine/redux/request-ledger.d.ts +68 -0
- package/dist/files/engine/redux/request-ledger.js +66 -0
- package/dist/files/engine/redux/request-ledger.js.map +1 -0
- package/dist/files/engine/redux/selectors.d.ts +3254 -0
- package/dist/files/engine/redux/selectors.js +428 -0
- package/dist/files/engine/redux/selectors.js.map +1 -0
- package/dist/files/engine/redux/slice.d.ts +157 -0
- package/dist/files/engine/redux/slice.js +836 -0
- package/dist/files/engine/redux/slice.js.map +1 -0
- package/dist/files/engine/redux/thunks.d.ts +449 -0
- package/dist/files/engine/redux/thunks.js +1684 -0
- package/dist/files/engine/redux/thunks.js.map +1 -0
- package/dist/files/engine/redux/tree-utils.d.ts +90 -0
- package/dist/files/engine/redux/tree-utils.js +200 -0
- package/dist/files/engine/redux/tree-utils.js.map +1 -0
- package/dist/files/engine/support/claimsUser.d.ts +77 -0
- package/dist/files/engine/support/claimsUser.js +48 -0
- package/dist/files/engine/support/claimsUser.js.map +1 -0
- package/dist/files/engine/support/datetime.d.ts +6 -0
- package/dist/files/engine/support/datetime.js +15 -0
- package/dist/files/engine/support/datetime.js.map +1 -0
- package/dist/files/engine/support/document-visibility.d.ts +14 -0
- package/dist/files/engine/support/document-visibility.js +28 -0
- package/dist/files/engine/support/document-visibility.js.map +1 -0
- package/dist/files/engine/support/logger.d.ts +28 -0
- package/dist/files/engine/support/logger.js +36 -0
- package/dist/files/engine/support/logger.js.map +1 -0
- package/dist/files/engine/types.d.ts +1081 -0
- package/dist/files/engine/types.js +30 -0
- package/dist/files/engine/types.js.map +1 -0
- package/dist/files/engine/upload/cloudUpload.d.ts +182 -0
- package/dist/files/engine/upload/cloudUpload.js +418 -0
- package/dist/files/engine/upload/cloudUpload.js.map +1 -0
- package/dist/files/engine/upload/tusUpload.d.ts +100 -0
- package/dist/files/engine/upload/tusUpload.js +258 -0
- package/dist/files/engine/upload/tusUpload.js.map +1 -0
- package/dist/files/engine/upload/uploadDedupGuard.d.ts +58 -0
- package/dist/files/engine/upload/uploadDedupGuard.js +41 -0
- package/dist/files/engine/upload/uploadDedupGuard.js.map +1 -0
- package/dist/files/engine/upload/uploadGuardOpeners.d.ts +70 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js +54 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js.map +1 -0
- package/dist/files/engine/utils/file-types.d.ts +243 -0
- package/dist/files/engine/utils/file-types.js +1722 -0
- package/dist/files/engine/utils/file-types.js.map +1 -0
- package/dist/files/engine/utils/user-visible.d.ts +133 -0
- package/dist/files/engine/utils/user-visible.js +115 -0
- package/dist/files/engine/utils/user-visible.js.map +1 -0
- package/package.json +393 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/utils/file-types.ts"],"sourcesContent":["/**\n * features/files/utils/file-types.ts\n *\n * THE SINGLE SOURCE OF TRUTH for cloud-files type capabilities.\n *\n * If you need to:\n * - Add support for a new file type\n * - Change which previewer a type uses\n * - Change which icon / color a type renders with\n * - Change whether a type renders a real thumbnail or a fallback icon\n * - Add or remove a thumbnail strategy\n * - Change a size cap for a particular kind\n *\n * → edit THIS file. Nothing else in the cloud-files feature should\n * encode this knowledge.\n *\n * The supporting modules (`mime.ts`, `icon-map.ts`,\n * `preview-capabilities.ts`) are thin wrappers that read from this\n * registry and exist only for backwards-compat with older imports.\n *\n * ─────────────────────────────────────────────────────────────────────\n * Structure\n * ─────────────────────────────────────────────────────────────────────\n *\n * FILE_TYPES is an array of declarative entries. Each entry says:\n *\n * - which extensions match it\n * - the canonical MIME type (used as a fallback when the server\n * didn't send one and to canonicalize between the dozens of\n * legacy aliases the web has accumulated)\n * - which preview component to use\n * - which thumbnail strategy to use in grid / list views\n * - the icon + color tokens that drive the iconography\n * - a human-friendly display name (e.g. \"PDF Document\" vs \"pdf\")\n * - any size cap that overrides the global preview cap\n *\n * Mime-first override: when the browser knows a MIME (e.g. it set\n * `image/avif` even though we don't have `.avif` in the extension\n * table), the wrapper helpers fall back to MIME-prefix matching for\n * `image/*`, `video/*`, `audio/*`, and `application/pdf`, so adding a\n * brand-new image format never requires more than a one-line entry.\n */\n\nimport {\n Archive,\n Braces,\n Code,\n Database,\n File as FileIcon,\n FileAudio,\n FileCode,\n FileImage,\n FileJson,\n FileSpreadsheet,\n FileText,\n FileType,\n FileVideo,\n Folder,\n FolderOpen,\n Image as ImageIcon,\n Music,\n Package,\n PenTool,\n Subtitles,\n Video,\n type LucideIcon,\n} from \"lucide-react\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Top-level grouping. Drives sidebar filters and color rhythm. */\nexport type FileCategory =\n | \"CODE\"\n | \"DOCUMENT\"\n | \"AUDIO\"\n | \"IMAGE\"\n | \"VIDEO\"\n | \"ARCHIVE\"\n | \"DATA\"\n | \"NOTEBOOK\"\n | \"SUBTITLES\"\n | \"EMAIL\"\n | \"EBOOK\"\n | \"MODEL_3D\"\n | \"UNKNOWN\"\n | \"FOLDER\";\n\n/** Which previewer should render this kind. `generic` means \"no preview\".\n *\n * `svg` is split out from `image` on purpose: SVG is text-shaped (XML), so\n * it has a dedicated previewer (`SvgPreview` — renders with a transparency\n * grid and lets the user toggle to the raw markup) AND is editable in the\n * standard Monaco inline editor with XML syntax highlighting. Routing it\n * through `image` would hide the editor handoff and the source-view toggle.\n */\nexport type PreviewKind =\n | \"code\"\n | \"image\"\n | \"svg\"\n | \"html\"\n | \"audio\"\n | \"video\"\n | \"pdf\"\n | \"text\"\n | \"markdown\"\n | \"data\"\n | \"spreadsheet\"\n | \"office\"\n | \"generic\";\n\n/**\n * Strategy hint for the small inline thumbnail rendered in grid / list views.\n *\n * Phase 1b note: server-rendered thumbnails are now **universal** —\n * every uploaded file has `FileRecord.thumbnail_url` populated (image,\n * PDF page 1, video frame, audio waveform, or mime-family icon for\n * unknown). `MediaThumbnail` reads `file.thumbnailUrl` first regardless\n * of the strategy below; this enum now only controls the \"live render\"\n * fallback for the brief window between upload and variant render.\n *\n * \"image\" — `<img src={masterCdnUrl}>` fallback when the\n * backend variant hasn't finished rendering yet.\n * \"video-poster\" — `<video preload=\"metadata\">` fallback for the\n * same window.\n * \"pdf-firstpage\" — historic; Phase 1c now ships `page1_url` via\n * the variants store. The grid path uses\n * `file.thumbnailUrl`; full-page detail views can\n * bind `Asset.variants[\"page1_url\"].url`.\n * \"backend-thumb\" — historic; now equivalent to `icon` for the live\n * render fallback because the universal pipeline\n * handles every backend-thumb case automatically.\n * \"icon\" — no live render fallback. The `file.thumbnailUrl`\n * lookup (or icon) is the only source.\n */\nexport type ThumbnailStrategy =\n \"image\" | \"video-poster\" | \"pdf-firstpage\" | \"backend-thumb\" | \"icon\";\n\n/**\n * One row in the registry. Most fields are static; the `extensions`,\n * `aliases`, `aliasPrefixes`, and `mime` fields are the lookup keys.\n */\nexport interface FileTypeEntry {\n /** Extensions (lowercased, no dot) that map to this entry. First wins. */\n extensions: readonly string[];\n /**\n * Exact filenames (matched as-is, then case-insensitively) that resolve to\n * this entry — covers the universe of \"no extension OR purely dotfile\"\n * text-config files. e.g. `[\"Dockerfile\", \".dockerignore\"]`.\n */\n aliases?: readonly string[];\n /**\n * Filename prefixes that resolve to this entry. Matched after exact\n * extensions / aliases fail. Useful for `.env*` style variants\n * (`.env.local`, `.env.production`, ...). Compared case-insensitively.\n */\n aliasPrefixes?: readonly string[];\n /** Canonical MIME. Used to fill `mime_type` when the server didn't send one. */\n mime: string;\n category: FileCategory;\n /** Specific name within the category, e.g. \"PYTHON\", \"MP3\". UPPERCASE. */\n subCategory: string;\n /** Human-friendly name shown in tooltips / \"What can I preview?\" lists. */\n displayName: string;\n previewKind: PreviewKind;\n thumbnailStrategy: ThumbnailStrategy;\n /** Tailwind text color token, e.g. `text-emerald-500`. */\n color: string;\n icon: LucideIcon;\n /** Override the global 10MB preview cap if the previewer can stream. */\n previewSizeCapOverride?: number | null;\n}\n\n/** What a caller actually needs to render any given file. */\nexport interface FileTypeDetails {\n category: FileCategory;\n subCategory: string;\n displayName: string;\n previewKind: PreviewKind;\n thumbnailStrategy: ThumbnailStrategy;\n color: string;\n icon: LucideIcon;\n /** Convenience — true when previewKind !== \"generic\". */\n canPreview: boolean;\n /** Convenience — null = use the global cap. */\n previewSizeCapOverride: number | null;\n /** The canonical MIME for the resolved type. */\n mime: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants — the actual table\n// ---------------------------------------------------------------------------\n\n/** Global default preview cap. Bigger files render a \"too large\" message. */\nexport const MAX_INLINE_PREVIEW_BYTES = 10 * 1024 * 1024; // 10MB\n\n/**\n * The registry. Order matters only when an extension belongs to multiple\n * entries — the first match wins. In practice every extension is unique.\n *\n * Adding a new file type:\n * 1. Append a new entry.\n * 2. If the previewKind is something new (not in `PreviewKind`), add it\n * to that union AND register a previewer in\n * `components/core/FilePreview/FilePreview.tsx`.\n * 3. If the thumbnail strategy is new, extend `ThumbnailStrategy` and\n * handle it in `MediaThumbnail`.\n *\n * That's it. No edits to `mime.ts`, `icon-map.ts`, etc.\n */\nexport const FILE_TYPES: readonly FileTypeEntry[] = [\n // ────────────────────── IMAGE ──────────────────────\n {\n extensions: [\"jpg\", \"jpeg\"],\n mime: \"image/jpeg\",\n category: \"IMAGE\",\n subCategory: \"JPEG\",\n displayName: \"JPEG image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"png\"],\n mime: \"image/png\",\n category: \"IMAGE\",\n subCategory: \"PNG\",\n displayName: \"PNG image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"gif\"],\n mime: \"image/gif\",\n category: \"IMAGE\",\n subCategory: \"GIF\",\n displayName: \"GIF image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"webp\"],\n mime: \"image/webp\",\n category: \"IMAGE\",\n subCategory: \"WEBP\",\n displayName: \"WebP image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n // AVIF — modern image format, supported natively by all evergreen browsers.\n {\n extensions: [\"avif\"],\n mime: \"image/avif\",\n category: \"IMAGE\",\n subCategory: \"AVIF\",\n displayName: \"AVIF image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n // SVG is text-under-the-hood (XML) so it gets its own previewKind. The\n // SvgPreview renderer shows the rasterized vector on a transparency grid\n // and exposes a \"Source\" toggle for the raw markup; the inline Monaco\n // editor opens it as XML for direct edits. A distinct PenTool icon flags\n // it as vector rather than the generic raster ImageIcon used by PNG/JPG.\n {\n extensions: [\"svg\"],\n mime: \"image/svg+xml\",\n category: \"IMAGE\",\n subCategory: \"SVG\",\n displayName: \"SVG image\",\n previewKind: \"svg\",\n thumbnailStrategy: \"image\",\n color: \"text-amber-500\",\n icon: PenTool,\n },\n // HEIC / HEIF — Apple still uses these heavily. The canonical asset\n // pipeline emits an aspect-preserving JPEG `preview_url` plus universal\n // thumbnail variants; clients never bind the source bytes to `<img>`.\n {\n extensions: [\"heic\"],\n mime: \"image/heic\",\n category: \"IMAGE\",\n subCategory: \"HEIC\",\n displayName: \"HEIC image (iOS)\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"heif\"],\n mime: \"image/heif\",\n category: \"IMAGE\",\n subCategory: \"HEIF\",\n displayName: \"HEIF image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n // BMP / TIFF — older raster formats. Browser support is uneven (TIFF only\n // on Safari) but `<img>` will fail gracefully to the icon on others.\n {\n extensions: [\"bmp\"],\n mime: \"image/bmp\",\n category: \"IMAGE\",\n subCategory: \"BMP\",\n displayName: \"Bitmap image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"tif\", \"tiff\"],\n mime: \"image/tiff\",\n category: \"IMAGE\",\n subCategory: \"TIFF\",\n displayName: \"TIFF image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n {\n extensions: [\"ico\"],\n mime: \"image/x-icon\",\n category: \"IMAGE\",\n subCategory: \"ICO\",\n displayName: \"Icon image\",\n previewKind: \"image\",\n thumbnailStrategy: \"image\",\n color: \"text-emerald-500\",\n icon: ImageIcon,\n },\n\n // ────────────────────── VIDEO ──────────────────────\n // Video gets a `video-poster` thumbnail strategy: a muted, tiny `<video>`\n // element renders the first frame in place of a generic icon.\n {\n extensions: [\"mp4\"],\n mime: \"video/mp4\",\n category: \"VIDEO\",\n subCategory: \"MP4\",\n displayName: \"MP4 video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null, // streamed inline — no cap\n },\n {\n extensions: [\"mov\"],\n mime: \"video/quicktime\",\n category: \"VIDEO\",\n subCategory: \"MOV\",\n displayName: \"QuickTime video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"webm\"],\n mime: \"video/webm\",\n category: \"VIDEO\",\n subCategory: \"WEBM\",\n displayName: \"WebM video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"mkv\"],\n mime: \"video/x-matroska\",\n category: \"VIDEO\",\n subCategory: \"MKV\",\n displayName: \"Matroska video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"avi\"],\n mime: \"video/x-msvideo\",\n category: \"VIDEO\",\n subCategory: \"AVI\",\n displayName: \"AVI video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"m4v\"],\n mime: \"video/mp4\",\n category: \"VIDEO\",\n subCategory: \"M4V\",\n displayName: \"iTunes video\",\n previewKind: \"video\",\n thumbnailStrategy: \"video-poster\",\n color: \"text-purple-500\",\n icon: Video,\n previewSizeCapOverride: null,\n },\n\n // ────────────────────── AUDIO ──────────────────────\n // Audio thumbs stay as icons — waveform extraction is not worth the\n // bundle cost. Long-term: a backend cover-art / waveform field.\n {\n extensions: [\"mp3\"],\n mime: \"audio/mpeg\",\n category: \"AUDIO\",\n subCategory: \"MP3\",\n displayName: \"MP3 audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"wav\"],\n mime: \"audio/wav\",\n category: \"AUDIO\",\n subCategory: \"WAV\",\n displayName: \"WAV audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"ogg\"],\n mime: \"audio/ogg\",\n category: \"AUDIO\",\n subCategory: \"OGG\",\n displayName: \"Ogg audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"m4a\"],\n mime: \"audio/mp4\",\n category: \"AUDIO\",\n subCategory: \"M4A\",\n displayName: \"AAC audio (M4A)\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"aac\"],\n mime: \"audio/aac\",\n category: \"AUDIO\",\n subCategory: \"AAC\",\n displayName: \"AAC audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"flac\"],\n mime: \"audio/flac\",\n category: \"AUDIO\",\n subCategory: \"FLAC\",\n displayName: \"FLAC audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"opus\"],\n mime: \"audio/opus\",\n category: \"AUDIO\",\n subCategory: \"OPUS\",\n displayName: \"Opus audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n // MIME-only — `.webm` extension stays mapped to VIDEO below because the\n // container can hold either track type. Rows whose server MIME is\n // `audio/webm` (MediaRecorder speech captures, FastFire, transcripts)\n // resolve via BY_MIME in `getFilePreviewProfile`. See\n // docs/files/transcript-recordings-system-relocation.md.\n {\n extensions: [],\n mime: \"audio/webm\",\n category: \"AUDIO\",\n subCategory: \"WEBM\",\n displayName: \"WebM audio\",\n previewKind: \"audio\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Music,\n previewSizeCapOverride: null,\n },\n\n // ────────────────────── PDF ──────────────────────\n {\n extensions: [\"pdf\"],\n mime: \"application/pdf\",\n category: \"DOCUMENT\",\n subCategory: \"PDF\",\n displayName: \"PDF document\",\n previewKind: \"pdf\",\n // First-page render via pdfjs is a future enhancement. For now we use the\n // category icon — pdfjs is too heavy to load just for thumbs in a folder\n // listing. When backend thumbs land we'll switch this to \"backend-thumb\".\n thumbnailStrategy: \"icon\",\n color: \"text-red-500\",\n icon: FileType,\n previewSizeCapOverride: null,\n },\n\n // ────────────────────── MARKDOWN ──────────────────────\n {\n extensions: [\"md\", \"markdown\"],\n mime: \"text/markdown\",\n category: \"DOCUMENT\",\n subCategory: \"MARKDOWN\",\n displayName: \"Markdown\",\n previewKind: \"markdown\",\n thumbnailStrategy: \"icon\",\n color: \"text-slate-400\",\n icon: FileText,\n },\n {\n extensions: [\"mdx\"],\n mime: \"text/markdown\",\n category: \"DOCUMENT\",\n subCategory: \"MDX\",\n displayName: \"MDX (Markdown + JSX)\",\n previewKind: \"markdown\",\n thumbnailStrategy: \"icon\",\n color: \"text-slate-400\",\n icon: FileText,\n },\n\n // ────────────────────── PLAIN TEXT / LOG / SUBTITLES ──────────────────────\n {\n extensions: [\"txt\", \"text\", \"asc\", \"me\"],\n aliases: [\n // Project metadata that ships without an extension\n \"README\",\n \"Readme\",\n \"readme\",\n \"LICENSE\",\n \"License\",\n \"license\",\n \"LICENCE\",\n \"COPYING\",\n \"COPYRIGHT\",\n \"NOTICE\",\n \"AUTHORS\",\n \"CONTRIBUTORS\",\n \"MAINTAINERS\",\n \"CHANGELOG\",\n \"CHANGES\",\n \"HISTORY\",\n \"NEWS\",\n \"TODO\",\n \"INSTALL\",\n \"VERSION\",\n \"MANIFEST\",\n \"PATENTS\",\n \"ROADMAP\",\n \"SECURITY\",\n ],\n mime: \"text/plain\",\n category: \"DOCUMENT\",\n subCategory: \"PLAIN\",\n displayName: \"Plain text\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-muted-foreground\",\n icon: FileText,\n },\n {\n extensions: [\"log\", \"out\", \"err\"],\n mime: \"text/plain\",\n category: \"DOCUMENT\",\n subCategory: \"LOG\",\n displayName: \"Log file\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-muted-foreground\",\n icon: FileText,\n },\n // Dockerfile-family — text with custom syntax. We keep them in the CODE\n // category so they get a syntax-highlighting language hint downstream.\n {\n extensions: [\"dockerfile\", \"containerfile\"],\n aliases: [\"Dockerfile\", \"Containerfile\", \"dockerfile\"],\n aliasPrefixes: [\"Dockerfile.\", \"dockerfile.\", \"Containerfile.\"],\n mime: \"text/x-dockerfile\",\n category: \"CODE\",\n subCategory: \"DOCKERFILE\",\n displayName: \"Dockerfile\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-500\",\n icon: Code,\n },\n // Makefile-family — also custom-syntax text.\n {\n extensions: [\"mk\", \"make\"],\n aliases: [\n \"Makefile\",\n \"makefile\",\n \"GNUmakefile\",\n \"BSDmakefile\",\n \"Procfile\",\n \"Pipfile\",\n \"Justfile\",\n \"justfile\",\n \"Earthfile\",\n ],\n aliasPrefixes: [\"Makefile.\", \"makefile.\"],\n mime: \"text/x-makefile\",\n category: \"CODE\",\n subCategory: \"MAKEFILE\",\n displayName: \"Makefile\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: Code,\n },\n // Patches & diffs — text.\n {\n extensions: [\"diff\", \"patch\"],\n mime: \"text/x-diff\",\n category: \"CODE\",\n subCategory: \"PATCH\",\n displayName: \"Patch / diff\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: FileText,\n },\n // INI / properties / cfg — historically common config text formats.\n {\n extensions: [\"ini\", \"cfg\", \"conf\", \"config\", \"properties\", \"prefs\"],\n mime: \"text/x-properties\",\n category: \"CODE\",\n subCategory: \"CONFIG\",\n displayName: \"Config file\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-400\",\n icon: Braces,\n },\n // BibTeX / TeX / LaTeX — plain text with markup.\n {\n extensions: [\"tex\", \"latex\", \"ltx\", \"sty\", \"cls\", \"bib\"],\n mime: \"text/x-tex\",\n category: \"CODE\",\n subCategory: \"TEX\",\n displayName: \"TeX / LaTeX\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-500\",\n icon: FileText,\n },\n // reStructuredText / AsciiDoc — text doc formats.\n {\n extensions: [\"rst\", \"adoc\", \"asciidoc\", \"org\"],\n mime: \"text/plain\",\n category: \"DOCUMENT\",\n subCategory: \"DOC\",\n displayName: \"Document\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-slate-400\",\n icon: FileText,\n },\n {\n extensions: [\"srt\"],\n mime: \"application/x-subrip\",\n category: \"SUBTITLES\",\n subCategory: \"SRT\",\n displayName: \"SubRip subtitles\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-cyan-500\",\n icon: Subtitles,\n },\n {\n extensions: [\"vtt\"],\n mime: \"text/vtt\",\n category: \"SUBTITLES\",\n subCategory: \"VTT\",\n displayName: \"WebVTT subtitles\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-cyan-500\",\n icon: Subtitles,\n },\n\n // ────────────────────── CODE ──────────────────────\n {\n extensions: [\"js\", \"mjs\", \"cjs\"],\n mime: \"text/javascript\",\n category: \"CODE\",\n subCategory: \"JAVASCRIPT\",\n displayName: \"JavaScript\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-yellow-500\",\n icon: FileCode,\n },\n {\n extensions: [\"jsx\"],\n mime: \"text/javascript\",\n category: \"CODE\",\n subCategory: \"JAVASCRIPT\",\n displayName: \"JSX\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-yellow-500\",\n icon: FileCode,\n },\n {\n extensions: [\"ts\"],\n mime: \"text/typescript\",\n category: \"CODE\",\n subCategory: \"TYPESCRIPT\",\n displayName: \"TypeScript\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-500\",\n icon: FileCode,\n },\n {\n extensions: [\"tsx\"],\n mime: \"text/typescript\",\n category: \"CODE\",\n subCategory: \"TYPESCRIPT\",\n displayName: \"TSX\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-500\",\n icon: FileCode,\n },\n {\n extensions: [\"py\"],\n mime: \"text/x-python\",\n category: \"CODE\",\n subCategory: \"PYTHON\",\n displayName: \"Python\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-500\",\n icon: Code,\n },\n {\n extensions: [\"rb\", \"rbw\", \"rake\", \"ru\", \"gemspec\"],\n aliases: [\n \"Rakefile\",\n \"Gemfile\",\n \"Vagrantfile\",\n \"Berksfile\",\n \"Brewfile\",\n \"Capfile\",\n \"Guardfile\",\n \"Podfile\",\n \"Fastfile\",\n \"Appfile\",\n \"Deliverfile\",\n \"Matchfile\",\n \"Pluginfile\",\n \"Snapfile\",\n \"Scanfile\",\n \"Gymfile\",\n ],\n mime: \"text/x-ruby\",\n category: \"CODE\",\n subCategory: \"RUBY\",\n displayName: \"Ruby\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-red-500\",\n icon: Code,\n },\n {\n extensions: [\"go\"],\n mime: \"text/x-go\",\n category: \"CODE\",\n subCategory: \"GO\",\n displayName: \"Go\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-cyan-500\",\n icon: Code,\n },\n {\n extensions: [\"rs\"],\n mime: \"text/x-rust\",\n category: \"CODE\",\n subCategory: \"RUST\",\n displayName: \"Rust\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-600\",\n icon: Code,\n },\n {\n extensions: [\"java\"],\n mime: \"text/x-java\",\n category: \"CODE\",\n subCategory: \"JAVA\",\n displayName: \"Java\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-red-600\",\n icon: Code,\n },\n {\n extensions: [\"swift\"],\n mime: \"text/x-swift\",\n category: \"CODE\",\n subCategory: \"SWIFT\",\n displayName: \"Swift\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: Code,\n },\n {\n extensions: [\"c\", \"h\"],\n mime: \"text/x-c\",\n category: \"CODE\",\n subCategory: \"C\",\n displayName: \"C\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-600\",\n icon: Code,\n },\n {\n extensions: [\"cpp\", \"cc\", \"cxx\", \"hpp\"],\n mime: \"text/x-c++\",\n category: \"CODE\",\n subCategory: \"CPP\",\n displayName: \"C++\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-700\",\n icon: Code,\n },\n {\n extensions: [\"cs\"],\n mime: \"text/x-csharp\",\n category: \"CODE\",\n subCategory: \"CSHARP\",\n displayName: \"C#\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-purple-600\",\n icon: Code,\n },\n // HTML — `previewKind: \"html\"` so the previewer can offer a Rendered /\n // Source toggle (sandboxed iframe vs syntax-highlighted markup). Without\n // the split, opening a saved web page in /files just showed its source,\n // which is the wrong default for almost every consumer. The Edit tab\n // mounts Monaco with `html` language (see CloudFileInlineEditor's\n // LANGUAGE_BY_EXT) and source-of-truth editing happens there.\n {\n extensions: [\"html\", \"htm\"],\n mime: \"text/html\",\n category: \"CODE\",\n subCategory: \"HTML\",\n displayName: \"HTML\",\n previewKind: \"html\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-400\",\n icon: Code,\n },\n {\n extensions: [\"css\"],\n mime: \"text/css\",\n category: \"CODE\",\n subCategory: \"CSS\",\n displayName: \"CSS\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-sky-400\",\n icon: Code,\n },\n {\n extensions: [\"scss\"],\n mime: \"text/x-scss\",\n category: \"CODE\",\n subCategory: \"SCSS\",\n displayName: \"SCSS\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-400\",\n icon: Code,\n },\n {\n extensions: [\"sh\", \"bash\", \"zsh\", \"fish\", \"ksh\", \"ash\"],\n aliases: [\n // Bash / shell config dotfiles\n \".bashrc\",\n \".bash_logout\",\n \".bash_profile\",\n \".bash_history\",\n \".bash_aliases\",\n \".profile\",\n \".zshrc\",\n \".zshenv\",\n \".zprofile\",\n \".zlogin\",\n \".zlogout\",\n \".zsh_history\",\n \".inputrc\",\n \".curlrc\",\n \".wgetrc\",\n \".npmrc\",\n \".yarnrc\",\n \".pypirc\",\n \".tool-versions\",\n \".nvmrc\",\n \".node-version\",\n \".python-version\",\n \".ruby-version\",\n // Env files (exact)\n \".env\",\n \".envrc\",\n \".sandbox_env\",\n // Editor / formatter / linter config dotfiles (no extension variants)\n \".editorconfig\",\n \".gitignore\",\n \".gitattributes\",\n \".gitmodules\",\n \".gitkeep\",\n \".gitconfig\",\n \".dockerignore\",\n \".eslintignore\",\n \".prettierignore\",\n \".npmignore\",\n \".browserslistrc\",\n \".stylelintignore\",\n \".rsync-filter\",\n // SSH / system config that is plain text\n \"authorized_keys\",\n \"known_hosts\",\n \"config\",\n \"hosts\",\n \"passwd\",\n \"group\",\n \"shadow\",\n \"fstab\",\n \"crontab\",\n \"resolv.conf\",\n \"ssh_config\",\n \"sshd_config\",\n // Build / cache markers (text)\n \"CACHEDIR.TAG\",\n \"CODEOWNERS\",\n ],\n aliasPrefixes: [\n // .env.local, .env.production, .env.development, .env.test, etc.\n \".env.\",\n \".envrc.\",\n ],\n mime: \"text/x-shellscript\",\n category: \"CODE\",\n subCategory: \"SHELL\",\n displayName: \"Shell script\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-400\",\n icon: Code,\n },\n {\n extensions: [\"sql\"],\n mime: \"application/sql\",\n category: \"CODE\",\n subCategory: \"SQL\",\n displayName: \"SQL\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-400\",\n icon: Code,\n },\n {\n extensions: [\"lua\"],\n mime: \"text/x-lua\",\n category: \"CODE\",\n subCategory: \"LUA\",\n displayName: \"Lua\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-500\",\n icon: Code,\n },\n {\n extensions: [\"pl\", \"pm\"],\n mime: \"text/x-perl\",\n category: \"CODE\",\n subCategory: \"PERL\",\n displayName: \"Perl\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-indigo-400\",\n icon: Code,\n },\n {\n extensions: [\"r\", \"R\", \"rmd\", \"Rmd\"],\n mime: \"text/x-r\",\n category: \"CODE\",\n subCategory: \"R\",\n displayName: \"R\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: Code,\n },\n {\n extensions: [\"dart\"],\n mime: \"text/x-dart\",\n category: \"CODE\",\n subCategory: \"DART\",\n displayName: \"Dart\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-cyan-500\",\n icon: Code,\n },\n {\n extensions: [\"kt\", \"kts\"],\n mime: \"text/x-kotlin\",\n category: \"CODE\",\n subCategory: \"KOTLIN\",\n displayName: \"Kotlin\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-400\",\n icon: Code,\n },\n {\n extensions: [\"scala\", \"sbt\"],\n mime: \"text/x-scala\",\n category: \"CODE\",\n subCategory: \"SCALA\",\n displayName: \"Scala\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-red-500\",\n icon: Code,\n },\n {\n extensions: [\"clj\", \"cljs\", \"cljc\", \"edn\"],\n mime: \"text/x-clojure\",\n category: \"CODE\",\n subCategory: \"CLOJURE\",\n displayName: \"Clojure\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-500\",\n icon: Code,\n },\n {\n extensions: [\"ex\", \"exs\"],\n mime: \"text/x-elixir\",\n category: \"CODE\",\n subCategory: \"ELIXIR\",\n displayName: \"Elixir\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-purple-400\",\n icon: Code,\n },\n {\n extensions: [\"erl\", \"hrl\"],\n mime: \"text/x-erlang\",\n category: \"CODE\",\n subCategory: \"ERLANG\",\n displayName: \"Erlang\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-rose-500\",\n icon: Code,\n },\n {\n extensions: [\"hs\", \"lhs\"],\n mime: \"text/x-haskell\",\n category: \"CODE\",\n subCategory: \"HASKELL\",\n displayName: \"Haskell\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-violet-500\",\n icon: Code,\n },\n {\n extensions: [\"ml\", \"mli\", \"fs\", \"fsi\", \"fsx\"],\n mime: \"text/x-ocaml\",\n category: \"CODE\",\n subCategory: \"ML\",\n displayName: \"OCaml / F#\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: Code,\n },\n {\n extensions: [\"zig\"],\n mime: \"text/x-zig\",\n category: \"CODE\",\n subCategory: \"ZIG\",\n displayName: \"Zig\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-yellow-500\",\n icon: Code,\n },\n {\n extensions: [\"nim\", \"nims\"],\n mime: \"text/x-nim\",\n category: \"CODE\",\n subCategory: \"NIM\",\n displayName: \"Nim\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-yellow-400\",\n icon: Code,\n },\n {\n extensions: [\"jl\"],\n mime: \"text/x-julia\",\n category: \"CODE\",\n subCategory: \"JULIA\",\n displayName: \"Julia\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-purple-500\",\n icon: Code,\n },\n {\n extensions: [\"vue\"],\n mime: \"text/x-vue\",\n category: \"CODE\",\n subCategory: \"VUE\",\n displayName: \"Vue\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-500\",\n icon: Code,\n },\n {\n extensions: [\"svelte\"],\n mime: \"text/x-svelte\",\n category: \"CODE\",\n subCategory: \"SVELTE\",\n displayName: \"Svelte\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: Code,\n },\n {\n extensions: [\"astro\"],\n mime: \"text/x-astro\",\n category: \"CODE\",\n subCategory: \"ASTRO\",\n displayName: \"Astro\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-fuchsia-500\",\n icon: Code,\n },\n {\n extensions: [\"graphql\", \"gql\", \"graphqls\"],\n mime: \"application/graphql\",\n category: \"CODE\",\n subCategory: \"GRAPHQL\",\n displayName: \"GraphQL\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-pink-500\",\n icon: Code,\n },\n {\n extensions: [\"proto\"],\n mime: \"text/x-protobuf\",\n category: \"CODE\",\n subCategory: \"PROTO\",\n displayName: \"Protocol Buffers\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: Code,\n },\n {\n extensions: [\"sol\"],\n mime: \"text/x-solidity\",\n category: \"CODE\",\n subCategory: \"SOLIDITY\",\n displayName: \"Solidity\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-slate-400\",\n icon: Code,\n },\n {\n extensions: [\"bat\", \"cmd\"],\n mime: \"text/x-msdos-batch\",\n category: \"CODE\",\n subCategory: \"BATCH\",\n displayName: \"Batch script\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-400\",\n icon: Code,\n },\n {\n extensions: [\"ps1\", \"psm1\", \"psd1\"],\n mime: \"application/x-powershell\",\n category: \"CODE\",\n subCategory: \"POWERSHELL\",\n displayName: \"PowerShell\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: Code,\n },\n {\n extensions: [\"less\"],\n mime: \"text/x-less\",\n category: \"CODE\",\n subCategory: \"LESS\",\n displayName: \"LESS\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: Code,\n },\n {\n extensions: [\"styl\", \"stylus\"],\n mime: \"text/x-stylus\",\n category: \"CODE\",\n subCategory: \"STYLUS\",\n displayName: \"Stylus\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-500\",\n icon: Code,\n },\n {\n extensions: [\"php\", \"phtml\", \"phps\"],\n mime: \"application/x-php\",\n category: \"CODE\",\n subCategory: \"PHP\",\n displayName: \"PHP\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-indigo-500\",\n icon: Code,\n },\n {\n extensions: [\"twig\", \"jinja\", \"j2\", \"njk\"],\n mime: \"text/x-twig\",\n category: \"CODE\",\n subCategory: \"TEMPLATE\",\n displayName: \"Template\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-400\",\n icon: Code,\n },\n {\n extensions: [\"hbs\", \"handlebars\", \"mustache\", \"ejs\", \"liquid\"],\n mime: \"text/x-template\",\n category: \"CODE\",\n subCategory: \"TEMPLATE\",\n displayName: \"Template\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: Code,\n },\n\n // ────────────────────── DATA / CONFIG ──────────────────────\n {\n extensions: [\"json\", \"jsonc\", \"json5\", \"har\", \"geojson\", \"topojson\"],\n aliases: [\n \".babelrc\",\n \".eslintrc\",\n \".prettierrc\",\n \".stylelintrc\",\n \".swcrc\",\n \".huskyrc\",\n \".lintstagedrc\",\n ],\n mime: \"application/json\",\n category: \"DATA\",\n subCategory: \"JSON\",\n displayName: \"JSON\",\n previewKind: \"data\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: FileJson,\n },\n {\n extensions: [\"yaml\", \"yml\"],\n aliases: [\".yamllint\", \".clang-format\", \".clang-tidy\"],\n mime: \"application/yaml\",\n category: \"CODE\",\n subCategory: \"YAML\",\n displayName: \"YAML\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: Braces,\n },\n {\n extensions: [\"toml\"],\n mime: \"application/toml\",\n category: \"CODE\",\n subCategory: \"TOML\",\n displayName: \"TOML\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: Braces,\n },\n {\n extensions: [\"xml\"],\n mime: \"application/xml\",\n category: \"DATA\",\n subCategory: \"XML\",\n displayName: \"XML\",\n previewKind: \"data\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: Braces,\n },\n {\n extensions: [\"csv\"],\n mime: \"text/csv\",\n category: \"DATA\",\n subCategory: \"CSV\",\n displayName: \"CSV\",\n previewKind: \"data\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: FileSpreadsheet,\n },\n {\n extensions: [\"tsv\"],\n mime: \"text/tab-separated-values\",\n category: \"DATA\",\n subCategory: \"TSV\",\n displayName: \"TSV\",\n previewKind: \"data\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-500\",\n icon: FileSpreadsheet,\n },\n {\n extensions: [\"sqlite\", \"sqlite3\", \"db\"],\n mime: \"application/x-sqlite3\",\n category: \"DATA\",\n subCategory: \"SQLITE\",\n displayName: \"SQLite database\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: Database,\n },\n // PEM-encoded certs / CSRs are armored base64 — i.e. text. `.pem`\n // and `.csr` are PEM by convention; `.crt` / `.cer` / `.key` / `.pub`\n // can be either PEM or DER (binary), so they stay `generic` and the\n // binary viewer's byte sniff offers \"View as text\" when the bytes\n // turn out to be ASCII-armored.\n {\n extensions: [\"pem\", \"csr\"],\n mime: \"application/x-pem-file\",\n category: \"CODE\",\n subCategory: \"PEM\",\n displayName: \"PEM certificate / key\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: FileText,\n },\n {\n extensions: [\"crt\", \"cer\", \"der\", \"key\", \"pub\", \"p7b\", \"p7c\", \"pfx\", \"p12\"],\n mime: \"application/x-x509-ca-cert\",\n category: \"CODE\",\n subCategory: \"CERT\",\n displayName: \"Certificate\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-500\",\n icon: FileText,\n },\n // RDF / Turtle / N-Triples — text-based RDF data formats.\n {\n extensions: [\"ttl\", \"n3\", \"nt\", \"nq\", \"trig\"],\n mime: \"text/turtle\",\n category: \"DATA\",\n subCategory: \"RDF\",\n displayName: \"RDF / Turtle\",\n previewKind: \"code\",\n thumbnailStrategy: \"icon\",\n color: \"text-fuchsia-400\",\n icon: Braces,\n },\n\n // ────────────────────── SPREADSHEET (Excel) ──────────────────────\n {\n extensions: [\"xlsx\"],\n mime: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n category: \"DOCUMENT\",\n subCategory: \"EXCEL\",\n displayName: \"Excel workbook\",\n previewKind: \"spreadsheet\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-600\",\n icon: FileSpreadsheet,\n previewSizeCapOverride: null,\n },\n {\n extensions: [\"xls\"],\n mime: \"application/vnd.ms-excel\",\n category: \"DOCUMENT\",\n subCategory: \"EXCEL\",\n displayName: \"Excel workbook (legacy)\",\n previewKind: \"spreadsheet\",\n thumbnailStrategy: \"icon\",\n color: \"text-emerald-600\",\n icon: FileSpreadsheet,\n previewSizeCapOverride: null,\n },\n\n // ────────────────────── OFFICE (Word / PowerPoint) ──────────────────────\n // Server-side extraction preview: OfficePreview calls aidream\n // `GET /office/{file_id}/markdown` (the matrx-files Office codec) and renders\n // the document / deck as markdown, slide-by-slide for pptx. Legacy .doc/.ppt\n // ride the server's LibreOffice bridge. xlsx stays on the SheetJS previewer.\n {\n extensions: [\"doc\", \"docx\"],\n mime: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n category: \"DOCUMENT\",\n subCategory: \"WORD\",\n displayName: \"Word document\",\n previewKind: \"office\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-500\",\n icon: FileText,\n // No size cap needed: \"office\" is in the streamable-kind list of\n // getFilePreviewProfile — bytes never reach the browser (server extracts).\n },\n {\n extensions: [\"ppt\", \"pptx\"],\n mime: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n category: \"DOCUMENT\",\n subCategory: \"POWERPOINT\",\n displayName: \"PowerPoint\",\n previewKind: \"office\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-600\",\n icon: FileType,\n },\n\n // ────────────────────── NOTEBOOK ──────────────────────\n // .ipynb is JSON under the hood, so it works in our DataPreview today.\n // Eventually we'll ship a dedicated notebook renderer (cells + outputs).\n {\n extensions: [\"ipynb\"],\n mime: \"application/x-ipynb+json\",\n category: \"NOTEBOOK\",\n subCategory: \"JUPYTER\",\n displayName: \"Jupyter notebook\",\n previewKind: \"data\",\n thumbnailStrategy: \"icon\",\n color: \"text-orange-400\",\n icon: FileJson,\n },\n\n // ────────────────────── EBOOK ──────────────────────\n {\n extensions: [\"epub\"],\n mime: \"application/epub+zip\",\n category: \"EBOOK\",\n subCategory: \"EPUB\",\n displayName: \"EPUB ebook\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-indigo-500\",\n icon: FileText,\n },\n\n // ────────────────────── EMAIL ──────────────────────\n {\n extensions: [\"eml\"],\n mime: \"message/rfc822\",\n category: \"EMAIL\",\n subCategory: \"EML\",\n displayName: \"Email message\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-blue-400\",\n icon: FileText,\n },\n\n // ────────────────────── 3D / CAD ──────────────────────\n // No previewer yet (Three.js loader would be needed). Icons only.\n {\n extensions: [\"glb\", \"gltf\"],\n mime: \"model/gltf-binary\",\n category: \"MODEL_3D\",\n subCategory: \"GLTF\",\n displayName: \"3D model (glTF)\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-violet-400\",\n icon: Package,\n },\n {\n extensions: [\"stl\", \"obj\", \"fbx\"],\n mime: \"model/stl\",\n category: \"MODEL_3D\",\n subCategory: \"MESH\",\n displayName: \"3D mesh\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-violet-400\",\n icon: Package,\n },\n\n // ────────────────────── ARCHIVES ──────────────────────\n {\n extensions: [\"zip\"],\n mime: \"application/zip\",\n category: \"ARCHIVE\",\n subCategory: \"ZIP\",\n displayName: \"ZIP archive\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-600\",\n icon: Archive,\n },\n {\n extensions: [\"rar\"],\n mime: \"application/vnd.rar\",\n category: \"ARCHIVE\",\n subCategory: \"RAR\",\n displayName: \"RAR archive\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-600\",\n icon: Archive,\n },\n {\n extensions: [\"7z\"],\n mime: \"application/x-7z-compressed\",\n category: \"ARCHIVE\",\n subCategory: \"7Z\",\n displayName: \"7-Zip archive\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-600\",\n icon: Archive,\n },\n {\n extensions: [\"tar\"],\n mime: \"application/x-tar\",\n category: \"ARCHIVE\",\n subCategory: \"TAR\",\n displayName: \"TAR archive\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-600\",\n icon: Package,\n },\n {\n extensions: [\"gz\", \"tgz\"],\n mime: \"application/gzip\",\n category: \"ARCHIVE\",\n subCategory: \"GZ\",\n displayName: \"Gzip archive\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-amber-600\",\n icon: Package,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Defaults — used when an extension isn't in the registry.\n// ---------------------------------------------------------------------------\n\nconst UNKNOWN_DETAILS: FileTypeDetails = {\n category: \"UNKNOWN\",\n subCategory: \"UNKNOWN\",\n displayName: \"File\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-muted-foreground\",\n icon: FileIcon,\n canPreview: false,\n previewSizeCapOverride: null,\n mime: \"application/octet-stream\",\n};\n\n/**\n * Soft default for \"almost certainly text but we don't know what kind\"\n * filenames — unrecognized dotfiles, files with no extension, etc.\n *\n * Returning this from the dotfile heuristic instead of `UNKNOWN_DETAILS`\n * makes the file open in the text editor instead of the binary viewer.\n */\nconst ASSUMED_TEXT_DETAILS: FileTypeDetails = {\n category: \"DOCUMENT\",\n subCategory: \"PLAIN\",\n displayName: \"Text\",\n previewKind: \"text\",\n thumbnailStrategy: \"icon\",\n color: \"text-muted-foreground\",\n icon: FileText,\n canPreview: true,\n previewSizeCapOverride: null,\n mime: \"text/plain\",\n};\n\nconst FOLDER_DETAILS: FileTypeDetails = {\n category: \"FOLDER\",\n subCategory: \"FOLDER\",\n displayName: \"Folder\",\n previewKind: \"generic\",\n thumbnailStrategy: \"icon\",\n color: \"text-sky-500\",\n icon: Folder,\n canPreview: false,\n previewSizeCapOverride: null,\n mime: \"inode/directory\",\n};\n\n// ---------------------------------------------------------------------------\n// Lookup index — built once at module load.\n// ---------------------------------------------------------------------------\n\nconst BY_EXTENSION = new Map<string, FileTypeEntry>();\nconst BY_MIME = new Map<string, FileTypeEntry>();\n\n/** Exact filenames (case-sensitive). e.g. \"Dockerfile\" → entry. */\nconst BY_NAME_EXACT = new Map<string, FileTypeEntry>();\n/** Exact filenames (lowercased). Tried after the case-sensitive map. */\nconst BY_NAME_LOWER = new Map<string, FileTypeEntry>();\n/**\n * Filename prefix rules. Sorted by descending prefix length so the most\n * specific match wins (e.g. \".envrc.\" beats \".env.\").\n */\nconst NAME_PREFIX_RULES: Array<{ prefix: string; entry: FileTypeEntry }> = [];\n\nfor (const entry of FILE_TYPES) {\n for (const ext of entry.extensions)\n BY_EXTENSION.set(ext.toLowerCase(), entry);\n // Only the first entry per MIME wins — important for shared MIMEs like\n // text/plain (txt + log) where the extension is the better key.\n if (!BY_MIME.has(entry.mime)) BY_MIME.set(entry.mime, entry);\n if (entry.aliases) {\n for (const alias of entry.aliases) {\n BY_NAME_EXACT.set(alias, entry);\n const lower = alias.toLowerCase();\n if (!BY_NAME_LOWER.has(lower)) BY_NAME_LOWER.set(lower, entry);\n }\n }\n if (entry.aliasPrefixes) {\n for (const prefix of entry.aliasPrefixes) {\n NAME_PREFIX_RULES.push({ prefix: prefix.toLowerCase(), entry });\n }\n }\n}\nNAME_PREFIX_RULES.sort((a, b) => b.prefix.length - a.prefix.length);\n\nfunction entryToDetails(entry: FileTypeEntry): FileTypeDetails {\n return {\n category: entry.category,\n subCategory: entry.subCategory,\n displayName: entry.displayName,\n previewKind: entry.previewKind,\n thumbnailStrategy: entry.thumbnailStrategy,\n color: entry.color,\n icon: entry.icon,\n canPreview: entry.previewKind !== \"generic\",\n previewSizeCapOverride:\n entry.previewSizeCapOverride !== undefined\n ? entry.previewSizeCapOverride\n : null,\n mime: entry.mime,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public helpers\n// ---------------------------------------------------------------------------\n\n/** Lowercase extension without the dot, or \"\" if there isn't one. */\nfunction extOf(filename: string): string {\n const i = filename.lastIndexOf(\".\");\n if (i <= 0 || i === filename.length - 1) return \"\";\n return filename.slice(i + 1).toLowerCase();\n}\n\n/** Just the basename (last path segment) — handles forward and back slashes. */\nfunction basenameOf(filename: string): string {\n const slash = Math.max(filename.lastIndexOf(\"/\"), filename.lastIndexOf(\"\\\\\"));\n return slash >= 0 ? filename.slice(slash + 1) : filename;\n}\n\n/**\n * Resolve a filename to a registered entry, in order of specificity:\n * 1. exact basename match (case-sensitive — e.g. \"Dockerfile\")\n * 2. exact basename match (case-insensitive — e.g. \"dockerfile\")\n * 3. extension match (e.g. \"*.ts\" → typescript entry)\n * 4. prefix rule match (e.g. \".env.local\" → \".env.\" → shell entry)\n *\n * Returns null when no entry matches; callers fall back to the dotfile\n * heuristic and finally to UNKNOWN_DETAILS.\n */\nfunction resolveEntry(filename: string): FileTypeEntry | null {\n const name = basenameOf(filename);\n const exact = BY_NAME_EXACT.get(name);\n if (exact) return exact;\n const ci = BY_NAME_LOWER.get(name.toLowerCase());\n if (ci) return ci;\n const ext = extOf(name);\n if (ext) {\n const byExt = BY_EXTENSION.get(ext);\n if (byExt) return byExt;\n }\n const lower = name.toLowerCase();\n for (const rule of NAME_PREFIX_RULES) {\n if (lower.startsWith(rule.prefix)) return rule.entry;\n }\n return null;\n}\n\n/**\n * Heuristic fallback for filenames that didn't match the registry. We\n * bias toward \"this is text\" — most files agents encounter on a sandbox\n * VM are text-shaped, and even when they aren't, the binary viewer's\n * \"View as text\" override gives users the escape hatch. Treating\n * unknowns as text avoids the awful UX where opening `.bashrc` or\n * `authorized_keys` shows \"this file type can't be previewed.\"\n *\n * The rule: filenames that start with `.` are almost always config\n * dotfiles. They get the assumed-text profile so they open in Monaco.\n * Other unknown-extension files stay UNKNOWN — the binary viewer's\n * runtime byte sniff still offers a \"View as text\" button when the\n * actual bytes are printable.\n */\nfunction dotfileLooksLikeText(filename: string): boolean {\n const name = basenameOf(filename);\n if (!name.startsWith(\".\")) return false;\n // Skip the leading dot, then make sure there's at least one alphanumeric\n // character — guards against pathological \"..\", \"...\", etc.\n for (let i = 1; i < name.length; i++) {\n const c = name.charCodeAt(i);\n if (\n (c >= 48 && c <= 57) || // 0-9\n (c >= 65 && c <= 90) || // A-Z\n (c >= 97 && c <= 122) || // a-z\n c === 95 || // _\n c === 45 // -\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** Look up details by file name (basename / alias / prefix / extension match). */\nexport function getFileTypeDetails(filename: string): FileTypeDetails {\n const entry = resolveEntry(filename);\n if (entry) return entryToDetails(entry);\n if (dotfileLooksLikeText(filename)) return ASSUMED_TEXT_DETAILS;\n return UNKNOWN_DETAILS;\n}\n\n/**\n * Same as `getFileTypeDetails` but never falls through to UNKNOWN — used\n * by the binary viewer's \"View as text\" override and any caller that has\n * already decided \"treat this as text, no matter what.\"\n */\nexport function getAssumedTextDetails(filename: string): FileTypeDetails {\n const entry = resolveEntry(filename);\n if (entry) {\n const details = entryToDetails(entry);\n if (details.previewKind === \"generic\") {\n return { ...details, previewKind: \"text\", canPreview: true };\n }\n // Force-text view explicitly demanded — for an HTML file this means the\n // user clicked \"View as text\" in the binary viewer / code workspace.\n // Demote to plain text so they see the raw markup, not the rendered\n // iframe. (The standard Preview tab still gets the rendered toggle.)\n if (details.previewKind === \"html\") {\n return { ...details, previewKind: \"text\" };\n }\n return details;\n }\n return ASSUMED_TEXT_DETAILS;\n}\n\n/** True when the filename is recognized as text-bearing without sniffing bytes. */\nexport function isLikelyTextFilename(filename: string): boolean {\n const entry = resolveEntry(filename);\n if (entry) {\n return (\n entry.previewKind === \"code\" ||\n entry.previewKind === \"text\" ||\n entry.previewKind === \"markdown\" ||\n entry.previewKind === \"data\" ||\n // HTML and SVG are markup-under-the-hood. The binary viewer / sniffer\n // should treat them as text so \"View as text\" works on edge cases.\n entry.previewKind === \"html\" ||\n entry.previewKind === \"svg\"\n );\n }\n return dotfileLooksLikeText(filename);\n}\n\n/** Folder details — used by sidebar + tree rendering. */\nexport function getFolderTypeDetails(open = false): FileTypeDetails {\n return open ? { ...FOLDER_DETAILS, icon: FolderOpen } : FOLDER_DETAILS;\n}\n\n/** Strip MIME parameters (`;codecs=opus`, etc.) for registry lookup. */\nfunction canonicalMimeBase(raw: string): string {\n return raw.split(\";\")[0]?.trim().toLowerCase() ?? raw;\n}\n\n/** Canonical MIME for an extension (or `application/octet-stream`). */\nexport function mimeFromFilename(filename: string): string {\n const entry = resolveEntry(filename);\n return entry ? entry.mime : \"application/octet-stream\";\n}\n\n/**\n * Resolve a final MIME, preferring the server-supplied value when it's\n * meaningful, otherwise falling back to the extension table.\n *\n * Parameters are stripped so `audio/webm;codecs=opus` resolves like\n * `audio/webm` — required for BY_MIME lookup and audio/video disambiguation\n * on `.webm` containers (MediaRecorder speech captures).\n */\nexport function resolveMime(\n explicit: string | null | undefined,\n filename: string,\n): string {\n if (explicit && explicit !== \"application/octet-stream\") {\n return canonicalMimeBase(explicit);\n }\n return mimeFromFilename(filename);\n}\n\n/**\n * Given a (filename, mime, size) tuple, return the full preview profile —\n * what to render at full size, what to render in a grid thumbnail, whether\n * either is allowed at the file's current byte size, and the resolved MIME.\n *\n * This is THE function components should call. `getPreviewCapability` and\n * `useFileTypeDetails` are thin views on top.\n */\nexport interface FilePreviewProfile {\n details: FileTypeDetails;\n /** Effective `previewKind` after MIME-prefix override. */\n previewKind: PreviewKind;\n /** True when the type is renderable AND the byte size fits the cap. */\n canPreview: boolean;\n /** False = file is too big for inline preview; offer download instead. */\n sizeOk: boolean;\n /** The cap that was effectively applied (in bytes). */\n effectiveSizeCap: number;\n /** True iff the registry says this file should render a real thumbnail. */\n thumbnailStrategy: ThumbnailStrategy;\n /** Resolved canonical MIME. */\n mime: string;\n}\n\nexport function getFilePreviewProfile(\n fileName: string,\n mimeType: string | null,\n fileSize: number | null,\n): FilePreviewProfile {\n const mime = resolveMime(mimeType, fileName);\n const fromExt = getFileTypeDetails(fileName);\n\n // When the server MIME disagrees with the extension guess, prefer the MIME\n // registry entry for display props. Canonical case: `audio/webm` rows stored\n // as `.webm` — extension lookup returns VIDEO (\"WebM video\") even though the\n // bytes are speech-only (transcript recordings, FastFire, scribe captures).\n const mimeEntry = BY_MIME.get(mime);\n let baseDetails = fromExt;\n // Browsers and upload clients routinely label SVG bytes as generic XML\n // (`application/xml` / `text/xml`). The filename is the stronger signal in\n // that narrow case: allowing the generic MIME registry entry to win turns\n // `icon.svg` into the tabular DataPreview, which can only reject it as an\n // unknown data format. Preserve the dedicated SVG renderer while keeping\n // true `.xml` files on the data path.\n const genericXmlSvg =\n fromExt.previewKind === \"svg\" &&\n (mime === \"application/xml\" || mime === \"text/xml\");\n if (\n mimeEntry &&\n !genericXmlSvg &&\n (mimeEntry.category !== fromExt.category ||\n mimeEntry.previewKind !== fromExt.previewKind)\n ) {\n baseDetails = entryToDetails(mimeEntry);\n }\n\n // MIME-prefix override — handles \"we got `image/avif` but the extension\n // table doesn't have .avif yet\" and similar future-proofing.\n //\n // SVG is intentionally NOT promoted to the generic \"image\" kind here:\n // its previewKind is already \"svg\" from the extension lookup and the\n // override would clobber that, hiding the dedicated SvgPreview + Edit\n // routing. Same idea would apply to any future text-shaped image MIME.\n let kind: PreviewKind = baseDetails.previewKind;\n let thumb: ThumbnailStrategy = baseDetails.thumbnailStrategy;\n if (mime === \"image/svg+xml\" || genericXmlSvg) {\n kind = \"svg\";\n thumb = \"image\";\n } else if (mime.startsWith(\"image/\")) {\n kind = \"image\";\n thumb = \"image\";\n } else if (mime.startsWith(\"video/\")) {\n kind = \"video\";\n thumb = thumb === \"icon\" ? \"video-poster\" : thumb;\n } else if (mime.startsWith(\"audio/\")) {\n kind = \"audio\";\n thumb = \"icon\";\n if (baseDetails.category === \"VIDEO\") {\n const audioEntry = BY_MIME.get(mime);\n if (audioEntry) baseDetails = entryToDetails(audioEntry);\n }\n } else if (mime === \"application/pdf\") {\n kind = \"pdf\";\n } else if (kind === \"generic\" && isTextMime(mime)) {\n kind = \"text\";\n }\n\n const canPreviewKind = kind !== \"generic\";\n\n const cap =\n baseDetails.previewSizeCapOverride ??\n // Streamable kinds are always allowed regardless of size. SVG joins the\n // group: even though it's text underneath, it streams as an image via\n // its durable URL — never read into memory by the renderer. HTML joins\n // too: the Rendered view drives a sandboxed iframe on the durable URL\n // (no client-side blob read), and the Source view fetches the bytes\n // through the standard blob path which has its own truncation.\n (kind === \"image\" ||\n kind === \"svg\" ||\n kind === \"html\" ||\n kind === \"video\" ||\n kind === \"audio\" ||\n kind === \"pdf\" ||\n kind === \"spreadsheet\" ||\n // Office previews are SERVER-extracted (the browser only receives\n // markdown) — the file's own byte size is irrelevant to the client.\n kind === \"office\"\n ? Number.POSITIVE_INFINITY\n : MAX_INLINE_PREVIEW_BYTES);\n\n const sizeOk = fileSize == null || fileSize <= cap;\n\n return {\n details: { ...baseDetails, previewKind: kind, thumbnailStrategy: thumb },\n previewKind: kind,\n canPreview: canPreviewKind && sizeOk,\n sizeOk,\n effectiveSizeCap: cap,\n thumbnailStrategy: thumb,\n mime,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Mime-prefix helpers (kept pure so consumers can still ask \"is this image?\")\n// ---------------------------------------------------------------------------\n\nexport function isTextMime(mime: string): boolean {\n if (mime.startsWith(\"text/\")) return true;\n return (\n mime === \"application/json\" ||\n mime === \"application/xml\" ||\n mime === \"application/yaml\" ||\n mime === \"application/x-yaml\" ||\n mime === \"application/toml\" ||\n mime === \"application/sql\" ||\n mime === \"application/x-subrip\" ||\n mime === \"message/rfc822\"\n );\n}\n\nexport function isImageMime(mime: string): boolean {\n return mime.startsWith(\"image/\");\n}\n\n/** Image bytes evergreen browsers can decode directly through `<img>`. */\nexport function isBrowserRenderableImageMime(mime: string): boolean {\n return (\n mime === \"image/avif\" ||\n mime === \"image/bmp\" ||\n mime === \"image/gif\" ||\n mime === \"image/jpeg\" ||\n mime === \"image/png\" ||\n mime === \"image/svg+xml\" ||\n mime === \"image/webp\"\n );\n}\n\nexport function isVideoMime(mime: string): boolean {\n return mime.startsWith(\"video/\");\n}\n\nexport function isAudioMime(mime: string): boolean {\n return mime.startsWith(\"audio/\");\n}\n\nexport function isPdfMime(mime: string): boolean {\n return mime === \"application/pdf\";\n}\n\n// ---------------------------------------------------------------------------\n// Byte sniffing — used by the code workspace's binary viewer to decide\n// whether a generic / unknown file is actually text-shaped at runtime, so\n// it can offer (or auto-engage) a \"View as text\" override even for\n// filenames the registry doesn't recognize.\n// ---------------------------------------------------------------------------\n\n/**\n * Result of `sniffTextBytes`. We split \"definitely text\" from \"looks text-y\n * but we're not sure\" so the UI can pick a different default action:\n *\n * - `confidence === \"high\"` — safe to render in Monaco automatically.\n * - `confidence === \"medium\"` — probably text; offer a button.\n * - `confidence === \"low\"` — almost certainly binary; only show on\n * manual override.\n */\nexport interface TextSniffResult {\n isText: boolean;\n confidence: \"high\" | \"medium\" | \"low\";\n reason: string;\n}\n\n/**\n * Heuristic byte-level text detector. Inspects the first ~4KB of the\n * sample and answers \"could this be displayed as text?\". Designed to be\n * permissive — false positives are recoverable (the user sees garbage\n * and closes the tab), while false negatives are exactly the bug we're\n * fighting (.bashrc, authorized_keys, etc. being declared \"binary\").\n *\n * Rules, in order:\n * 1. Empty bytes → text (an empty file is fine to open in Monaco).\n * 2. UTF-8 BOM, UTF-16 BOM, UTF-32 BOM → text (high confidence).\n * 3. Strict UTF-8 decode succeeds → text (high confidence).\n * 4. Otherwise count printable / control bytes:\n * - any null byte (0x00) → binary (high confidence).\n * - >= 95% printable + tab/newline/CR → text (medium).\n * - everything else → binary (low).\n */\nexport function sniffTextBytes(\n bytes: Uint8Array,\n sampleSize = 4096,\n): TextSniffResult {\n if (bytes.length === 0) {\n return { isText: true, confidence: \"high\", reason: \"empty file\" };\n }\n const sample = bytes.subarray(0, Math.min(sampleSize, bytes.length));\n\n // BOM checks first — these are unambiguous.\n if (\n sample.length >= 3 &&\n sample[0] === 0xef &&\n sample[1] === 0xbb &&\n sample[2] === 0xbf\n ) {\n return { isText: true, confidence: \"high\", reason: \"UTF-8 BOM\" };\n }\n if (\n sample.length >= 2 &&\n ((sample[0] === 0xff && sample[1] === 0xfe) ||\n (sample[0] === 0xfe && sample[1] === 0xff))\n ) {\n return { isText: true, confidence: \"high\", reason: \"UTF-16 BOM\" };\n }\n\n // Strict UTF-8 decode — succeeds for the vast majority of text we see\n // in sandboxes (source code, configs, logs). Falls through on truncated\n // multi-byte sequences at the sample boundary, which is fine.\n if (typeof TextDecoder !== \"undefined\") {\n try {\n new TextDecoder(\"utf-8\", { fatal: true }).decode(sample);\n return { isText: true, confidence: \"high\", reason: \"valid UTF-8\" };\n } catch {\n // Fall through to printability check.\n }\n }\n\n // Printability fallback — covers files that are mostly ASCII but had a\n // single bad byte at the sample boundary, plus legacy 8-bit encodings.\n let printable = 0;\n for (let i = 0; i < sample.length; i++) {\n const b = sample[i];\n if (b === 0) {\n return { isText: false, confidence: \"high\", reason: \"null byte\" };\n }\n if (b === 9 || b === 10 || b === 13 || (b >= 0x20 && b <= 0x7e)) {\n printable++;\n }\n }\n const ratio = printable / sample.length;\n if (ratio >= 0.95) {\n return {\n isText: true,\n confidence: \"medium\",\n reason: `${Math.round(ratio * 100)}% printable`,\n };\n }\n return {\n isText: false,\n confidence: \"low\",\n reason: `only ${Math.round(ratio * 100)}% printable`,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Documentation helpers — exported so /admin pages or QA dashboards can\n// render \"what does cloud-files support today?\" tables straight from the\n// registry, without ever drifting from runtime behavior.\n// ---------------------------------------------------------------------------\n\n/** Every supported (extension, displayName, previewKind, thumbnailStrategy). */\nexport interface SupportedTypeRow {\n extensions: readonly string[];\n mime: string;\n category: FileCategory;\n displayName: string;\n previewKind: PreviewKind;\n thumbnailStrategy: ThumbnailStrategy;\n /** True when previewKind !== \"generic\" — i.e. we have a real previewer. */\n canPreview: boolean;\n}\n\nexport function listSupportedTypes(): SupportedTypeRow[] {\n return FILE_TYPES.map((e) => ({\n extensions: e.extensions,\n mime: e.mime,\n category: e.category,\n displayName: e.displayName,\n previewKind: e.previewKind,\n thumbnailStrategy: e.thumbnailStrategy,\n canPreview: e.previewKind !== \"generic\",\n }));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* features/files/utils/user-visible.ts
|
|
3
|
+
*
|
|
4
|
+
* THE ONE VISIBILITY RULE, MIRRORED — never a second rule.
|
|
5
|
+
*
|
|
6
|
+
* The rule that decides whether a cloud row is the user's own file (as opposed
|
|
7
|
+
* to machine infrastructure) is declared ONCE, in the database:
|
|
8
|
+
*
|
|
9
|
+
* files.is_user_visible_path(text) — files (system-files/, generations/, .matrx-tmp/,
|
|
10
|
+
* machine namespaces coding-sessions/, tool-images/)
|
|
11
|
+
* files.is_user_visible(files.files) — + not derived, not a produced artifact (artifact_kind)
|
|
12
|
+
* files.is_user_visible_folder_path(text) — folders (system + machine namespaces)
|
|
13
|
+
* files.is_recent_activity(files.files) — RECENTS: visible, not device-written
|
|
14
|
+
* (origin_device_id), not machine output under
|
|
15
|
+
* the person's roots, not carried for an
|
|
16
|
+
* organization (`page-captures-<org>/…`), and
|
|
17
|
+
* not a SYSTEM FILE (metadata.system_artifact,
|
|
18
|
+
* matrx-files 050, 2026-09-29)
|
|
19
|
+
*
|
|
20
|
+
* SYSTEM FILES (Arman 2026-09-29: "By default, it should never show system files unless that
|
|
21
|
+
* feature is turned on. default off. And they should never show in recents anyways."): a file the
|
|
22
|
+
* system marked as its own — `metadata.system_artifact = true`, stamped by aidream
|
|
23
|
+
* `user_visible.machine_artifact_marker` on every machine path, the same marker
|
|
24
|
+
* `platform._inventory_filter` hides — is never Recents, and every other list shows it only when
|
|
25
|
+
* the Feature Knob `files.show_system_files` (default off; organization and person may override)
|
|
26
|
+
* is on. `isListedFile` / `isListedFolderPath` below are that ONE rule for every list.
|
|
27
|
+
*
|
|
28
|
+
* Declared once in aidream packages/matrx-files/matrx_files/user_visible.py. The
|
|
29
|
+
* rule and how a machine writer registers: common-docs
|
|
30
|
+
* systems/files/file-service/USER_FILES_VS_MACHINE_FILES.md.
|
|
31
|
+
*
|
|
32
|
+
* `public.get_user_file_tree` and the daemon's sync feed both call those, so
|
|
33
|
+
* anything that arrives through the RPC is ALREADY filtered — callers must not
|
|
34
|
+
* filter it a second time. This module exists for the ONE consumer the RPC
|
|
35
|
+
* cannot serve: Supabase Realtime `postgres_changes` payloads, which arrive
|
|
36
|
+
* straight off the write-ahead log with no predicate applied.
|
|
37
|
+
*
|
|
38
|
+
* Because that consumer needs the answer synchronously, per row, the rule is
|
|
39
|
+
* MIRRORED here rather than re-declared — the same "one declaration, several
|
|
40
|
+
* call shapes, parity is the guard, not the syntax" posture the server takes
|
|
41
|
+
* between its SQL function and its ORM filter set
|
|
42
|
+
* (common-docs projects/folder-sync/specs/SPEC-SERVER.md §1.2).
|
|
43
|
+
*
|
|
44
|
+
* 🚨 The guard is `user-visible.parity.test.ts`: it runs every path below
|
|
45
|
+
* through the LIVE database functions and asserts identical answers. If
|
|
46
|
+
* somebody changes the SQL rule and not this file, that test goes red. Never
|
|
47
|
+
* edit the logic here without running it.
|
|
48
|
+
*
|
|
49
|
+
* What this rule is NOT: a permission check (RLS is). Recents is a strict
|
|
50
|
+
* SUBSET of it — `isRecentActivityFile` / `isRecentActivityPath` below, mirrors
|
|
51
|
+
* of `files.is_recent_activity*`, and the ONLY Recents rule in the browser.
|
|
52
|
+
*
|
|
53
|
+
* FastFire note: top-level `FastFire/**` used to be hidden here by a rule the
|
|
54
|
+
* server never had, so the desktop daemon would sync those bytes to disk while
|
|
55
|
+
* the browser refused to show them. `FastFire` is now a REGISTERED
|
|
56
|
+
* machine-written prefix (files.machine_written_prefixes) — the desktop
|
|
57
|
+
* first-run deselects it by default, and nothing hides it
|
|
58
|
+
* (SPEC-SERVER §1.4 S18, folder-sync DECISIONS D16).
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* Mirror of `files.is_user_visible_path(text)` — the FILE rule.
|
|
62
|
+
* True when the path is the user's own content rather than machine output.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isUserVisibleFilePath(path: string | null | undefined): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Mirror of `files.is_user_visible_folder_path(text)` — the FOLDER rule.
|
|
67
|
+
* Deliberately NOT the same as the file rule: `.matrx-tmp` folders stay in
|
|
68
|
+
* scope (unchanged since the tree first spelled it). Mirroring the divergence
|
|
69
|
+
* is honest; inventing a stricter client rule is what this module exists to stop.
|
|
70
|
+
*/
|
|
71
|
+
export declare function isUserVisibleFolderPath(path: string | null | undefined): boolean;
|
|
72
|
+
/** The shape a realtime `files.files` payload carries (published columns). */
|
|
73
|
+
export interface UserVisibleFileRowShape {
|
|
74
|
+
file_path: string | null;
|
|
75
|
+
parent_file_id: string | null;
|
|
76
|
+
derivation_kind: string | null;
|
|
77
|
+
/** A machine-produced artifact (e.g. 'coding_session_artifact'). */
|
|
78
|
+
artifact_kind?: string | null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Mirror of `files.is_user_visible(files.files)` — the ROW rule: not a
|
|
82
|
+
* derivative, and a user-visible path. `parent_file_id` and `derivation_kind`
|
|
83
|
+
* are both required because 824 live rows are parentless variants, so neither
|
|
84
|
+
* column alone excludes derivatives (SPEC-SERVER §1.2).
|
|
85
|
+
*/
|
|
86
|
+
export declare function isUserVisibleFileRow(row: UserVisibleFileRowShape): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Mirror of `files.is_recent_activity_path(text)` — the path half of RECENTS:
|
|
89
|
+
* a user-visible path that is not machine output filed under the person's
|
|
90
|
+
* roots. Also the Recents rule for a FOLDER row.
|
|
91
|
+
*/
|
|
92
|
+
export declare function isRecentActivityPath(path: string | null | undefined): boolean;
|
|
93
|
+
/** A file record's metadata, the only field the system-file rule reads. */
|
|
94
|
+
export interface SystemFileShape {
|
|
95
|
+
metadata?: Record<string, unknown> | null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A SYSTEM FILE: one the system made and marked as its own
|
|
99
|
+
* (`metadata.system_artifact = true`). Mirror of the conjunct
|
|
100
|
+
* `NOT (metadata @> '{"system_artifact": true}')` that `files.is_recent_activity`
|
|
101
|
+
* and `platform._inventory_filter` both carry.
|
|
102
|
+
*/
|
|
103
|
+
export declare function isSystemFile(file: SystemFileShape): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* A folder only the system writes into: a system-carried root or anything under it, or a variant
|
|
106
|
+
* folder. Mirror of aidream `system_folder_marker`, which stamps `metadata.system_artifact` on
|
|
107
|
+
* these folders at the one folder door; `platform._inventory_filter` hides them unless
|
|
108
|
+
* `files.show_system_files` is on (V5-B, 2026-09-30).
|
|
109
|
+
*/
|
|
110
|
+
export declare function isSystemFolderPath(path: string | null | undefined): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* THE ONE LIST RULE for files: every file list (the Files page, the file picker,
|
|
113
|
+
* search) shows a system file only when `files.show_system_files` is on.
|
|
114
|
+
* Recents never shows one either way (`isRecentActivityFile`).
|
|
115
|
+
*/
|
|
116
|
+
export declare function isListedFile(file: SystemFileShape, showSystemFiles: boolean): boolean;
|
|
117
|
+
/** The folder half of the list rule. */
|
|
118
|
+
export declare function isListedFolderPath(path: string | null | undefined, showSystemFiles: boolean): boolean;
|
|
119
|
+
/** The fields Recents needs from a file record (domain or row shape). */
|
|
120
|
+
export interface RecentActivityFileShape extends SystemFileShape {
|
|
121
|
+
filePath: string | null | undefined;
|
|
122
|
+
/** The registered device that wrote the row; null for an in-app write. */
|
|
123
|
+
originDeviceId?: string | null;
|
|
124
|
+
parentFileId?: string | null;
|
|
125
|
+
derivationKind?: string | null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Mirror of `files.is_recent_activity(files.files)` — RECENTS: the file is the
|
|
129
|
+
* person's recent activity in the app. A file a desktop sync client wrote
|
|
130
|
+
* (`originDeviceId`) is the person's file, but never their recent activity
|
|
131
|
+
* (folder-sync DECISIONS R3).
|
|
132
|
+
*/
|
|
133
|
+
export declare function isRecentActivityFile(file: RecentActivityFileShape): boolean;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const SYSTEM_SEGMENTS = ["system-files", "generations"];
|
|
2
|
+
const TMP_SEGMENT = ".matrx-tmp";
|
|
3
|
+
const MACHINE_SEGMENTS = ["coding-sessions", "tool-images"];
|
|
4
|
+
const RECENT_EXCLUDED_ROOTS = [
|
|
5
|
+
"Images/Generated",
|
|
6
|
+
"Generated",
|
|
7
|
+
"Agent Apps/blocks",
|
|
8
|
+
"Images/agent-blocks",
|
|
9
|
+
"Transcripts/Recordings",
|
|
10
|
+
"FastFire/sessions",
|
|
11
|
+
"FastFire/responses"
|
|
12
|
+
];
|
|
13
|
+
const CARRIED_ROOTS = [
|
|
14
|
+
"record-store",
|
|
15
|
+
"web-crawls",
|
|
16
|
+
"seo-data",
|
|
17
|
+
"login-captures",
|
|
18
|
+
"payroll-exports",
|
|
19
|
+
"timesheet-reports",
|
|
20
|
+
"hr-letters",
|
|
21
|
+
"page-captures",
|
|
22
|
+
"text-messages",
|
|
23
|
+
"coding-transcripts",
|
|
24
|
+
"e-signatures"
|
|
25
|
+
];
|
|
26
|
+
const CARRIED_ROOT_SEGMENT = new RegExp(
|
|
27
|
+
`^(${CARRIED_ROOTS.join("|")})-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`
|
|
28
|
+
);
|
|
29
|
+
function stripLeadingSlashes(path) {
|
|
30
|
+
let i = 0;
|
|
31
|
+
while (i < path.length && path[i] === "/") i += 1;
|
|
32
|
+
return path.slice(i);
|
|
33
|
+
}
|
|
34
|
+
function firstSegment(path) {
|
|
35
|
+
const rest = stripLeadingSlashes(path);
|
|
36
|
+
const slash = rest.indexOf("/");
|
|
37
|
+
return slash === -1 ? rest : rest.slice(0, slash);
|
|
38
|
+
}
|
|
39
|
+
function isUserVisibleFilePath(path) {
|
|
40
|
+
if (path === null || path === void 0) return false;
|
|
41
|
+
const head = firstSegment(path);
|
|
42
|
+
if (SYSTEM_SEGMENTS.includes(head)) return false;
|
|
43
|
+
if (MACHINE_SEGMENTS.includes(head)) return false;
|
|
44
|
+
return head !== TMP_SEGMENT;
|
|
45
|
+
}
|
|
46
|
+
function isUserVisibleFolderPath(path) {
|
|
47
|
+
if (path === null || path === void 0) return false;
|
|
48
|
+
const head = firstSegment(path);
|
|
49
|
+
if (SYSTEM_SEGMENTS.includes(head)) return false;
|
|
50
|
+
return !MACHINE_SEGMENTS.includes(head);
|
|
51
|
+
}
|
|
52
|
+
function isUserVisibleFileRow(row) {
|
|
53
|
+
if (row.parent_file_id !== null && row.parent_file_id !== void 0)
|
|
54
|
+
return false;
|
|
55
|
+
if (row.derivation_kind !== null && row.derivation_kind !== void 0)
|
|
56
|
+
return false;
|
|
57
|
+
if (row.artifact_kind !== null && row.artifact_kind !== void 0)
|
|
58
|
+
return false;
|
|
59
|
+
return isUserVisibleFilePath(row.file_path);
|
|
60
|
+
}
|
|
61
|
+
function isRecentActivityPath(path) {
|
|
62
|
+
if (!isUserVisibleFilePath(path)) return false;
|
|
63
|
+
const trimmed = stripLeadingSlashes(path);
|
|
64
|
+
if (CARRIED_ROOT_SEGMENT.test(firstSegment(trimmed))) return false;
|
|
65
|
+
return !RECENT_EXCLUDED_ROOTS.some(
|
|
66
|
+
(root) => trimmed === root || trimmed.startsWith(`${root}/`)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function isSystemFile(file) {
|
|
70
|
+
return file.metadata?.["system_artifact"] === true;
|
|
71
|
+
}
|
|
72
|
+
const SYSTEM_CARRIED_ROOTS = [
|
|
73
|
+
"record-store",
|
|
74
|
+
"web-crawls",
|
|
75
|
+
"seo-data",
|
|
76
|
+
"login-captures",
|
|
77
|
+
"page-captures",
|
|
78
|
+
"coding-transcripts"
|
|
79
|
+
];
|
|
80
|
+
const SYSTEM_CARRIED_ROOT_SEGMENT = new RegExp(
|
|
81
|
+
`^(${SYSTEM_CARRIED_ROOTS.join("|")})-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`
|
|
82
|
+
);
|
|
83
|
+
const VARIANT_FOLDER = /^.+\/v(\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?$/;
|
|
84
|
+
function isSystemFolderPath(path) {
|
|
85
|
+
if (path === null || path === void 0) return false;
|
|
86
|
+
if (SYSTEM_CARRIED_ROOT_SEGMENT.test(firstSegment(path))) return true;
|
|
87
|
+
let trimmed = stripLeadingSlashes(path);
|
|
88
|
+
while (trimmed.endsWith("/")) trimmed = trimmed.slice(0, -1);
|
|
89
|
+
return VARIANT_FOLDER.test(trimmed);
|
|
90
|
+
}
|
|
91
|
+
function isListedFile(file, showSystemFiles) {
|
|
92
|
+
return showSystemFiles || !isSystemFile(file);
|
|
93
|
+
}
|
|
94
|
+
function isListedFolderPath(path, showSystemFiles) {
|
|
95
|
+
return showSystemFiles || !isSystemFolderPath(path);
|
|
96
|
+
}
|
|
97
|
+
function isRecentActivityFile(file) {
|
|
98
|
+
if (isSystemFile(file)) return false;
|
|
99
|
+
if (file.originDeviceId) return false;
|
|
100
|
+
if (file.parentFileId) return false;
|
|
101
|
+
if (file.derivationKind) return false;
|
|
102
|
+
return isRecentActivityPath(file.filePath);
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
isListedFile,
|
|
106
|
+
isListedFolderPath,
|
|
107
|
+
isRecentActivityFile,
|
|
108
|
+
isRecentActivityPath,
|
|
109
|
+
isSystemFile,
|
|
110
|
+
isSystemFolderPath,
|
|
111
|
+
isUserVisibleFilePath,
|
|
112
|
+
isUserVisibleFileRow,
|
|
113
|
+
isUserVisibleFolderPath
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=user-visible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/utils/user-visible.ts"],"sourcesContent":["/**\n * features/files/utils/user-visible.ts\n *\n * THE ONE VISIBILITY RULE, MIRRORED — never a second rule.\n *\n * The rule that decides whether a cloud row is the user's own file (as opposed\n * to machine infrastructure) is declared ONCE, in the database:\n *\n * files.is_user_visible_path(text) — files (system-files/, generations/, .matrx-tmp/,\n * machine namespaces coding-sessions/, tool-images/)\n * files.is_user_visible(files.files) — + not derived, not a produced artifact (artifact_kind)\n * files.is_user_visible_folder_path(text) — folders (system + machine namespaces)\n * files.is_recent_activity(files.files) — RECENTS: visible, not device-written\n * (origin_device_id), not machine output under\n * the person's roots, not carried for an\n * organization (`page-captures-<org>/…`), and\n * not a SYSTEM FILE (metadata.system_artifact,\n * matrx-files 050, 2026-09-29)\n *\n * SYSTEM FILES (Arman 2026-09-29: \"By default, it should never show system files unless that\n * feature is turned on. default off. And they should never show in recents anyways.\"): a file the\n * system marked as its own — `metadata.system_artifact = true`, stamped by aidream\n * `user_visible.machine_artifact_marker` on every machine path, the same marker\n * `platform._inventory_filter` hides — is never Recents, and every other list shows it only when\n * the Feature Knob `files.show_system_files` (default off; organization and person may override)\n * is on. `isListedFile` / `isListedFolderPath` below are that ONE rule for every list.\n *\n * Declared once in aidream packages/matrx-files/matrx_files/user_visible.py. The\n * rule and how a machine writer registers: common-docs\n * systems/files/file-service/USER_FILES_VS_MACHINE_FILES.md.\n *\n * `public.get_user_file_tree` and the daemon's sync feed both call those, so\n * anything that arrives through the RPC is ALREADY filtered — callers must not\n * filter it a second time. This module exists for the ONE consumer the RPC\n * cannot serve: Supabase Realtime `postgres_changes` payloads, which arrive\n * straight off the write-ahead log with no predicate applied.\n *\n * Because that consumer needs the answer synchronously, per row, the rule is\n * MIRRORED here rather than re-declared — the same \"one declaration, several\n * call shapes, parity is the guard, not the syntax\" posture the server takes\n * between its SQL function and its ORM filter set\n * (common-docs projects/folder-sync/specs/SPEC-SERVER.md §1.2).\n *\n * 🚨 The guard is `user-visible.parity.test.ts`: it runs every path below\n * through the LIVE database functions and asserts identical answers. If\n * somebody changes the SQL rule and not this file, that test goes red. Never\n * edit the logic here without running it.\n *\n * What this rule is NOT: a permission check (RLS is). Recents is a strict\n * SUBSET of it — `isRecentActivityFile` / `isRecentActivityPath` below, mirrors\n * of `files.is_recent_activity*`, and the ONLY Recents rule in the browser.\n *\n * FastFire note: top-level `FastFire/**` used to be hidden here by a rule the\n * server never had, so the desktop daemon would sync those bytes to disk while\n * the browser refused to show them. `FastFire` is now a REGISTERED\n * machine-written prefix (files.machine_written_prefixes) — the desktop\n * first-run deselects it by default, and nothing hides it\n * (SPEC-SERVER §1.4 S18, folder-sync DECISIONS D16).\n */\n\n/** Top-level path segments the database treats as machine infrastructure. */\nconst SYSTEM_SEGMENTS = [\"system-files\", \"generations\"] as const;\nconst TMP_SEGMENT = \".matrx-tmp\";\n/** Mirror of `MACHINE_NAMESPACES` — top-level segments only machines write into. */\nconst MACHINE_SEGMENTS = [\"coding-sessions\", \"tool-images\"] as const;\n/** Mirror of `RECENT_EXCLUDED_ROOTS` — machine output filed under the person's roots. */\nconst RECENT_EXCLUDED_ROOTS = [\n \"Images/Generated\",\n \"Generated\",\n \"Agent Apps/blocks\",\n \"Images/agent-blocks\",\n \"Transcripts/Recordings\",\n \"FastFire/sessions\",\n \"FastFire/responses\",\n] as const;\n\n/**\n * Mirror of `CARRIED_ROOTS` / `CARRIED_ROOT_SEGMENT_PATTERN` — the roots a\n * service writes into when it carries a file FOR an organization\n * (`<root>-<organization uuid>/…`). Browsable in the tree; never Recents.\n */\nconst CARRIED_ROOTS = [\n \"record-store\",\n \"web-crawls\",\n \"seo-data\",\n \"login-captures\",\n \"payroll-exports\",\n \"timesheet-reports\",\n \"hr-letters\",\n \"page-captures\",\n \"text-messages\",\n \"coding-transcripts\",\n \"e-signatures\",\n] as const;\nconst CARRIED_ROOT_SEGMENT = new RegExp(\n `^(${CARRIED_ROOTS.join(\"|\")})-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`,\n);\n\n/** `ltrim(p,'/')` — the database's own leading-slash shape. */\nfunction stripLeadingSlashes(path: string): string {\n let i = 0;\n while (i < path.length && path[i] === \"/\") i += 1;\n return path.slice(i);\n}\n\n/** `split_part(ltrim(p,'/'),'/',1)` — the database's own first-segment shape. */\nfunction firstSegment(path: string): string {\n const rest = stripLeadingSlashes(path);\n const slash = rest.indexOf(\"/\");\n return slash === -1 ? rest : rest.slice(0, slash);\n}\n\n/**\n * Mirror of `files.is_user_visible_path(text)` — the FILE rule.\n * True when the path is the user's own content rather than machine output.\n */\nexport function isUserVisibleFilePath(\n path: string | null | undefined,\n): boolean {\n if (path === null || path === undefined) return false;\n const head = firstSegment(path);\n if ((SYSTEM_SEGMENTS as readonly string[]).includes(head)) return false;\n if ((MACHINE_SEGMENTS as readonly string[]).includes(head)) return false;\n return head !== TMP_SEGMENT;\n}\n\n/**\n * Mirror of `files.is_user_visible_folder_path(text)` — the FOLDER rule.\n * Deliberately NOT the same as the file rule: `.matrx-tmp` folders stay in\n * scope (unchanged since the tree first spelled it). Mirroring the divergence\n * is honest; inventing a stricter client rule is what this module exists to stop.\n */\nexport function isUserVisibleFolderPath(\n path: string | null | undefined,\n): boolean {\n if (path === null || path === undefined) return false;\n const head = firstSegment(path);\n if ((SYSTEM_SEGMENTS as readonly string[]).includes(head)) return false;\n return !(MACHINE_SEGMENTS as readonly string[]).includes(head);\n}\n\n/** The shape a realtime `files.files` payload carries (published columns). */\nexport interface UserVisibleFileRowShape {\n file_path: string | null;\n parent_file_id: string | null;\n derivation_kind: string | null;\n /** A machine-produced artifact (e.g. 'coding_session_artifact'). */\n artifact_kind?: string | null;\n}\n\n/**\n * Mirror of `files.is_user_visible(files.files)` — the ROW rule: not a\n * derivative, and a user-visible path. `parent_file_id` and `derivation_kind`\n * are both required because 824 live rows are parentless variants, so neither\n * column alone excludes derivatives (SPEC-SERVER §1.2).\n */\nexport function isUserVisibleFileRow(row: UserVisibleFileRowShape): boolean {\n if (row.parent_file_id !== null && row.parent_file_id !== undefined)\n return false;\n if (row.derivation_kind !== null && row.derivation_kind !== undefined)\n return false;\n if (row.artifact_kind !== null && row.artifact_kind !== undefined)\n return false;\n return isUserVisibleFilePath(row.file_path);\n}\n\n/**\n * Mirror of `files.is_recent_activity_path(text)` — the path half of RECENTS:\n * a user-visible path that is not machine output filed under the person's\n * roots. Also the Recents rule for a FOLDER row.\n */\nexport function isRecentActivityPath(\n path: string | null | undefined,\n): boolean {\n if (!isUserVisibleFilePath(path)) return false;\n const trimmed = stripLeadingSlashes(path as string);\n if (CARRIED_ROOT_SEGMENT.test(firstSegment(trimmed))) return false;\n return !RECENT_EXCLUDED_ROOTS.some(\n (root) => trimmed === root || trimmed.startsWith(`${root}/`),\n );\n}\n\n/** A file record's metadata, the only field the system-file rule reads. */\nexport interface SystemFileShape {\n metadata?: Record<string, unknown> | null;\n}\n\n/**\n * A SYSTEM FILE: one the system made and marked as its own\n * (`metadata.system_artifact = true`). Mirror of the conjunct\n * `NOT (metadata @> '{\"system_artifact\": true}')` that `files.is_recent_activity`\n * and `platform._inventory_filter` both carry.\n */\nexport function isSystemFile(file: SystemFileShape): boolean {\n return file.metadata?.[\"system_artifact\"] === true;\n}\n\n/**\n * Mirror of aidream `MACHINE_CARRIED_ROOTS` — the carried roots whose every file\n * is stamped a system file (`machine_artifact_marker`). A folder at such a root\n * (`page-captures-<org uuid>/…`) holds nothing but system files. The person's own\n * deliverables (`PERSON_CARRIED_ROOTS`: payroll exports, timesheet reports, HR\n * letters, texted attachments) are not here.\n */\nconst SYSTEM_CARRIED_ROOTS = [\n \"record-store\",\n \"web-crawls\",\n \"seo-data\",\n \"login-captures\",\n \"page-captures\",\n \"coding-transcripts\",\n] as const;\nconst SYSTEM_CARRIED_ROOT_SEGMENT = new RegExp(\n `^(${SYSTEM_CARRIED_ROOTS.join(\"|\")})-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`,\n);\n\n/**\n * Mirror of aidream `VARIANT_FOLDER_PATTERN`: `<master dir>/v` and `<master dir>/v/<master file id>`\n * hold nothing but derived copies (cloud_sync/variants.py). Never a top-level `v`.\n */\nconst VARIANT_FOLDER =\n /^.+\\/v(\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?$/;\n\n/**\n * A folder only the system writes into: a system-carried root or anything under it, or a variant\n * folder. Mirror of aidream `system_folder_marker`, which stamps `metadata.system_artifact` on\n * these folders at the one folder door; `platform._inventory_filter` hides them unless\n * `files.show_system_files` is on (V5-B, 2026-09-30).\n */\nexport function isSystemFolderPath(path: string | null | undefined): boolean {\n if (path === null || path === undefined) return false;\n if (SYSTEM_CARRIED_ROOT_SEGMENT.test(firstSegment(path))) return true;\n let trimmed = stripLeadingSlashes(path);\n while (trimmed.endsWith(\"/\")) trimmed = trimmed.slice(0, -1);\n return VARIANT_FOLDER.test(trimmed);\n}\n\n/**\n * THE ONE LIST RULE for files: every file list (the Files page, the file picker,\n * search) shows a system file only when `files.show_system_files` is on.\n * Recents never shows one either way (`isRecentActivityFile`).\n */\nexport function isListedFile(file: SystemFileShape, showSystemFiles: boolean): boolean {\n return showSystemFiles || !isSystemFile(file);\n}\n\n/** The folder half of the list rule. */\nexport function isListedFolderPath(\n path: string | null | undefined,\n showSystemFiles: boolean,\n): boolean {\n return showSystemFiles || !isSystemFolderPath(path);\n}\n\n/** The fields Recents needs from a file record (domain or row shape). */\nexport interface RecentActivityFileShape extends SystemFileShape {\n filePath: string | null | undefined;\n /** The registered device that wrote the row; null for an in-app write. */\n originDeviceId?: string | null;\n parentFileId?: string | null;\n derivationKind?: string | null;\n}\n\n/**\n * Mirror of `files.is_recent_activity(files.files)` — RECENTS: the file is the\n * person's recent activity in the app. A file a desktop sync client wrote\n * (`originDeviceId`) is the person's file, but never their recent activity\n * (folder-sync DECISIONS R3).\n */\nexport function isRecentActivityFile(file: RecentActivityFileShape): boolean {\n if (isSystemFile(file)) return false;\n if (file.originDeviceId) return false;\n if (file.parentFileId) return false;\n if (file.derivationKind) return false;\n return isRecentActivityPath(file.filePath);\n}\n"],"mappings":"AA6DA,MAAM,kBAAkB,CAAC,gBAAgB,aAAa;AACtD,MAAM,cAAc;AAEpB,MAAM,mBAAmB,CAAC,mBAAmB,aAAa;AAE1D,MAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOA,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,MAAM,uBAAuB,IAAI;AAAA,EAC/B,KAAK,cAAc,KAAK,GAAG,CAAC;AAC9B;AAGA,SAAS,oBAAoB,MAAsB;AACjD,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK,MAAK;AAChD,SAAO,KAAK,MAAM,CAAC;AACrB;AAGA,SAAS,aAAa,MAAsB;AAC1C,QAAM,OAAO,oBAAoB,IAAI;AACrC,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,SAAO,UAAU,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK;AAClD;AAMO,SAAS,sBACd,MACS;AACT,MAAI,SAAS,QAAQ,SAAS,OAAW,QAAO;AAChD,QAAM,OAAO,aAAa,IAAI;AAC9B,MAAK,gBAAsC,SAAS,IAAI,EAAG,QAAO;AAClE,MAAK,iBAAuC,SAAS,IAAI,EAAG,QAAO;AACnE,SAAO,SAAS;AAClB;AAQO,SAAS,wBACd,MACS;AACT,MAAI,SAAS,QAAQ,SAAS,OAAW,QAAO;AAChD,QAAM,OAAO,aAAa,IAAI;AAC9B,MAAK,gBAAsC,SAAS,IAAI,EAAG,QAAO;AAClE,SAAO,CAAE,iBAAuC,SAAS,IAAI;AAC/D;AAiBO,SAAS,qBAAqB,KAAuC;AAC1E,MAAI,IAAI,mBAAmB,QAAQ,IAAI,mBAAmB;AACxD,WAAO;AACT,MAAI,IAAI,oBAAoB,QAAQ,IAAI,oBAAoB;AAC1D,WAAO;AACT,MAAI,IAAI,kBAAkB,QAAQ,IAAI,kBAAkB;AACtD,WAAO;AACT,SAAO,sBAAsB,IAAI,SAAS;AAC5C;AAOO,SAAS,qBACd,MACS;AACT,MAAI,CAAC,sBAAsB,IAAI,EAAG,QAAO;AACzC,QAAM,UAAU,oBAAoB,IAAc;AAClD,MAAI,qBAAqB,KAAK,aAAa,OAAO,CAAC,EAAG,QAAO;AAC7D,SAAO,CAAC,sBAAsB;AAAA,IAC5B,CAAC,SAAS,YAAY,QAAQ,QAAQ,WAAW,GAAG,IAAI,GAAG;AAAA,EAC7D;AACF;AAaO,SAAS,aAAa,MAAgC;AAC3D,SAAO,KAAK,WAAW,iBAAiB,MAAM;AAChD;AASA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,MAAM,8BAA8B,IAAI;AAAA,EACtC,KAAK,qBAAqB,KAAK,GAAG,CAAC;AACrC;AAMA,MAAM,iBACJ;AAQK,SAAS,mBAAmB,MAA0C;AAC3E,MAAI,SAAS,QAAQ,SAAS,OAAW,QAAO;AAChD,MAAI,4BAA4B,KAAK,aAAa,IAAI,CAAC,EAAG,QAAO;AACjE,MAAI,UAAU,oBAAoB,IAAI;AACtC,SAAO,QAAQ,SAAS,GAAG,EAAG,WAAU,QAAQ,MAAM,GAAG,EAAE;AAC3D,SAAO,eAAe,KAAK,OAAO;AACpC;AAOO,SAAS,aAAa,MAAuB,iBAAmC;AACrF,SAAO,mBAAmB,CAAC,aAAa,IAAI;AAC9C;AAGO,SAAS,mBACd,MACA,iBACS;AACT,SAAO,mBAAmB,CAAC,mBAAmB,IAAI;AACpD;AAiBO,SAAS,qBAAqB,MAAwC;AAC3E,MAAI,aAAa,IAAI,EAAG,QAAO;AAC/B,MAAI,KAAK,eAAgB,QAAO;AAChC,MAAI,KAAK,aAAc,QAAO;AAC9B,MAAI,KAAK,eAAgB,QAAO;AAChC,SAAO,qBAAqB,KAAK,QAAQ;AAC3C;","names":[]}
|