import { useEffect } from "preact/hooks"; import { render } from "preact"; import { filesScope } from "../../stores/artifacts"; import { project } from "../../stores/session"; import { browseFiles, setFilesContentType, setFilesOrigin, setFilesQuery } from "../../features/artifacts/files-index"; import { loadProjectArtifacts, setFilesScope } from "../../features/artifacts/load"; import { filesT } from "../../features/artifacts/copy"; import { filesContentType, filesHasMore, filesIndexError, filesIndexLoading, filesOrigin, filesQuery, } from "../../features/artifacts/state"; import { renderFilesGrid } from "../../features/artifacts/ui"; import { FILES_PAGE_SIZE } from "../../features/artifacts/types"; import type { FilesOrigin } from "../../features/artifacts/types"; import "../../features/artifacts/artifacts.css"; let searchTimer: ReturnType | null = null; function scheduleSearch(value: string): void { setFilesQuery(value); if (searchTimer !== null) clearTimeout(searchTimer); searchTimer = setTimeout(() => { searchTimer = null; void browseFiles({ reset: true }).then(() => renderFilesGrid()); }, 200); } function applyOrigin(origin: FilesOrigin): void { setFilesOrigin(origin); void browseFiles({ reset: true }).then(() => renderFilesGrid()); } function applyType(value: string): void { setFilesContentType(value); void browseFiles({ reset: true }).then(() => renderFilesGrid()); } /** * Files dock chrome (M-03). Mounted into the shell's `#dock-files`. * Frozen ids: `#results-list`, `#results-count`, `#files-scope`. * The grid itself is painted imperatively by `renderFilesGrid` so a * signal-driven re-render of this chrome cannot wipe cards. */ export function FilesPanel() { const origin = filesOrigin.value; const scope = filesScope.value; const pid = project.value; useEffect(() => { if (scope === "project") { void loadProjectArtifacts().then(() => renderFilesGrid()); } }, [pid, scope]); useEffect(() => { renderFilesGrid(); }); return ( <>
{filesT("dock.files.heading")} 0
scheduleSearch((e.currentTarget as HTMLInputElement).value)} /> applyType((e.currentTarget as HTMLInputElement).value)} /> {(["", "uploaded", "generated"] as const).map((key) => ( ))}
{filesIndexError.value ?
{filesIndexError.value}
: null}
{filesHasMore.value ? ( ) : null} ); } /** Lane-local mount: paint into the shell `#dock-files` without editing Shell.tsx. */ export function mountFilesPanel(host?: HTMLElement | null): void { const node = host || (typeof document !== "undefined" ? document.getElementById("dock-files") : null); if (!node || node.dataset.m03Mounted === "1") return; node.dataset.m03Mounted = "1"; render(, node); }