@av-pi-studio/web-client 0.0.49 → 0.0.51
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/dist/.tsbuildinfo +1 -1
- package/dist/web/assets/{BinaryFallbackViewer-BqWD5myp.js → BinaryFallbackViewer-BCheCWlj.js} +2 -2
- package/dist/web/assets/{BinaryFallbackViewer-BqWD5myp.js.map → BinaryFallbackViewer-BCheCWlj.js.map} +1 -1
- package/dist/web/assets/{ChatPanel-Cghr4FA9.js → ChatPanel-K1uSkIOF.js} +3 -3
- package/dist/web/assets/{ChatPanel-Cghr4FA9.js.map → ChatPanel-K1uSkIOF.js.map} +1 -1
- package/dist/web/assets/{CodeView-Cr7QCw_g.js → CodeView-wz9a-TBl.js} +2 -2
- package/dist/web/assets/{CodeView-Cr7QCw_g.js.map → CodeView-wz9a-TBl.js.map} +1 -1
- package/dist/web/assets/FilePanel-B88cfGeN.js +2 -0
- package/dist/web/assets/{FilePanel-D10heC-L.js.map → FilePanel-B88cfGeN.js.map} +1 -1
- package/dist/web/assets/{ImageViewer-5clHM3Nh.js → ImageViewer-DMuVw1TC.js} +2 -2
- package/dist/web/assets/{ImageViewer-5clHM3Nh.js.map → ImageViewer-DMuVw1TC.js.map} +1 -1
- package/dist/web/assets/MarkdownFileViewer-D9pKcm5W.js +2 -0
- package/dist/web/assets/{MarkdownFileViewer-Dd70piQk.js.map → MarkdownFileViewer-D9pKcm5W.js.map} +1 -1
- package/dist/web/assets/{MoleculeViewerPanel-DFx9LSN6.js → MoleculeViewerPanel-DWiCnr-2.js} +2 -2
- package/dist/web/assets/{MoleculeViewerPanel-DFx9LSN6.js.map → MoleculeViewerPanel-DWiCnr-2.js.map} +1 -1
- package/dist/web/assets/{TerminalPanel-L9DJMwB6.js → TerminalPanel-DgCqvB_u.js} +2 -2
- package/dist/web/assets/{TerminalPanel-L9DJMwB6.js.map → TerminalPanel-DgCqvB_u.js.map} +1 -1
- package/dist/web/assets/{TextViewer-BDXrL58w.js → TextViewer-CCNwnC6m.js} +2 -2
- package/dist/web/assets/{TextViewer-BDXrL58w.js.map → TextViewer-CCNwnC6m.js.map} +1 -1
- package/dist/web/assets/{VideoViewer-DfNnw1AJ.js → VideoViewer-F211X3SB.js} +2 -2
- package/dist/web/assets/{VideoViewer-DfNnw1AJ.js.map → VideoViewer-F211X3SB.js.map} +1 -1
- package/dist/web/assets/index-BLc00KUW.css +1 -0
- package/dist/web/assets/index-CqmgQdsG.js +3 -0
- package/dist/web/assets/index-CqmgQdsG.js.map +1 -0
- package/dist/web/assets/markdown-CTYolZ3e.js +2 -0
- package/dist/web/assets/{markdown-0OCAeyJh.js.map → markdown-CTYolZ3e.js.map} +1 -1
- package/dist/web/assets/{use-file-download-C5ZdZp1T.js → use-file-download-BoIvv6b7.js} +2 -2
- package/dist/web/assets/{use-file-download-C5ZdZp1T.js.map → use-file-download-BoIvv6b7.js.map} +1 -1
- package/dist/web/assets/{use-file-watch-2C2o-DDo.js → use-file-watch-BY4_Mzyt.js} +2 -2
- package/dist/web/assets/{use-file-watch-2C2o-DDo.js.map → use-file-watch-BY4_Mzyt.js.map} +1 -1
- package/dist/web/assets/{vendor-icons-CAhpOZSZ.js → vendor-icons-CctYQ66i.js} +72 -47
- package/dist/web/assets/vendor-icons-CctYQ66i.js.map +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +3 -3
- package/dist/web/assets/FilePanel-D10heC-L.js +0 -2
- package/dist/web/assets/MarkdownFileViewer-Dd70piQk.js +0 -2
- package/dist/web/assets/index-Bu36Yj2q.css +0 -1
- package/dist/web/assets/index-Ds0VVcbZ.js +0 -3
- package/dist/web/assets/index-Ds0VVcbZ.js.map +0 -1
- package/dist/web/assets/markdown-0OCAeyJh.js +0 -2
- package/dist/web/assets/paths--dEDMUm1.js +0 -2
- package/dist/web/assets/paths--dEDMUm1.js.map +0 -1
- package/dist/web/assets/vendor-icons-CAhpOZSZ.js.map +0 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as d,j as s}from"./vendor-react-p-LLqNcx.js";import{c as b}from"./vendor-highlight-Cry4x-mr.js";import{n as I,c as x,d as j,a as f,i as _,j as w,k as v,b as S,h as E,S as T,l as M}from"./index-CqmgQdsG.js";import{M as C,r as y}from"./vendor-markdown-ePgMI45n.js";const k="github-dark";let m=null;const g=new Set(["plaintext"]);function H(){return m||(m=b({themes:[k],langs:["plaintext"]})),m}async function N(i,e){if(g.has(e))return e;try{return await i.loadLanguage(e),g.add(e),e}catch{return"plaintext"}}async function F(i,e){const t=await H(),n=await N(t,e);return t.codeToHtml(i,{lang:n,theme:k})}const L="_prose_11tgt_1",U="_inlineCode_11tgt_32",z="_inlineImage_11tgt_58",R="_inlineImageSkeleton_11tgt_67",P="_inlineImageFallback_11tgt_78",c={prose:L,inlineCode:U,inlineImage:z,inlineImageSkeleton:R,inlineImageFallback:P},W=new Set(["http:","https:","data:","blob:"]),$=/^([a-zA-Z][a-zA-Z0-9+.-]*):/;function A(i,e,t){const n=i.trim();if(!n)return{kind:"unresolvable"};const a=n.split(/[?#]/,1)[0];if(!a)return{kind:"unresolvable"};const l=$.exec(a);if(l){const u=`${l[1].toLowerCase()}:`;return W.has(u)?{kind:"remote"}:{kind:"unresolvable"}}let o;return a.startsWith("/")?o=a:a==="~"||a.startsWith("~/")?o=t?I(a,t):null:o=x(a,e??""),o?j(o)!=="image"?{kind:"unresolvable"}:{kind:"local",path:o}:{kind:"unresolvable"}}function V(i,e,t,n){return i.kind==="remote"?{kind:"remote",src:t||"",alt:n}:i.kind==="unresolvable"?{kind:"unresolvable",fallbackText:n||t||"(image)"}:e.status==="loading"||e.status==="idle"?{kind:"loading"}:e.status==="ready"?{kind:"ready",objectUrl:e.objectUrl,alt:n,path:i.path}:{kind:"error",fallbackText:n||i.path||"(image)"}}function Z(i,e,t){if(!i||!e){t({status:"idle"});return}let n=!1;return t({status:"loading"}),_(i,e).then(a=>{n||t({status:"ready",objectUrl:a.objectUrl})}).catch(a=>{n||t({status:"error",message:a instanceof Error?a.message:String(a)})}),()=>{n=!0,w(i)}}function q(i){const e=f(l=>l.client),t=f(l=>l.daemon),[n,a]=d.useState({status:"idle"});return d.useEffect(()=>Z(i,e&&t?o=>v(t).download(o):null,a),[e,t,i]),n}const B=d.memo(function({src:e,alt:t,assetBase:n}){const a=S(),l=E(p=>p.open),o=d.useMemo(()=>A(e||"",n,a),[e,n,a]),u=q(o.kind==="local"?o.path:null),r=V(o,u,e,t);if(r.kind==="remote")return s.jsx("img",{src:r.src,alt:r.alt,className:c.inlineImage});if(r.kind==="unresolvable"||r.kind==="error")return s.jsx("code",{className:c.inlineImageFallback,children:r.fallbackText});if(r.kind==="loading")return s.jsx("div",{className:c.inlineImageSkeleton,children:s.jsx(T,{size:"sm"})});const h=()=>{l({id:M.file(r.path),kind:"file",label:r.path.split("/").pop()||r.path,closable:!0,data:{path:r.path},workspaceCwd:n||"~"})};return s.jsx("img",{src:r.objectUrl,alt:r.alt,className:c.inlineImage,title:r.path,onClick:h,style:{cursor:"pointer"}})});function D({language:i,code:e}){const[t,n]=d.useState(null);return d.useEffect(()=>{let a=!1;return F(e,i).then(l=>{a||n(l)}),()=>{a=!0}},[e,i]),t?s.jsx("div",{className:c.prose,dangerouslySetInnerHTML:{__html:t}}):s.jsx("pre",{children:s.jsx("code",{children:e})})}function G({className:i,children:e,...t}){const n=/language-(\w+)/.exec(i??""),a=!!n,l=String(e).replace(/\n$/,"");return a&&n?s.jsx(D,{language:n[1]??"plaintext",code:l}):s.jsx("code",{className:c.inlineCode,...t,children:e})}const X=d.memo(function({text:e,assetBase:t=null}){return s.jsx("div",{className:c.prose,children:s.jsx(C,{remarkPlugins:[y],components:{code:G,img:n=>s.jsx(B,{...n,assetBase:t})},children:e})})});export{X as M};
|
|
2
|
+
//# sourceMappingURL=markdown-CTYolZ3e.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-0OCAeyJh.js","sources":["../../../src/timeline/highlight.ts","../../../src/timeline/image-src.ts","../../../src/timeline/inline-image-view.ts","../../../src/hooks/use-inline-image.ts","../../../src/timeline/InlineImage.tsx","../../../src/timeline/markdown.tsx"],"sourcesContent":["/**\n * Shiki wrapper + lang-from-path map. Replaces the POC's CDN `highlight.js`\n * (POC_TO_APP_PLAN_UI.md §2, §4.8). Grammars/themes are loaded lazily and cached — the\n * highlighter is not paid for until a code block/file view actually renders.\n */\n\nimport { createHighlighter, type Highlighter } from \"shiki\";\n\n/** POC `getLangFromPath` extension → language map, ported verbatim. */\nconst EXT_TO_LANG: Record<string, string> = {\n js: \"javascript\",\n ts: \"typescript\",\n jsx: \"javascript\",\n tsx: \"typescript\",\n py: \"python\",\n rb: \"ruby\",\n rs: \"rust\",\n go: \"go\",\n java: \"java\",\n c: \"c\",\n cpp: \"cpp\",\n h: \"c\",\n hpp: \"cpp\",\n cs: \"csharp\",\n html: \"html\",\n css: \"css\",\n scss: \"scss\",\n json: \"json\",\n yaml: \"yaml\",\n yml: \"yaml\",\n toml: \"toml\",\n xml: \"xml\",\n md: \"markdown\",\n sh: \"bash\",\n bash: \"bash\",\n sql: \"sql\",\n swift: \"swift\",\n kt: \"kotlin\",\n dart: \"dart\",\n vue: \"xml\",\n svelte: \"xml\",\n};\n\nexport function langFromPath(path: string): string {\n const ext = path.split(\".\").pop()?.toLowerCase() ?? \"\";\n return EXT_TO_LANG[ext] ?? \"plaintext\";\n}\n\nconst THEME = \"github-dark\";\n\nlet highlighterPromise: Promise<Highlighter> | null = null;\nconst loadedLangs = new Set<string>([\"plaintext\"]);\n\nfunction getHighlighter(): Promise<Highlighter> {\n if (!highlighterPromise) {\n highlighterPromise = createHighlighter({ themes: [THEME], langs: [\"plaintext\"] });\n }\n return highlighterPromise;\n}\n\nasync function ensureLang(highlighter: Highlighter, lang: string): Promise<string> {\n if (loadedLangs.has(lang)) return lang;\n try {\n await highlighter.loadLanguage(lang as never);\n loadedLangs.add(lang);\n return lang;\n } catch {\n return \"plaintext\"; // unknown grammar to Shiki — fall back rather than throw\n }\n}\n\n/** Highlight `code` for `lang` (or a path via `langFromPath`) to sanitized HTML. */\nexport async function highlightToHtml(code: string, lang: string): Promise<string> {\n const highlighter = await getHighlighter();\n const resolved = await ensureLang(highlighter, lang);\n return highlighter.codeToHtml(code, { lang: resolved, theme: THEME });\n}\n","/**\n * Markdown image source classification (features/inline-image-rendering.md § Public Contract →\n * Markdown image source classification, § Asset base). The single gate every `` in a\n * rendered chat message passes through before any network request is even considered — deciding\n * remote (render directly), local (fetch via the daemon's file-transfer path, task-003), or\n * unresolvable (render as plain fallback text, never attempt a request).\n *\n * Pure: no store/hook/network imports, so this file needs no React or client mocks to test.\n */\n\nimport { detectViewerKind } from \"@pi-studio-ui/features/files/viewer-registry.js\";\nimport { normalizeCwd } from \"@pi-studio-ui/features/sessions/workspace-grouping.js\";\nimport { resolveWorkspacePath } from \"@pi-studio-ui/lib/paths.js\";\n\nexport type ImageSrcClassification =\n | { kind: \"remote\" }\n | { kind: \"local\"; path: string }\n | { kind: \"unresolvable\" };\n\nconst REMOTE_SCHEMES = new Set([\"http:\", \"https:\", \"data:\", \"blob:\"]);\n\n/** Matches a leading `scheme:` (e.g. `http:`, `file:`, `mailto:`) — not a Windows drive letter\n * (`C:\\...`) or a bare `:` with no scheme name. */\nconst SCHEME_RE = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;\n\n/**\n * Classifies a markdown image `src`. `base` is the asset base (absolute, tilde-normalized) or\n * `null` when unknown; `homeDir` may be `null` when the daemon's home directory hasn't been\n * learned yet. Order matters — this mirrors the scope's classification table exactly:\n *\n * 1. empty/whitespace → unresolvable\n * 2. `http:`/`https:`/`data:`/`blob:` → remote\n * 3. any other `scheme:` prefix (including `file:`) → unresolvable\n * 4. `/…` → candidate local path as-is\n * 5. `~`/`~/…` → expand against `homeDir` (unknown home dir → unresolvable)\n * 6. `./…`, `../…`, bare relative → resolve against `base` (no base → unresolvable)\n * 7. final gate: the candidate must be an image per `detectViewerKind`, else unresolvable\n */\nexport function classifyImageSrc(\n src: string,\n base: string | null,\n homeDir: string | null,\n): ImageSrcClassification {\n const trimmed = src.trim();\n if (!trimmed) return { kind: \"unresolvable\" };\n\n // Markdown may carry a title/version via `#fragment`/`?query`; strip both before classifying so\n // a trailing `?` never defeats the extension gate below.\n const stripped = trimmed.split(/[?#]/, 1)[0]!;\n if (!stripped) return { kind: \"unresolvable\" };\n\n const schemeMatch = SCHEME_RE.exec(stripped);\n if (schemeMatch) {\n const scheme = `${schemeMatch[1]!.toLowerCase()}:`;\n return REMOTE_SCHEMES.has(scheme) ? { kind: \"remote\" } : { kind: \"unresolvable\" };\n }\n\n let candidate: string | null;\n if (stripped.startsWith(\"/\")) {\n candidate = stripped;\n } else if (stripped === \"~\" || stripped.startsWith(\"~/\")) {\n candidate = homeDir ? normalizeCwd(stripped, homeDir) : null;\n } else {\n candidate = resolveWorkspacePath(stripped, base ?? \"\");\n }\n\n if (!candidate) return { kind: \"unresolvable\" };\n if (detectViewerKind(candidate) !== \"image\") return { kind: \"unresolvable\" };\n return { kind: \"local\", path: candidate };\n}\n","/**\n * Pure, testable logic for rendering inline images — extracted from the component\n * so tests can verify branch logic without JSX rendering (mirror of molecule-reload.ts,\n * text-viewer-state.ts; see AGENTS.md testing conventions).\n *\n * Returns a discriminated union describing what to render: the component switches\n * on it to decide remote passthrough, skeleton, ready image, or text fallback.\n */\n\nimport type { ImageSrcClassification } from \"./image-src.js\";\nimport type { InlineImageState } from \"@pi-studio-ui/hooks/use-inline-image.js\";\n\nexport type InlineImageView =\n | { kind: \"remote\"; src: string; alt?: string }\n | { kind: \"unresolvable\"; fallbackText: string }\n | { kind: \"loading\" }\n | { kind: \"ready\"; objectUrl: string; alt?: string; path: string }\n | { kind: \"error\"; fallbackText: string };\n\n/**\n * Decides what to render for an inline image given its classification and loading state.\n * Pure: no hooks, no store access — all state passed in as arguments.\n */\nexport function selectInlineImageView(\n classification: ImageSrcClassification,\n imageState: InlineImageState,\n src: string | undefined,\n alt: string | undefined,\n): InlineImageView {\n if (classification.kind === \"remote\") {\n return { kind: \"remote\", src: src || \"\", alt };\n }\n\n if (classification.kind === \"unresolvable\") {\n const fallbackText = alt || src || \"(image)\";\n return { kind: \"unresolvable\", fallbackText };\n }\n\n // classification.kind === \"local\"\n if (imageState.status === \"loading\" || imageState.status === \"idle\") {\n return { kind: \"loading\" };\n }\n\n if (imageState.status === \"ready\") {\n return { kind: \"ready\", objectUrl: imageState.objectUrl, alt, path: classification.path };\n }\n\n // imageState.status === \"error\"\n const fallbackText = alt || classification.path || \"(image)\";\n return { kind: \"error\", fallbackText };\n}\n","/**\n * `use-inline-image` — fetches (via the shared ref-counted `inline-image-cache`) and hands back\n * an object URL for an inline chat image (features/inline-image-rendering.md § Inline image fetch\n * + cache). Consumed by the markdown `img` node override (task-004) once a `classifyImageSrc`\n * result is `local`.\n *\n * Deliberately NOT a TanStack Query hook, and therefore no `rpcKeys` entry: the whole point of\n * `inline-image-cache.ts` is a retention policy (revoke only on LRU eviction, never on unmount)\n * that Query's cache cannot express. Do not \"fix\" this into a `useQuery` — see that module's\n * header for why.\n *\n * `loadInlineImage` is the actual effect body, extracted for direct unit testing without\n * rendering — this package has no jsdom/React-Testing-Library environment (`AGENTS.md`'s testing\n * convention; mirrors `use-file-watch.ts`'s `watchFile` extraction).\n */\n\nimport { useEffect, useState } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { acquireInlineImage, releaseInlineImage } from \"@pi-studio-ui/lib/inline-image-cache.js\";\nimport { transferFor } from \"./file-transfer-instance.js\";\n\nexport type InlineImageState =\n | { status: \"idle\" } // no client yet, or path is null\n | { status: \"loading\" }\n | { status: \"ready\"; objectUrl: string }\n | { status: \"error\"; message: string };\n\n/**\n * Runs one effect cycle: `download === null` (no live connection, or no path) is the idle case —\n * nothing to acquire, nothing to clean up. Otherwise acquires `path`, reports the loading →\n * ready/error transition via `onStateChange`, and returns a cleanup that releases the path and\n * ignores a resolution that lands after it (the `cancelled` flag pattern used across the existing\n * hooks, e.g. `use-checkout-status.ts`).\n */\nexport function loadInlineImage(\n path: string | null,\n download: ((path: string) => Promise<{ bytes: Uint8Array; mimeType?: string }>) | null,\n onStateChange: (state: InlineImageState) => void,\n): (() => void) | void {\n if (!path || !download) {\n onStateChange({ status: \"idle\" });\n return;\n }\n\n let cancelled = false;\n onStateChange({ status: \"loading\" });\n\n acquireInlineImage(path, download)\n .then((entry) => {\n if (!cancelled) onStateChange({ status: \"ready\", objectUrl: entry.objectUrl });\n })\n .catch((error: unknown) => {\n if (!cancelled) {\n onStateChange({\n status: \"error\",\n message: error instanceof Error ? error.message : String(error),\n });\n }\n });\n\n return () => {\n cancelled = true;\n releaseInlineImage(path);\n };\n}\n\nexport function useInlineImage(path: string | null): InlineImageState {\n const client = useConnectionStore((s) => s.client);\n const daemon = useConnectionStore((s) => s.daemon);\n const [state, setState] = useState<InlineImageState>({ status: \"idle\" });\n\n useEffect(() => {\n const download = client && daemon ? (p: string) => transferFor(daemon).download(p) : null;\n return loadInlineImage(path, download, setState);\n }, [client, daemon, path]);\n\n return state;\n}\n","/**\n * `` renderer for finalized assistant markdown (features/inline-image-rendering.md §\n * Behavior & Algorithms → Render pipeline). Handles remote passthrough, local file fetch via\n * useInlineImage (task-003), classification (task-002), and click-to-open file tab.\n *\n * Props are optional (react-markdown may omit them) and must be handled defensively.\n */\n\nimport { memo, useMemo } from \"react\";\nimport { classifyImageSrc } from \"./image-src.js\";\nimport { selectInlineImageView } from \"./inline-image-view.js\";\nimport { useInlineImage } from \"@pi-studio-ui/hooks/use-inline-image.js\";\nimport { useHomeDir } from \"@pi-studio-ui/hooks/use-home-dir.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport styles from \"./markdown.module.css\";\n\nexport interface InlineImageProps {\n src?: string;\n alt?: string;\n assetBase: string | null;\n}\n\nexport const InlineImage = memo(function InlineImage({ src, alt, assetBase }: InlineImageProps) {\n const homeDir = useHomeDir();\n const openTab = useTabStore((s) => s.open);\n\n // Classify the source once at render time.\n const classification = useMemo(\n () => classifyImageSrc(src || \"\", assetBase, homeDir),\n [src, assetBase, homeDir],\n );\n\n // For local images, fetch and track state.\n const imageState = useInlineImage(classification.kind === \"local\" ? classification.path : null);\n\n // Decide what to render.\n const view = selectInlineImageView(classification, imageState, src, alt);\n\n // Remote images: pass through unchanged (what react-markdown's default would do).\n if (view.kind === \"remote\") {\n return <img src={view.src} alt={view.alt} className={styles.inlineImage} />;\n }\n\n // Unresolvable or error: render text fallback in inline monospace.\n if (view.kind === \"unresolvable\" || view.kind === \"error\") {\n return <code className={styles.inlineImageFallback}>{view.fallbackText}</code>;\n }\n\n // Loading or idle: skeleton.\n if (view.kind === \"loading\") {\n return (\n <div className={styles.inlineImageSkeleton}>\n <Spinner size=\"sm\" />\n </div>\n );\n }\n\n // Ready: render the fetched image with click-to-open handler.\n const handleClick = () => {\n openTab({\n id: tabIds.file(view.path),\n kind: \"file\",\n label: view.path.split(\"/\").pop() || view.path,\n closable: true,\n data: { path: view.path },\n workspaceCwd: assetBase || \"~\",\n });\n };\n\n return (\n <img\n src={view.objectUrl}\n alt={view.alt}\n className={styles.inlineImage}\n title={view.path}\n onClick={handleClick}\n style={{ cursor: \"pointer\" }}\n />\n );\n});\n","/**\n * react-markdown + remark-gfm rendering, replacing the POC's CDN `marked` + `innerHTML`\n * (POC_TO_APP_PLAN_UI.md §4.8). Sanitized by default — react-markdown never injects raw HTML.\n * Fenced code blocks highlight asynchronously via Shiki (`highlight.ts`).\n */\n\nimport { memo, useEffect, useState, type ComponentPropsWithoutRef } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { highlightToHtml } from \"./highlight.js\";\nimport styles from \"./markdown.module.css\";\nimport { InlineImage } from \"./InlineImage.js\";\n\ninterface CodeBlockProps {\n language: string;\n code: string;\n}\n\nfunction CodeBlock({ language, code }: CodeBlockProps) {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n void highlightToHtml(code, language).then((result) => {\n if (!cancelled) setHtml(result);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language]);\n\n if (html) {\n // Shiki's output is generated HTML from a trusted local highlighter (no user HTML is ever\n // injected) — the only risk is `code`, which Shiki escapes internally before tokenizing.\n return <div className={styles.prose} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n return (\n <pre>\n <code>{code}</code>\n </pre>\n );\n}\n\ntype MarkdownCodeProps = ComponentPropsWithoutRef<\"code\"> & { className?: string };\n\nfunction CodeRenderer({ className, children, ...rest }: MarkdownCodeProps) {\n const match = /language-(\\w+)/.exec(className ?? \"\");\n const isBlock = Boolean(match);\n const code = String(children).replace(/\\n$/, \"\");\n if (isBlock && match) {\n return <CodeBlock language={match[1] ?? \"plaintext\"} code={code} />;\n }\n return (\n <code className={styles.inlineCode} {...rest}>\n {children}\n </code>\n );\n}\n\nexport interface MarkdownProps {\n text: string;\n assetBase?: string | null;\n}\n\n/** Renders assistant/tool markdown text. Memoized — text is immutable once a turn finalizes. */\nexport const Markdown = memo(function Markdown({ text, assetBase = null }: MarkdownProps) {\n return (\n <div className={styles.prose}>\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n components={{\n code: CodeRenderer,\n img: (props) => <InlineImage {...props} assetBase={assetBase} />,\n }}\n >\n {text}\n </ReactMarkdown>\n </div>\n );\n});\n"],"names":["THEME","highlighterPromise","loadedLangs","getHighlighter","createHighlighter","ensureLang","highlighter","lang","highlightToHtml","code","resolved","REMOTE_SCHEMES","SCHEME_RE","classifyImageSrc","src","base","homeDir","trimmed","stripped","schemeMatch","scheme","candidate","normalizeCwd","resolveWorkspacePath","detectViewerKind","selectInlineImageView","classification","imageState","alt","fallbackText","loadInlineImage","path","download","onStateChange","cancelled","acquireInlineImage","entry","error","releaseInlineImage","useInlineImage","client","useConnectionStore","s","daemon","state","setState","useState","useEffect","p","transferFor","InlineImage","memo","assetBase","useHomeDir","openTab","useTabStore","useMemo","view","jsx","styles","Spinner","handleClick","tabIds","CodeBlock","language","html","setHtml","result","CodeRenderer","className","children","rest","match","isBlock","Markdown","text","ReactMarkdown","remarkGfm","props"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"markdown-CTYolZ3e.js","sources":["../../../src/timeline/highlight.ts","../../../src/timeline/image-src.ts","../../../src/timeline/inline-image-view.ts","../../../src/hooks/use-inline-image.ts","../../../src/timeline/InlineImage.tsx","../../../src/timeline/markdown.tsx"],"sourcesContent":["/**\n * Shiki wrapper + lang-from-path map. Replaces the POC's CDN `highlight.js`\n * (POC_TO_APP_PLAN_UI.md §2, §4.8). Grammars/themes are loaded lazily and cached — the\n * highlighter is not paid for until a code block/file view actually renders.\n */\n\nimport { createHighlighter, type Highlighter } from \"shiki\";\n\n/** POC `getLangFromPath` extension → language map, ported verbatim. */\nconst EXT_TO_LANG: Record<string, string> = {\n js: \"javascript\",\n ts: \"typescript\",\n jsx: \"javascript\",\n tsx: \"typescript\",\n py: \"python\",\n rb: \"ruby\",\n rs: \"rust\",\n go: \"go\",\n java: \"java\",\n c: \"c\",\n cpp: \"cpp\",\n h: \"c\",\n hpp: \"cpp\",\n cs: \"csharp\",\n html: \"html\",\n css: \"css\",\n scss: \"scss\",\n json: \"json\",\n yaml: \"yaml\",\n yml: \"yaml\",\n toml: \"toml\",\n xml: \"xml\",\n md: \"markdown\",\n sh: \"bash\",\n bash: \"bash\",\n sql: \"sql\",\n swift: \"swift\",\n kt: \"kotlin\",\n dart: \"dart\",\n vue: \"xml\",\n svelte: \"xml\",\n};\n\nexport function langFromPath(path: string): string {\n const ext = path.split(\".\").pop()?.toLowerCase() ?? \"\";\n return EXT_TO_LANG[ext] ?? \"plaintext\";\n}\n\nconst THEME = \"github-dark\";\n\nlet highlighterPromise: Promise<Highlighter> | null = null;\nconst loadedLangs = new Set<string>([\"plaintext\"]);\n\nfunction getHighlighter(): Promise<Highlighter> {\n if (!highlighterPromise) {\n highlighterPromise = createHighlighter({ themes: [THEME], langs: [\"plaintext\"] });\n }\n return highlighterPromise;\n}\n\nasync function ensureLang(highlighter: Highlighter, lang: string): Promise<string> {\n if (loadedLangs.has(lang)) return lang;\n try {\n await highlighter.loadLanguage(lang as never);\n loadedLangs.add(lang);\n return lang;\n } catch {\n return \"plaintext\"; // unknown grammar to Shiki — fall back rather than throw\n }\n}\n\n/** Highlight `code` for `lang` (or a path via `langFromPath`) to sanitized HTML. */\nexport async function highlightToHtml(code: string, lang: string): Promise<string> {\n const highlighter = await getHighlighter();\n const resolved = await ensureLang(highlighter, lang);\n return highlighter.codeToHtml(code, { lang: resolved, theme: THEME });\n}\n","/**\n * Markdown image source classification (features/inline-image-rendering.md § Public Contract →\n * Markdown image source classification, § Asset base). The single gate every `` in a\n * rendered chat message passes through before any network request is even considered — deciding\n * remote (render directly), local (fetch via the daemon's file-transfer path, task-003), or\n * unresolvable (render as plain fallback text, never attempt a request).\n *\n * Pure: no store/hook/network imports, so this file needs no React or client mocks to test.\n */\n\nimport { detectViewerKind } from \"@pi-studio-ui/features/files/viewer-registry.js\";\nimport { normalizeCwd } from \"@pi-studio-ui/features/sessions/workspace-grouping.js\";\nimport { resolveWorkspacePath } from \"@pi-studio-ui/lib/paths.js\";\n\nexport type ImageSrcClassification =\n | { kind: \"remote\" }\n | { kind: \"local\"; path: string }\n | { kind: \"unresolvable\" };\n\nconst REMOTE_SCHEMES = new Set([\"http:\", \"https:\", \"data:\", \"blob:\"]);\n\n/** Matches a leading `scheme:` (e.g. `http:`, `file:`, `mailto:`) — not a Windows drive letter\n * (`C:\\...`) or a bare `:` with no scheme name. */\nconst SCHEME_RE = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;\n\n/**\n * Classifies a markdown image `src`. `base` is the asset base (absolute, tilde-normalized) or\n * `null` when unknown; `homeDir` may be `null` when the daemon's home directory hasn't been\n * learned yet. Order matters — this mirrors the scope's classification table exactly:\n *\n * 1. empty/whitespace → unresolvable\n * 2. `http:`/`https:`/`data:`/`blob:` → remote\n * 3. any other `scheme:` prefix (including `file:`) → unresolvable\n * 4. `/…` → candidate local path as-is\n * 5. `~`/`~/…` → expand against `homeDir` (unknown home dir → unresolvable)\n * 6. `./…`, `../…`, bare relative → resolve against `base` (no base → unresolvable)\n * 7. final gate: the candidate must be an image per `detectViewerKind`, else unresolvable\n */\nexport function classifyImageSrc(\n src: string,\n base: string | null,\n homeDir: string | null,\n): ImageSrcClassification {\n const trimmed = src.trim();\n if (!trimmed) return { kind: \"unresolvable\" };\n\n // Markdown may carry a title/version via `#fragment`/`?query`; strip both before classifying so\n // a trailing `?` never defeats the extension gate below.\n const stripped = trimmed.split(/[?#]/, 1)[0]!;\n if (!stripped) return { kind: \"unresolvable\" };\n\n const schemeMatch = SCHEME_RE.exec(stripped);\n if (schemeMatch) {\n const scheme = `${schemeMatch[1]!.toLowerCase()}:`;\n return REMOTE_SCHEMES.has(scheme) ? { kind: \"remote\" } : { kind: \"unresolvable\" };\n }\n\n let candidate: string | null;\n if (stripped.startsWith(\"/\")) {\n candidate = stripped;\n } else if (stripped === \"~\" || stripped.startsWith(\"~/\")) {\n candidate = homeDir ? normalizeCwd(stripped, homeDir) : null;\n } else {\n candidate = resolveWorkspacePath(stripped, base ?? \"\");\n }\n\n if (!candidate) return { kind: \"unresolvable\" };\n if (detectViewerKind(candidate) !== \"image\") return { kind: \"unresolvable\" };\n return { kind: \"local\", path: candidate };\n}\n","/**\n * Pure, testable logic for rendering inline images — extracted from the component\n * so tests can verify branch logic without JSX rendering (mirror of molecule-reload.ts,\n * text-viewer-state.ts; see AGENTS.md testing conventions).\n *\n * Returns a discriminated union describing what to render: the component switches\n * on it to decide remote passthrough, skeleton, ready image, or text fallback.\n */\n\nimport type { ImageSrcClassification } from \"./image-src.js\";\nimport type { InlineImageState } from \"@pi-studio-ui/hooks/use-inline-image.js\";\n\nexport type InlineImageView =\n | { kind: \"remote\"; src: string; alt?: string }\n | { kind: \"unresolvable\"; fallbackText: string }\n | { kind: \"loading\" }\n | { kind: \"ready\"; objectUrl: string; alt?: string; path: string }\n | { kind: \"error\"; fallbackText: string };\n\n/**\n * Decides what to render for an inline image given its classification and loading state.\n * Pure: no hooks, no store access — all state passed in as arguments.\n */\nexport function selectInlineImageView(\n classification: ImageSrcClassification,\n imageState: InlineImageState,\n src: string | undefined,\n alt: string | undefined,\n): InlineImageView {\n if (classification.kind === \"remote\") {\n return { kind: \"remote\", src: src || \"\", alt };\n }\n\n if (classification.kind === \"unresolvable\") {\n const fallbackText = alt || src || \"(image)\";\n return { kind: \"unresolvable\", fallbackText };\n }\n\n // classification.kind === \"local\"\n if (imageState.status === \"loading\" || imageState.status === \"idle\") {\n return { kind: \"loading\" };\n }\n\n if (imageState.status === \"ready\") {\n return { kind: \"ready\", objectUrl: imageState.objectUrl, alt, path: classification.path };\n }\n\n // imageState.status === \"error\"\n const fallbackText = alt || classification.path || \"(image)\";\n return { kind: \"error\", fallbackText };\n}\n","/**\n * `use-inline-image` — fetches (via the shared ref-counted `inline-image-cache`) and hands back\n * an object URL for an inline chat image (features/inline-image-rendering.md § Inline image fetch\n * + cache). Consumed by the markdown `img` node override (task-004) once a `classifyImageSrc`\n * result is `local`.\n *\n * Deliberately NOT a TanStack Query hook, and therefore no `rpcKeys` entry: the whole point of\n * `inline-image-cache.ts` is a retention policy (revoke only on LRU eviction, never on unmount)\n * that Query's cache cannot express. Do not \"fix\" this into a `useQuery` — see that module's\n * header for why.\n *\n * `loadInlineImage` is the actual effect body, extracted for direct unit testing without\n * rendering — this package has no jsdom/React-Testing-Library environment (`AGENTS.md`'s testing\n * convention; mirrors `use-file-watch.ts`'s `watchFile` extraction).\n */\n\nimport { useEffect, useState } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { acquireInlineImage, releaseInlineImage } from \"@pi-studio-ui/lib/inline-image-cache.js\";\nimport { transferFor } from \"./file-transfer-instance.js\";\n\nexport type InlineImageState =\n | { status: \"idle\" } // no client yet, or path is null\n | { status: \"loading\" }\n | { status: \"ready\"; objectUrl: string }\n | { status: \"error\"; message: string };\n\n/**\n * Runs one effect cycle: `download === null` (no live connection, or no path) is the idle case —\n * nothing to acquire, nothing to clean up. Otherwise acquires `path`, reports the loading →\n * ready/error transition via `onStateChange`, and returns a cleanup that releases the path and\n * ignores a resolution that lands after it (the `cancelled` flag pattern used across the existing\n * hooks, e.g. `use-checkout-status.ts`).\n */\nexport function loadInlineImage(\n path: string | null,\n download: ((path: string) => Promise<{ bytes: Uint8Array; mimeType?: string }>) | null,\n onStateChange: (state: InlineImageState) => void,\n): (() => void) | void {\n if (!path || !download) {\n onStateChange({ status: \"idle\" });\n return;\n }\n\n let cancelled = false;\n onStateChange({ status: \"loading\" });\n\n acquireInlineImage(path, download)\n .then((entry) => {\n if (!cancelled) onStateChange({ status: \"ready\", objectUrl: entry.objectUrl });\n })\n .catch((error: unknown) => {\n if (!cancelled) {\n onStateChange({\n status: \"error\",\n message: error instanceof Error ? error.message : String(error),\n });\n }\n });\n\n return () => {\n cancelled = true;\n releaseInlineImage(path);\n };\n}\n\nexport function useInlineImage(path: string | null): InlineImageState {\n const client = useConnectionStore((s) => s.client);\n const daemon = useConnectionStore((s) => s.daemon);\n const [state, setState] = useState<InlineImageState>({ status: \"idle\" });\n\n useEffect(() => {\n const download = client && daemon ? (p: string) => transferFor(daemon).download(p) : null;\n return loadInlineImage(path, download, setState);\n }, [client, daemon, path]);\n\n return state;\n}\n","/**\n * `` renderer for finalized assistant markdown (features/inline-image-rendering.md §\n * Behavior & Algorithms → Render pipeline). Handles remote passthrough, local file fetch via\n * useInlineImage (task-003), classification (task-002), and click-to-open file tab.\n *\n * Props are optional (react-markdown may omit them) and must be handled defensively.\n */\n\nimport { memo, useMemo } from \"react\";\nimport { classifyImageSrc } from \"./image-src.js\";\nimport { selectInlineImageView } from \"./inline-image-view.js\";\nimport { useInlineImage } from \"@pi-studio-ui/hooks/use-inline-image.js\";\nimport { useHomeDir } from \"@pi-studio-ui/hooks/use-home-dir.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport styles from \"./markdown.module.css\";\n\nexport interface InlineImageProps {\n src?: string;\n alt?: string;\n assetBase: string | null;\n}\n\nexport const InlineImage = memo(function InlineImage({ src, alt, assetBase }: InlineImageProps) {\n const homeDir = useHomeDir();\n const openTab = useTabStore((s) => s.open);\n\n // Classify the source once at render time.\n const classification = useMemo(\n () => classifyImageSrc(src || \"\", assetBase, homeDir),\n [src, assetBase, homeDir],\n );\n\n // For local images, fetch and track state.\n const imageState = useInlineImage(classification.kind === \"local\" ? classification.path : null);\n\n // Decide what to render.\n const view = selectInlineImageView(classification, imageState, src, alt);\n\n // Remote images: pass through unchanged (what react-markdown's default would do).\n if (view.kind === \"remote\") {\n return <img src={view.src} alt={view.alt} className={styles.inlineImage} />;\n }\n\n // Unresolvable or error: render text fallback in inline monospace.\n if (view.kind === \"unresolvable\" || view.kind === \"error\") {\n return <code className={styles.inlineImageFallback}>{view.fallbackText}</code>;\n }\n\n // Loading or idle: skeleton.\n if (view.kind === \"loading\") {\n return (\n <div className={styles.inlineImageSkeleton}>\n <Spinner size=\"sm\" />\n </div>\n );\n }\n\n // Ready: render the fetched image with click-to-open handler.\n const handleClick = () => {\n openTab({\n id: tabIds.file(view.path),\n kind: \"file\",\n label: view.path.split(\"/\").pop() || view.path,\n closable: true,\n data: { path: view.path },\n workspaceCwd: assetBase || \"~\",\n });\n };\n\n return (\n <img\n src={view.objectUrl}\n alt={view.alt}\n className={styles.inlineImage}\n title={view.path}\n onClick={handleClick}\n style={{ cursor: \"pointer\" }}\n />\n );\n});\n","/**\n * react-markdown + remark-gfm rendering, replacing the POC's CDN `marked` + `innerHTML`\n * (POC_TO_APP_PLAN_UI.md §4.8). Sanitized by default — react-markdown never injects raw HTML.\n * Fenced code blocks highlight asynchronously via Shiki (`highlight.ts`).\n */\n\nimport { memo, useEffect, useState, type ComponentPropsWithoutRef } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { highlightToHtml } from \"./highlight.js\";\nimport styles from \"./markdown.module.css\";\nimport { InlineImage } from \"./InlineImage.js\";\n\ninterface CodeBlockProps {\n language: string;\n code: string;\n}\n\nfunction CodeBlock({ language, code }: CodeBlockProps) {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n void highlightToHtml(code, language).then((result) => {\n if (!cancelled) setHtml(result);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language]);\n\n if (html) {\n // Shiki's output is generated HTML from a trusted local highlighter (no user HTML is ever\n // injected) — the only risk is `code`, which Shiki escapes internally before tokenizing.\n return <div className={styles.prose} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n return (\n <pre>\n <code>{code}</code>\n </pre>\n );\n}\n\ntype MarkdownCodeProps = ComponentPropsWithoutRef<\"code\"> & { className?: string };\n\nfunction CodeRenderer({ className, children, ...rest }: MarkdownCodeProps) {\n const match = /language-(\\w+)/.exec(className ?? \"\");\n const isBlock = Boolean(match);\n const code = String(children).replace(/\\n$/, \"\");\n if (isBlock && match) {\n return <CodeBlock language={match[1] ?? \"plaintext\"} code={code} />;\n }\n return (\n <code className={styles.inlineCode} {...rest}>\n {children}\n </code>\n );\n}\n\nexport interface MarkdownProps {\n text: string;\n assetBase?: string | null;\n}\n\n/** Renders assistant/tool markdown text. Memoized — text is immutable once a turn finalizes. */\nexport const Markdown = memo(function Markdown({ text, assetBase = null }: MarkdownProps) {\n return (\n <div className={styles.prose}>\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n components={{\n code: CodeRenderer,\n img: (props) => <InlineImage {...props} assetBase={assetBase} />,\n }}\n >\n {text}\n </ReactMarkdown>\n </div>\n );\n});\n"],"names":["THEME","highlighterPromise","loadedLangs","getHighlighter","createHighlighter","ensureLang","highlighter","lang","highlightToHtml","code","resolved","REMOTE_SCHEMES","SCHEME_RE","classifyImageSrc","src","base","homeDir","trimmed","stripped","schemeMatch","scheme","candidate","normalizeCwd","resolveWorkspacePath","detectViewerKind","selectInlineImageView","classification","imageState","alt","fallbackText","loadInlineImage","path","download","onStateChange","cancelled","acquireInlineImage","entry","error","releaseInlineImage","useInlineImage","client","useConnectionStore","s","daemon","state","setState","useState","useEffect","p","transferFor","InlineImage","memo","assetBase","useHomeDir","openTab","useTabStore","useMemo","view","jsx","styles","Spinner","handleClick","tabIds","CodeBlock","language","html","setHtml","result","CodeRenderer","className","children","rest","match","isBlock","Markdown","text","ReactMarkdown","remarkGfm","props"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as a}from"./vendor-react-p-LLqNcx.js";import{u as f}from"./vendor-query-chxBrUuj.js";import{a as s,
|
|
2
|
-
//# sourceMappingURL=use-file-download-
|
|
1
|
+
import{r as a}from"./vendor-react-p-LLqNcx.js";import{u as f}from"./vendor-query-chxBrUuj.js";import{a as s,k as m,r as y}from"./index-CqmgQdsG.js";function p(o,u=!0){var i;const l=s(e=>e.client),n=s(e=>e.daemon),c=f({queryKey:y.fileDownload(o),queryFn:async()=>{if(!l||!n)throw new Error("not connected");const e=await m(n).download(o),t=new Blob([Uint8Array.from(e.bytes)],{type:e.mimeType||"application/octet-stream"});return{objectUrl:URL.createObjectURL(t),mimeType:e.mimeType,fileName:e.fileName}},enabled:!!l&&!!n&&!!o&&u,staleTime:1/0,gcTime:0}),r=a.useRef(null);return a.useEffect(()=>{var t;const e=((t=c.data)==null?void 0:t.objectUrl)??null;e&&r.current&&r.current!==e&&URL.revokeObjectURL(r.current),r.current=e},[(i=c.data)==null?void 0:i.objectUrl]),a.useEffect(()=>()=>{r.current&&URL.revokeObjectURL(r.current)},[]),c}export{p as u};
|
|
2
|
+
//# sourceMappingURL=use-file-download-BoIvv6b7.js.map
|
package/dist/web/assets/{use-file-download-C5ZdZp1T.js.map → use-file-download-BoIvv6b7.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-file-download-
|
|
1
|
+
{"version":3,"file":"use-file-download-BoIvv6b7.js","sources":["../../../src/hooks/use-file-download.ts"],"sourcesContent":["/**\n * `use-file-download` — TanStack Query wrapper over the binary file-transfer download path\n * (`file_download_token_request` → `file_download_request` → chunked `Begin`/`Chunk`/`End`\n * binary frames), for viewers that need raw bytes rather than the UTF-8 preview\n * `file_read_request` returns (images, video — POC_TO_APP_PLAN_UI.md §4.5 follow-up: modular\n * file preview). Resolves to an object URL, revoked once superseded by a new one or once this\n * consumer unmounts.\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { rpcKeys } from \"@pi-studio-ui/lib/connection/rpc-keys.js\";\nimport { transferFor } from \"./file-transfer-instance.js\";\n\nexport interface FileDownload {\n objectUrl: string;\n mimeType?: string;\n fileName?: string;\n}\n\nexport function useFileDownload(path: string, enabled = true) {\n const client = useConnectionStore((s) => s.client);\n const daemon = useConnectionStore((s) => s.daemon);\n\n const query = useQuery({\n queryKey: rpcKeys.fileDownload(path),\n queryFn: async (): Promise<FileDownload> => {\n if (!client || !daemon) throw new Error(\"not connected\");\n const file = await transferFor(daemon).download(path);\n // `Blob`'s DOM typings require an `ArrayBuffer`-backed view; the assembled `Uint8Array`'s\n // `ArrayBufferLike` backing (a decode-time implementation detail) doesn't satisfy that on\n // its own, so copy into a plain `ArrayBuffer`-backed `Uint8Array` first.\n const blob = new Blob([Uint8Array.from(file.bytes)], {\n type: file.mimeType || \"application/octet-stream\",\n });\n return { objectUrl: URL.createObjectURL(blob), mimeType: file.mimeType, fileName: file.fileName };\n },\n enabled: Boolean(client) && Boolean(daemon) && Boolean(path) && enabled,\n // Object URLs are process-local, non-serializable browser resources — never persist them or\n // silently refetch behind this hook's back (that would leak the previous blob's URL).\n staleTime: Infinity,\n gcTime: 0,\n });\n\n // Revoke the previous object URL once a new one supersedes it, and the current one on unmount\n // — TanStack Query's cache has no notion that this value owns a browser resource to release.\n const urlRef = useRef<string | null>(null);\n useEffect(() => {\n const nextUrl = query.data?.objectUrl ?? null;\n if (nextUrl && urlRef.current && urlRef.current !== nextUrl) {\n URL.revokeObjectURL(urlRef.current);\n }\n urlRef.current = nextUrl;\n }, [query.data?.objectUrl]);\n\n useEffect(() => {\n return () => {\n if (urlRef.current) URL.revokeObjectURL(urlRef.current);\n };\n }, []);\n\n return query;\n}\n"],"names":["useFileDownload","path","enabled","client","useConnectionStore","s","daemon","query","useQuery","rpcKeys","file","transferFor","blob","urlRef","useRef","useEffect","nextUrl","_a"],"mappings":"oJAqBO,SAASA,EAAgBC,EAAcC,EAAU,GAAM,OAC5D,MAAMC,EAASC,EAAoBC,GAAMA,EAAE,MAAM,EAC3CC,EAASF,EAAoBC,GAAMA,EAAE,MAAM,EAE3CE,EAAQC,EAAS,CACrB,SAAUC,EAAQ,aAAaR,CAAI,EACnC,QAAS,SAAmC,CAC1C,GAAI,CAACE,GAAU,CAACG,EAAQ,MAAM,IAAI,MAAM,eAAe,EACvD,MAAMI,EAAO,MAAMC,EAAYL,CAAM,EAAE,SAASL,CAAI,EAI9CW,EAAO,IAAI,KAAK,CAAC,WAAW,KAAKF,EAAK,KAAK,CAAC,EAAG,CACnD,KAAMA,EAAK,UAAY,0BAAA,CACxB,EACD,MAAO,CAAE,UAAW,IAAI,gBAAgBE,CAAI,EAAG,SAAUF,EAAK,SAAU,SAAUA,EAAK,QAAA,CACzF,EACA,QAAS,EAAQP,GAAW,EAAQG,GAAW,EAAQL,GAASC,EAGhE,UAAW,IACX,OAAQ,CAAA,CACT,EAIKW,EAASC,EAAAA,OAAsB,IAAI,EACzCC,OAAAA,EAAAA,UAAU,IAAM,OACd,MAAMC,IAAUC,EAAAV,EAAM,OAAN,YAAAU,EAAY,YAAa,KACrCD,GAAWH,EAAO,SAAWA,EAAO,UAAYG,GAClD,IAAI,gBAAgBH,EAAO,OAAO,EAEpCA,EAAO,QAAUG,CACnB,EAAG,EAACC,EAAAV,EAAM,OAAN,YAAAU,EAAY,SAAS,CAAC,EAE1BF,EAAAA,UAAU,IACD,IAAM,CACPF,EAAO,SAAS,IAAI,gBAAgBA,EAAO,OAAO,CACxD,EACC,CAAA,CAAE,EAEEN,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as a}from"./vendor-react-p-LLqNcx.js";import{a as w}from"./index-
|
|
2
|
-
//# sourceMappingURL=use-file-watch-
|
|
1
|
+
import{r as a}from"./vendor-react-p-LLqNcx.js";import{a as w}from"./index-CqmgQdsG.js";function b(e){return typeof e=="object"&&e!==null&&"type"in e&&e.type==="file_changed"&&"path"in e&&typeof e.path=="string"}const l=new WeakMap;function p(e,t,i){let s=l.get(e);s||(s=new Map,l.set(e,s));let o=s.get(t);if(!o){const r={count:0,resolvedPath:null,listeners:new Set,dispose:()=>{}},f=e.connection.onSessionMessage(c=>{const n=c;if(!b(n)||n.path!==t&&n.path!==r.resolvedPath)return;const d=Date.now();for(const h of r.listeners)h(d)});e.connection.request("file_watch_subscribe",{path:t}).then(c=>{const n=c;if((n==null?void 0:n.ok)===!1){console.warn(`watchFile: ${n.error??"subscribe failed"} for "${t}"`);return}typeof(n==null?void 0:n.path)=="string"&&(r.resolvedPath=n.path)}).catch(()=>{}),r.dispose=()=>{f(),e.connection.request("file_watch_unsubscribe",{path:t}).catch(()=>{})},o=r,s.set(t,o)}o.count++,o.listeners.add(i);let u=!1;return()=>{if(u)return;u=!0;const r=s.get(t);r&&(r.listeners.delete(i),r.count--,r.count===0&&(s.delete(t),r.dispose()))}}function v(e){const t=w(o=>o.client),[i,s]=a.useState(null);return a.useEffect(()=>{if(!(!t||!e))return p(t,e,s)},[t,e]),{changedAt:i}}export{v as u};
|
|
2
|
+
//# sourceMappingURL=use-file-watch-BY4_Mzyt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-file-watch-
|
|
1
|
+
{"version":3,"file":"use-file-watch-BY4_Mzyt.js","sources":["../../../src/hooks/use-file-watch.ts"],"sourcesContent":["/**\n * `use-file-watch` — subscribes to live daemon `file_changed` pushes for a path (task-006's\n * `FileWatchService`), structurally mirroring `use-checkout-status.ts`: subscribe on mount/path\n * change via `file_watch_subscribe`, route matching pushes through `onSessionMessage`, and on\n * cleanup detach the message handler AND send `file_watch_unsubscribe` for the path being left —\n * `use-checkout-status.ts`'s own header comment calls out that leaving either half undone is the\n * leak this pattern exists to avoid.\n *\n * The actual subscribe/route/cleanup wiring lives in `watchFile`, a plain function with no React\n * dependency (mirrors `molecule-source.ts`/`molecule-reload.ts`'s \"extract the testable core\"\n * convention) — this repo has no DOM test environment set up, so it is the seam\n * `use-file-watch.test.ts` exercises directly with a fake client rather than mounting the hook.\n *\n * A `null` path (an empty \"+\"-menu molecule tab, or the absence of a client) subscribes nothing.\n *\n * `watchFile` ref-counts subscriptions per `(client, path)` in a module-level registry (mirrors\n * `file-transfer-instance.ts`'s `WeakMap<DaemonClient, …>` shared-instance convention): more than\n * one component can watch the same file (a file tab and a diff tab on the same path) and share\n * ONE daemon watch, rather than the second `file_watch_unsubscribe` killing it for both — the\n * daemon's `SessionSubscriptions.add` disposes any existing entry for the same resolved-path key.\n * The entry also remembers the RESOLVED path echoed back by `file_watch_subscribe_response.path`\n * (the daemon expands a leading `~` server-side before pushing `file_changed`), and matches a\n * push against either the requested spelling or that resolved path.\n */\n\nimport { useEffect, useState } from \"react\";\nimport type { PiStudioClient } from \"@av-pi-studio/client\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\n\ninterface FileChangedMessage {\n type: \"file_changed\";\n path: string;\n}\n\nfunction isFileChangedMessage(msg: unknown): msg is FileChangedMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"file_changed\" &&\n \"path\" in msg &&\n typeof msg.path === \"string\"\n );\n}\n\ninterface FileWatchSubscribeResponse {\n ok?: boolean;\n error?: string;\n path?: string;\n}\n\ninterface WatchEntry {\n count: number;\n /** Resolved path echoed by `file_watch_subscribe_response.path`; `null` until it arrives. */\n resolvedPath: string | null;\n listeners: Set<(changedAt: number) => void>;\n dispose: () => void;\n}\n\nconst watchesByClient = new WeakMap<Pick<PiStudioClient, \"connection\">, Map<string, WatchEntry>>();\n\n/** Subscribes `path` on `client.connection`, calls `onChanged` for every matching `file_changed`\n * push, and returns a cleanup function that detaches the handler AND sends\n * `file_watch_unsubscribe` for `path` once the LAST subscriber for that `(client, path)` pair\n * releases it. Framework-agnostic on purpose — see module header. */\nexport function watchFile(\n client: Pick<PiStudioClient, \"connection\">,\n path: string,\n onChanged: (changedAt: number) => void,\n): () => void {\n let entries = watchesByClient.get(client);\n if (!entries) {\n entries = new Map<string, WatchEntry>();\n watchesByClient.set(client, entries);\n }\n\n let entry = entries.get(path);\n if (!entry) {\n const created: WatchEntry = {\n count: 0,\n resolvedPath: null,\n listeners: new Set(),\n dispose: () => {},\n };\n\n const unsubscribeMessages = client.connection.onSessionMessage((msg) => {\n const raw: unknown = msg;\n if (!isFileChangedMessage(raw)) return;\n if (raw.path !== path && raw.path !== created.resolvedPath) return;\n const changedAt = Date.now();\n for (const listener of created.listeners) listener(changedAt);\n });\n\n void client.connection\n .request(\"file_watch_subscribe\", { path })\n .then((res) => {\n const response = res as FileWatchSubscribeResponse;\n if (response?.ok === false) {\n console.warn(`watchFile: ${response.error ?? \"subscribe failed\"} for \"${path}\"`);\n return;\n }\n if (typeof response?.path === \"string\") {\n created.resolvedPath = response.path;\n }\n })\n .catch(() => {});\n\n created.dispose = () => {\n unsubscribeMessages();\n void client.connection.request(\"file_watch_unsubscribe\", { path }).catch(() => {});\n };\n\n entry = created;\n entries.set(path, entry);\n }\n\n entry.count++;\n entry.listeners.add(onChanged);\n\n let released = false;\n return () => {\n if (released) return; // Idempotent: a second call must never double-release.\n released = true;\n const current = entries!.get(path);\n if (!current) return;\n current.listeners.delete(onChanged);\n current.count--;\n if (current.count === 0) {\n entries!.delete(path);\n current.dispose();\n }\n };\n}\n\nexport interface UseFileWatchResult {\n /** Monotonic timestamp of the last `file_changed` push for this path; `null` if none yet. */\n changedAt: number | null;\n}\n\nexport function useFileWatch(path: string | null): UseFileWatchResult {\n const client = useConnectionStore((s) => s.client);\n const [changedAt, setChangedAt] = useState<number | null>(null);\n\n useEffect(() => {\n if (!client || !path) return;\n return watchFile(client, path, setChangedAt);\n }, [client, path]);\n\n return { changedAt };\n}\n"],"names":["isFileChangedMessage","msg","watchesByClient","watchFile","client","path","onChanged","entries","entry","created","unsubscribeMessages","raw","changedAt","listener","res","response","released","current","useFileWatch","useConnectionStore","s","setChangedAt","useState","useEffect"],"mappings":"uFAkCA,SAASA,EAAqBC,EAAyC,CACrE,OACE,OAAOA,GAAQ,UACfA,IAAQ,MACR,SAAUA,GACVA,EAAI,OAAS,gBACb,SAAUA,GACV,OAAOA,EAAI,MAAS,QAExB,CAgBA,MAAMC,MAAsB,QAMrB,SAASC,EACdC,EACAC,EACAC,EACY,CACZ,IAAIC,EAAUL,EAAgB,IAAIE,CAAM,EACnCG,IACHA,MAAc,IACdL,EAAgB,IAAIE,EAAQG,CAAO,GAGrC,IAAIC,EAAQD,EAAQ,IAAIF,CAAI,EAC5B,GAAI,CAACG,EAAO,CACV,MAAMC,EAAsB,CAC1B,MAAO,EACP,aAAc,KACd,cAAe,IACf,QAAS,IAAM,CAAC,CAAA,EAGZC,EAAsBN,EAAO,WAAW,iBAAkBH,GAAQ,CACtE,MAAMU,EAAeV,EAErB,GADI,CAACD,EAAqBW,CAAG,GACzBA,EAAI,OAASN,GAAQM,EAAI,OAASF,EAAQ,aAAc,OAC5D,MAAMG,EAAY,KAAK,IAAA,EACvB,UAAWC,KAAYJ,EAAQ,UAAWI,EAASD,CAAS,CAC9D,CAAC,EAEIR,EAAO,WACT,QAAQ,uBAAwB,CAAE,KAAAC,EAAM,EACxC,KAAMS,GAAQ,CACb,MAAMC,EAAWD,EACjB,IAAIC,GAAA,YAAAA,EAAU,MAAO,GAAO,CAC1B,QAAQ,KAAK,cAAcA,EAAS,OAAS,kBAAkB,SAASV,CAAI,GAAG,EAC/E,MACF,CACI,OAAOU,GAAA,YAAAA,EAAU,OAAS,WAC5BN,EAAQ,aAAeM,EAAS,KAEpC,CAAC,EACA,MAAM,IAAM,CAAC,CAAC,EAEjBN,EAAQ,QAAU,IAAM,CACtBC,EAAA,EACKN,EAAO,WAAW,QAAQ,yBAA0B,CAAE,KAAAC,CAAA,CAAM,EAAE,MAAM,IAAM,CAAC,CAAC,CACnF,EAEAG,EAAQC,EACRF,EAAQ,IAAIF,EAAMG,CAAK,CACzB,CAEAA,EAAM,QACNA,EAAM,UAAU,IAAIF,CAAS,EAE7B,IAAIU,EAAW,GACf,MAAO,IAAM,CACX,GAAIA,EAAU,OACdA,EAAW,GACX,MAAMC,EAAUV,EAAS,IAAIF,CAAI,EAC5BY,IACLA,EAAQ,UAAU,OAAOX,CAAS,EAClCW,EAAQ,QACJA,EAAQ,QAAU,IACpBV,EAAS,OAAOF,CAAI,EACpBY,EAAQ,QAAA,GAEZ,CACF,CAOO,SAASC,EAAab,EAAyC,CACpE,MAAMD,EAASe,EAAoBC,GAAMA,EAAE,MAAM,EAC3C,CAACR,EAAWS,CAAY,EAAIC,EAAAA,SAAwB,IAAI,EAE9DC,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,GAACnB,GAAU,CAACC,GAChB,OAAOF,EAAUC,EAAQC,EAAMgB,CAAY,CAC7C,EAAG,CAACjB,EAAQC,CAAI,CAAC,EAEV,CAAE,UAAAO,CAAA,CACX"}
|
|
@@ -3,7 +3,7 @@ import{r as s}from"./vendor-react-p-LLqNcx.js";/**
|
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const p=a=>a.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),
|
|
6
|
+
*/const p=a=>a.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),y=(...a)=>a.filter((t,o,c)=>!!t&&t.trim()!==""&&c.indexOf(t)===o).join(" ").trim();/**
|
|
7
7
|
* @license lucide-react v0.475.0 - ISC
|
|
8
8
|
*
|
|
9
9
|
* This source code is licensed under the ISC license.
|
|
@@ -13,225 +13,250 @@ import{r as s}from"./vendor-react-p-LLqNcx.js";/**
|
|
|
13
13
|
*
|
|
14
14
|
* This source code is licensed under the ISC license.
|
|
15
15
|
* See the LICENSE file in the root directory of this source tree.
|
|
16
|
-
*/const M=s.forwardRef(({color:a="currentColor",size:t=24,strokeWidth:o=2,absoluteStrokeWidth:c,className:h="",children:n,iconNode:
|
|
16
|
+
*/const M=s.forwardRef(({color:a="currentColor",size:t=24,strokeWidth:o=2,absoluteStrokeWidth:c,className:h="",children:n,iconNode:d,...i},r)=>s.createElement("svg",{ref:r,..._,width:t,height:t,stroke:a,strokeWidth:c?Number(o)*24/Number(t):o,className:y("lucide",h),...i},[...d.map(([l,k])=>s.createElement(l,k)),...Array.isArray(n)?n:[n]]));/**
|
|
17
17
|
* @license lucide-react v0.475.0 - ISC
|
|
18
18
|
*
|
|
19
19
|
* This source code is licensed under the ISC license.
|
|
20
20
|
* See the LICENSE file in the root directory of this source tree.
|
|
21
|
-
*/const e=(a,t)=>{const o=s.forwardRef(({className:c,...h},n)=>s.createElement(M,{ref:n,iconNode:t,className:
|
|
21
|
+
*/const e=(a,t)=>{const o=s.forwardRef(({className:c,...h},n)=>s.createElement(M,{ref:n,iconNode:t,className:y(`lucide-${p(a)}`,c),...h}));return o.displayName=`${a}`,o};/**
|
|
22
22
|
* @license lucide-react v0.475.0 - ISC
|
|
23
23
|
*
|
|
24
24
|
* This source code is licensed under the ISC license.
|
|
25
25
|
* See the LICENSE file in the root directory of this source tree.
|
|
26
|
-
*/const
|
|
26
|
+
*/const x=[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",key:"1s80jp"}],["path",{d:"M10 12h4",key:"a56b0p"}]],l2=e("Archive",x);/**
|
|
27
27
|
* @license lucide-react v0.475.0 - ISC
|
|
28
28
|
*
|
|
29
29
|
* This source code is licensed under the ISC license.
|
|
30
30
|
* See the LICENSE file in the root directory of this source tree.
|
|
31
|
-
*/const v=[["
|
|
31
|
+
*/const v=[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]],k2=e("ArrowUp",v);/**
|
|
32
32
|
* @license lucide-react v0.475.0 - ISC
|
|
33
33
|
*
|
|
34
34
|
* This source code is licensed under the ISC license.
|
|
35
35
|
* See the LICENSE file in the root directory of this source tree.
|
|
36
|
-
*/const
|
|
36
|
+
*/const m=[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["path",{d:"M20.2 20.2c2.04-2.03.02-7.36-4.5-11.9-4.54-4.52-9.87-6.54-11.9-4.5-2.04 2.03-.02 7.36 4.5 11.9 4.54 4.52 9.87 6.54 11.9 4.5Z",key:"1l2ple"}],["path",{d:"M15.7 15.7c4.52-4.54 6.54-9.87 4.5-11.9-2.03-2.04-7.36-.02-11.9 4.5-4.52 4.54-6.54 9.87-4.5 11.9 2.03 2.04 7.36.02 11.9-4.5Z",key:"1wam0m"}]],p2=e("Atom",m);/**
|
|
37
37
|
* @license lucide-react v0.475.0 - ISC
|
|
38
38
|
*
|
|
39
39
|
* This source code is licensed under the ISC license.
|
|
40
40
|
* See the LICENSE file in the root directory of this source tree.
|
|
41
|
-
*/const g=[["path",{d:"
|
|
41
|
+
*/const g=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],_2=e("Check",g);/**
|
|
42
42
|
* @license lucide-react v0.475.0 - ISC
|
|
43
43
|
*
|
|
44
44
|
* This source code is licensed under the ISC license.
|
|
45
45
|
* See the LICENSE file in the root directory of this source tree.
|
|
46
|
-
*/const N=[["path",{d:"
|
|
46
|
+
*/const N=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],M2=e("ChevronDown",N);/**
|
|
47
47
|
* @license lucide-react v0.475.0 - ISC
|
|
48
48
|
*
|
|
49
49
|
* This source code is licensed under the ISC license.
|
|
50
50
|
* See the LICENSE file in the root directory of this source tree.
|
|
51
|
-
*/const $=[["
|
|
51
|
+
*/const $=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],x2=e("ChevronRight",$);/**
|
|
52
52
|
* @license lucide-react v0.475.0 - ISC
|
|
53
53
|
*
|
|
54
54
|
* This source code is licensed under the ISC license.
|
|
55
55
|
* See the LICENSE file in the root directory of this source tree.
|
|
56
|
-
*/const u=[["circle",{cx:"
|
|
56
|
+
*/const u=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16 14",key:"68esgv"}]],v2=e("Clock",u);/**
|
|
57
57
|
* @license lucide-react v0.475.0 - ISC
|
|
58
58
|
*
|
|
59
59
|
* This source code is licensed under the ISC license.
|
|
60
60
|
* See the LICENSE file in the root directory of this source tree.
|
|
61
|
-
*/const w=[["
|
|
61
|
+
*/const w=[["circle",{cx:"8",cy:"8",r:"6",key:"3yglwk"}],["path",{d:"M18.09 10.37A6 6 0 1 1 10.34 18",key:"t5s6rm"}],["path",{d:"M7 6h1v4",key:"1obek4"}],["path",{d:"m16.71 13.88.7.71-2.82 2.82",key:"1rbuyh"}]],m2=e("Coins",w);/**
|
|
62
62
|
* @license lucide-react v0.475.0 - ISC
|
|
63
63
|
*
|
|
64
64
|
* This source code is licensed under the ISC license.
|
|
65
65
|
* See the LICENSE file in the root directory of this source tree.
|
|
66
|
-
*/const f=[["
|
|
66
|
+
*/const f=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],g2=e("Copy",f);/**
|
|
67
67
|
* @license lucide-react v0.475.0 - ISC
|
|
68
68
|
*
|
|
69
69
|
* This source code is licensed under the ISC license.
|
|
70
70
|
* See the LICENSE file in the root directory of this source tree.
|
|
71
|
-
*/const q=[["
|
|
71
|
+
*/const q=[["rect",{width:"16",height:"16",x:"4",y:"4",rx:"2",key:"14l7u7"}],["rect",{width:"6",height:"6",x:"9",y:"9",rx:"1",key:"5aljv4"}],["path",{d:"M15 2v2",key:"13l42r"}],["path",{d:"M15 20v2",key:"15mkzm"}],["path",{d:"M2 15h2",key:"1gxd5l"}],["path",{d:"M2 9h2",key:"1bbxkp"}],["path",{d:"M20 15h2",key:"19e6y8"}],["path",{d:"M20 9h2",key:"19tzq7"}],["path",{d:"M9 2v2",key:"165o2o"}],["path",{d:"M9 20v2",key:"i2bqo8"}]],N2=e("Cpu",q);/**
|
|
72
72
|
* @license lucide-react v0.475.0 - ISC
|
|
73
73
|
*
|
|
74
74
|
* This source code is licensed under the ISC license.
|
|
75
75
|
* See the LICENSE file in the root directory of this source tree.
|
|
76
|
-
*/const C=[["path",{d:"
|
|
76
|
+
*/const C=[["line",{x1:"12",x2:"12",y1:"2",y2:"22",key:"7eqyqh"}],["path",{d:"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6",key:"1b0p4s"}]],$2=e("DollarSign",C);/**
|
|
77
77
|
* @license lucide-react v0.475.0 - ISC
|
|
78
78
|
*
|
|
79
79
|
* This source code is licensed under the ISC license.
|
|
80
80
|
* See the LICENSE file in the root directory of this source tree.
|
|
81
|
-
*/const b=[["path",{d:"
|
|
81
|
+
*/const b=[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]],u2=e("Download",b);/**
|
|
82
82
|
* @license lucide-react v0.475.0 - ISC
|
|
83
83
|
*
|
|
84
84
|
* This source code is licensed under the ISC license.
|
|
85
85
|
* See the LICENSE file in the root directory of this source tree.
|
|
86
|
-
*/const
|
|
86
|
+
*/const H=[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["circle",{cx:"12",cy:"5",r:"1",key:"gxeob9"}],["circle",{cx:"12",cy:"19",r:"1",key:"lyex9k"}]],w2=e("EllipsisVertical",H);/**
|
|
87
87
|
* @license lucide-react v0.475.0 - ISC
|
|
88
88
|
*
|
|
89
89
|
* This source code is licensed under the ISC license.
|
|
90
90
|
* See the LICENSE file in the root directory of this source tree.
|
|
91
|
-
*/const
|
|
91
|
+
*/const z=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],f2=e("ExternalLink",z);/**
|
|
92
92
|
* @license lucide-react v0.475.0 - ISC
|
|
93
93
|
*
|
|
94
94
|
* This source code is licensed under the ISC license.
|
|
95
95
|
* See the LICENSE file in the root directory of this source tree.
|
|
96
|
-
*/const
|
|
96
|
+
*/const L=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],q2=e("Eye",L);/**
|
|
97
97
|
* @license lucide-react v0.475.0 - ISC
|
|
98
98
|
*
|
|
99
99
|
* This source code is licensed under the ISC license.
|
|
100
100
|
* See the LICENSE file in the root directory of this source tree.
|
|
101
|
-
*/const
|
|
101
|
+
*/const P=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M9 15h6",key:"cctwl0"}],["path",{d:"M12 18v-6",key:"17g6i2"}]],C2=e("FilePlus",P);/**
|
|
102
102
|
* @license lucide-react v0.475.0 - ISC
|
|
103
103
|
*
|
|
104
104
|
* This source code is licensed under the ISC license.
|
|
105
105
|
* See the LICENSE file in the root directory of this source tree.
|
|
106
|
-
*/const
|
|
106
|
+
*/const F=[["path",{d:"M12 17h.01",key:"p32p05"}],["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z",key:"1mlx9k"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3",key:"mhlwft"}]],b2=e("FileQuestion",F);/**
|
|
107
107
|
* @license lucide-react v0.475.0 - ISC
|
|
108
108
|
*
|
|
109
109
|
* This source code is licensed under the ISC license.
|
|
110
110
|
* See the LICENSE file in the root directory of this source tree.
|
|
111
|
-
*/const
|
|
111
|
+
*/const V=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]],H2=e("FileText",V);/**
|
|
112
112
|
* @license lucide-react v0.475.0 - ISC
|
|
113
113
|
*
|
|
114
114
|
* This source code is licensed under the ISC license.
|
|
115
115
|
* See the LICENSE file in the root directory of this source tree.
|
|
116
|
-
*/const
|
|
116
|
+
*/const A=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}]],z2=e("File",A);/**
|
|
117
117
|
* @license lucide-react v0.475.0 - ISC
|
|
118
118
|
*
|
|
119
119
|
* This source code is licensed under the ISC license.
|
|
120
120
|
* See the LICENSE file in the root directory of this source tree.
|
|
121
|
-
*/const
|
|
121
|
+
*/const S=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"M2 10h20",key:"1ir3d8"}]],L2=e("FolderClosed",S);/**
|
|
122
122
|
* @license lucide-react v0.475.0 - ISC
|
|
123
123
|
*
|
|
124
124
|
* This source code is licensed under the ISC license.
|
|
125
125
|
* See the LICENSE file in the root directory of this source tree.
|
|
126
|
-
*/const
|
|
126
|
+
*/const E=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]],P2=e("FolderOpen",E);/**
|
|
127
127
|
* @license lucide-react v0.475.0 - ISC
|
|
128
128
|
*
|
|
129
129
|
* This source code is licensed under the ISC license.
|
|
130
130
|
* See the LICENSE file in the root directory of this source tree.
|
|
131
|
-
*/const
|
|
131
|
+
*/const j=[["path",{d:"M12 10v6",key:"1bos4e"}],["path",{d:"M9 13h6",key:"1uhe8q"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]],F2=e("FolderPlus",j);/**
|
|
132
132
|
* @license lucide-react v0.475.0 - ISC
|
|
133
133
|
*
|
|
134
134
|
* This source code is licensed under the ISC license.
|
|
135
135
|
* See the LICENSE file in the root directory of this source tree.
|
|
136
|
-
*/const
|
|
136
|
+
*/const R=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]],V2=e("Folder",R);/**
|
|
137
137
|
* @license lucide-react v0.475.0 - ISC
|
|
138
138
|
*
|
|
139
139
|
* This source code is licensed under the ISC license.
|
|
140
140
|
* See the LICENSE file in the root directory of this source tree.
|
|
141
|
-
*/const T=[["path",{d:"
|
|
141
|
+
*/const T=[["path",{d:"m12 14 4-4",key:"9kzdfg"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0",key:"19p75a"}]],A2=e("Gauge",T);/**
|
|
142
142
|
* @license lucide-react v0.475.0 - ISC
|
|
143
143
|
*
|
|
144
144
|
* This source code is licensed under the ISC license.
|
|
145
145
|
* See the LICENSE file in the root directory of this source tree.
|
|
146
|
-
*/const
|
|
146
|
+
*/const G=[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]],S2=e("GitBranch",G);/**
|
|
147
147
|
* @license lucide-react v0.475.0 - ISC
|
|
148
148
|
*
|
|
149
149
|
* This source code is licensed under the ISC license.
|
|
150
150
|
* See the LICENSE file in the root directory of this source tree.
|
|
151
|
-
*/const
|
|
151
|
+
*/const Z=[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M13 6h3a2 2 0 0 1 2 2v7",key:"1yeb86"}],["path",{d:"M11 18H8a2 2 0 0 1-2-2V9",key:"19pyzm"}]],E2=e("GitCompare",Z);/**
|
|
152
152
|
* @license lucide-react v0.475.0 - ISC
|
|
153
153
|
*
|
|
154
154
|
* This source code is licensed under the ISC license.
|
|
155
155
|
* See the LICENSE file in the root directory of this source tree.
|
|
156
|
-
*/const
|
|
156
|
+
*/const D=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]],j2=e("Globe",D);/**
|
|
157
157
|
* @license lucide-react v0.475.0 - ISC
|
|
158
158
|
*
|
|
159
159
|
* This source code is licensed under the ISC license.
|
|
160
160
|
* See the LICENSE file in the root directory of this source tree.
|
|
161
|
-
*/const
|
|
161
|
+
*/const O=[["path",{d:"M21 12h-8",key:"1bmf0i"}],["path",{d:"M21 6H8",key:"1pqkrb"}],["path",{d:"M21 18h-8",key:"1tm79t"}],["path",{d:"M3 6v4c0 1.1.9 2 2 2h3",key:"1ywdgy"}],["path",{d:"M3 10v6c0 1.1.9 2 2 2h3",key:"2wc746"}]],R2=e("ListTree",O);/**
|
|
162
162
|
* @license lucide-react v0.475.0 - ISC
|
|
163
163
|
*
|
|
164
164
|
* This source code is licensed under the ISC license.
|
|
165
165
|
* See the LICENSE file in the root directory of this source tree.
|
|
166
|
-
*/const
|
|
166
|
+
*/const B=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],T2=e("LoaderCircle",B);/**
|
|
167
167
|
* @license lucide-react v0.475.0 - ISC
|
|
168
168
|
*
|
|
169
169
|
* This source code is licensed under the ISC license.
|
|
170
170
|
* See the LICENSE file in the root directory of this source tree.
|
|
171
|
-
*/const U=[["
|
|
171
|
+
*/const U=[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z",key:"1lielz"}]],G2=e("MessageSquare",U);/**
|
|
172
172
|
* @license lucide-react v0.475.0 - ISC
|
|
173
173
|
*
|
|
174
174
|
* This source code is licensed under the ISC license.
|
|
175
175
|
* See the LICENSE file in the root directory of this source tree.
|
|
176
|
-
*/const I=[["
|
|
176
|
+
*/const I=[["polygon",{points:"3 11 22 2 13 21 11 13 3 11",key:"1ltx0t"}]],Z2=e("Navigation",I);/**
|
|
177
177
|
* @license lucide-react v0.475.0 - ISC
|
|
178
178
|
*
|
|
179
179
|
* This source code is licensed under the ISC license.
|
|
180
180
|
* See the LICENSE file in the root directory of this source tree.
|
|
181
|
-
*/const
|
|
181
|
+
*/const K=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m16 15-3-3 3-3",key:"14y99z"}]],D2=e("PanelLeftClose",K);/**
|
|
182
182
|
* @license lucide-react v0.475.0 - ISC
|
|
183
183
|
*
|
|
184
184
|
* This source code is licensed under the ISC license.
|
|
185
185
|
* See the LICENSE file in the root directory of this source tree.
|
|
186
|
-
*/const
|
|
186
|
+
*/const Q=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m14 9 3 3-3 3",key:"8010ee"}]],O2=e("PanelLeftOpen",Q);/**
|
|
187
187
|
* @license lucide-react v0.475.0 - ISC
|
|
188
188
|
*
|
|
189
189
|
* This source code is licensed under the ISC license.
|
|
190
190
|
* See the LICENSE file in the root directory of this source tree.
|
|
191
|
-
*/const
|
|
191
|
+
*/const J=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M15 3v18",key:"14nvp0"}],["path",{d:"m8 9 3 3-3 3",key:"12hl5m"}]],B2=e("PanelRightClose",J);/**
|
|
192
192
|
* @license lucide-react v0.475.0 - ISC
|
|
193
193
|
*
|
|
194
194
|
* This source code is licensed under the ISC license.
|
|
195
195
|
* See the LICENSE file in the root directory of this source tree.
|
|
196
|
-
*/const
|
|
196
|
+
*/const X=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M15 3v18",key:"14nvp0"}],["path",{d:"m10 15-3-3 3-3",key:"1pgupc"}]],U2=e("PanelRightOpen",X);/**
|
|
197
197
|
* @license lucide-react v0.475.0 - ISC
|
|
198
198
|
*
|
|
199
199
|
* This source code is licensed under the ISC license.
|
|
200
200
|
* See the LICENSE file in the root directory of this source tree.
|
|
201
|
-
*/const
|
|
201
|
+
*/const W=[["path",{d:"M13.234 20.252 21 12.3",key:"1cbrk9"}],["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 0 2.828 2 2 0 0 0 2.828 0l8.414-8.586a4 4 0 0 0 0-5.656 4 4 0 0 0-5.656 0l-8.415 8.585a6 6 0 1 0 8.486 8.486",key:"1pkts6"}]],I2=e("Paperclip",W);/**
|
|
202
202
|
* @license lucide-react v0.475.0 - ISC
|
|
203
203
|
*
|
|
204
204
|
* This source code is licensed under the ISC license.
|
|
205
205
|
* See the LICENSE file in the root directory of this source tree.
|
|
206
|
-
*/const Y=[["path",{d:"
|
|
206
|
+
*/const Y=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]],K2=e("Pencil",Y);/**
|
|
207
207
|
* @license lucide-react v0.475.0 - ISC
|
|
208
208
|
*
|
|
209
209
|
* This source code is licensed under the ISC license.
|
|
210
210
|
* See the LICENSE file in the root directory of this source tree.
|
|
211
|
-
*/const e2=[["path",{d:"
|
|
211
|
+
*/const e2=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],Q2=e("Plus",e2);/**
|
|
212
212
|
* @license lucide-react v0.475.0 - ISC
|
|
213
213
|
*
|
|
214
214
|
* This source code is licensed under the ISC license.
|
|
215
215
|
* See the LICENSE file in the root directory of this source tree.
|
|
216
|
-
*/const t2=[["path",{d:"
|
|
216
|
+
*/const t2=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8",key:"1p45f6"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}]],J2=e("RotateCw",t2);/**
|
|
217
217
|
* @license lucide-react v0.475.0 - ISC
|
|
218
218
|
*
|
|
219
219
|
* This source code is licensed under the ISC license.
|
|
220
220
|
* See the LICENSE file in the root directory of this source tree.
|
|
221
|
-
*/const a2=[["
|
|
221
|
+
*/const a2=[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]],X2=e("Search",a2);/**
|
|
222
222
|
* @license lucide-react v0.475.0 - ISC
|
|
223
223
|
*
|
|
224
224
|
* This source code is licensed under the ISC license.
|
|
225
225
|
* See the LICENSE file in the root directory of this source tree.
|
|
226
|
-
*/const o2=[["
|
|
226
|
+
*/const o2=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]],W2=e("Send",o2);/**
|
|
227
227
|
* @license lucide-react v0.475.0 - ISC
|
|
228
228
|
*
|
|
229
229
|
* This source code is licensed under the ISC license.
|
|
230
230
|
* See the LICENSE file in the root directory of this source tree.
|
|
231
|
-
*/const c2=[["path",{d:"
|
|
231
|
+
*/const c2=[["path",{d:"M22 2 2 22",key:"y4kqgn"}]],Y2=e("Slash",c2);/**
|
|
232
232
|
* @license lucide-react v0.475.0 - ISC
|
|
233
233
|
*
|
|
234
234
|
* This source code is licensed under the ISC license.
|
|
235
235
|
* See the LICENSE file in the root directory of this source tree.
|
|
236
|
-
*/const n2=[["path",{d:"
|
|
237
|
-
|
|
236
|
+
*/const n2=[["path",{d:"m7 11 2-2-2-2",key:"1lz0vl"}],["path",{d:"M11 13h4",key:"1p7l4v"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}]],e1=e("SquareTerminal",n2);/**
|
|
237
|
+
* @license lucide-react v0.475.0 - ISC
|
|
238
|
+
*
|
|
239
|
+
* This source code is licensed under the ISC license.
|
|
240
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
241
|
+
*/const s2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],t1=e("Square",s2);/**
|
|
242
|
+
* @license lucide-react v0.475.0 - ISC
|
|
243
|
+
*
|
|
244
|
+
* This source code is licensed under the ISC license.
|
|
245
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
246
|
+
*/const h2=[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]],a1=e("Terminal",h2);/**
|
|
247
|
+
* @license lucide-react v0.475.0 - ISC
|
|
248
|
+
*
|
|
249
|
+
* This source code is licensed under the ISC license.
|
|
250
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
251
|
+
*/const y2=[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]],o1=e("Trash2",y2);/**
|
|
252
|
+
* @license lucide-react v0.475.0 - ISC
|
|
253
|
+
*
|
|
254
|
+
* This source code is licensed under the ISC license.
|
|
255
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
256
|
+
*/const d2=[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]],c1=e("Upload",d2);/**
|
|
257
|
+
* @license lucide-react v0.475.0 - ISC
|
|
258
|
+
*
|
|
259
|
+
* This source code is licensed under the ISC license.
|
|
260
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
261
|
+
*/const i2=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],n1=e("X",i2);export{l2 as A,q2 as B,x2 as C,u2 as D,w2 as E,L2 as F,E2 as G,a1 as H,v2 as I,Y2 as J,I2 as K,T2 as L,G2 as M,Z2 as N,W2 as O,O2 as P,b2 as Q,J2 as R,t1 as S,o1 as T,c1 as U,n1 as X,D2 as a,U2 as b,B2 as c,K2 as d,M2 as e,Q2 as f,P2 as g,p2 as h,e1 as i,H2 as j,V2 as k,z2 as l,C2 as m,F2 as n,g2 as o,f2 as p,_2 as q,S2 as r,A2 as s,m2 as t,$2 as u,N2 as v,k2 as w,R2 as x,j2 as y,X2 as z};
|
|
262
|
+
//# sourceMappingURL=vendor-icons-CctYQ66i.js.map
|