// 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 `