@sparkelf/dsh-artifact-app 0.1.0 → 0.1.1

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/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useRef","useState","useState","useCallback","useMemo","useState"],"sources":["../src/client/CockpitViewer.tsx","../src/client/CockpitTab.tsx","../src/client/ArtifactFileViewer.tsx","../src/client/index.tsx"],"sourcesContent":["/**\n * Sandboxed cockpit viewer.\n *\n * The iframe is served by the plugin's render route and runs with\n * `sandbox=\"allow-scripts allow-same-origin\"` (opaque origin). The workspace token reaches it only\n * through postMessage, and only after this parent verified the message source.\n *\n * @module client/CockpitViewer\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react'\n\n/** Addresses resolved from the host config route. */\ninterface ViewerConfig {\n renderBaseUrl: string\n gatewayBaseUrl: string\n dataopsWebBaseUrl: string\n}\n\n/** Message the runtime posts to its parent. */\ninterface RuntimeMessage {\n source?: string\n type?: string\n resourceRef?: string\n}\n\nconst frameStyle: CSSProperties = {\n width: '100%',\n height: '100%',\n // 宿主侧栏的 tab 容器不一定是可伸展的 flex 父级,只写 height:100% 会被压到 ~140px\n // (实测 clientHeight=142,画布被裁成一条)。给一个下限,让驾驶舱在任何容器里都能看。\n minHeight: '520px',\n border: '0',\n display: 'block',\n background: '#060a14',\n}\n\n/**\n * Render one cockpit in a sandboxed iframe and keep its bootstrap config fed.\n * @param props.artifactId - cockpit (or preview draft) id.\n * @param props.refreshKey - changing it reloads the iframe, used by \"updated\" events.\n * @param props.reloadToken - bump from outside to force a reload.\n */\nexport function CockpitViewer({ artifactId, reloadToken }: { artifactId: string; reloadToken?: number }) {\n const frameRef = useRef<HTMLIFrameElement | null>(null)\n /** iframe 加载完成后重发一次引导配置:`hello` 可能在监听器挂上之前就发出去了。 */\n const resendRef = useRef<(() => void) | null>(null)\n const [config, setConfig] = useState<ViewerConfig | null>(null)\n const [token, setToken] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let alive = true\n void (async () => {\n try {\n const [configResponse, tokenResponse] = await Promise.all([\n fetch('/dsh-artifact-app/config', { headers: { accept: 'application/json' } }),\n fetch('/dsh-artifact-app/token', { headers: { accept: 'application/json' } }),\n ])\n const configPayload = await configResponse.json() as ViewerConfig & { error?: string }\n const tokenPayload = await tokenResponse.json() as { token?: string; error?: string }\n if (!configResponse.ok) throw new Error(configPayload.error ?? '读取驾驶舱配置失败')\n if (!tokenResponse.ok || typeof tokenPayload.token !== 'string') throw new Error(tokenPayload.error ?? '读取 DataOps 登录凭证失败')\n if (alive) {\n setConfig(configPayload)\n setToken(tokenPayload.token)\n }\n } catch (cause) {\n if (alive) setError(cause instanceof Error ? cause.message : String(cause))\n }\n })()\n return () => { alive = false }\n }, [])\n\n useEffect(() => {\n if (config === null || token === null) return undefined\n /** 把引导配置推给 iframe;`hello` 与 iframe 的 load 事件都会触发它。 */\n const sendConfig = (): void => {\n frameRef.current?.contentWindow?.postMessage({\n source: 'dataops-artifact-host',\n type: 'config',\n payload: {\n artifactId,\n token,\n gatewayBaseUrl: config.gatewayBaseUrl,\n refreshIntervalSeconds: 300,\n keepConnection: false,\n locale: 'zh-CN',\n theme: 'dark',\n },\n }, '*')\n }\n const onMessage = (event: MessageEvent): void => {\n const frame = frameRef.current\n if (frame === null || event.source !== frame.contentWindow) return\n const data = event.data as RuntimeMessage | null\n if (data === null || typeof data !== 'object' || data.source !== 'dataops-artifact-app') return\n if (data.type === 'request-access') {\n const target = config.dataopsWebBaseUrl.replace(/\\/+$/u, '')\n window.open(target, '_blank', 'noopener,noreferrer')\n return\n }\n if (data.type !== 'hello' && data.type !== 'ready' && data.type !== 'token:expired') return\n sendConfig()\n }\n resendRef.current = sendConfig\n window.addEventListener('message', onMessage)\n return () => { resendRef.current = null; window.removeEventListener('message', onMessage) }\n }, [artifactId, config, token])\n\n if (error !== null) {\n return <div role=\"alert\" style={{ padding: 18, color: '#b91c1c', fontSize: 12 }}>{error}</div>\n }\n if (config === null || token === null) {\n return <div style={{ padding: 18, color: 'var(--dsw-alias-label-secondary, #6b7280)', fontSize: 12 }}>正在准备驾驶舱…</div>\n }\n const src = config.renderBaseUrl.replace(/\\/+$/u, '') + '/' + encodeURIComponent(artifactId) + (reloadToken === undefined ? '' : '?r=' + String(reloadToken))\n return (\n <iframe\n ref={frameRef}\n title=\"驾驶舱\"\n src={src}\n sandbox=\"allow-scripts allow-same-origin\"\n allow=\"fullscreen\"\n onLoad={() => resendRef.current?.()}\n style={frameStyle}\n data-artifact-viewer={artifactId}\n />\n )\n}","/**\n * The `dataops-artifact-apps` right-sidebar tab.\n *\n * One pane holds the cockpit list and, once a card is opened, the sandboxed\n * viewer. The list is proxied by the host so the browser holds no DataOps token\n * for reads.\n *\n * @module client/CockpitTab\n */\n\nimport { useCallback, useEffect, useMemo, useState, type CSSProperties } from 'react'\nimport { CockpitViewer } from './CockpitViewer.tsx'\n\n/** Minimal tab record Better Sidebar passes. */\ninterface TabRecord {\n meta?: unknown\n title?: string\n}\n\n/** Props this tab consumes. */\nexport interface CockpitTabProps {\n tab: TabRecord\n scope?: { sessionId?: string }\n visible?: boolean\n}\n\ninterface AppRow {\n id: string\n name: string\n description?: string | null\n catalog?: string | null\n status: string\n currentVersion: number\n publishedVersion: number\n updatedAt: string\n}\n\nconst readMeta = (value: unknown): { cockpitId?: string; cockpitName?: string; nonce?: string } => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return {}\n const record = value as Record<string, unknown>\n return {\n cockpitId: typeof record.cockpitId === 'string' ? record.cockpitId : undefined,\n cockpitName: typeof record.cockpitName === 'string' ? record.cockpitName : undefined,\n nonce: typeof record.nonce === 'string' ? record.nonce : undefined,\n }\n}\n\nconst cardStyle: CSSProperties = {\n display: 'grid',\n gap: 4,\n width: '100%',\n minWidth: 0,\n textAlign: 'left',\n padding: '10px 12px',\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border-l1, #e5e7eb)',\n background: 'var(--dsw-alias-bg-layer-1, #ffffff)',\n color: 'inherit',\n cursor: 'pointer',\n}\n\nconst muted: CSSProperties = { fontSize: 10, color: 'var(--dsw-alias-label-secondary, #6b7280)' }\n\n/** Cockpit list plus viewer. */\nexport function CockpitTab({ tab }: CockpitTabProps) {\n const meta = readMeta(tab.meta)\n const [apps, setApps] = useState<AppRow[]>([])\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [selected, setSelected] = useState<{ id: string; name: string } | null>(\n meta.cockpitId === undefined ? null : { id: meta.cockpitId, name: meta.cockpitName ?? '驾驶舱' },\n )\n const [reloadToken, setReloadToken] = useState(0)\n\n const load = useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const response = await fetch('/dsh-artifact-app/apps?pageSize=50', { headers: { accept: 'application/json' } })\n const payload = await response.json() as { items?: AppRow[]; error?: string }\n if (!response.ok) throw new Error(payload.error ?? 'HTTP ' + String(response.status))\n setApps(payload.items ?? [])\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => { void load() }, [load])\n\n useEffect(() => {\n if (meta.cockpitId === undefined) return\n setSelected({ id: meta.cockpitId, name: meta.cockpitName ?? '驾驶舱' })\n setReloadToken((value) => value + 1)\n }, [meta.cockpitId, meta.cockpitName, meta.nonce])\n\n const catalogs = useMemo(() => [...new Set(apps.map((app) => app.catalog).filter((value): value is string => typeof value === 'string' && value !== ''))], [apps])\n\n /**\n * 导出一份可脱离 DSH 单独分发的 HTML:服务端返回的 HTML 已烧死 appId 与网关地址,\n * 这里再把运行时与 ECharts 内联进去,产物双击即可打开(地址栏加 #token=… 取数)。\n */\n const [exporting, setExporting] = useState(false)\n const exportHtml = async (): Promise<void> => {\n if (selected === null || exporting) return\n setExporting(true)\n try {\n const base = '/dsh-artifact-app/gateway/api/artifact-apps'\n const [htmlResponse, runtimeResponse, echartsResponse] = await Promise.all([\n fetch(base.replace(/\\/+$/u, '') + '/' + encodeURIComponent(selected.id) + '/html', { headers: { accept: 'application/json' } }),\n fetch(base.replace(/\\/+$/u, '') + '/runtime.js'),\n fetch(base.replace(/\\/+$/u, '') + '/echarts.js'),\n ])\n if (!htmlResponse.ok) throw new Error('读取驾驶舱 HTML 失败')\n const payload = await htmlResponse.json() as { html?: string }\n const runtime = runtimeResponse.ok ? await runtimeResponse.text() : ''\n const echarts = echartsResponse.ok ? await echartsResponse.text() : ''\n const inline = (runtime === '' ? '' : '<script>' + runtime + '<\\/script>') + (echarts === '' ? '' : '<script>' + echarts + '<\\/script>')\n const html = (payload.html ?? '').replace('</body>', inline + '</body>')\n const blob = new Blob([html], { type: 'text/html;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = (selected.name || 'cockpit') + '.html'\n document.body.appendChild(link)\n link.click()\n link.remove()\n URL.revokeObjectURL(url)\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setExporting(false)\n }\n }\n\n if (selected !== null) {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', borderBottom: '1px solid var(--dsw-alias-border-l1, #e5e7eb)' }}>\n <button type=\"button\" onClick={() => setSelected(null)} style={{ border: 'none', background: 'transparent', color: 'inherit', cursor: 'pointer', fontSize: 12, padding: 0 }}>← 列表</button>\n <div style={{ flex: 1, minWidth: 0, fontSize: 12, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{selected.name}</div>\n <button\n type=\"button\"\n title=\"刷新\"\n aria-label=\"刷新\"\n onClick={() => setReloadToken((value) => value + 1)}\n style={{ border: 'none', background: 'transparent', color: 'inherit', cursor: 'pointer', display: 'grid', placeItems: 'center', width: 26, height: 26, borderRadius: 6 }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 12a9 9 0 1 1-3-6.7\" />\n <path d=\"M21 4v5h-5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n title=\"全屏查看\"\n aria-label=\"全屏查看\"\n onClick={() => {\n const host = document.querySelector('[data-artifact-fullscreen-host]') as HTMLElement | null\n const target = host ?? document.querySelector('iframe[data-artifact-viewer]')?.parentElement ?? null\n if (target === null) return\n if (document.fullscreenElement !== null) void document.exitFullscreen()\n else void target.requestFullscreen?.().catch(() => undefined)\n }}\n style={{ border: 'none', background: 'transparent', color: 'inherit', cursor: 'pointer', display: 'grid', placeItems: 'center', width: 26, height: 26, borderRadius: 6 }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 9V4h5\" /><path d=\"M20 9V4h-5\" /><path d=\"M4 15v5h5\" /><path d=\"M20 15v5h-5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n title=\"导出 HTML\"\n aria-label=\"导出 HTML\"\n disabled={exporting}\n onClick={() => void exportHtml()}\n style={{ border: 'none', background: 'transparent', color: 'inherit', cursor: exporting ? 'progress' : 'pointer', display: 'grid', placeItems: 'center', width: 26, height: 26, borderRadius: 6, opacity: exporting ? 0.5 : 1 }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M12 3v12\" />\n <path d=\"M7 10l5 5 5-5\" />\n <path d=\"M4 21h16\" />\n </svg>\n </button>\n </div>\n <div data-artifact-fullscreen-host style={{ flex: 1, minHeight: 0 }}>\n <CockpitViewer artifactId={selected.id} reloadToken={reloadToken} />\n </div>\n </div>\n )\n }\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px', borderBottom: '1px solid var(--dsw-alias-border-l1, #e5e7eb)' }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 12, fontWeight: 600 }}>驾驶舱</div>\n <div style={muted}>DataOps 驾驶舱资产 · {apps.length} 个{catalogs.length > 0 ? ' · ' + catalogs.join(' / ') : ''}</div>\n </div>\n <button type=\"button\" onClick={() => void load()} style={{ border: 'none', background: 'transparent', color: 'inherit', cursor: 'pointer', fontSize: 12 }}>刷新</button>\n </div>\n <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: 10, display: 'grid', gap: 8, alignContent: 'start' }}>\n {loading ? <div style={{ ...muted, textAlign: 'center', padding: 18 }}>正在加载驾驶舱…</div> : null}\n {error !== null ? <div role=\"alert\" style={{ padding: '8px 10px', borderRadius: 8, background: '#fef2f2', color: '#b91c1c', fontSize: 11 }}>{error}</div> : null}\n {!loading && error === null && apps.length === 0 ? <div style={{ ...muted, textAlign: 'center', padding: 18 }}>暂无驾驶舱</div> : null}\n {apps.map((app) => (\n <button\n key={app.id}\n type=\"button\"\n style={cardStyle}\n onClick={() => setSelected({ id: app.id, name: app.name })}\n data-cockpit-id={app.id}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>\n <span style={{ flex: 1, minWidth: 0, fontSize: 12, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{app.name}</span>\n <span style={{ ...muted, flex: 'none' }}>{app.status === 'PUBLISHED' ? '已发布' : '草稿'}</span>\n </span>\n {app.description ? <span style={{ ...muted, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{app.description}</span> : null}\n <span style={muted}>{app.catalog ?? '未分类'} · v{app.publishedVersion || app.currentVersion}</span>\n </button>\n ))}\n </div>\n </div>\n )\n}","/**\n * HTML file viewer for cockpit files.\n *\n * The descriptor only matches `/artifact-apps/**` HTML carrying the\n * `artifact-app` meta marker. Opening such a file creates or updates one draft\n * through the host and renders it exactly like a published cockpit, so the file\n * on disk is the draft.\n *\n * @module client/ArtifactFileViewer\n */\n\nimport { useEffect, useState } from 'react'\nimport { CockpitViewer } from './CockpitViewer.tsx'\n\n/** Props the Better Sidebar file viewer passes. */\nexport interface ArtifactFileViewerProps {\n path: string\n title?: string\n content?: string\n largeFile?: { size: number }\n}\n\n/** Marker the file must carry for this viewer to claim it. */\nexport const ARTIFACT_FILE_MARKER = /<meta[^>]+name=[\"']artifact-app[\"'][^>]*>/iu\n\n/**\n * Whether a file is a cockpit document.\n * @param path - workspace path.\n * @param head - the first bytes the viewer sniffed.\n */\nexport const isArtifactFile = (path: string, head: Uint8Array | string): boolean => {\n if (!/\\/artifact-apps\\/[^/]+\\.html?$/iu.test(path)) return false\n const text = typeof head === 'string' ? head : new TextDecoder().decode(head)\n return ARTIFACT_FILE_MARKER.test(text)\n}\n\n/** Create/update the preview draft, then render it. */\nexport function ArtifactFileViewer({ path, content, largeFile }: ArtifactFileViewerProps) {\n const [artifactId, setArtifactId] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n if (content === undefined) return undefined\n let alive = true\n setBusy(true)\n setError(null)\n void (async () => {\n try {\n const response = await fetch('/dsh-artifact-app/preview', {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({ path, html: content }),\n })\n const payload = await response.json() as { artifactId?: string; error?: string }\n if (!response.ok || typeof payload.artifactId !== 'string') {\n throw new Error(payload.error ?? 'HTTP ' + String(response.status))\n }\n if (alive) setArtifactId(payload.artifactId)\n } catch (cause) {\n if (alive) setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n if (alive) setBusy(false)\n }\n })()\n return () => { alive = false }\n }, [path, content, attempt])\n\n if (largeFile !== undefined) {\n return <div style={{ padding: 18, fontSize: 12, color: 'var(--dsw-alias-label-secondary, #6b7280)' }}>文件过大({Math.round(largeFile.size / 1024)} KB),请缩小后再预览驾驶舱。</div>\n }\n if (error !== null) {\n return (\n <div role=\"alert\" style={{ padding: 12, margin: 12, borderRadius: 8, background: '#fef2f2', color: '#b91c1c', fontSize: 12, display: 'grid', gap: 8 }}>\n <span style={{ whiteSpace: 'pre-wrap' }}>{error}</span>\n <button type=\"button\" onClick={() => setAttempt((value) => value + 1)} style={{ justifySelf: 'start', border: '1px solid #fca5a5', background: 'transparent', color: '#b91c1c', borderRadius: 6, padding: '3px 10px', cursor: 'pointer', fontSize: 11 }}>重试</button>\n </div>\n )\n }\n if (busy || artifactId === null) {\n return <div style={{ padding: 18, fontSize: 12, color: 'var(--dsw-alias-label-secondary, #6b7280)' }}>正在生成驾驶舱预览…</div>\n }\n return <CockpitViewer artifactId={artifactId} />\n}","/**\n * Client half of the artifact-app plugin.\n *\n * Registers the `dataops-artifact-apps` right-sidebar tab, the cockpit HTML file\n * viewer, and the SSE bridge that lets `cockpit_open` drive the sidebar. All three\n * are optional when a profile has no Better Sidebar: the host routes and tools keep\n * working without a browser surface.\n *\n * @module @sparkelf/dsh-artifact-app/client\n */\n\nimport { useEffect } from 'react'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport { CockpitTab } from './CockpitTab.tsx'\nimport { ArtifactFileViewer, isArtifactFile } from './ArtifactFileViewer.tsx'\n\n/** Client services this plugin reads; `slots` owns the always-mounted bridge seat. */\nexport const inject = ['slots']\n\n/** Tab id shared by registration, the bridge and the tools. */\nexport const ARTIFACT_TAB_TYPE = 'dataops-artifact-apps'\n/** File viewer id. */\nexport const ARTIFACT_VIEWER_ID = 'artifact-app:cockpit'\n\n/** The part of Better Sidebar this plugin uses. */\ninterface BetterSidebarLike {\n registerTab?(descriptor: Record<string, unknown>): () => void\n registerFileViewer?(descriptor: Record<string, unknown>): () => void\n openTab?(seed: { type: string; id?: string; title?: string; meta?: unknown }, scope?: { sessionId?: string }): void\n updateTab?(tabId: string, patch: { title?: string; meta?: unknown }, sessionId?: string): void\n}\n\ninterface OpenEvent {\n type: 'open_cockpit'\n cockpitId: string\n cockpitName: string | null\n mode: string\n nonce: string\n requestId: string\n}\n\ninterface UpdatedEvent {\n type: 'cockpit_updated'\n cockpitId: string\n nonce: string\n}\n\n/** Invisible bridge: SSE navigation events become Better Sidebar calls. */\nfunction OpenCockpitBridge({ service, scope }: { service: BetterSidebarLike; scope?: { sessionId?: string } }) {\n useEffect(() => {\n if (typeof EventSource !== 'function') return undefined\n const source = new EventSource('/dsh-artifact-app/open/events')\n const open = (event: MessageEvent): void => {\n let parsed: OpenEvent\n try { parsed = JSON.parse(String(event.data)) as OpenEvent } catch { return }\n if (parsed.type !== 'open_cockpit' || typeof parsed.cockpitId !== 'string') return\n const meta = { cockpitId: parsed.cockpitId, cockpitName: parsed.cockpitName, nonce: parsed.nonce, mode: parsed.mode }\n service.openTab?.({ type: ARTIFACT_TAB_TYPE, id: ARTIFACT_TAB_TYPE, title: '驾驶舱', meta }, scope)\n service.updateTab?.(ARTIFACT_TAB_TYPE, { title: '驾驶舱', meta }, scope?.sessionId)\n }\n const updated = (event: MessageEvent): void => {\n let parsed: UpdatedEvent\n try { parsed = JSON.parse(String(event.data)) as UpdatedEvent } catch { return }\n if (parsed.type !== 'cockpit_updated' || typeof parsed.cockpitId !== 'string') return\n service.updateTab?.(ARTIFACT_TAB_TYPE, {\n title: '驾驶舱',\n meta: { cockpitId: parsed.cockpitId, nonce: parsed.nonce },\n }, scope?.sessionId)\n }\n source.addEventListener('dataops-open-cockpit', open as EventListener)\n source.addEventListener('dataops-cockpit-updated', updated as EventListener)\n return () => {\n source.removeEventListener('dataops-open-cockpit', open as EventListener)\n source.removeEventListener('dataops-cockpit-updated', updated as EventListener)\n source.close()\n }\n }, [service, scope])\n return null\n}\n\n/**\n * Register the tab, the file viewer and the bridge.\n * @param ctx - DSH client context.\n */\nexport function apply(ctx: ClientContext): void {\n try {\n ctx.effect(() => {\n let disposeTab: (() => void) | undefined\n let disposeViewer: (() => void) | undefined\n let disposeBridge: (() => void) | undefined\n let betterSidebarService: BetterSidebarLike | undefined\n const inject = (ctx as unknown as {\n inject?: (names: readonly string[], callback: (injected: { betterSidebar?: BetterSidebarLike }) => void) => unknown\n }).inject\n inject?.(['betterSidebar'], (injected) => {\n const service = injected.betterSidebar\n if (service?.registerTab === undefined) return\n betterSidebarService = service\n disposeTab = service.registerTab({\n id: ARTIFACT_TAB_TYPE,\n title: '驾驶舱',\n icon: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 18a8 8 0 1 1 16 0\" />\n <path d=\"M12 18l4.2-5\" />\n <circle cx=\"12\" cy=\"18\" r=\"1.4\" />\n </svg>\n ),\n\n description: 'DataOps 驾驶舱资产、预览与发布',\n order: 62,\n single: true,\n component: ((props: Record<string, unknown>) => CockpitTab(props as never)) as never,\n })\n disposeViewer = service.registerFileViewer?.({\n id: ARTIFACT_VIEWER_ID,\n title: '驾驶舱预览',\n // Detection is the only claim. Better Sidebar 0.29 still falls through\n // to `exts.includes(ext)` after a failed `detect`, so a non-empty list\n // here would capture every `.html` in the workspace and run it through\n // the cockpit template contract — plain pages then fail with\n // TEMPLATE_CONTRACT_MISSING instead of opening in the normal preview.\n exts: [],\n priority: 60,\n fetchStrategy: 'fsRead',\n detect: (path: string, head: Uint8Array) => isArtifactFile(path, head),\n component: ((props: Record<string, unknown>) => ArtifactFileViewer(props as never)) as never,\n })\n })\n const slots = (ctx as unknown as {\n slots: {\n inject(name: string, factory: () => () => void): () => void\n register(options: Record<string, unknown>, component: (props: never) => unknown): () => void\n }\n }).slots\n disposeBridge = slots.inject('sidebar.footer.action', () => slots.register({\n name: 'sidebar.footer.action',\n id: 'artifact-app-bridge',\n order: 96,\n }, ((props: { scope?: { sessionId?: string } }) => (betterSidebarService === undefined\n ? null\n : OpenCockpitBridge({ service: betterSidebarService, scope: props?.scope }))) as never))\n return () => {\n disposeBridge?.()\n disposeBridge = undefined\n disposeViewer?.()\n disposeViewer = undefined\n disposeTab?.()\n disposeTab = undefined\n }\n }, 'artifact-app: sidebar tab, file viewer and open bridge')\n } catch (error) {\n console.error('[artifact-app] apply failed', error)\n throw error\n }\n}"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useRef","useState","useState","useCallback","useMemo","useState"],"sources":["../src/client/CockpitViewer.tsx","../src/client/CockpitTab.tsx","../src/client/ArtifactFileViewer.tsx","../src/client/index.tsx"],"sourcesContent":["/**\n * Sandboxed cockpit viewer.\n *\n * The iframe is served by the plugin's render route and runs with\n * `sandbox=\"allow-scripts allow-same-origin\"` (opaque origin). The workspace token reaches it only\n * through postMessage, and only after this parent verified the message source.\n *\n * @module client/CockpitViewer\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react'\n\n/** Addresses resolved from the host config route. */\ninterface ViewerConfig {\n renderBaseUrl: string\n gatewayBaseUrl: string\n dataopsWebBaseUrl: string\n}\n\n/** Message the runtime posts to its parent. */\ninterface RuntimeMessage {\n source?: string\n type?: string\n resourceRef?: string\n}\n\n/**\n * Render routes are served by this DSH host, so the iframe must stay on the same origin.\n * A stale absolute base (`http://localhost:<port>/dsh-artifact-app/render` behind a proxy)\n * would point the frame at a port the browser cannot reach; keep only the pathname.\n */\nconst renderBase = (value: string): string => {\n try {\n const base = new URL(value, window.location.origin)\n return base.pathname.replace(/\\/+$/u, '')\n } catch {\n return value.replace(/\\/+$/u, '')\n }\n}\n\nconst frameStyle: CSSProperties = {\n width: '100%',\n height: '100%',\n // 宿主侧栏的 tab 容器不一定是可伸展的 flex 父级,只写 height:100% 会被压到 ~140px\n // (实测 clientHeight=142,画布被裁成一条)。给一个下限,让驾驶舱在任何容器里都能看。\n minHeight: '520px',\n border: '0',\n display: 'block',\n background: '#060a14',\n}\n\n/** How long a 401 waits for the parent to push a refreshed token before surfacing. */\nconst TOKEN_REFRESH_WAIT_MS = 5000\n\n/**\n * Render one cockpit in a sandboxed iframe and keep its bootstrap config fed.\n * @param props.artifactId - cockpit (or preview draft) id.\n * @param props.refreshKey - changing it reloads the iframe, used by \"updated\" events.\n * @param props.reloadToken - bump from outside to force a reload.\n */\nexport function CockpitViewer({ artifactId, reloadToken }: { artifactId: string; reloadToken?: number }) {\n const frameRef = useRef<HTMLIFrameElement | null>(null)\n /** iframe 加载完成后重发一次引导配置:`hello` 可能在监听器挂上之前就发出去了。 */\n const resendRef = useRef<(() => void) | null>(null)\n /** `ready` 回执:收到前持续补发 config,覆盖「hello 早于监听器」的竞态。 */\n const ackRef = useRef(false)\n const refreshInFlightRef = useRef(false)\n const [config, setConfig] = useState<ViewerConfig | null>(null)\n const [token, setToken] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let alive = true\n void (async () => {\n try {\n const [configResponse, tokenResponse] = await Promise.all([\n fetch('/dsh-artifact-app/config', { headers: { accept: 'application/json' } }),\n fetch('/dsh-artifact-app/token', { headers: { accept: 'application/json' } }),\n ])\n const configPayload = await configResponse.json() as ViewerConfig & { error?: string }\n const tokenPayload = await tokenResponse.json() as { token?: string; error?: string }\n if (!configResponse.ok) throw new Error(configPayload.error ?? '读取驾驶舱配置失败')\n if (!tokenResponse.ok || typeof tokenPayload.token !== 'string' || tokenPayload.token.trim() === '') throw new Error(tokenPayload.error ?? '读取 DataOps 登录凭证失败')\n if (alive) {\n setConfig(configPayload)\n setToken(tokenPayload.token)\n }\n } catch (cause) {\n if (alive) setError(cause instanceof Error ? cause.message : String(cause))\n }\n })()\n return () => { alive = false }\n }, [])\n\n useEffect(() => {\n if (config === null || token === null) return undefined\n /** 把引导配置推给 iframe;`hello`、`ready` 与 iframe 的 load 事件都会触发它。 */\n const sendConfig = (tokenOverride?: string): void => {\n const effectiveToken = tokenOverride ?? token\n if (effectiveToken === null || effectiveToken === '') return\n frameRef.current?.contentWindow?.postMessage({\n source: 'dataops-artifact-host',\n type: 'config',\n payload: {\n artifactId,\n token: effectiveToken,\n gatewayBaseUrl: config.gatewayBaseUrl,\n refreshIntervalSeconds: 300,\n keepConnection: false,\n locale: 'zh-CN',\n theme: 'dark',\n },\n }, '*')\n }\n /**\n * 401 后重新拉凭证并**直接补发** config。\n * 不能只靠 setToken:刷新拿到的 token 可能与 state 相同(凭据没变),\n * 那样 effect 不会重跑,运行时的 waitForNewConfig 会空等到超时。\n */\n const refreshAndResend = (): void => {\n if (refreshInFlightRef.current) return\n refreshInFlightRef.current = true\n void (async () => {\n try {\n const response = await fetch('/dsh-artifact-app/token', { headers: { accept: 'application/json' }, cache: 'no-store' })\n const payload = await response.json() as { token?: string; error?: string }\n if (!response.ok || typeof payload.token !== 'string' || payload.token.trim() === '') {\n throw new Error(payload.error ?? '读取 DataOps 登录凭证失败')\n }\n setToken(payload.token)\n console.info('[artifact-app] token refreshed; resending config')\n sendConfig(payload.token)\n } catch (cause) {\n console.error('[artifact-app] token refresh failed', cause)\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n refreshInFlightRef.current = false\n }\n })()\n }\n /**\n * 判断消息是否来自本 tab 的 iframe。\n *\n * Chrome 对 sandbox iframe 的 `event.source` 与 `iframe.contentWindow` 实测可能\n * 不是同一个代理对象(严格相等为 false),只靠身份比较会把 ready/token:expired 全丢掉。\n * 依次尝试:同窗口代理 → frameElement 归属 → 同源兜底。\n */\n const fromOwnedFrame = (event: MessageEvent): boolean => {\n const frame = frameRef.current\n if (frame === null || event.source === null) return false\n if (event.source === frame.contentWindow) return true\n try {\n if ((event.source as Window).frameElement === frame) return true\n } catch { /* 跨源时 frameElement 不可读 */ }\n return event.origin === window.location.origin\n }\n const onMessage = (event: MessageEvent): void => {\n if (!fromOwnedFrame(event)) return\n const data = event.data as RuntimeMessage | null\n if (data === null || typeof data !== 'object' || data.source !== 'dataops-artifact-app') return\n if (data.type === 'request-access') {\n const target = config.dataopsWebBaseUrl.replace(/\\/+$/u, '')\n window.open(target, '_blank', 'noopener,noreferrer')\n return\n }\n if (data.type === 'ready') {\n ackRef.current = true\n sendConfig()\n return\n }\n if (data.type === 'hello') {\n sendConfig()\n return\n }\n if (data.type === 'token:expired') {\n // 父侧重新拉一次凭证并直接补发 config;运行时收到新 token 后会重试刚才失败的那次查询。\n console.info('[artifact-app] token:expired received; refreshing token')\n refreshAndResend()\n return\n }\n }\n resendRef.current = sendConfig\n window.addEventListener('message', onMessage)\n // 主动下发一次,并在收到 ready 回执前补发几次:iframe 的 hello 可能早于本监听器,\n // 只依赖 hello/ready 会让首条查询在 token 还没落地时发出。\n ackRef.current = false\n sendConfig()\n const retryTimers = [300, 900, 1800].map((delay) => window.setTimeout(() => {\n if (!ackRef.current) sendConfig()\n }, delay))\n return () => {\n retryTimers.forEach((timer) => window.clearTimeout(timer))\n resendRef.current = null\n window.removeEventListener('message', onMessage)\n }\n }, [artifactId, config, token])\n\n if (error !== null) {\n return <div role=\"alert\" style={{ padding: 18, color: '#b91c1c', fontSize: 12 }}>{error}</div>\n }\n if (config === null) {\n return <div style={{ padding: 18, color: 'var(--dsw-alias-label-secondary, #6b7280)', fontSize: 12 }}>正在准备驾驶舱…</div>\n }\n if (token === null) {\n return <div style={{ padding: 18, color: 'var(--dsw-alias-label-secondary, #6b7280)', fontSize: 12 }}>正在获取凭据…</div>\n }\n const src = renderBase(config.renderBaseUrl) + '/' + encodeURIComponent(artifactId) + (reloadToken === undefined ? '' : '?r=' + String(reloadToken))\n return (\n <iframe\n ref={frameRef}\n title=\"驾驶舱\"\n src={src}\n sandbox=\"allow-scripts allow-same-origin\"\n allow=\"fullscreen\"\n onLoad={() => resendRef.current?.()}\n style={frameStyle}\n data-artifact-viewer={artifactId}\n />\n )\n}","/**\n * The `dataops-artifact-apps` right-sidebar tab.\n *\n * One pane holds the cockpit catalogue (published apps plus cockpit templates) and, once a card\n * is opened, the sandboxed viewer. Both lists are proxied by the host so the browser holds no\n * DataOps token for reads.\n *\n * @module client/CockpitTab\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { CockpitViewer } from './CockpitViewer.tsx'\n\n/** Minimal tab record Better Sidebar passes. */\ninterface TabRecord {\n meta?: unknown\n title?: string\n}\n\n/** What a card can hand back to the composer when \"添加到对话\" is pressed. */\nexport interface CockpitReferenceInput {\n kind: 'app' | 'template'\n id: string\n name: string\n catalog?: string | null\n}\n\n/** Props this tab consumes. */\nexport interface CockpitTabProps {\n tab: TabRecord\n scope?: { sessionId?: string }\n visible?: boolean\n /** Insert a cockpit/template reference into the active composer. */\n insertArtifactReference?: (item: CockpitReferenceInput, sessionId?: string) => void\n}\n\ninterface AppRow {\n id: string\n name: string\n description?: string | null\n catalog?: string | null\n status: string\n currentVersion: number\n publishedVersion: number\n updatedAt: string\n}\n\ninterface TemplateRow {\n id: string\n name: string\n description?: string | null\n catalog?: string | null\n status?: string\n currentVersion?: number\n contract?: { slots?: string[]; question?: string } | null\n updatedAt?: string\n}\n\ninterface SelectedCockpit {\n kind: 'app' | 'template' | 'component'\n /** Address the render route understands: `<appId>` or `template/<templateId>`. */\n viewerId: string\n name: string\n}\n\nconst readMeta = (value: unknown): { cockpitId?: string; cockpitName?: string; nonce?: string } => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return {}\n const record = value as Record<string, unknown>\n return {\n cockpitId: typeof record.cockpitId === 'string' ? record.cockpitId : undefined,\n cockpitName: typeof record.cockpitName === 'string' ? record.cockpitName : undefined,\n nonce: typeof record.nonce === 'string' ? record.nonce : undefined,\n }\n}\n\nconst readItems = <T,>(payload: unknown): T[] => {\n if (Array.isArray(payload)) return payload as T[]\n if (typeof payload === 'object' && payload !== null && Array.isArray((payload as { items?: unknown }).items)) {\n return (payload as { items: T[] }).items\n }\n return []\n}\n\nconst versionLabel = (row: { currentVersion?: number; publishedVersion?: number }): string => {\n const version = row.publishedVersion || row.currentVersion || 1\n return 'v' + String(version)\n}\n\n/**\n * All styling is local to the tab and consumes the host's `--dsw-*` tokens, so the panel follows\n * both light and dark themes without a second palette.\n */\nconst CSS = `\n.dsh-ac {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--dsw-alias-bg-base, #151517);\n color: var(--dsw-alias-label-primary, #f9fafb);\n}\n.dsh-ac__head {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n padding: 10px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06));\n background: color-mix(in srgb, var(--dsw-alias-bg-base, #151517) 92%, transparent);\n backdrop-filter: blur(10px);\n}\n.dsh-ac__glyph {\n display: grid;\n place-items: center;\n width: 26px;\n height: 26px;\n border-radius: 7px;\n color: var(--dsw-alias-state-business-primary, #7aaaff);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #7aaaff) 14%, transparent);\n flex: none;\n}\n.dsh-ac__heading { flex: 1; min-width: 0; }\n.dsh-ac__name {\n font-size: 12px;\n font-weight: 600;\n line-height: 16px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.dsh-ac__sub {\n margin-top: 1px;\n font-size: 10px;\n line-height: 14px;\n color: var(--dsw-alias-label-caption, #81858c);\n font-variant-numeric: tabular-nums;\n}\n.dsh-ac__icon {\n display: grid;\n place-items: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n cursor: pointer;\n flex: none;\n}\n.dsh-ac__icon:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.08)); color: var(--dsw-alias-label-primary, #f9fafb); }\n.dsh-ac__icon:focus-visible, .dsh-ac__btn:focus-visible, .dsh-ac__card:focus-visible {\n outline: 2px solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary, #7aaaff));\n outline-offset: 1px;\n}\n.dsh-ac__body {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.dsh-ac__section { display: flex; flex-direction: column; gap: 6px; }\n.dsh-ac__section-head {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 2px 1px;\n font-size: 11px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n.dsh-ac__count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 16px;\n height: 16px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-3, #353638);\n color: var(--dsw-alias-label-caption, #81858c);\n font-size: 10px;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n}\n.dsh-ac__list { display: flex; flex-direction: column; gap: 6px; }\n.dsh-ac__card {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n padding: 10px 11px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06));\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2, #2c2c2e);\n color: inherit;\n cursor: pointer;\n transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;\n}\n.dsh-ac__card:hover {\n border-color: var(--dsw-alias-border-l3, rgba(255,255,255,.16));\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2, #2c2c2e) 82%, var(--dsw-alias-bg-layer-3, #353638));\n transform: translateY(-1px);\n}\n.dsh-ac__card-top { display: flex; align-items: center; gap: 5px; min-width: 0; }\n.dsh-ac__badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 7px;\n border-radius: 999px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: .01em;\n flex: none;\n}\n.dsh-ac__badge.is-app {\n color: var(--dsw-alias-state-business-primary, #7aaaff);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #7aaaff) 15%, transparent);\n}\n.dsh-ac__badge.is-component {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary, #22c55e) 15%, transparent);\n}\n.dsh-ac__badge.is-template {\n /* 模板用「蓝 × 红」两个既有 token 混出紫色,不引入新的硬编码色值。 */\n color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #7aaaff) 55%, var(--dsw-alias-state-error-secondary, #f25a5a));\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #7aaaff) 12%, var(--dsw-alias-state-error-secondary, #f25a5a) 10%, transparent);\n}\n.dsh-ac__status {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n font-size: 10px;\n color: var(--dsw-alias-label-caption, #81858c);\n flex: none;\n}\n.dsh-ac__status::before {\n content: \"\";\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: currentColor;\n}\n.dsh-ac__status.is-published { color: var(--dsw-alias-state-success-primary, #22c55e); }\n.dsh-ac__status.is-draft { color: var(--dsw-alias-state-warn-primary, #f59e0b); }\n.dsh-ac__card-title {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n line-height: 17px;\n color: var(--dsw-alias-label-primary, #f9fafb);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-ac__card-desc {\n margin: 0;\n font-size: 10px;\n line-height: 14px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.dsh-ac__meta {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n font-size: 10px;\n color: var(--dsw-alias-label-caption, #81858c);\n font-variant-numeric: tabular-nums;\n}\n.dsh-ac__meta .dot { width: 2px; height: 2px; border-radius: 50%; background: currentColor; opacity: .7; }\n.dsh-ac__actions {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 2px;\n}\n.dsh-ac__btn {\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 24px;\n padding: 0 9px;\n border: 1px solid var(--dsw-alias-border-l3, rgba(255,255,255,.16));\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f9fafb);\n font-size: 10px;\n font-weight: 500;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n.dsh-ac__btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.08)); }\n.dsh-ac__btn.is-primary {\n border-color: var(--dsw-alias-state-business-primary, #7aaaff);\n background: var(--dsw-alias-state-business-primary, #7aaaff);\n color: var(--dsw-alias-label-primary-foreground, #0f1115);\n}\n.dsh-ac__btn.is-primary:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #7aaaff) 86%, white); }\n.dsh-ac__btn:disabled { opacity: .45; cursor: not-allowed; }\n.dsh-ac__state {\n padding: 22px 12px;\n text-align: center;\n font-size: 11px;\n color: var(--dsw-alias-label-caption, #81858c);\n}\n.dsh-ac__error {\n padding: 8px 10px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary, #f25a5a) 35%, transparent);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #f25a5a) 9%, transparent);\n color: var(--dsw-alias-state-error-primary, #f25a5a);\n font-size: 10px;\n line-height: 15px;\n}\n.dsh-ac__toast {\n position: absolute;\n left: 10px;\n right: 10px;\n bottom: 10px;\n z-index: 5;\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12));\n border-radius: 7px;\n background: var(--dsw-alias-bg-layer-3, #353638);\n color: var(--dsw-alias-label-primary, #f9fafb);\n font-size: 10px;\n text-align: center;\n box-shadow: 0 8px 24px rgba(0,0,0,.28);\n pointer-events: none;\n}\n.dsh-ac-viewer { display: flex; flex-direction: column; height: 100%; min-height: 0; }\n.dsh-ac-viewer__head {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n padding: 8px 10px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06));\n}\n.dsh-ac-viewer__back {\n border: none;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n cursor: pointer;\n font-size: 11px;\n padding: 2px 4px;\n border-radius: 5px;\n}\n.dsh-ac-viewer__back:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.08)); color: var(--dsw-alias-label-primary, #f9fafb); }\n.dsh-ac-viewer__title {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-ac__dialog-mask {\n position: absolute;\n inset: 0;\n z-index: 30;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n background: color-mix(in srgb, var(--dsw-alias-bg-base, #151517) 55%, transparent);\n backdrop-filter: blur(3px);\n}\n.dsh-ac__dialog {\n width: min(420px, 100%);\n max-height: 100%;\n overflow: auto;\n border: 1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12));\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2, #2c2c2e);\n box-shadow: var(--dsw-elevation-prominent, 0 18px 48px rgba(0,0,0,.4));\n}\n.dsh-ac__dialog-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06));\n font-size: 12px;\n font-weight: 600;\n}\n.dsh-ac__dialog-body { display: flex; flex-direction: column; gap: 10px; padding: 12px; }\n.dsh-ac__export-option {\n display: flex;\n flex-direction: column;\n gap: 5px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06));\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1, #232324);\n}\n.dsh-ac__export-title { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-primary, #f9fafb); }\n.dsh-ac__export-desc { margin: 0; font-size: 10px; line-height: 15px; color: var(--dsw-alias-label-secondary, #cfd3d6); }\n.dsh-ac__export-notice {\n padding: 6px 9px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary, #22c55e) 12%, transparent);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n font-size: 10px;\n}\n`\n\n/** Cockpit catalogue plus viewer. */\nexport function CockpitTab({ tab, scope, insertArtifactReference }: CockpitTabProps) {\n const meta = readMeta(tab.meta)\n const [apps, setApps] = useState<AppRow[]>([])\n const [templates, setTemplates] = useState<TemplateRow[]>([])\n const [components, setComponents] = useState<TemplateRow[]>([])\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [templateError, setTemplateError] = useState<string | null>(null)\n const [componentError, setComponentError] = useState<string | null>(null)\n const [notice, setNotice] = useState('')\n const [selected, setSelected] = useState<SelectedCockpit | null>(\n meta.cockpitId === undefined\n ? null\n : { kind: 'app', viewerId: meta.cockpitId, name: meta.cockpitName ?? '驾驶舱' },\n )\n const [reloadToken, setReloadToken] = useState(0)\n\n const load = useCallback(async () => {\n setLoading(true)\n setError(null)\n setTemplateError(null)\n setComponentError(null)\n const fetchList = (url: string) => fetch(url, { headers: { accept: 'application/json' } })\n .then(async (response) => {\n const payload = await response.json() as { items?: TemplateRow[]; error?: string }\n if (!response.ok) throw new Error(payload.error ?? 'HTTP ' + String(response.status))\n return readItems<TemplateRow>(payload)\n })\n const [appsResult, templatesResult, componentsResult] = await Promise.allSettled([\n fetch('/dsh-artifact-app/apps?pageSize=50', { headers: { accept: 'application/json' } })\n .then(async (response) => {\n const payload = await response.json() as { items?: AppRow[]; error?: string }\n if (!response.ok) throw new Error(payload.error ?? 'HTTP ' + String(response.status))\n return payload.items ?? []\n }),\n fetchList('/dsh-artifact-app/templates'),\n fetchList('/dsh-artifact-app/templates?appType=COMPONENT'),\n ])\n if (appsResult.status === 'fulfilled') setApps(appsResult.value)\n else setError(appsResult.reason instanceof Error ? appsResult.reason.message : String(appsResult.reason))\n if (templatesResult.status === 'fulfilled') setTemplates(templatesResult.value)\n else setTemplateError(templatesResult.reason instanceof Error ? templatesResult.reason.message : String(templatesResult.reason))\n if (componentsResult.status === 'fulfilled') setComponents(componentsResult.value)\n else setComponentError(componentsResult.reason instanceof Error ? componentsResult.reason.message : String(componentsResult.reason))\n setLoading(false)\n }, [])\n\n useEffect(() => { void load() }, [load])\n\n useEffect(() => {\n if (meta.cockpitId === undefined) return\n setSelected({ kind: 'app', viewerId: meta.cockpitId, name: meta.cockpitName ?? '驾驶舱' })\n setReloadToken((value) => value + 1)\n }, [meta.cockpitId, meta.cockpitName, meta.nonce])\n\n useEffect(() => {\n if (notice === '') return undefined\n const timer = window.setTimeout(() => setNotice(''), 2400)\n return () => window.clearTimeout(timer)\n }, [notice])\n\n const catalogs = useMemo(\n () => [...new Set([\n ...apps.map((app) => app.catalog),\n ...templates.map((template) => template.catalog),\n ...components.map((component) => component.catalog),\n ].filter((value): value is string => typeof value === 'string' && value !== ''))],\n [apps, templates, components],\n )\n\n const openApp = (app: AppRow): void => setSelected({ kind: 'app', viewerId: app.id, name: app.name })\n const openTemplate = (template: TemplateRow): void => setSelected({\n kind: 'template',\n viewerId: 'template/' + template.id,\n name: template.name,\n })\n const openComponent = (component: TemplateRow): void => setSelected({\n kind: 'component',\n viewerId: 'template/' + component.id,\n name: component.name,\n })\n\n const addToConversation = (item: CockpitReferenceInput): void => {\n if (insertArtifactReference === undefined) {\n setNotice('当前会话不可用,无法插入引用')\n return\n }\n try {\n insertArtifactReference(item, scope?.sessionId)\n setNotice('已添加到对话')\n } catch (cause) {\n setNotice('插入引用失败:' + (cause instanceof Error ? cause.message : String(cause)))\n }\n }\n\n /**\n * 导出对话框:常规(zip,素材外链)与全量(单文件,内联 assets)。\n * 两者都走后端 `GET /api/artifact-apps/:id/export?mode=…`,经插件网关带调用者 token 转发。\n */\n const [exportOpen, setExportOpen] = useState(false)\n const [exporting, setExporting] = useState<'external' | 'inline' | null>(null)\n const [exportNotice, setExportNotice] = useState('')\n\n const fetchToken = async (): Promise<string> => {\n const response = await fetch('/dsh-artifact-app/token', { headers: { accept: 'application/json' }, cache: 'no-store' })\n const payload = await response.json() as { token?: string; error?: string }\n if (!response.ok || typeof payload.token !== 'string' || payload.token.trim() === '') {\n throw new Error(payload.error ?? '读取 DataOps 登录凭证失败')\n }\n return payload.token\n }\n\n const downloadExport = async (mode: 'external' | 'inline'): Promise<void> => {\n if (selected === null || selected.kind !== 'app' || exporting !== null) return\n setExporting(mode)\n setExportNotice('')\n try {\n const token = await fetchToken()\n const url = '/dsh-artifact-app/gateway/api/artifact-apps/' + encodeURIComponent(selected.viewerId) + '/export?mode=' + mode\n const response = await fetch(url, { headers: { 'x-dataops-token': token } })\n if (!response.ok) throw new Error('导出失败(HTTP ' + String(response.status) + ')')\n const blob = await response.blob()\n const objectUrl = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = objectUrl\n link.download = (selected.name || 'cockpit') + (mode === 'external' ? '.zip' : '.html')\n document.body.appendChild(link)\n link.click()\n link.remove()\n URL.revokeObjectURL(objectUrl)\n setExportNotice('已开始下载')\n } catch (cause) {\n setExportNotice('导出失败:' + (cause instanceof Error ? cause.message : String(cause)))\n } finally {\n setExporting(null)\n }\n }\n\n const copyToken = async (): Promise<void> => {\n try {\n const token = await fetchToken()\n try {\n await navigator.clipboard.writeText(token)\n } catch {\n const area = document.createElement('textarea')\n area.value = token\n document.body.appendChild(area)\n area.select()\n document.execCommand('copy')\n area.remove()\n }\n setExportNotice('token 已复制')\n } catch (cause) {\n setExportNotice('复制失败:' + (cause instanceof Error ? cause.message : String(cause)))\n }\n }\n\n if (selected !== null) {\n return (\n <div className=\"dsh-ac\">\n <style>{CSS}</style>\n <div className=\"dsh-ac-viewer\">\n <div className=\"dsh-ac-viewer__head\">\n <button type=\"button\" className=\"dsh-ac-viewer__back\" onClick={() => setSelected(null)}>← 列表</button>\n <span className={`dsh-ac__badge ${selected.kind === 'component' ? 'is-component' : selected.kind === 'template' ? 'is-template' : 'is-app'}`}>\n {selected.kind === 'component' ? '组件' : selected.kind === 'template' ? '模板' : '应用'}\n </span>\n <div className=\"dsh-ac-viewer__title\">{selected.name}</div>\n <button\n type=\"button\"\n className=\"dsh-ac__icon\"\n title=\"刷新\"\n aria-label=\"刷新\"\n onClick={() => setReloadToken((value) => value + 1)}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 12a9 9 0 1 1-3-6.7\" />\n <path d=\"M21 4v5h-5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"dsh-ac__icon\"\n title=\"全屏查看\"\n aria-label=\"全屏查看\"\n onClick={() => {\n const host = document.querySelector('[data-artifact-fullscreen-host]') as HTMLElement | null\n const target = host ?? document.querySelector('iframe[data-artifact-viewer]')?.parentElement ?? null\n if (target === null) return\n if (document.fullscreenElement !== null) void document.exitFullscreen()\n else void target.requestFullscreen?.().catch(() => undefined)\n }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 9V4h5\" /><path d=\"M20 9V4h-5\" /><path d=\"M4 15v5h5\" /><path d=\"M20 15v5h-5\" />\n </svg>\n </button>\n {selected.kind === 'app' ? (\n <button\n type=\"button\"\n className=\"dsh-ac__icon\"\n title=\"导出\"\n aria-label=\"导出\"\n onClick={() => { setExportNotice(''); setExportOpen(true) }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M12 3v12\" />\n <path d=\"M7 10l5 5 5-5\" />\n <path d=\"M4 21h16\" />\n </svg>\n </button>\n ) : null}\n </div>\n <div data-artifact-fullscreen-host style={{ flex: 1, minHeight: 0 }}>\n <CockpitViewer artifactId={selected.viewerId} reloadToken={reloadToken} />\n </div>\n </div>\n {exportOpen ? (\n <div className=\"dsh-ac__dialog-mask\" data-export-dialog role=\"dialog\" aria-modal=\"true\" aria-label=\"导出驾驶舱\">\n <div className=\"dsh-ac__dialog\">\n <div className=\"dsh-ac__dialog-head\">\n <span>导出驾驶舱</span>\n <button type=\"button\" className=\"dsh-ac__icon\" aria-label=\"关闭\" onClick={() => setExportOpen(false)}>×</button>\n </div>\n <div className=\"dsh-ac__dialog-body\">\n <section className=\"dsh-ac__export-option\">\n <div className=\"dsh-ac__export-title\">常规导出</div>\n <p className=\"dsh-ac__export-desc\">zip 包:HTML 与 assets 分离,解压后需静态托管;HTML 通过相对路径引用素材。</p>\n <button\n type=\"button\"\n className=\"dsh-ac__btn is-primary\"\n data-export-mode=\"external\"\n disabled={exporting !== null}\n onClick={() => void downloadExport('external')}\n >{exporting === 'external' ? '导出中…' : '下载 zip'}</button>\n </section>\n <section className=\"dsh-ac__export-option\">\n <div className=\"dsh-ac__export-title\">全量导出(内联 assets)</div>\n <p className=\"dsh-ac__export-desc\">单文件 HTML:双击即可打开;取数时需要粘贴 token。</p>\n <button\n type=\"button\"\n className=\"dsh-ac__btn is-primary\"\n data-export-mode=\"inline\"\n disabled={exporting !== null}\n onClick={() => void downloadExport('inline')}\n >{exporting === 'inline' ? '导出中…' : '下载 HTML'}</button>\n </section>\n <section className=\"dsh-ac__export-option\">\n <div className=\"dsh-ac__export-title\">当前 token</div>\n <p className=\"dsh-ac__export-desc\">全量导出取数时使用。当前 JWT 约到 2027-09,实际以登录态为准。</p>\n <button type=\"button\" className=\"dsh-ac__btn\" data-copy-token onClick={() => void copyToken()}>复制当前 token</button>\n </section>\n {exportNotice !== '' ? <div className=\"dsh-ac__export-notice\" role=\"status\">{exportNotice}</div> : null}\n </div>\n </div>\n </div>\n ) : null}\n {notice !== '' ? <div className=\"dsh-ac__toast\" role=\"status\">{notice}</div> : null}\n </div>\n )\n }\n\n return (\n <div className=\"dsh-ac\">\n <style>{CSS}</style>\n <div className=\"dsh-ac__head\">\n <span className=\"dsh-ac__glyph\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M4 18a8 8 0 1 1 16 0\" />\n <path d=\"M12 18l4.2-5\" />\n <circle cx=\"12\" cy=\"18\" r=\"1.4\" />\n </svg>\n </span>\n <div className=\"dsh-ac__heading\">\n <div className=\"dsh-ac__name\">驾驶舱</div>\n <div className=\"dsh-ac__sub\">\n {apps.length} 个应用 · {templates.length} 个模板 · {components.length} 个组件\n {catalogs.length > 0 ? ' · ' + catalogs.join(' / ') : ''}\n </div>\n </div>\n <button type=\"button\" className=\"dsh-ac__icon\" title=\"刷新\" aria-label=\"刷新\" onClick={() => void load()}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 12a9 9 0 1 1-3-6.7\" />\n <path d=\"M21 4v5h-5\" />\n </svg>\n </button>\n </div>\n\n <div className=\"dsh-ac__body\">\n {loading ? <div className=\"dsh-ac__state\">正在加载驾驶舱资产…</div> : null}\n {error !== null ? <div className=\"dsh-ac__error\" role=\"alert\">{error}</div> : null}\n\n {!loading && error === null ? (\n <section className=\"dsh-ac__section\">\n <div className=\"dsh-ac__section-head\">\n <span>应用</span>\n <span className=\"dsh-ac__count\">{apps.length}</span>\n </div>\n {apps.length === 0 ? <div className=\"dsh-ac__state\">暂无驾驶舱应用</div> : (\n <div className=\"dsh-ac__list\">\n {apps.map((app) => (\n <article\n key={app.id}\n className=\"dsh-ac__card\"\n data-cockpit-id={app.id}\n tabIndex={0}\n onClick={() => openApp(app)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); openApp(app) }\n }}\n >\n <div className=\"dsh-ac__card-top\">\n <span className=\"dsh-ac__badge is-app\">应用</span>\n <span className={`dsh-ac__status ${app.status === 'PUBLISHED' ? 'is-published' : 'is-draft'}`}>\n {app.status === 'PUBLISHED' ? '已发布' : '草稿'}\n </span>\n </div>\n <h3 className=\"dsh-ac__card-title\" title={app.name}>{app.name}</h3>\n {app.description ? <p className=\"dsh-ac__card-desc\">{app.description}</p> : null}\n <div className=\"dsh-ac__meta\">\n <span>{app.catalog ?? '未分类'}</span>\n <span className=\"dot\" />\n <span>{versionLabel(app)}</span>\n </div>\n <div className=\"dsh-ac__actions\" onClick={(event) => event.stopPropagation()}>\n <button type=\"button\" className=\"dsh-ac__btn is-primary\" data-action=\"view\" onClick={() => openApp(app)}>查看</button>\n <button\n type=\"button\"\n className=\"dsh-ac__btn\"\n data-action=\"insert\"\n onClick={() => addToConversation({ kind: 'app', id: app.id, name: app.name, catalog: app.catalog })}\n >添加到对话</button>\n </div>\n </article>\n ))}\n </div>\n )}\n </section>\n ) : null}\n\n {!loading ? (\n <section className=\"dsh-ac__section\">\n <div className=\"dsh-ac__section-head\">\n <span>模板</span>\n <span className=\"dsh-ac__count\">{templates.length}</span>\n </div>\n {templateError !== null ? <div className=\"dsh-ac__error\" role=\"alert\">模板加载失败:{templateError}</div> : null}\n {templateError === null && templates.length === 0 ? <div className=\"dsh-ac__state\">暂无驾驶舱模板</div> : null}\n {templates.length > 0 ? (\n <div className=\"dsh-ac__list\">\n {templates.map((template) => {\n const slots = template.contract?.slots ?? []\n return (\n <article\n key={template.id}\n className=\"dsh-ac__card\"\n data-template-id={template.id}\n tabIndex={0}\n onClick={() => openTemplate(template)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); openTemplate(template) }\n }}\n >\n <div className=\"dsh-ac__card-top\">\n <span className=\"dsh-ac__badge is-template\">模板</span>\n <span className=\"dsh-ac__status\">{versionLabel(template)}</span>\n </div>\n <h3 className=\"dsh-ac__card-title\" title={template.name}>{template.name}</h3>\n <p className=\"dsh-ac__card-desc\">\n {template.description || template.contract?.question || '驾驶舱模板,可选择后填写数据槽位。'}\n </p>\n <div className=\"dsh-ac__meta\">\n <span>{template.catalog ?? '未分类'}</span>\n {slots.length > 0 ? <span className=\"dot\" /> : null}\n {slots.length > 0 ? <span>{slots.length} 个槽位</span> : null}\n </div>\n <div className=\"dsh-ac__actions\" onClick={(event) => event.stopPropagation()}>\n <button type=\"button\" className=\"dsh-ac__btn is-primary\" data-action=\"view\" onClick={() => openTemplate(template)}>查看</button>\n <button\n type=\"button\"\n className=\"dsh-ac__btn\"\n data-action=\"insert\"\n onClick={() => addToConversation({ kind: 'template', id: template.id, name: template.name, catalog: template.catalog })}\n >添加到对话</button>\n </div>\n </article>\n )\n })}\n </div>\n ) : null}\n </section>\n ) : null}\n\n {!loading ? (\n <section className=\"dsh-ac__section\">\n <div className=\"dsh-ac__section-head\">\n <span>组件</span>\n <span className=\"dsh-ac__count\">{components.length}</span>\n </div>\n {componentError !== null ? <div className=\"dsh-ac__error\" role=\"alert\">组件加载失败:{componentError}</div> : null}\n {componentError === null && components.length === 0 ? <div className=\"dsh-ac__state\">暂无组件参考</div> : null}\n {components.length > 0 ? (\n <div className=\"dsh-ac__list\">\n {components.map((component) => {\n const tags = Array.isArray((component.contract as { tags?: unknown } | null)?.tags)\n ? ((component.contract as { tags: string[] }).tags)\n : []\n return (\n <article\n key={component.id}\n className=\"dsh-ac__card\"\n data-component-id={component.id}\n tabIndex={0}\n onClick={() => openComponent(component)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); openComponent(component) }\n }}\n >\n <div className=\"dsh-ac__card-top\">\n <span className=\"dsh-ac__badge is-component\">组件</span>\n <span className=\"dsh-ac__status\">{versionLabel(component)}</span>\n </div>\n <h3 className=\"dsh-ac__card-title\" title={component.name}>{component.name}</h3>\n <p className=\"dsh-ac__card-desc\">{component.description || '可预览、供 AI 参考的组件实现。'}</p>\n <div className=\"dsh-ac__meta\">\n <span>{component.catalog ?? '未分类'}</span>\n {tags.length > 0 ? <span className=\"dot\" /> : null}\n {tags.slice(0, 4).map((tag) => <span key={tag}>{tag}</span>)}\n </div>\n <div className=\"dsh-ac__actions\" onClick={(event) => event.stopPropagation()}>\n <button type=\"button\" className=\"dsh-ac__btn is-primary\" data-action=\"view\" onClick={() => openComponent(component)}>查看</button>\n </div>\n </article>\n )\n })}\n </div>\n ) : null}\n </section>\n ) : null}\n </div>\n {notice !== '' ? <div className=\"dsh-ac__toast\" role=\"status\">{notice}</div> : null}\n </div>\n )\n}","/**\n * HTML file viewer for cockpit files.\n *\n * The descriptor only matches `/artifact-apps/**` HTML carrying the\n * `artifact-app` meta marker. Opening such a file creates or updates one draft\n * through the host and renders it exactly like a published cockpit, so the file\n * on disk is the draft.\n *\n * @module client/ArtifactFileViewer\n */\n\nimport { useEffect, useState } from 'react'\nimport { CockpitViewer } from './CockpitViewer.tsx'\n\n/** Props the Better Sidebar file viewer passes. */\nexport interface ArtifactFileViewerProps {\n path: string\n title?: string\n content?: string\n largeFile?: { size: number }\n}\n\n/** Marker the file must carry for this viewer to claim it. */\nexport const ARTIFACT_FILE_MARKER = /<meta[^>]+name=[\"']artifact-app[\"'][^>]*>/iu\n\n/**\n * Whether a file is a cockpit document.\n * @param path - workspace path.\n * @param head - the first bytes the viewer sniffed.\n */\nexport const isArtifactFile = (path: string, head: Uint8Array | string): boolean => {\n if (!/\\/artifact-apps\\/[^/]+\\.html?$/iu.test(path)) return false\n const text = typeof head === 'string' ? head : new TextDecoder().decode(head)\n return ARTIFACT_FILE_MARKER.test(text)\n}\n\n/** Create/update the preview draft, then render it. */\nexport function ArtifactFileViewer({ path, content, largeFile }: ArtifactFileViewerProps) {\n const [artifactId, setArtifactId] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n if (content === undefined) return undefined\n let alive = true\n setBusy(true)\n setError(null)\n void (async () => {\n try {\n const response = await fetch('/dsh-artifact-app/preview', {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({ path, html: content }),\n })\n const payload = await response.json() as { artifactId?: string; error?: string }\n if (!response.ok || typeof payload.artifactId !== 'string') {\n throw new Error(payload.error ?? 'HTTP ' + String(response.status))\n }\n if (alive) setArtifactId(payload.artifactId)\n } catch (cause) {\n if (alive) setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n if (alive) setBusy(false)\n }\n })()\n return () => { alive = false }\n }, [path, content, attempt])\n\n if (largeFile !== undefined) {\n return <div style={{ padding: 18, fontSize: 12, color: 'var(--dsw-alias-label-secondary, #6b7280)' }}>文件过大({Math.round(largeFile.size / 1024)} KB),请缩小后再预览驾驶舱。</div>\n }\n if (error !== null) {\n return (\n <div role=\"alert\" style={{ padding: 12, margin: 12, borderRadius: 8, background: '#fef2f2', color: '#b91c1c', fontSize: 12, display: 'grid', gap: 8 }}>\n <span style={{ whiteSpace: 'pre-wrap' }}>{error}</span>\n <button type=\"button\" onClick={() => setAttempt((value) => value + 1)} style={{ justifySelf: 'start', border: '1px solid #fca5a5', background: 'transparent', color: '#b91c1c', borderRadius: 6, padding: '3px 10px', cursor: 'pointer', fontSize: 11 }}>重试</button>\n </div>\n )\n }\n if (busy || artifactId === null) {\n return <div style={{ padding: 18, fontSize: 12, color: 'var(--dsw-alias-label-secondary, #6b7280)' }}>正在生成驾驶舱预览…</div>\n }\n return <CockpitViewer artifactId={artifactId} />\n}","/**\n * Client half of the artifact-app plugin.\n *\n * Registers the `dataops-artifact-apps` right-sidebar tab, the cockpit HTML file\n * viewer, and the SSE bridge that lets `cockpit_open` drive the sidebar. All three\n * are optional when a profile has no Better Sidebar: the host routes and tools keep\n * working without a browser surface.\n *\n * @module @sparkelf/dsh-artifact-app/client\n */\n\nimport { useEffect } from 'react'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport { CockpitTab, type CockpitReferenceInput } from './CockpitTab.tsx'\nimport { ArtifactFileViewer, isArtifactFile } from './ArtifactFileViewer.tsx'\n\n/** Client services this plugin reads; `slots` owns the always-mounted bridge seat. */\nexport const inject = ['slots']\n\n/** Minimal composer surface used by the \"添加到对话\" hand-off. */\ninterface SessionInputLike {\n insertReference?: (ref: unknown, span: { start: number; end: number; draftRev: number }) => boolean\n state?: { getSnapshot?: () => { draft: string; draftRev: number } }\n}\n\n/** Services the reference hand-off resolves lazily, mirroring the managed resource tab. */\ninterface ConversationLike {\n input?: { for?: (scope: unknown) => SessionInputLike | undefined }\n}\n\ninterface SessionsLike {\n scope?: (sessionId: string) => unknown | undefined\n}\n\ninterface InputTriggersLike {\n registerSource?: (source: unknown) => () => void\n}\n\n/** Reference source id shared by the chip codec and the inserted reference. */\nconst REFERENCE_SOURCE = 'dataops-artifact-app'\n\n/** Tab id shared by registration, the bridge and the tools. */\nexport const ARTIFACT_TAB_TYPE = 'dataops-artifact-apps'\n/** File viewer id. */\nexport const ARTIFACT_VIEWER_ID = 'artifact-app:cockpit'\n\n/** The part of Better Sidebar this plugin uses. */\ninterface BetterSidebarLike {\n registerTab?(descriptor: Record<string, unknown>): () => void\n registerFileViewer?(descriptor: Record<string, unknown>): () => void\n openTab?(seed: { type: string; id?: string; title?: string; meta?: unknown }, scope?: { sessionId?: string }): void\n updateTab?(tabId: string, patch: { title?: string; meta?: unknown }, sessionId?: string): void\n}\n\ninterface OpenEvent {\n type: 'open_cockpit'\n cockpitId: string\n cockpitName: string | null\n mode: string\n nonce: string\n requestId: string\n}\n\ninterface UpdatedEvent {\n type: 'cockpit_updated'\n cockpitId: string\n nonce: string\n}\n\n/** Invisible bridge: SSE navigation events become Better Sidebar calls. */\nfunction OpenCockpitBridge({ service, scope }: { service: BetterSidebarLike; scope?: { sessionId?: string } }) {\n useEffect(() => {\n if (typeof EventSource !== 'function') return undefined\n const source = new EventSource('/dsh-artifact-app/open/events')\n const open = (event: MessageEvent): void => {\n let parsed: OpenEvent\n try { parsed = JSON.parse(String(event.data)) as OpenEvent } catch { return }\n if (parsed.type !== 'open_cockpit' || typeof parsed.cockpitId !== 'string') return\n const meta = { cockpitId: parsed.cockpitId, cockpitName: parsed.cockpitName, nonce: parsed.nonce, mode: parsed.mode }\n service.openTab?.({ type: ARTIFACT_TAB_TYPE, id: ARTIFACT_TAB_TYPE, title: '驾驶舱', meta }, scope)\n service.updateTab?.(ARTIFACT_TAB_TYPE, { title: '驾驶舱', meta }, scope?.sessionId)\n }\n const updated = (event: MessageEvent): void => {\n let parsed: UpdatedEvent\n try { parsed = JSON.parse(String(event.data)) as UpdatedEvent } catch { return }\n if (parsed.type !== 'cockpit_updated' || typeof parsed.cockpitId !== 'string') return\n service.updateTab?.(ARTIFACT_TAB_TYPE, {\n title: '驾驶舱',\n meta: { cockpitId: parsed.cockpitId, nonce: parsed.nonce },\n }, scope?.sessionId)\n }\n source.addEventListener('dataops-open-cockpit', open as EventListener)\n source.addEventListener('dataops-cockpit-updated', updated as EventListener)\n return () => {\n source.removeEventListener('dataops-open-cockpit', open as EventListener)\n source.removeEventListener('dataops-cockpit-updated', updated as EventListener)\n source.close()\n }\n }, [service, scope])\n return null\n}\n\n/**\n * Register the tab, the file viewer and the bridge.\n * @param ctx - DSH client context.\n */\nexport function apply(ctx: ClientContext): void {\n try {\n ctx.effect(() => {\n let disposeTab: (() => void) | undefined\n let disposeViewer: (() => void) | undefined\n let disposeBridge: (() => void) | undefined\n let betterSidebarService: BetterSidebarLike | undefined\n const inject = (ctx as unknown as {\n inject?: (names: readonly string[], callback: (injected: {\n betterSidebar?: BetterSidebarLike\n conversation?: ConversationLike\n sessions?: SessionsLike\n inputTriggers?: InputTriggersLike\n }) => void) => unknown\n }).inject\n inject?.(['betterSidebar', 'conversation', 'sessions', 'inputTriggers'], (injected) => {\n const service = injected.betterSidebar\n if (service?.registerTab === undefined) return\n betterSidebarService = service\n\n // The composer resolves this source's codec on submit and asks it for the model text.\n // Same protocol as the DataOps resource tab: chip in the draft, readable block on send.\n if (injected.inputTriggers?.registerSource !== undefined) {\n const source = {\n trigger: '@',\n name: REFERENCE_SOURCE,\n showGroupTitle: false,\n candidates: async () => [],\n codec: {\n clipboardText: (ref: string) => {\n try { return '@' + String((JSON.parse(ref) as { name?: unknown }).name ?? '驾驶舱') } catch { return '@驾驶舱' }\n },\n serialize: async (ref: string) => {\n try {\n const parsed = JSON.parse(ref) as { assetKind?: unknown; id?: unknown; name?: unknown; catalog?: unknown }\n return '引用驾驶舱:\\n\\n```cockpit-ref\\n' + JSON.stringify({\n kind: REFERENCE_SOURCE,\n assetKind: parsed.assetKind === 'template' ? 'template' : 'app',\n id: parsed.id,\n name: parsed.name,\n catalog: parsed.catalog ?? null,\n }, null, 2) + '\\n```\\n'\n } catch { return ref }\n },\n },\n }\n try { injected.inputTriggers.registerSource(source) } catch { /* HMR / duplicate source: keep the existing owner */ }\n }\n\n const insertArtifactReference = (item: CockpitReferenceInput, sessionId?: string): void => {\n if (sessionId === undefined) throw new Error('no active session')\n const input = injected.conversation?.input?.for?.(injected.sessions?.scope?.(sessionId))\n if (input?.insertReference === undefined || input.state?.getSnapshot === undefined) throw new Error('composer is unavailable')\n const state = input.state.getSnapshot()\n const span = { start: state.draft.length, end: state.draft.length, draftRev: state.draftRev }\n const inserted = input.insertReference({\n source: REFERENCE_SOURCE,\n ref: JSON.stringify({\n kind: REFERENCE_SOURCE,\n assetKind: item.kind,\n id: item.id,\n name: item.name,\n catalog: item.catalog ?? null,\n }),\n label: item.name,\n appearance: 'resource',\n clipboardText: '@' + item.name,\n } as never, span)\n if (!inserted) throw new Error('输入框正忙,请稍后再试')\n }\n\n disposeTab = service.registerTab({\n id: ARTIFACT_TAB_TYPE,\n title: '驾驶舱',\n // A component, not a fixed element: the tab strip asks for 14 and the start-guide tile\n // asks for 22, and the tile must not draw a larger glyph than its neighbours.\n icon: ({ size = 14 }: { size?: number }) => (\n <svg width={size >= 22 ? 22 : size} height={size >= 22 ? 22 : size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 18a8 8 0 1 1 16 0\" />\n <path d=\"M12 18l4.2-5\" />\n <circle cx=\"12\" cy=\"18\" r=\"1.4\" />\n </svg>\n ),\n\n description: 'DataOps 驾驶舱资产、预览与发布',\n order: 62,\n single: true,\n component: ((props: Record<string, unknown>) => CockpitTab({\n ...(props as { tab: { meta?: unknown }; scope?: { sessionId?: string } }),\n insertArtifactReference,\n })) as never,\n })\n disposeViewer = service.registerFileViewer?.({\n id: ARTIFACT_VIEWER_ID,\n title: '驾驶舱预览',\n // Detection is the only claim. Better Sidebar 0.29 still falls through\n // to `exts.includes(ext)` after a failed `detect`, so a non-empty list\n // here would capture every `.html` in the workspace and run it through\n // the cockpit template contract — plain pages then fail with\n // TEMPLATE_CONTRACT_MISSING instead of opening in the normal preview.\n exts: [],\n priority: 60,\n fetchStrategy: 'fsRead',\n detect: (path: string, head: Uint8Array) => isArtifactFile(path, head),\n component: ((props: Record<string, unknown>) => ArtifactFileViewer(props as never)) as never,\n })\n })\n const slots = (ctx as unknown as {\n slots: {\n inject(name: string, factory: () => () => void): () => void\n register(options: Record<string, unknown>, component: (props: never) => unknown): () => void\n }\n }).slots\n disposeBridge = slots.inject('sidebar.footer.action', () => slots.register({\n name: 'sidebar.footer.action',\n id: 'artifact-app-bridge',\n order: 96,\n }, ((props: { scope?: { sessionId?: string } }) => (betterSidebarService === undefined\n ? null\n : OpenCockpitBridge({ service: betterSidebarService, scope: props?.scope }))) as never))\n return () => {\n disposeBridge?.()\n disposeBridge = undefined\n disposeViewer?.()\n disposeViewer = undefined\n disposeTab?.()\n disposeTab = undefined\n }\n }, 'artifact-app: sidebar tab, file viewer and open bridge')\n } catch (error) {\n console.error('[artifact-app] apply failed', error)\n throw error\n }\n}"],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -418,8 +418,24 @@ declare class ArtifactApi {
418
418
  }>;
