import { computed, ref, onMounted, watch, onUnmounted, defineComponent, mergeProps, unref, withCtx, openBlock, createBlock, createVNode, toDisplayString, Fragment, renderList, createCommentVNode, createTextVNode, useSSRContext } from "vue";
import { ssrRenderAttrs, ssrRenderList, ssrRenderClass, ssrInterpolate, ssrRenderAttr, ssrRenderComponent } from "vue/server-renderer";
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import { i as useViewerStore, g as getApiBase, w as ws, l as lastViewerPath, s as scrollbarOptions, _ as _export_sfc } from "../main.mjs";
import { ArrowDownTrayIcon, FolderOpenIcon, FolderIcon, DocumentIcon } from "@heroicons/vue/20/solid";
import { useRoute, useRouter } from "vue-router";
import { marked } from "marked";
import "@unhead/vue/server";
import "pinia";
import "@heroicons/vue/24/outline";
import "overlayscrollbars";
function useViewer() {
const route = useRoute();
const router = useRouter();
const { onMessage } = ws;
const viewerStore = useViewerStore();
const fstoken = computed(() => viewerStore.fstoken);
const viewerRoots = computed(() => viewerStore.roots);
const token = fstoken;
function normalizePath(path) {
if (!path) return path;
const roots = viewerStore.roots;
if (!roots.length) return path;
const [prefix, ...rest] = path.split("/");
if (roots.includes(prefix)) return path;
const canonical = roots.find((r) => r === `workspace-${prefix}` || r.endsWith(`-${prefix}`));
return canonical ? [canonical, ...rest].join("/") : path;
}
const currentPath = ref(normalizePath(
route.query.path || localStorage.getItem("viewer_last_path") || ""
));
const sidebarCollapsed = ref(window.innerWidth < 768);
const content = ref("");
const fileType = ref("");
const dirFiles = ref([]);
const dirDirs = ref([]);
const loading = ref(false);
const showLoading = ref(false);
let loadingTimer = null;
const fetchError = ref("");
const pdfCacheBust = ref(Date.now());
const mdRaw = ref(false);
const mdLineCount = computed(() => {
if (!content.value) return 0;
return content.value.split("\n").length;
});
const renderedMd = computed(() => {
if (fileType.value !== "md" || !content.value) return "";
return marked.parse(content.value);
});
const pdfSrc = computed(() => {
if (fileType.value !== "pdf" || !currentPath.value || !token.value) return "";
const base = getBaseUrl();
return `${base}/api/viewer/file?path=${encodeURIComponent(currentPath.value)}&token=${encodeURIComponent(token.value)}&t=${pdfCacheBust.value}`;
});
function getBaseUrl() {
return getApiBase();
}
function extOf(p) {
const m = p.match(/\.([^./]+)$/);
return m ? m[1].toLowerCase() : "";
}
function startLoading(silent) {
if (silent) return;
loading.value = true;
showLoading.value = false;
if (loadingTimer) clearTimeout(loadingTimer);
loadingTimer = setTimeout(() => {
showLoading.value = true;
}, 3e3);
}
function stopLoading() {
loading.value = false;
showLoading.value = false;
if (loadingTimer) {
clearTimeout(loadingTimer);
loadingTimer = null;
}
}
async function fetchContent(path, silent = false, _retry = false) {
startLoading(silent);
try {
const base = getBaseUrl();
const url = `${base}/api/viewer/file?path=${encodeURIComponent(path)}&token=${encodeURIComponent(token.value)}`;
const res = await fetch(url);
if (res.status === 401 && !_retry) {
viewerStore.invalidate();
await viewerStore.acquire(true);
return fetchContent(path, silent, true);
}
if (!res.ok) {
fetchError.value = `${res.status}: ${await res.text()}`;
return;
}
fetchError.value = "";
content.value = await res.text();
} catch (e) {
fetchError.value = e.message || "Fetch failed";
} finally {
stopLoading();
}
}
async function openFile(path) {
path = normalizePath(path);
currentPath.value = path;
localStorage.setItem("viewer_last_path", path);
lastViewerPath.value = path;
if (route.query.path !== path) {
router.push({ name: "viewer", query: { path } });
}
const ext = extOf(path);
if (!path) {
fileType.value = "dir";
content.value = "";
fetchError.value = "";
dirDirs.value = viewerRoots.value.length ? viewerRoots.value : ["shared", "workspace-titan"];
dirFiles.value = [];
return;
}
if (!ext) {
fileType.value = "dir";
content.value = "";
fetchError.value = "";
startLoading(false);
try {
const base = getBaseUrl();
const url = `${base}/api/viewer/tree?root=${encodeURIComponent(path)}&token=${encodeURIComponent(token.value)}`;
const res = await fetch(url);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const data = await res.json();
dirDirs.value = data.dirs || [];
dirFiles.value = data.files || [];
} catch (e) {
dirDirs.value = [];
dirFiles.value = [];
fetchError.value = e.message || "Failed to load directory";
} finally {
stopLoading();
}
return;
}
if (ext === "pdf") {
fileType.value = "pdf";
content.value = "";
fetchError.value = "";
pdfCacheBust.value = Date.now();
fetch(`${getBaseUrl()}/api/viewer/file?path=${encodeURIComponent(path)}&token=${encodeURIComponent(token.value)}`, { method: "HEAD" }).catch(() => {
});
return;
}
fileType.value = ext === "md" ? "md" : "text";
fetchError.value = "";
await fetchContent(path, false);
}
function onCopy(e) {
const sel = window.getSelection();
if (!sel || sel.isCollapsed) return;
const html = (() => {
const div = document.createElement("div");
div.appendChild(sel.getRangeAt(0).cloneContents());
return div.innerHTML;
})();
const clean = html.replace(/background(-color)?:[^;"]*(;|(?="))/gi, "");
const plain = sel.toString();
e.clipboardData.setData("text/html", clean);
e.clipboardData.setData("text/plain", plain);
e.preventDefault();
}
async function refreshFile(path) {
const ext = extOf(path);
if (ext === "pdf") {
pdfCacheBust.value = Date.now();
return;
}
await fetchContent(path, true);
}
let unsubscribe = null;
onMounted(() => {
unsubscribe = onMessage((data) => {
if (data.type === "viewer_file_changed" && data.path === currentPath.value) {
refreshFile(currentPath.value);
}
if (data.type === "viewer_tree_changed" && data.path === currentPath.value && fileType.value === "dir") {
openFile(currentPath.value);
}
});
viewerStore.acquire();
if (currentPath.value) openFile(currentPath.value);
});
watch(() => route.query.path, (newPath) => {
if (newPath && newPath !== currentPath.value) {
openFile(newPath);
}
});
onUnmounted(() => {
if (unsubscribe) unsubscribe();
});
return {
fstoken,
viewerRoots,
currentPath,
sidebarCollapsed,
content,
fileType,
loading,
fetchError,
mdRaw,
mdLineCount,
renderedMd,
pdfSrc,
openFile,
onCopy,
dirFiles,
dirDirs,
showLoading
};
}
const _sfc_main = /* @__PURE__ */ defineComponent({
...{ name: "ViewerView" },
__name: "ViewerView",
__ssrInlineRender: true,
setup(__props) {
const {
fstoken,
currentPath,
content,
fileType,
showLoading,
fetchError,
mdRaw,
renderedMd,
pdfSrc,
openFile,
onCopy,
dirFiles,
dirDirs
} = useViewer();
const contentLines = computed(() => content.value.split("\n"));
const fileName = computed(() => {
if (!currentPath.value) return "";
return currentPath.value.split("/").pop() || "";
});
const downloadUrl = computed(() => {
if (!currentPath.value || !fstoken.value) return "";
return `${getApiBase()}/api/viewer/file?path=${encodeURIComponent(currentPath.value)}&token=${encodeURIComponent(fstoken.value)}&dl=1`;
});
const breadcrumbs = computed(() => {
const crumbs = [{ label: "files", path: "" }];
if (!currentPath.value) return crumbs;
const parts = currentPath.value.split("/");
for (let i = 0; i < parts.length; i++) {
crumbs.push({ label: parts[i], path: parts.slice(0, i + 1).join("/") });
}
return crumbs;
});
return (_ctx, _push, _parent, _attrs) => {
_push(`
`);
if (unref(currentPath) || unref(fileType) === "dir") {
_push(``);
} else {
_push(``);
}
_push(ssrRenderComponent(unref(OverlayScrollbarsComponent), {
class: "content flex-1 min-h-0",
options: unref(scrollbarOptions),
element: "div"
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
if (!unref(currentPath) && unref(fileType) !== "dir") {
_push2(``);
_push2(ssrRenderComponent(unref(FolderOpenIcon), { class: "w-8 h-8 text-text-dim" }, null, _parent2, _scopeId));
_push2(`
Select a file from the tree
`);
} else if (unref(showLoading)) {
_push2(`loading…
`);
} else if (unref(fetchError)) {
_push2(`${ssrInterpolate(unref(fetchError))}
`);
} else if (unref(fileType) === "dir") {
_push2(``);
ssrRenderList(unref(dirDirs), (d) => {
_push2(`
`);
_push2(ssrRenderComponent(unref(FolderIcon), { class: "w-4 h-4" }, null, _parent2, _scopeId));
_push2(`${ssrInterpolate(d)}/
`);
});
_push2(``);
ssrRenderList(unref(dirFiles), (f) => {
_push2(`
`);
_push2(ssrRenderComponent(unref(DocumentIcon), { class: "w-4 h-4" }, null, _parent2, _scopeId));
_push2(`${ssrInterpolate(f.name)}
`);
});
_push2(``);
if (!unref(dirDirs).length && !unref(dirFiles).length) {
_push2(`
`);
} else {
_push2(``);
}
_push2(`
`);
} else if (unref(fileType) === "pdf") {
_push2(``);
} else if (unref(fileType) === "md" && !unref(mdRaw)) {
_push2(`${unref(renderedMd) ?? ""}
`);
} else if (unref(fileType) === "md" && unref(mdRaw)) {
_push2(``);
ssrRenderList(contentLines.value, (line, i) => {
_push2(`${ssrInterpolate(i + 1)}${ssrInterpolate(line)}
`);
});
_push2(`
`);
} else {
_push2(``);
ssrRenderList(contentLines.value, (line, i) => {
_push2(`${ssrInterpolate(i + 1)}${ssrInterpolate(line)}
`);
});
_push2(`
`);
}
} else {
return [
!unref(currentPath) && unref(fileType) !== "dir" ? (openBlock(), createBlock("div", {
key: 0,
class: "viewer-empty"
}, [
createVNode(unref(FolderOpenIcon), { class: "w-8 h-8 text-text-dim" }),
createVNode("p", null, "Select a file from the tree")
])) : unref(showLoading) ? (openBlock(), createBlock("div", {
key: 1,
class: "viewer-loading"
}, "loading…")) : unref(fetchError) ? (openBlock(), createBlock("div", {
key: 2,
class: "viewer-error"
}, toDisplayString(unref(fetchError)), 1)) : unref(fileType) === "dir" ? (openBlock(), createBlock("div", {
key: 3,
class: "viewer-dir"
}, [
(openBlock(true), createBlock(Fragment, null, renderList(unref(dirDirs), (d) => {
return openBlock(), createBlock("div", {
key: d,
class: "dir-entry dir-entry--dir",
onClick: ($event) => unref(openFile)(unref(currentPath) ? unref(currentPath) + "/" + d : d)
}, [
createVNode(unref(FolderIcon), { class: "w-4 h-4" }),
createVNode("span", null, toDisplayString(d) + "/", 1)
], 8, ["onClick"]);
}), 128)),
(openBlock(true), createBlock(Fragment, null, renderList(unref(dirFiles), (f) => {
return openBlock(), createBlock("div", {
key: f.path,
class: "dir-entry dir-entry--file",
onClick: ($event) => unref(openFile)(f.path)
}, [
createVNode(unref(DocumentIcon), { class: "w-4 h-4" }),
createVNode("span", null, toDisplayString(f.name), 1)
], 8, ["onClick"]);
}), 128)),
!unref(dirDirs).length && !unref(dirFiles).length ? (openBlock(), createBlock("div", {
key: 0,
class: "viewer-empty"
}, [
createVNode("p", null, "Empty directory")
])) : createCommentVNode("", true)
])) : unref(fileType) === "pdf" ? (openBlock(), createBlock("iframe", {
key: 4,
class: "viewer-pdf",
src: unref(pdfSrc),
frameborder: "0"
}, null, 8, ["src"])) : unref(fileType) === "md" && !unref(mdRaw) ? (openBlock(), createBlock("div", {
key: 5,
class: "viewer-md md-body",
innerHTML: unref(renderedMd),
onCopy: unref(onCopy)
}, null, 40, ["innerHTML", "onCopy"])) : unref(fileType) === "md" && unref(mdRaw) ? (openBlock(), createBlock("div", {
key: 6,
class: "viewer-raw-md"
}, [
createVNode("pre", { class: "viewer-raw-code" }, [
createVNode("code", null, [
(openBlock(true), createBlock(Fragment, null, renderList(contentLines.value, (line, i) => {
return openBlock(), createBlock("span", {
key: i,
class: "code-line"
}, [
createVNode("span", { class: "line-num" }, toDisplayString(i + 1), 1),
createTextVNode(toDisplayString(line) + "\n", 1)
]);
}), 128))
])
])
])) : (openBlock(), createBlock("pre", {
key: 7,
class: "viewer-raw-code"
}, [
createVNode("code", null, [
(openBlock(true), createBlock(Fragment, null, renderList(contentLines.value, (line, i) => {
return openBlock(), createBlock("span", {
key: i,
class: "code-line"
}, [
createVNode("span", { class: "line-num" }, toDisplayString(i + 1), 1),
createTextVNode(toDisplayString(line) + "\n", 1)
]);
}), 128))
])
]))
];
}
}),
_: 1
}, _parent));
_push(` `);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/ViewerView.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const ViewerView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-6b5cb038"]]);
export {
ViewerView as default
};