// Composable: derive content-type flags and formatted views from the // current selection. Extracted from FilesView.vue (#507 step 5). import { computed, watch, type Ref } from "vue"; import type { FileContent } from "./useFileContentLoader"; import { wrapHtmlWithPreviewCsp } from "../utils/html/previewCsp"; import { cspExtra, loadCspExtra } from "./useCspExtra"; import { tokenizeJson, tokenizeJsonl, prettyJson } from "../utils/format/jsonSyntax"; import { parseFrontmatter } from "@mulmoclaude/markdown-utils/markdown/frontmatter"; function hasExt(filePath: string | null, exts: string[]): boolean { if (!filePath) return false; const lower = filePath.toLowerCase(); return exts.some((ext) => lower.endsWith(ext)); } // Workspace-relative prefix of HTML artifacts that the server exposes // as a path-based static mount (`server/index.ts` → `/artifacts/html`). // Files-view previews under this prefix load via iframe `src=...`, so // the browser resolves relative refs (``) // against the file's real URL instead of `about:srcdoc`. Files // elsewhere fall back to the existing `srcdoc` path. const HTML_PREVIEW_DIR_PREFIX = "artifacts/html/"; export function htmlPreviewUrlFor(filePath: string | null): string | null { if (!filePath) return null; const lower = filePath.toLowerCase(); if (!lower.endsWith(".html") && !lower.endsWith(".htm")) return null; if (!filePath.startsWith(HTML_PREVIEW_DIR_PREFIX)) return null; const rest = filePath.slice(HTML_PREVIEW_DIR_PREFIX.length); if (rest.length === 0) return null; return `/artifacts/html/${rest.split("/").map(encodeURIComponent).join("/")}`; } // SVG artifacts are served by the `/artifacts/svg` static mount — // loaded into View / Preview via ``. The browser refuses // to execute `