419
419
  };
420
420
  }>;
421
- /** List published cockpit templates. */
422
- listTemplates(): Promise<ArtifactTemplateRow[]>;
421
+ /** List published templates or components (`appType=COCKPIT|COMPONENT`). */
422
+ listTemplates(appType?: 'COCKPIT' | 'COMPONENT'): Promise<ArtifactTemplateRow[]>;
423
+ /**
424
+ * Search the component reference library.
425
+ * @param query - keyword matched against name/description/tags and the HTML body.
426
+ * @param tags - capability tags, e.g. `geo3d,3d-map`.
427
+ * @param limit - max hits (1..20).
428
+ */
429
+ searchComponents(query?: {
430
+ q?: string;
431
+ tags?: string[];
432
+ limit?: number;
433
+ }): Promise<Array<ArtifactTemplateRow & {
434
+ snippet?: string;
435
+ tags?: string[];
436
+ capabilities?: string[];
437
+ requires?: string[];
438
+ }>>;
423
439
  /** Read one template's metadata and contract. */
424
440
  getTemplate(id: string): Promise<ArtifactTemplateRow>;
425
441
  /** Read one template's HTML. */
@@ -530,6 +546,8 @@ declare const CONFIG_PATH = "/dsh-artifact-app/config";
530
546
  declare const TOKEN_PATH = "/dsh-artifact-app/token";
531
547
  /** Same-origin app-list proxy used by the sidebar tab. */
532
548
  declare const APPS_PATH = "/dsh-artifact-app/apps";
549
+ /** Same-origin cockpit-template list proxy used by the sidebar tab. */
550
+ declare const TEMPLATES_PATH = "/dsh-artifact-app/templates";
533
551
  /** Same-origin preview creation used by the HTML file viewer. */
534
552
  declare const PREVIEW_PATH = "/dsh-artifact-app/preview";
535
553
  /** Render route prefix; `:id` follows it. */
@@ -635,4 +653,4 @@ declare const inject: string[];
635
653
  */
636
654
  declare function apply(ctx: Context, config: Config$1): void;
637
655
  //#endregion
638
- export { APPS_PATH, ARTIFACT_TAB_TYPE, ArtifactApi, ArtifactAppRow, ArtifactTemplateRow, CONFIG_PATH, CockpitUpdatedEvent, Config, DataOpsError, GATEWAY_PATH, GrantInput, OPEN_EVENTS_PATH, OpenCockpitEvent, OpenCockpitHub, PREVIEW_PATH, PreviewInput, PublishInput, RENDER_PATH, TOKEN_PATH, TemplateContractError, TokenSource, apply, assertCockpitHtml, cockpitCsp, inject, injectCockpitRuntime, name };
656
+ export { APPS_PATH, ARTIFACT_TAB_TYPE, ArtifactApi, ArtifactAppRow, ArtifactTemplateRow, CONFIG_PATH, CockpitUpdatedEvent, Config, DataOpsError, GATEWAY_PATH, GrantInput, OPEN_EVENTS_PATH, OpenCockpitEvent, OpenCockpitHub, PREVIEW_PATH, PreviewInput, PublishInput, RENDER_PATH, TEMPLATES_PATH, TOKEN_PATH, TemplateContractError, TokenSource, apply, assertCockpitHtml, cockpitCsp, inject, injectCockpitRuntime, name };
package/lib/index.js CHANGED
@@ -63,9 +63,23 @@ var ArtifactApi = class {
63
63
  me() {
64
64
  return this.request("/api/auth/me");
65
65
  }
66
- /** List published cockpit templates. */
67
- listTemplates() {
68
- return this.request("/api/artifact-templates?appType=COCKPIT");
66
+ /** List published templates or components (`appType=COCKPIT|COMPONENT`). */
67
+ listTemplates(appType = "COCKPIT") {
68
+ return this.request("/api/artifact-templates?appType=" + appType);
69
+ }
70
+ /**
71
+ * Search the component reference library.
72
+ * @param query - keyword matched against name/description/tags and the HTML body.
73
+ * @param tags - capability tags, e.g. `geo3d,3d-map`.
74
+ * @param limit - max hits (1..20).
75
+ */
76
+ searchComponents(query = {}) {
77
+ const params = new URLSearchParams();
78
+ if (query.q !== void 0 && query.q.trim() !== "") params.set("q", query.q.trim());
79
+ if (query.tags !== void 0 && query.tags.length > 0) params.set("tags", query.tags.join(","));
80
+ if (query.limit !== void 0) params.set("limit", String(query.limit));
81
+ const suffix = params.toString();
82
+ return this.request("/api/artifact-templates/components/search" + (suffix === "" ? "" : "?" + suffix));
69
83
  }
70
84
  /** Read one template's metadata and contract. */
71
85
  getTemplate(id) {
@@ -1115,6 +1129,8 @@ const CONFIG_PATH = "/dsh-artifact-app/config";
1115
1129
  const TOKEN_PATH = "/dsh-artifact-app/token";
1116
1130
  /** Same-origin app-list proxy used by the sidebar tab. */
1117
1131
  const APPS_PATH = "/dsh-artifact-app/apps";
1132
+ /** Same-origin cockpit-template list proxy used by the sidebar tab. */
1133
+ const TEMPLATES_PATH = "/dsh-artifact-app/templates";
1118
1134
  /** Same-origin preview creation used by the HTML file viewer. */
1119
1135
  const PREVIEW_PATH = "/dsh-artifact-app/preview";
1120
1136
  /** Render route prefix; `:id` follows it. */
@@ -1442,6 +1458,21 @@ const readCockpitHtml = async (config, htmlPath) => {
1442
1458
  return html;
1443
1459
  };
1444
1460
  /** Shape one app row for a tool answer. */
1461
+ /**
1462
+ * 组件 HTML 里内联的可信 vendor 包(echarts-gl 等)不返回给模型。
1463
+ * 与后端 `stripVendorBundles` 同一约定:只认 `data-vendor` 标记,不是用户输入。
1464
+ */
1465
+ const stripVendorBundles = (html) => html.replace(/<script\b[^>]*\bdata-vendor\b[^>]*>[\s\S]*?<\/script>/giu, "<!-- vendor bundle omitted -->");
1466
+ /** 组件契约(contract 是 Json,读时做窄化)。 */
1467
+ const componentMeta = (contract) => {
1468
+ const record = typeof contract === "object" && contract !== null ? contract : {};
1469
+ const strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim() !== "") : [];
1470
+ return {
1471
+ tags: strings(record.tags),
1472
+ capabilities: strings(record.capabilities),
1473
+ requires: strings(record.requires)
1474
+ };
1475
+ };
1445
1476
  const appSummary = (app) => ({
1446
1477
  id: app.id,
1447
1478
  name: app.name,
@@ -1535,11 +1566,92 @@ function registerCockpitTools(deps) {
1535
1566
  catalog: meta.catalog ?? null,
1536
1567
  slots: meta.contract?.slots ?? [],
1537
1568
  question: meta.contract?.question ?? null,
1538
- version: body.version,
1569
+ version: meta.currentVersion ?? body.version ?? 1,
1539
1570
  html: body.html
1540
1571
  });
1541
1572
  }
1542
1573
  })), "artifact-app: template get tool");
1574
+ ctx.effect(() => ctx.tools.register(defineTool({
1575
+ name: "component_search",
1576
+ description: "优先复用模板里已有的组件实现(直接改数据/文案/样式)。\n只有当模板中没有该组件、或模板里的实现达不到需求,且你自己不确定怎么写时,才检索组件库获取参考实现。\n适用:已选定模板/正在改模板,但其中某个组件(图表、地图、交互控件)不知道怎么落地。\n不适用:还没选模板(先看模板库);或自己完全会写。",
1577
+ parameters: {
1578
+ query: {
1579
+ type: "string",
1580
+ description: "关键词:能力、图表类型或业务名,如 geo3d / 南宁 / 3D 地图。"
1581
+ },
1582
+ tags: {
1583
+ type: "string",
1584
+ description: "逗号分隔的能力标签,如 geo3d,3d-map,echarts-gl。"
1585
+ },
1586
+ limit: {
1587
+ type: "number",
1588
+ description: "最大条数,默认 10,最多 20。"
1589
+ }
1590
+ },
1591
+ output: {
1592
+ schema: open,
1593
+ render: textOutput
1594
+ },
1595
+ execute: async (args) => {
1596
+ const tags = args.tags === void 0 ? void 0 : args.tags.split(",").map((tag) => tag.trim()).filter((tag) => tag !== "");
1597
+ const items = await api.searchComponents({
1598
+ q: args.query,
1599
+ tags,
1600
+ limit: args.limit
1601
+ });
1602
+ return toolJson({
1603
+ ok: true,
1604
+ total: items.length,
1605
+ items: items.map((item) => ({
1606
+ id: item.id,
1607
+ name: item.name,
1608
+ description: item.description ?? null,
1609
+ tags: item.tags ?? [],
1610
+ capabilities: item.capabilities ?? [],
1611
+ requires: item.requires ?? [],
1612
+ version: item.currentVersion ?? 1,
1613
+ snippet: item.snippet ?? ""
1614
+ }))
1615
+ });
1616
+ }
1617
+ })), "artifact-app: component search tool");
1618
+ ctx.effect(() => ctx.tools.register(defineTool({
1619
+ name: "component_read",
1620
+ description: "读取一个组件参考实现的完整 HTML(内联 vendor 库已省略,依赖见 requires)与元数据。改造模板已有组件时,优先在模板实现上改,不要整段替换。",
1621
+ parameters: {
1622
+ id: {
1623
+ type: "string",
1624
+ required: true,
1625
+ description: "component_search 返回的组件 id,如 nanning-geo3d。"
1626
+ },
1627
+ part: {
1628
+ type: "string",
1629
+ description: "meta | html | all(默认 all)。"
1630
+ }
1631
+ },
1632
+ output: {
1633
+ schema: open,
1634
+ render: textOutput
1635
+ },
1636
+ execute: async (args) => {
1637
+ const [meta, body] = await Promise.all([api.getTemplate(args.id), api.getTemplateHtml(args.id)]);
1638
+ const contract = componentMeta(meta.contract);
1639
+ const part = args.part === "meta" || args.part === "html" ? args.part : "all";
1640
+ return toolJson({
1641
+ ok: true,
1642
+ id: meta.id,
1643
+ name: meta.name,
1644
+ description: meta.description ?? null,
1645
+ catalog: meta.catalog ?? null,
1646
+ tags: contract.tags,
1647
+ capabilities: contract.capabilities,
1648
+ requires: contract.requires,
1649
+ version: meta.currentVersion ?? body.version ?? 1,
1650
+ note: "若是改造模板已有组件,优先在模板实现上改,不要整段替换;内联 vendor 库已省略(见 requires)。",
1651
+ html: part === "meta" ? null : stripVendorBundles(body.html)
1652
+ });
1653
+ }
1654
+ })), "artifact-app: component read tool");
1543
1655
  ctx.effect(() => ctx.tools.register(defineTool({
1544
1656
  name: "cockpit_list",
1545
1657
  description: "List cockpit apps the current user can see, newest first. Use it to find an existing cockpit before publishing a new one.",
@@ -1959,7 +2071,7 @@ function apply(ctx, config) {
1959
2071
  renderBaseUrl: RENDER_PATH,
1960
2072
  gatewayBaseUrl: GATEWAY_PATH + "/api/artifact-apps",
1961
2073
  tabType: ARTIFACT_TAB_TYPE,
1962
- dataopsWebBaseUrl: config.webBaseUrl.trim() || origin
2074
+ dataopsWebBaseUrl: config.webBaseUrl.trim() || requestOrigin(request)
1963
2075
  });
1964
2076
  }
1965
2077
  }), "artifact-app: config route");
@@ -1998,6 +2110,19 @@ function apply(ctx, config) {
1998
2110
  }
1999
2111
  }
2000
2112
  }), "artifact-app: apps route");
2113
+ ctx.effect(() => ctx.webServer.register({
2114
+ kind: "exact",
2115
+ path: TEMPLATES_PATH,
2116
+ handler: async (request, response) => {
2117
+ try {
2118
+ const appType = new URL(request.url ?? "/", "http://dsh.internal").searchParams.get("appType") === "COMPONENT" ? "COMPONENT" : "COCKPIT";
2119
+ const rows = await api.listTemplates(appType);
2120
+ writeJson(response, 200, { items: rows });
2121
+ } catch (error) {
2122
+ writeJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2123
+ }
2124
+ }
2125
+ }), "artifact-app: templates route");
2001
2126
  ctx.effect(() => ctx.webServer.register({
2002
2127
  kind: "exact",
2003
2128
  path: PREVIEW_PATH,
@@ -2040,17 +2165,28 @@ function apply(ctx, config) {
2040
2165
  try {
2041
2166
  const rest = new URL(request.url ?? "/", "http://dsh.internal").pathname.slice(24).replace(/^\/+/u, "");
2042
2167
  const id = decodeURIComponent(rest);
2043
- if (id === "" || id.includes("/")) {
2168
+ const templateId = id.startsWith("template/") ? id.slice(9) : void 0;
2169
+ const targetId = templateId ?? id;
2170
+ if (targetId === "" || targetId.includes("/") || targetId.includes("\\") || targetId.includes("..") || targetId.startsWith(".")) {
2044
2171
  writeJson(response, 400, { error: "缺少驾驶舱 id" });
2045
2172
  return;
2046
2173
  }
2047
- const body = await api.getAppHtml(id);
2048
- assertCockpitHtml(body.html, config.htmlMaxBytes);
2174
+ let body;
2175
+ let isComponent = false;
2176
+ if (templateId === void 0) body = await api.getAppHtml(targetId);
2177
+ else {
2178
+ isComponent = (await api.getTemplate(targetId)).appType === "COMPONENT";
2179
+ body = {
2180
+ html: (await api.getTemplateHtml(targetId)).html,
2181
+ templateId
2182
+ };
2183
+ }
2184
+ if (!isComponent) assertCockpitHtml(body.html, config.htmlMaxBytes);
2049
2185
  const logicalNames = [...new Set([...body.html.matchAll(/asset:\/\/([a-zA-Z0-9][a-zA-Z0-9._-]{0,62})/gu)].map((m) => m[1]))];
2050
2186
  let resolvedHtml = body.html;
2051
2187
  if (logicalNames.length > 0) {
2052
- const templateId = body.templateId ?? void 0;
2053
- const templateUrls = templateId === void 0 ? {} : await api.resolveAssets(logicalNames, "template", templateId).catch(() => ({}));
2188
+ const assetTemplateId = body.templateId ?? void 0;
2189
+ const templateUrls = assetTemplateId === void 0 ? {} : await api.resolveAssets(logicalNames, "template", assetTemplateId).catch(() => ({}));
2054
2190
  const rest = logicalNames.filter((name) => templateUrls[name] === void 0);
2055
2191
  const sharedUrls = rest.length === 0 ? {} : await api.resolveAssets(rest, "shared").catch(() => ({}));
2056
2192
  for (const [name, url] of Object.entries({
@@ -2085,8 +2221,8 @@ function apply(ctx, config) {
2085
2221
  handler: async (request, response) => {
2086
2222
  const cors = {
2087
2223
  "access-control-allow-origin": "*",
2088
- "access-control-allow-methods": "POST, OPTIONS",
2089
- "access-control-allow-headers": "authorization, content-type, accept",
2224
+ "access-control-allow-methods": "GET, POST, OPTIONS",
2225
+ "access-control-allow-headers": "authorization, content-type, accept, x-dataops-token",
2090
2226
  "access-control-max-age": "600"
2091
2227
  };
2092
2228
  if (request.method === "OPTIONS") {
@@ -2095,8 +2231,10 @@ function apply(ctx, config) {
2095
2231
  return;
2096
2232
  }
2097
2233
  try {
2098
- const upstream = new URL(request.url ?? "/", "http://dsh.internal").pathname.slice(25);
2099
- if (!/^\/api\/artifact-apps\/(?:[^/]+(?:\/(?:query|call-api|html))?|(?:runtime|echarts)\.js)$/u.test(upstream)) {
2234
+ const url = new URL(request.url ?? "/", "http://dsh.internal");
2235
+ const upstreamPath = url.pathname.slice(25);
2236
+ const upstream = upstreamPath + url.search;
2237
+ if (!/^\/api\/artifact-apps\/(?:[^/]+(?:\/(?:query|call-api|html|export))?|(?:runtime|echarts)\.js)$/u.test(upstreamPath)) {
2100
2238
  response.writeHead(403, {
2101
2239
  ...cors,
2102
2240
  "content-type": "application/json; charset=utf-8"
@@ -2110,11 +2248,16 @@ function apply(ctx, config) {
2110
2248
  const bodyToken = typeof rawBody?.__dataopsToken === "string" ? rawBody.__dataopsToken : void 0;
2111
2249
  if (rawBody !== void 0) delete rawBody.__dataopsToken;
2112
2250
  const effectiveAuth = bodyToken !== void 0 && bodyToken !== "" ? `Bearer ${bodyToken}` : authorization;
2251
+ let gatewayAuth = typeof effectiveAuth === "string" && effectiveAuth.trim() !== "" ? effectiveAuth : void 0;
2252
+ if (gatewayAuth === void 0) try {
2253
+ gatewayAuth = "Bearer " + await api.token();
2254
+ ctx.logger.warn("artifact-app: gateway used the workspace credential for " + upstream);
2255
+ } catch {}
2113
2256
  const init = {
2114
2257
  method: request.method,
2115
2258
  ...rawBody !== void 0 ? { body: JSON.stringify(rawBody) } : {}
2116
2259
  };
2117
- const result = await api.gateway(upstream, init, typeof effectiveAuth === "string" ? effectiveAuth : void 0);
2260
+ const result = await api.gateway(upstream, init, gatewayAuth);
2118
2261
  response.writeHead(result.status, {
2119
2262
  ...cors,
2120
2263
  "content-type": result.contentType,
@@ -2151,4 +2294,4 @@ function apply(ctx, config) {
2151
2294
  ctx.skills.registerProvider(() => provider);
2152
2295
  }
2153
2296
  //#endregion
2154
- export { APPS_PATH, ARTIFACT_TAB_TYPE, ArtifactApi, CONFIG_PATH, Config, DataOpsError, GATEWAY_PATH, OPEN_EVENTS_PATH, OpenCockpitHub, PREVIEW_PATH, RENDER_PATH, TOKEN_PATH, TemplateContractError, apply, assertCockpitHtml, cockpitCsp, inject, injectCockpitRuntime, name };
2297
+ export { APPS_PATH, ARTIFACT_TAB_TYPE, ArtifactApi, CONFIG_PATH, Config, DataOpsError, GATEWAY_PATH, OPEN_EVENTS_PATH, OpenCockpitHub, PREVIEW_PATH, RENDER_PATH, TEMPLATES_PATH, TOKEN_PATH, TemplateContractError, apply, assertCockpitHtml, cockpitCsp, inject, injectCockpitRuntime, name };