@forgeax/view 0.5.0-dev.13472.92dbf4b81984 → 0.5.0-dev.13601.16969c83ee09
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/README.md +25 -7
- package/dist/build-revision.json +1 -1
- package/dist/modules/backend.mjs +12 -12
- package/dist/modules/browser.mjs +16 -16
- package/dist/modules/chunks/{App-E3M52XTQ.mjs → App-4XQ5SNNT.mjs} +2 -2
- package/dist/modules/chunks/{chunk-3GIGTM7E.mjs → chunk-27XASYEH.mjs} +2 -2
- package/dist/modules/chunks/{chunk-UAGQZPAB.mjs → chunk-2Z5KEV6L.mjs} +2 -2
- package/dist/modules/chunks/{chunk-4NGODRIE.mjs → chunk-46UEGAXY.mjs} +4 -4
- package/dist/modules/chunks/{chunk-UZHTLAIZ.mjs → chunk-47RJSHLM.mjs} +2 -2
- package/dist/modules/chunks/{chunk-ACOMGSSB.mjs → chunk-5PHAXNFO.mjs} +2 -2
- package/dist/modules/chunks/{chunk-IXPBGENY.mjs → chunk-6I7RY73G.mjs} +3 -3
- package/dist/modules/chunks/{chunk-45ZU2HHR.mjs → chunk-B6QUJ33G.mjs} +27 -23
- package/dist/modules/chunks/chunk-B6QUJ33G.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-FKLYSAVN.mjs → chunk-CXOD7FW6.mjs} +7 -4
- package/dist/modules/chunks/chunk-CXOD7FW6.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-CRNEX7YH.mjs → chunk-D25SR5FS.mjs} +2 -2
- package/dist/modules/chunks/{chunk-CDO6LELS.mjs → chunk-D2RCKG7Q.mjs} +3 -3
- package/dist/modules/chunks/{chunk-L5EV6NEE.mjs → chunk-DWT3TA6M.mjs} +6 -6
- package/dist/modules/chunks/{chunk-MHE5EBU4.mjs → chunk-E3PJGTLI.mjs} +22 -14
- package/dist/modules/chunks/chunk-E3PJGTLI.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-3Q26HNFS.mjs → chunk-FICPCJKR.mjs} +2 -2
- package/dist/modules/chunks/{chunk-CWNB67UF.mjs → chunk-FMCHH5R4.mjs} +2 -2
- package/dist/modules/chunks/{chunk-MO5LILCP.mjs → chunk-HWZCLROY.mjs} +6 -2
- package/dist/modules/chunks/chunk-HWZCLROY.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-TTFLJHQZ.mjs → chunk-IJPWKKIU.mjs} +6 -6
- package/dist/modules/chunks/{chunk-TTFLJHQZ.mjs.map → chunk-IJPWKKIU.mjs.map} +1 -1
- package/dist/modules/chunks/{chunk-2VJPRNYB.mjs → chunk-IZAHIOFU.mjs} +4 -4
- package/dist/modules/chunks/{chunk-IBNFKPW4.mjs → chunk-JK7K7RG3.mjs} +2 -2
- package/dist/modules/chunks/{chunk-BGK4ZEQ5.mjs → chunk-KX2WU5Z6.mjs} +2 -2
- package/dist/modules/chunks/{chunk-TBZZ37VI.mjs → chunk-LZ57Y73Z.mjs} +4 -4
- package/dist/modules/chunks/{chunk-M5X56JWH.mjs → chunk-PW5CE2Y6.mjs} +3 -3
- package/dist/modules/chunks/{chunk-OHVTNEQ2.mjs → chunk-PZIAKZJC.mjs} +2 -2
- package/dist/modules/chunks/{chunk-PG4AJFDC.mjs → chunk-RJVOH7G2.mjs} +12 -12
- package/dist/modules/chunks/{chunk-N2GEQFFK.mjs → chunk-U5J7OTEB.mjs} +2 -2
- package/dist/modules/chunks/{chunk-N2GEQFFK.mjs.map → chunk-U5J7OTEB.mjs.map} +2 -2
- package/dist/modules/chunks/{chunk-JZ3V27HQ.mjs → chunk-U64FRQKP.mjs} +3 -3
- package/dist/modules/chunks/{chunk-W46L4HSP.mjs → chunk-U6UMATPV.mjs} +3 -3
- package/dist/modules/chunks/{chunk-7ICJKZYO.mjs → chunk-VKNIYV2W.mjs} +26 -21
- package/dist/modules/chunks/chunk-VKNIYV2W.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-7R6CGQDU.mjs → chunk-VW2ZF3P4.mjs} +4 -4
- package/dist/modules/chunks/{chunk-AIIYGGGC.mjs → chunk-X4NNZ5X6.mjs} +2 -2
- package/dist/modules/chunks/{chunk-5AKXNYKX.mjs → chunk-XE6ITXHT.mjs} +2 -2
- package/dist/modules/chunks/{chunk-WHVPE2P6.mjs → chunk-XMP4THLF.mjs} +4 -4
- package/dist/modules/chunks/{chunk-WHVPE2P6.mjs.map → chunk-XMP4THLF.mjs.map} +1 -1
- package/dist/modules/chunks/{chunk-BCTPTLA6.mjs → chunk-XQYF2P6M.mjs} +3 -3
- package/dist/modules/chunks/{chunk-KSF2EQTE.mjs → chunk-YCMZAYBM.mjs} +2 -2
- package/dist/modules/chunks/chunk-YCMZAYBM.mjs.map +7 -0
- package/dist/modules/chunks/{chunk-6WCG6WPC.mjs → chunk-YNHEYXDG.mjs} +3 -3
- package/dist/modules/chunks/{chunk-7MJUWNVI.mjs → chunk-ZGOGO7SS.mjs} +1 -1
- package/dist/modules/chunks/{chunk-VXSD4FK5.mjs → chunk-ZHO2G4JR.mjs} +3 -3
- package/dist/modules/cli-contributions.mjs +3 -3
- package/dist/modules/cli.mjs +2 -2
- package/dist/modules/command-executor.mjs +2 -2
- package/dist/modules/embedded.mjs +100 -21
- package/dist/modules/embedded.mjs.map +3 -3
- package/dist/modules/frontend-root.mjs +15 -15
- package/dist/modules/index.mjs +26 -26
- package/dist/modules/plugin.mjs +14 -14
- package/dist/modules/plugins/asset/frontend.mjs +4 -4
- package/dist/modules/plugins/asset/identity.mjs +3 -3
- package/dist/modules/plugins/asset/ui.mjs +2 -2
- package/dist/modules/plugins/content-browser/ui.mjs +2 -2
- package/dist/modules/plugins/inspector/ui.mjs +2 -2
- package/dist/modules/plugins/material/frontend.mjs +4 -4
- package/dist/modules/plugins/material/identity.mjs +3 -3
- package/dist/modules/plugins/material/ui.mjs +5 -5
- package/dist/modules/plugins/mesh/frontend.mjs +4 -4
- package/dist/modules/plugins/mesh/identity.mjs +3 -3
- package/dist/modules/plugins/mesh/ui.mjs +5 -5
- package/dist/modules/plugins/plugins/ui/panel.mjs +2 -2
- package/dist/modules/plugins/plugins/ui.mjs +3 -3
- package/dist/modules/plugins/scene-tree/ui.mjs +2 -2
- package/dist/modules/plugins/texture/frontend.mjs +4 -4
- package/dist/modules/plugins/texture/identity.mjs +3 -3
- package/dist/modules/plugins/texture/ui.mjs +5 -5
- package/dist/modules/plugins/viewport/frontend.mjs +1 -1
- package/dist/modules/product.mjs +14 -14
- package/dist/modules/shell/frontend.mjs +1 -1
- package/dist/modules/start-cli.mjs +1 -1
- package/dist/modules/start.mjs +1 -1
- package/dist/modules/viewer/catalog/backend.mjs +10 -10
- package/dist/modules/viewer/catalog/frontend.mjs +14 -14
- package/dist/modules/viewer/catalog/shared.mjs +7 -7
- package/dist/modules/viewer/catalog.mjs +17 -17
- package/dist/modules/viewer-host/frontend.mjs +16 -16
- package/dist/modules/viewer.mjs +17 -17
- package/dist/modules/workspace/backend.mjs +2 -2
- package/dist/modules/workspace/broker.mjs +1 -1
- package/dist/modules/workspace/preview-surface.mjs +1 -1
- package/dist/modules/workspace/resident.mjs +1 -1
- package/dist/modules/workspace/resource-page.mjs +2 -2
- package/dist/viewer/assets/{embedded-Dv06jc6s.js → embedded-Ch4NawY-.js} +2 -2
- package/dist/viewer/assets/{embedded-Dv06jc6s.js.map → embedded-Ch4NawY-.js.map} +1 -1
- package/dist/viewer/assets/main-DUQJ1NlR.js +22 -0
- package/dist/viewer/assets/main-DUQJ1NlR.js.map +1 -0
- package/dist/viewer/assets/{panels-lazy-DjINjNT-.js → panels-lazy-DbOAvYN1.js} +3 -3
- package/dist/viewer/assets/panels-lazy-DbOAvYN1.js.map +1 -0
- package/dist/viewer/assets/panels-ui-DICIsCag.js +12 -0
- package/dist/viewer/assets/panels-ui-DICIsCag.js.map +1 -0
- package/dist/viewer/assets/style-gOtSMZif.css +2 -0
- package/dist/viewer/index.html +4 -4
- package/dist/viewer/panels.js +1 -1
- package/package.json +3 -4
- package/scripts/browser-proof-workload.mjs +0 -1
- package/scripts/verify-runtime-content-engine.mjs +12 -6
- package/dist/modules/chunks/chunk-45ZU2HHR.mjs.map +0 -7
- package/dist/modules/chunks/chunk-7ICJKZYO.mjs.map +0 -7
- package/dist/modules/chunks/chunk-FKLYSAVN.mjs.map +0 -7
- package/dist/modules/chunks/chunk-KSF2EQTE.mjs.map +0 -7
- package/dist/modules/chunks/chunk-MHE5EBU4.mjs.map +0 -7
- package/dist/modules/chunks/chunk-MO5LILCP.mjs.map +0 -7
- package/dist/viewer/assets/main-De95aP3K.js +0 -22
- package/dist/viewer/assets/main-De95aP3K.js.map +0 -1
- package/dist/viewer/assets/panels-lazy-DjINjNT-.js.map +0 -1
- package/dist/viewer/assets/panels-ui-DEJVGEKx.js +0 -12
- package/dist/viewer/assets/panels-ui-DEJVGEKx.js.map +0 -1
- package/dist/viewer/assets/style-BM0losky.css +0 -2
- package/scripts/verify-game3d-workspace.mjs +0 -318
- /package/dist/modules/chunks/{App-E3M52XTQ.mjs.map → App-4XQ5SNNT.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-3GIGTM7E.mjs.map → chunk-27XASYEH.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-UAGQZPAB.mjs.map → chunk-2Z5KEV6L.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-4NGODRIE.mjs.map → chunk-46UEGAXY.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-UZHTLAIZ.mjs.map → chunk-47RJSHLM.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-ACOMGSSB.mjs.map → chunk-5PHAXNFO.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-IXPBGENY.mjs.map → chunk-6I7RY73G.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-CRNEX7YH.mjs.map → chunk-D25SR5FS.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-CDO6LELS.mjs.map → chunk-D2RCKG7Q.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-L5EV6NEE.mjs.map → chunk-DWT3TA6M.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-3Q26HNFS.mjs.map → chunk-FICPCJKR.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-CWNB67UF.mjs.map → chunk-FMCHH5R4.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-2VJPRNYB.mjs.map → chunk-IZAHIOFU.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-IBNFKPW4.mjs.map → chunk-JK7K7RG3.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-BGK4ZEQ5.mjs.map → chunk-KX2WU5Z6.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-TBZZ37VI.mjs.map → chunk-LZ57Y73Z.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-M5X56JWH.mjs.map → chunk-PW5CE2Y6.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-OHVTNEQ2.mjs.map → chunk-PZIAKZJC.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-PG4AJFDC.mjs.map → chunk-RJVOH7G2.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-JZ3V27HQ.mjs.map → chunk-U64FRQKP.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-W46L4HSP.mjs.map → chunk-U6UMATPV.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-7R6CGQDU.mjs.map → chunk-VW2ZF3P4.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-AIIYGGGC.mjs.map → chunk-X4NNZ5X6.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-5AKXNYKX.mjs.map → chunk-XE6ITXHT.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-BCTPTLA6.mjs.map → chunk-XQYF2P6M.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-6WCG6WPC.mjs.map → chunk-YNHEYXDG.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-7MJUWNVI.mjs.map → chunk-ZGOGO7SS.mjs.map} +0 -0
- /package/dist/modules/chunks/{chunk-VXSD4FK5.mjs.map → chunk-ZHO2G4JR.mjs.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../packages/view-workspace-core/src/resident.ts"],
|
|
4
|
-
"sourcesContent": ["import type { HostCallerIdentity, HostTransportServer, HostTransportClient } from '@forgeax/engine-host/transport';\nimport type { FrontendConfig } from './frontend-contract.ts';\nimport type { IncomingMessage } from 'node:http';\nimport type { Duplex } from 'node:stream';\nexport type ViewHostServerOptions = { backendHost?: { transport: HostTransportServer }; transport?: HostTransportServer; accessToken?: string; host?: string; port?: number; path?: string; staticRoot?: string | URL; frontendConfig?: Pick<FrontendConfig, 'layout'>; onClientConnect?: (caller: HostCallerIdentity) => unknown; onClientDisconnect?: (caller: HostCallerIdentity) => unknown };\n\nimport { createReadStream, existsSync, statSync } from 'node:fs';\nimport { join, normalize } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createServer } from 'node:http';\nimport { randomUUID } from 'node:crypto';\n\nimport { attachHostWebSocketServer, createHostWebSocketClient } from '@forgeax/engine-host/transport';\nimport { WebSocket, WebSocketServer } from 'ws';\n\nimport { createViewWorkspaceClient } from './client.ts';\nimport { requireWorkspaceString, workspaceError } from './contract.ts';\n\nconst DEFAULT_PATH = '/__forgeax/view-workspace';\nconst BOOTSTRAP_PATH = '/__forgeax/view/bootstrap';\n\nconst STATIC_MIME: Readonly<Record<string, string>> = Object.freeze({\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.json': 'application/json; charset=utf-8',\n});\n\nfunction staticContentType(filePath: string) {\n const index = filePath.lastIndexOf('.');\n if (index === -1) return 'application/octet-stream';\n return STATIC_MIME[filePath.slice(index)] ?? 'application/octet-stream';\n}\n\nfunction isLoopbackHost(host: string) {\n const normalized = host.toLowerCase().replace(/^\\[(.*)\\]$/, '$1');\n return (\n normalized === 'localhost' ||\n normalized === '::1' ||\n normalized === '0:0:0:0:0:0:0:1' ||\n normalized.startsWith('127.')\n );\n}\n\nfunction resolveStaticFile(root: string, requestPath: string) {\n const relative = normalize(requestPath).replace(/^(\\.\\.(\\/|\\\\|$))+/, '');\n const absolute = join(root, relative);\n if (!absolute.startsWith(root)) return undefined;\n return absolute;\n}\n\nfunction rejectUpgrade(socket: Duplex, status: string, message: string) {\n try {\n socket.write(`HTTP/1.1 ${status}\\r\\nConnection: close\\r\\nContent-Length: ${Buffer.byteLength(message)}\\r\\n\\r\\n${message}`);\n } finally {\n socket.destroy();\n }\n}\n\nfunction endpointWithToken(endpoint: string, token?: string, role?: string) {\n const url = new URL(endpoint);\n url.searchParams.set('token', requireWorkspaceString(token, 'token'));\n if (role === 'frontend') {\n url.searchParams.set('role', role);\n } else {\n url.searchParams.delete('role');\n url.searchParams.delete('frontendTicket');\n if (role !== undefined) url.searchParams.set('role', role);\n }\n return url.toString();\n}\n\n/**\n * Expose the already-running View Host through one authenticated WebSocket.\n * The Engine Host transport remains the sole RPC/generation implementation;\n * this server only authenticates the connection and adapts a socket.\n */\nexport async function startViewHostServer({\n backendHost,\n transport = backendHost?.transport,\n accessToken,\n host = '127.0.0.1',\n port = 0,\n path = DEFAULT_PATH,\n onClientConnect,\n onClientDisconnect,\n staticRoot,\n frontendConfig,\n}: ViewHostServerOptions = {}) {\n if (!transport || typeof transport.connect !== 'function') {\n throw new TypeError('resident View Host requires an Engine Host transport');\n }\n const token = requireWorkspaceString(accessToken, 'accessToken');\n const frontendTicket = randomUUID();\n if (typeof host !== 'string' || host.length === 0) throw new TypeError('resident View Host requires a host');\n if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {\n throw new TypeError('resident View Host port must be an integer from 0 to 65535');\n }\n if (typeof path !== 'string' || !path.startsWith('/')) throw new TypeError('resident View Host path must start with /');\n\n const staticRootPath =\n staticRoot === undefined\n ? undefined\n : typeof staticRoot === 'string'\n ? staticRoot\n : fileURLToPath(staticRoot);\n if (staticRootPath && !isLoopbackHost(host)) {\n throw new Error(\n 'The View browser bootstrap contains host credentials and may only bind to a loopback host',\n );\n }\n const http = createServer((request, response) => {\n const url = new URL(request.url ?? '/', 'http://127.0.0.1');\n if (url.pathname === '/__forgeax/view/health') {\n response.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Cross-Origin-Resource-Policy': 'cross-origin',\n 'Cache-Control': 'no-store' });\n response.end();\n return;\n }\n if (url.pathname === BOOTSTRAP_PATH) {\n const address = http.address();\n const selectedPort = typeof address === 'object' && address !== null ? address.port : port;\n const loopbackHost = host === '0.0.0.0' ? '127.0.0.1' : host;\n const endpoint = `ws://${loopbackHost}:${selectedPort}${path}`;\n const frontendEndpoint = `${endpoint}?frontendTicket=${encodeURIComponent(frontendTicket)}`;\n response.statusCode = 200;\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.setHeader('Cache-Control', 'no-store');\n response.end(\n JSON.stringify({\n token,\n endpoint,\n frontendEndpoint,\n frontendTicket,\n workspacePath: path,\n ...(frontendConfig?.layout === undefined ? {} : { frontendConfig: { layout: frontendConfig.layout } }),\n }),\n );\n return;\n }\n if (!staticRootPath) {\n response.statusCode = 404;\n response.end('View workspace WebSocket endpoint only');\n return;\n }\n let filePath = resolveStaticFile(\n staticRootPath,\n url.pathname === '/' ? '/index.html' : url.pathname,\n );\n if (filePath && existsSync(filePath) && statSync(filePath).isFile()) {\n response.statusCode = 200;\n response.setHeader('Content-Type', staticContentType(filePath));\n createReadStream(filePath).pipe(response);\n return;\n }\n const spa = resolveStaticFile(staticRootPath, '/index.html');\n if (spa && existsSync(spa)) {\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n createReadStream(spa).pipe(response);\n return;\n }\n response.statusCode = 404;\n response.end('Not found');\n });\n const sockets = new Set<WebSocket>();\n const disposers = new Set<() => void>();\n // A page refresh replaces the trusted frontend socket. Forward both the\n // admission and the loss of that page so the workspace can reset per-page\n // session state instead of replaying the previous page's history.\n const isTrustedFrontendCaller = (caller: HostCallerIdentity) =>\n caller?.kind === 'frontend' && caller?.sourceId === 'view-frontend';\n // Lifecycle notifications run user code against possibly dead targets; a\n // throw here would break transport admission, and an unhandled rejection\n // would crash the backend process.\n const notify = (callback: ((caller: HostCallerIdentity) => unknown) | undefined, caller: HostCallerIdentity) => {\n try {\n void Promise.resolve(callback?.(caller)).catch(() => {});\n } catch {\n // Admission and cleanup continue without the failed notification.\n }\n };\n const removeTransportConnect = transport.onClientConnect?.((caller) => {\n if (isTrustedFrontendCaller(caller)) notify(onClientConnect, caller);\n });\n const removeTransportDisconnect = transport.onClientDisconnect?.((caller) => {\n if (caller?.kind === 'websocket' || isTrustedFrontendCaller(caller)) notify(onClientDisconnect, caller);\n });\n const socketServer = new WebSocketServer({ noServer: true });\n let closed = false;\n\n const onUpgrade = (request: IncomingMessage, socket: Duplex, head: Buffer) => {\n if (closed) return rejectUpgrade(socket, '503 Service Unavailable', 'View Host is stopping');\n let url;\n try {\n url = new URL(request.url ?? '/', 'http://127.0.0.1');\n } catch {\n return rejectUpgrade(socket, '400 Bad Request', 'Invalid View Host request');\n }\n if (url.pathname !== path) return;\n if (url.searchParams.get('token') !== token) {\n return rejectUpgrade(socket, '401 Unauthorized', 'View Host token rejected');\n }\n const role = url.searchParams.get('role');\n const trustedFrontend =\n role === 'frontend' && url.searchParams.get('frontendTicket') === frontendTicket;\n socketServer.handleUpgrade(request, socket, head, (client) => {\n (client as WebSocket & { __forgeaxHostRole?: HostCallerIdentity['kind'] }).__forgeaxHostRole = trustedFrontend ? 'frontend' : 'websocket';\n socketServer.emit('connection', client, request);\n });\n };\n const onConnection = (socket: WebSocket & { __forgeaxHostRole?: HostCallerIdentity['kind'] }) => {\n sockets.add(socket);\n const role = socket.__forgeaxHostRole;\n const dispose = attachHostWebSocketServer(\n socket,\n transport,\n role === 'frontend' ? { kind: 'frontend', sourceId: 'view-frontend' } : { kind: 'websocket' },\n );\n disposers.add(dispose);\n const remove = () => {\n sockets.delete(socket);\n disposers.delete(dispose);\n };\n socket.once?.('close', remove);\n socket.once?.('error', remove);\n };\n socketServer.on('connection', onConnection);\n http.on('upgrade', onUpgrade);\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n http.off('listening', onListening);\n reject(error);\n };\n const onListening = () => {\n http.off('error', onError);\n resolve();\n };\n http.once('error', onError);\n http.once('listening', onListening);\n http.listen(port, host);\n });\n const address = http.address();\n const selectedPort = typeof address === 'object' && address !== null ? address.port : port;\n const endpoint = `ws://${host === '0.0.0.0' ? '127.0.0.1' : host}:${selectedPort}${path}`;\n const frontendEndpoint = `${endpoint}?frontendTicket=${encodeURIComponent(frontendTicket)}`;\n let closing: Promise<void> | undefined;\n const close = (reason = 'resident View Host stopped') => closing ??= (async () => {\n closed = true;\n removeTransportConnect?.();\n removeTransportDisconnect?.();\n http.off('upgrade', onUpgrade);\n for (const dispose of [...disposers]) dispose();\n disposers.clear();\n for (const socket of [...sockets]) socket.close?.(1001, String(reason).slice(0, 123));\n sockets.clear();\n const results = await Promise.allSettled([\n new Promise<void>((resolve, reject) => {\n socketServer.close(error => error ? reject(error) : resolve());\n }),\n new Promise<void>((resolve, reject) => {\n if (!http.listening) return resolve();\n http.close(error => error ? reject(error) : resolve());\n }),\n ]);\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason);\n if (failures.length) throw new AggregateError(failures, 'View server cleanup failed');\n })();\n const httpUrl = `http://${host === '0.0.0.0' ? '127.0.0.1' : host}:${selectedPort}`;\n return Object.freeze({\n host,\n port: selectedPort,\n path,\n token,\n endpoint,\n url: endpointWithToken(endpoint, token),\n frontendEndpoint,\n frontendUrl: endpointWithToken(frontendEndpoint, token, 'frontend'),\n frontendTicket,\n server: http,\n socketServer,\n ...(staticRootPath\n ? {\n viewerUrl: httpUrl,\n bootstrapUrl: `${httpUrl}${BOOTSTRAP_PATH}`,\n }\n : {}),\n close,\n });\n}\n\n/** Connect an independent Node CLI process to the resident View Host. */\nexport async function connectViewWorkspace({ endpoint, token, clientId, timeout = 30_000 }: { endpoint?: string; token?: string; clientId?: string; timeout?: number } = {}) {\n const url = endpointWithToken(requireWorkspaceString(endpoint, 'endpoint'), token, 'cli');\n let transport: HostTransportClient;\n const socket = new WebSocket(url, { handshakeTimeout: timeout });\n let unauthorizedResponse = false;\n socket.once?.('unexpected-response', (_request, response) => {\n unauthorizedResponse = response?.statusCode === 401;\n response?.resume?.();\n socket.close?.();\n });\n try {\n transport = await createHostWebSocketClient(socket);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (unauthorizedResponse || /401|unauthor|token rejected/i.test(message)) {\n throw workspaceError(\n 'view-workspace-unauthorized',\n 'the connected client to have permission for the View workspace',\n 'Connect with the token issued by the View Host.',\n { endpoint },\n );\n }\n throw error;\n }\n return createViewWorkspaceClient({ transport, clientId: requireWorkspaceString(clientId, 'clientId'), token });\n}\n\nexport { DEFAULT_PATH as VIEW_WORKSPACE_HOST_PATH };\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { HostCallerIdentity, HostTransportServer, HostTransportClient } from '@forgeax/engine-host/transport';\nimport type { FrontendConfig } from './frontend-contract.ts';\nimport type { IncomingMessage } from 'node:http';\nimport type { Duplex } from 'node:stream';\nexport type ViewHostServerOptions = { backendHost?: { transport: HostTransportServer }; transport?: HostTransportServer; accessToken?: string; host?: string; port?: number; path?: string; staticRoot?: string | URL; frontendConfig?: Pick<FrontendConfig, 'layout'>; onClientConnect?: (caller: HostCallerIdentity) => unknown; onClientDisconnect?: (caller: HostCallerIdentity) => unknown };\n\nimport { createReadStream, existsSync, statSync } from 'node:fs';\nimport { join, normalize } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createServer } from 'node:http';\nimport { randomUUID } from 'node:crypto';\n\nimport { attachHostWebSocketServer, createHostWebSocketClient } from '@forgeax/engine-host/transport';\nimport { WebSocket, WebSocketServer } from 'ws';\n\nimport { createViewWorkspaceClient } from './client.ts';\nimport { requireWorkspaceString, workspaceError } from './contract.ts';\n\nconst DEFAULT_PATH = '/__forgeax/view-workspace';\nconst BOOTSTRAP_PATH = '/__forgeax/view/bootstrap';\n\nconst STATIC_MIME: Readonly<Record<string, string>> = Object.freeze({\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.json': 'application/json; charset=utf-8',\n});\n\nfunction staticContentType(filePath: string) {\n const index = filePath.lastIndexOf('.');\n if (index === -1) return 'application/octet-stream';\n return STATIC_MIME[filePath.slice(index)] ?? 'application/octet-stream';\n}\n\nfunction isLoopbackHost(host: string) {\n const normalized = host.toLowerCase().replace(/^\\[(.*)\\]$/, '$1');\n return (\n normalized === 'localhost' ||\n normalized === '::1' ||\n normalized === '0:0:0:0:0:0:0:1' ||\n normalized.startsWith('127.')\n );\n}\n\nfunction resolveStaticFile(root: string, requestPath: string) {\n const relative = normalize(requestPath).replace(/^(\\.\\.(\\/|\\\\|$))+/, '');\n const absolute = join(root, relative);\n if (!absolute.startsWith(root)) return undefined;\n return absolute;\n}\n\nfunction rejectUpgrade(socket: Duplex, status: string, message: string) {\n try {\n socket.write(`HTTP/1.1 ${status}\\r\\nConnection: close\\r\\nContent-Length: ${Buffer.byteLength(message)}\\r\\n\\r\\n${message}`);\n } finally {\n socket.destroy();\n }\n}\n\nfunction endpointWithToken(endpoint: string, token?: string, role?: string) {\n const url = new URL(endpoint);\n url.searchParams.set('token', requireWorkspaceString(token, 'token'));\n if (role === 'frontend') {\n url.searchParams.set('role', role);\n } else {\n url.searchParams.delete('role');\n url.searchParams.delete('frontendTicket');\n if (role !== undefined) url.searchParams.set('role', role);\n }\n return url.toString();\n}\n\n/**\n * Expose the already-running View Host through one authenticated WebSocket.\n * The Engine Host transport remains the sole RPC/generation implementation;\n * this server only authenticates the connection and adapts a socket.\n */\nexport async function startViewHostServer({\n backendHost,\n transport = backendHost?.transport,\n accessToken,\n host = '127.0.0.1',\n port = 0,\n path = DEFAULT_PATH,\n onClientConnect,\n onClientDisconnect,\n staticRoot,\n frontendConfig,\n}: ViewHostServerOptions = {}) {\n if (!transport || typeof transport.connect !== 'function') {\n throw new TypeError('resident View Host requires an Engine Host transport');\n }\n const token = requireWorkspaceString(accessToken, 'accessToken');\n const frontendTicket = randomUUID();\n if (typeof host !== 'string' || host.length === 0) throw new TypeError('resident View Host requires a host');\n if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {\n throw new TypeError('resident View Host port must be an integer from 0 to 65535');\n }\n if (typeof path !== 'string' || !path.startsWith('/')) throw new TypeError('resident View Host path must start with /');\n\n const staticRootPath =\n staticRoot === undefined\n ? undefined\n : typeof staticRoot === 'string'\n ? staticRoot\n : fileURLToPath(staticRoot);\n if (staticRootPath && !isLoopbackHost(host)) {\n throw new Error(\n 'The View browser bootstrap contains host credentials and may only bind to a loopback host',\n );\n }\n const http = createServer((request, response) => {\n const url = new URL(request.url ?? '/', 'http://127.0.0.1');\n if (url.pathname === '/__forgeax/view/health') {\n response.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Cross-Origin-Resource-Policy': 'cross-origin',\n 'Cache-Control': 'no-store' });\n response.end();\n return;\n }\n if (url.pathname === BOOTSTRAP_PATH) {\n const address = http.address();\n const selectedPort = typeof address === 'object' && address !== null ? address.port : port;\n const loopbackHost = host === '0.0.0.0' ? '127.0.0.1' : host;\n const endpoint = `ws://${loopbackHost}:${selectedPort}${path}`;\n const frontendEndpoint = `${endpoint}?frontendTicket=${encodeURIComponent(frontendTicket)}`;\n response.statusCode = 200;\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.setHeader('Cache-Control', 'no-store');\n response.end(\n JSON.stringify({\n token,\n endpoint,\n frontendEndpoint,\n frontendTicket,\n workspacePath: path,\n ...(frontendConfig?.layout === undefined ? {} : { frontendConfig: { layout: frontendConfig.layout } }),\n }),\n );\n return;\n }\n if (!staticRootPath) {\n response.statusCode = 404;\n response.end('View workspace WebSocket endpoint only');\n return;\n }\n let filePath = resolveStaticFile(\n staticRootPath,\n url.pathname === '/' ? '/index.html' : url.pathname,\n );\n if (filePath && existsSync(filePath) && statSync(filePath).isFile()) {\n response.statusCode = 200;\n response.setHeader('Content-Type', staticContentType(filePath));\n createReadStream(filePath).pipe(response);\n return;\n }\n const spa = resolveStaticFile(staticRootPath, '/index.html');\n if (spa && existsSync(spa)) {\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n createReadStream(spa).pipe(response);\n return;\n }\n response.statusCode = 404;\n response.end('Not found');\n });\n const sockets = new Set<WebSocket>();\n const disposers = new Set<() => void>();\n // A page refresh replaces the trusted frontend socket. Forward both the\n // admission and the loss of that page so the workspace can reset per-page\n // session state instead of replaying the previous page's history.\n const isTrustedFrontendCaller = (caller: HostCallerIdentity) =>\n caller?.kind === 'frontend' && caller?.sourceId === 'view-frontend';\n // Lifecycle notifications run user code against possibly dead targets; a\n // throw here would break transport admission, and an unhandled rejection\n // would crash the backend process.\n const notify = (callback: ((caller: HostCallerIdentity) => unknown) | undefined, caller: HostCallerIdentity) => {\n try {\n void Promise.resolve(callback?.(caller)).catch(() => {});\n } catch {\n // Admission and cleanup continue without the failed notification.\n }\n };\n const removeTransportConnect = transport.onClientConnect?.((caller) => {\n if (isTrustedFrontendCaller(caller)) notify(onClientConnect, caller);\n });\n const removeTransportDisconnect = transport.onClientDisconnect?.((caller) => {\n if (caller?.kind === 'websocket' || isTrustedFrontendCaller(caller)) notify(onClientDisconnect, caller);\n });\n const socketServer = new WebSocketServer({ noServer: true });\n let closed = false;\n\n const onUpgrade = (request: IncomingMessage, socket: Duplex, head: Buffer) => {\n if (closed) return rejectUpgrade(socket, '503 Service Unavailable', 'View Host is stopping');\n let url;\n try {\n url = new URL(request.url ?? '/', 'http://127.0.0.1');\n } catch {\n return rejectUpgrade(socket, '400 Bad Request', 'Invalid View Host request');\n }\n if (url.pathname !== path) return;\n if (url.searchParams.get('token') !== token) {\n return rejectUpgrade(socket, '401 Unauthorized', 'View Host token rejected');\n }\n const role = url.searchParams.get('role');\n const trustedFrontend =\n role === 'frontend' && url.searchParams.get('frontendTicket') === frontendTicket;\n socketServer.handleUpgrade(request, socket, head, (client) => {\n (client as WebSocket & { __forgeaxHostRole?: HostCallerIdentity['kind'] }).__forgeaxHostRole = trustedFrontend ? 'frontend' : 'websocket';\n socketServer.emit('connection', client, request);\n });\n };\n const onConnection = (socket: WebSocket & { __forgeaxHostRole?: HostCallerIdentity['kind'] }) => {\n sockets.add(socket);\n const role = socket.__forgeaxHostRole;\n const dispose = attachHostWebSocketServer(\n socket,\n transport,\n role === 'frontend' ? { kind: 'frontend', sourceId: 'view-frontend' } : { kind: 'websocket' },\n );\n disposers.add(dispose);\n const remove = () => {\n sockets.delete(socket);\n disposers.delete(dispose);\n };\n socket.once?.('close', remove);\n socket.once?.('error', remove);\n };\n socketServer.on('connection', onConnection);\n http.on('upgrade', onUpgrade);\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n http.off('listening', onListening);\n reject(error);\n };\n const onListening = () => {\n http.off('error', onError);\n resolve();\n };\n http.once('error', onError);\n http.once('listening', onListening);\n http.listen(port, host);\n });\n const address = http.address();\n const selectedPort = typeof address === 'object' && address !== null ? address.port : port;\n const endpoint = `ws://${host === '0.0.0.0' ? '127.0.0.1' : host}:${selectedPort}${path}`;\n const frontendEndpoint = `${endpoint}?frontendTicket=${encodeURIComponent(frontendTicket)}`;\n let closing: Promise<void> | undefined;\n const close = (reason = 'resident View Host stopped') => closing ??= (async () => {\n closed = true;\n removeTransportConnect?.();\n removeTransportDisconnect?.();\n http.off('upgrade', onUpgrade);\n for (const dispose of [...disposers]) dispose();\n disposers.clear();\n // Authority is already detached. A vanished browser cannot complete the\n // close handshake, so release the owner socket before joining server close.\n for (const socket of [...sockets]) socket.terminate();\n sockets.clear();\n const results = await Promise.allSettled([\n new Promise<void>((resolve, reject) => {\n socketServer.close(error => error ? reject(error) : resolve());\n }),\n new Promise<void>((resolve, reject) => {\n if (!http.listening) return resolve();\n http.close(error => error ? reject(error) : resolve());\n }),\n ]);\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason);\n if (failures.length) throw new AggregateError(failures, 'View server cleanup failed');\n })();\n const httpUrl = `http://${host === '0.0.0.0' ? '127.0.0.1' : host}:${selectedPort}`;\n return Object.freeze({\n host,\n port: selectedPort,\n path,\n token,\n endpoint,\n url: endpointWithToken(endpoint, token),\n frontendEndpoint,\n frontendUrl: endpointWithToken(frontendEndpoint, token, 'frontend'),\n frontendTicket,\n server: http,\n socketServer,\n ...(staticRootPath\n ? {\n viewerUrl: httpUrl,\n bootstrapUrl: `${httpUrl}${BOOTSTRAP_PATH}`,\n }\n : {}),\n close,\n });\n}\n\n/** Connect an independent Node CLI process to the resident View Host. */\nexport async function connectViewWorkspace({ endpoint, token, clientId, timeout = 30_000 }: { endpoint?: string; token?: string; clientId?: string; timeout?: number } = {}) {\n const url = endpointWithToken(requireWorkspaceString(endpoint, 'endpoint'), token, 'cli');\n let transport: HostTransportClient;\n const socket = new WebSocket(url, { handshakeTimeout: timeout });\n let unauthorizedResponse = false;\n socket.once?.('unexpected-response', (_request, response) => {\n unauthorizedResponse = response?.statusCode === 401;\n response?.resume?.();\n socket.close?.();\n });\n try {\n transport = await createHostWebSocketClient(socket);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (unauthorizedResponse || /401|unauthor|token rejected/i.test(message)) {\n throw workspaceError(\n 'view-workspace-unauthorized',\n 'the connected client to have permission for the View workspace',\n 'Connect with the token issued by the View Host.',\n { endpoint },\n );\n }\n throw error;\n }\n return createViewWorkspaceClient({ transport, clientId: requireWorkspaceString(clientId, 'clientId'), token });\n}\n\nexport { DEFAULT_PATH as VIEW_WORKSPACE_HOST_PATH };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["address", "selectedPort", "endpoint", "frontendEndpoint"]
|
|
7
7
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FRONTEND_PLUGIN_ID,
|
|
3
3
|
identity_exports
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-X4NNZ5X6.mjs";
|
|
5
5
|
import {
|
|
6
6
|
applyResourcePagePlugin
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KX2WU5Z6.mjs";
|
|
8
8
|
|
|
9
9
|
// packages/view-plugin-mesh/src/frontend.ts
|
|
10
10
|
var VIEW_MESH_FRONTEND_PLUGIN_ID = FRONTEND_PLUGIN_ID;
|
|
@@ -22,4 +22,4 @@ export {
|
|
|
22
22
|
viewMeshFrontendPlugin,
|
|
23
23
|
frontend_default
|
|
24
24
|
};
|
|
25
|
-
//# sourceMappingURL=chunk-
|
|
25
|
+
//# sourceMappingURL=chunk-U64FRQKP.mjs.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FRONTEND_PLUGIN_ID,
|
|
3
3
|
identity_exports
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FMCHH5R4.mjs";
|
|
5
5
|
import {
|
|
6
6
|
applyResourcePagePlugin
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KX2WU5Z6.mjs";
|
|
8
8
|
|
|
9
9
|
// packages/view-plugin-texture/src/frontend.ts
|
|
10
10
|
var VIEW_TEXTURE_FRONTEND_PLUGIN_ID = FRONTEND_PLUGIN_ID;
|
|
@@ -22,4 +22,4 @@ export {
|
|
|
22
22
|
viewTextureFrontendPlugin,
|
|
23
23
|
frontend_default
|
|
24
24
|
};
|
|
25
|
-
//# sourceMappingURL=chunk-
|
|
25
|
+
//# sourceMappingURL=chunk-U6UMATPV.mjs.map
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
Toolbar,
|
|
17
17
|
anchorRectFromPoint,
|
|
18
18
|
useWorkspaceClientConnected
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CXOD7FW6.mjs";
|
|
20
20
|
import {
|
|
21
21
|
getLocale,
|
|
22
22
|
subscribeLocale,
|
|
@@ -1726,6 +1726,20 @@ function projectCatalog(root, assets) {
|
|
|
1726
1726
|
}
|
|
1727
1727
|
return { files: [...files.values()], folders: [...folders.values()] };
|
|
1728
1728
|
}
|
|
1729
|
+
function visibleCatalogFolders(folders, collapsed) {
|
|
1730
|
+
return folders.filter((folder) => {
|
|
1731
|
+
const parts = folder.path.split("/");
|
|
1732
|
+
return parts.every((_, index) => index === 0 || !collapsed.has(parts.slice(0, index).join("/")));
|
|
1733
|
+
}).sort((left, right) => {
|
|
1734
|
+
const a = left.path.split("/");
|
|
1735
|
+
const b = right.path.split("/");
|
|
1736
|
+
for (let index = 0; index < Math.min(a.length, b.length); index++) {
|
|
1737
|
+
const order = a[index].localeCompare(b[index]);
|
|
1738
|
+
if (order !== 0) return order;
|
|
1739
|
+
}
|
|
1740
|
+
return a.length - b.length;
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1729
1743
|
|
|
1730
1744
|
// packages/view-plugin-content-browser/src/ui/ContentBrowserPanel.tsx
|
|
1731
1745
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -1788,7 +1802,6 @@ function ContentBrowserPanel({
|
|
|
1788
1802
|
const catalog = useMemo2(() => projectCatalog(project?.root ?? "", snapshot.assets ?? []), [project?.root, snapshot.assets]);
|
|
1789
1803
|
const files = catalog.files;
|
|
1790
1804
|
const folders = catalog.folders;
|
|
1791
|
-
const directoryTree = folders.map((folder2) => ({ ...folder2, depth: folder2.path.split("/").length - 1 }));
|
|
1792
1805
|
const clientRef = useRef4(client);
|
|
1793
1806
|
clientRef.current = client;
|
|
1794
1807
|
const connected = useWorkspaceClientConnected(client);
|
|
@@ -2075,7 +2088,7 @@ function ContentBrowserPanel({
|
|
|
2075
2088
|
setActiveKinds(next.kinds);
|
|
2076
2089
|
};
|
|
2077
2090
|
const renderTreeRow = (path, name, depth, favorite = false) => {
|
|
2078
|
-
const hasChildren =
|
|
2091
|
+
const hasChildren = folders.some((entry) => entry.path.startsWith(`${path}/`));
|
|
2079
2092
|
const open = !collapsed.has(path);
|
|
2080
2093
|
const folderItem = folders.find((entry) => entry.path === path);
|
|
2081
2094
|
const selected = folder === path;
|
|
@@ -2170,23 +2183,15 @@ function ContentBrowserPanel({
|
|
|
2170
2183
|
groups.favorites ? /* @__PURE__ */ jsx10("div", { className: "cb-source-group-body", children: favorites.size ? [...favorites].map((path) => renderTreeRow(path, path.split("/").at(-1) ?? path, 0, true)) : /* @__PURE__ */ jsx10("div", { className: "cb-source-empty", children: t2("contentBrowser.noFavorites") }) }) : null
|
|
2171
2184
|
] }),
|
|
2172
2185
|
/* @__PURE__ */ jsxs8("section", { className: "cb-source-group", children: [
|
|
2173
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", "data-action": CONTENT_BROWSER_ACTIONS.toggleSourceGroup, className: `cb-source-group-head${groups.project ? "" : " collapsed"}`, onClick: () => setGroups((current) => ({ ...current, project: !current.project })), children: [
|
|
2174
|
-
/* @__PURE__ */ jsx10("span", { className: "cb-source-chev",
|
|
2186
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", "data-action": CONTENT_BROWSER_ACTIONS.toggleSourceGroup, className: `cb-source-group-head${folder === "" ? " is-sel" : ""}${groups.project ? "" : " collapsed"}`, "aria-expanded": groups.project, onClick: () => navigate(""), onDoubleClick: () => setGroups((current) => ({ ...current, project: !current.project })), onContextMenu: (event) => showMenu(event, folderMenu("", projectLabel)), children: [
|
|
2187
|
+
/* @__PURE__ */ jsx10("span", { className: "cb-source-chev", onClick: (event) => {
|
|
2188
|
+
event.stopPropagation();
|
|
2189
|
+
setGroups((current) => ({ ...current, project: !current.project }));
|
|
2190
|
+
}, children: /* @__PURE__ */ jsx10(ChevronDown2, { size: 12 }) }),
|
|
2175
2191
|
/* @__PURE__ */ jsx10(FolderOpen2, { className: "cb-source-group-icon cb-folder-icon", size: 15 }),
|
|
2176
2192
|
/* @__PURE__ */ jsx10("span", { className: "cb-source-group-name", children: projectLabel })
|
|
2177
2193
|
] }),
|
|
2178
|
-
groups.project ? /* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", className: `cb-source-row cb-source-virtual-root${folder === "" ? " is-sel" : ""}`, onClick: () => navigate(""), onContextMenu: (event) => showMenu(event, folderMenu("", projectLabel)), children: [
|
|
2180
|
-
/* @__PURE__ */ jsx10("span", { className: "cb-source-chev", children: /* @__PURE__ */ jsx10(ChevronDown2, { size: 12 }) }),
|
|
2181
|
-
/* @__PURE__ */ jsx10("span", { className: "cb-source-icon", children: /* @__PURE__ */ jsx10(FolderOpen2, { size: 15 }) }),
|
|
2182
|
-
/* @__PURE__ */ jsx10("span", { className: "cb-source-name", children: t2("contentBrowser.all") })
|
|
2183
|
-
] }),
|
|
2184
|
-
directoryTree.filter((entry) => {
|
|
2185
|
-
if (entry.depth === 0) return true;
|
|
2186
|
-
const parent = entry.path.slice(0, entry.path.lastIndexOf("/"));
|
|
2187
|
-
return !collapsed.has(parent);
|
|
2188
|
-
}).map((entry) => renderTreeRow(entry.path, entry.name, entry.depth + 1))
|
|
2189
|
-
] }) : null
|
|
2194
|
+
groups.project ? /* @__PURE__ */ jsx10("div", { className: "cb-source-group-body", children: visibleCatalogFolders(folders, collapsed).map((entry) => renderTreeRow(entry.path, entry.name, entry.path.split("/").length - 1)) }) : null
|
|
2190
2195
|
] })
|
|
2191
2196
|
] }) }),
|
|
2192
2197
|
/* @__PURE__ */ jsx10(
|
|
@@ -2451,10 +2456,10 @@ function ContentBrowserPanel({
|
|
|
2451
2456
|
}
|
|
2452
2457
|
|
|
2453
2458
|
// packages/view-plugin-content-browser/src/ui/content-browser.css
|
|
2454
|
-
var css2 = '/* packages/view-plugin-content-browser/src/ui/content-browser.css */\n.cb-root {\n --cb-layout-min-width: calc(var(--fx-component-panel-chat-min-width) + var(--fx-ref-space-48) + var(--fx-ref-space-32));\n --cb-source-width: calc(var(--fx-component-panel-config-min-width) - var(--fx-ref-space-48));\n --cb-source-min-width: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-32) + var(--fx-ref-space-12));\n --cb-resize-width: var(--fx-ref-space-6);\n --cb-resize-offset: calc(-1 * var(--fx-ref-space-2));\n --cb-grid-min-width: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-24));\n --cb-grid-min-height: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-32) + var(--fx-ref-space-12));\n --cb-thumb-default: calc(var(--fx-ref-space-48) + var(--fx-ref-space-32) + var(--fx-ref-space-8));\n --cb-import-pop-left: calc(var(--fx-component-navigation-rail-width) + var(--fx-ref-space-12));\n --cb-card-line: 13px;\n --cb-folder-icon: #c9a86a;\n --cb-group-stack: 6px;\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-width: var(--cb-layout-min-width);\n min-height: 0;\n overflow: hidden;\n background: var(--fx-color-surface-canvas);\n color: var(--fx-color-text-secondary);\n font: var(--fx-type-compact-font-weight) var(--fx-type-compact-font-size)/var(--fx-type-compact-line-height) var(--fx-type-compact-font-family);\n user-select: none;\n}\n.cb-root * {\n box-sizing: border-box;\n}\n.cb-root :where(input, textarea, .cb-address, .cb-address-input, .cb-preview-source, .cb-preview-code-plain, .cb-preview-code-shiki) {\n user-select: text;\n}\n.cb-root button,\n.cb-root input {\n font: inherit;\n}\n.cb-split {\n display: flex;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n.cb-source-panel {\n flex: 0 0 var(--cb-src-w, var(--cb-source-width));\n width: var(--cb-src-w, var(--cb-source-width));\n min-width: var(--cb-source-min-width);\n overflow: auto;\n background: var(--fx-color-surface-sidebar);\n}\n.cb-resize-handle {\n position: relative;\n z-index: var(--fx-layer-sticky);\n flex: 0 0 var(--cb-resize-width);\n width: var(--cb-resize-width);\n margin: 0 var(--cb-resize-offset);\n cursor: col-resize;\n}\n.cb-resize-handle::after {\n content: "";\n position: absolute;\n inset: 0 auto 0 50%;\n width: var(--fx-border-width);\n background: var(--fx-color-border-subtle);\n}\n.cb-resize-handle:hover::after {\n background: var(--fx-color-border-focus);\n}\n.cb-source-tree {\n min-width: 0;\n padding: var(--fx-ref-space-4) 0;\n}\n.cb-source-group + .cb-source-group {\n margin-top: var(--fx-ref-space-2);\n}\n.cb-source-group-head,\n.cb-source-row {\n appearance: none;\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n border: var(--fx-border-width) solid transparent;\n text-align: left;\n cursor: pointer;\n}\n.cb-source-group-head {\n gap: var(--fx-ref-space-4);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-secondary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-source-group-head:hover,\n.cb-source-row:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-source-group-head.collapsed .cb-source-chev,\n.cb-source-chev.collapsed {\n transform: rotate(-90deg);\n}\n.cb-source-group-icon {\n flex: 0 0 auto;\n color: var(--fx-color-text-muted);\n}\n.cb-source-group-head .cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-source-group-name,\n.cb-source-name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-source-group-body {\n padding: var(--fx-ref-space-2) 0 var(--fx-ref-space-4);\n}\n.cb-source-empty {\n padding: var(--fx-ref-space-4) var(--fx-ref-space-12) var(--fx-ref-space-4) var(--fx-ref-space-16);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n font-style: italic;\n}\n.cb-source-row {\n gap: var(--fx-ref-space-4);\n min-height: var(--fx-component-editor-row-height);\n padding-top: var(--fx-ref-space-4);\n padding-right: var(--fx-ref-space-12);\n padding-bottom: var(--fx-ref-space-4);\n background: transparent;\n color: var(--fx-color-text-secondary);\n}\n.cb-source-row.is-path:not(.is-sel) {\n background: color-mix(in srgb, var(--fx-color-surface-hover) 55%, transparent);\n color: var(--fx-color-text-primary);\n}\n.cb-source-row.is-sel {\n background: var(--fx-color-selection-bg);\n color: var(--fx-color-selection-fg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-source-row:focus-visible,\n.cb-source-group-head:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: calc(-1 * var(--fx-border-focus-width));\n}\n.cb-source-chev {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-component-choice-size);\n height: var(--fx-component-choice-size);\n flex: 0 0 var(--fx-component-choice-size);\n color: var(--fx-color-text-muted);\n transition: transform var(--fx-motion-hover) var(--fx-motion-ease);\n}\n.cb-source-chev.hidden {\n visibility: hidden;\n}\n.cb-source-icon {\n display: inline-flex;\n flex: 0 0 var(--fx-component-choice-size);\n color: var(--cb-folder-icon);\n}\n.cb-source-name {\n flex: 1;\n}\n.cb-source-fav-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-width: 0;\n}\n.cb-source-fav-row .cb-source-row {\n flex: 1;\n width: auto;\n min-width: 0;\n padding-right: var(--fx-ref-space-4);\n background: transparent;\n box-shadow: none;\n}\n.cb-source-fav-row .cb-source-row:hover {\n background: transparent;\n}\n.cb-source-fav-row:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-source-fav-row.is-path:not(.is-sel) {\n background: color-mix(in srgb, var(--fx-color-surface-hover) 55%, transparent);\n color: var(--fx-color-text-primary);\n}\n.cb-source-fav-row.is-sel {\n background: var(--fx-color-selection-bg);\n color: var(--fx-color-selection-fg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-source-unfav {\n appearance: none;\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-component-control-icon-hit-area-min);\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--fx-color-status-warning-fg);\n cursor: pointer;\n}\n.cb-source-unfav:hover {\n color: var(--fx-color-text-primary);\n}\n.cb-source-virtual-root {\n padding-left: var(--fx-ref-space-16);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-asset-view {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.cb-toolbar {\n position: relative;\n flex: 0 0 var(--fx-component-panel-header-height);\n gap: var(--fx-ref-space-6);\n min-width: 0;\n padding: 0 var(--fx-ref-space-8);\n}\n.cb-toolbar > .fx-panel-header__slot:nth-child(2) {\n flex: 1 1 auto;\n}\n.cb-toolbar-group {\n flex: 0 0 auto;\n}\n.cb-navigation-bar {\n flex: 1 1 auto;\n min-width: calc(var(--fx-ref-space-48) + var(--fx-ref-space-32));\n overflow: hidden;\n}\n.cb-address-slot {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-width: var(--fx-ref-space-32);\n height: var(--fx-component-control-height-compact);\n}\n.cb-address,\n.cb-address-input {\n width: 100%;\n height: var(--fx-component-control-height-compact);\n min-width: 0;\n box-sizing: border-box;\n border: var(--fx-border-width) solid var(--fx-color-border-control);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n color: var(--fx-color-text-secondary);\n}\n.cb-address {\n display: flex;\n align-items: center;\n padding: 0 var(--fx-ref-space-8);\n overflow: hidden;\n cursor: text;\n}\n.cb-address-crumb {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n min-width: 0;\n overflow: hidden;\n}\n.cb-root .cb-address:focus-visible,\n.cb-root .cb-address-input:focus,\n.cb-root .cb-address-input:focus-visible {\n outline: none;\n border-color: var(--fx-color-border-essential);\n}\n.cb-address-input {\n margin: 0;\n padding: 0 var(--fx-ref-space-8);\n font: inherit;\n color: var(--fx-color-text-primary);\n appearance: none;\n outline: none;\n}\n.cb-address-input::placeholder {\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-segment {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-item {\n min-width: 0;\n padding: var(--fx-ref-space-2) var(--fx-ref-space-4);\n border: 0;\n border-radius: var(--fx-ref-radius-4);\n background: transparent;\n color: var(--fx-color-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n.cb-crumb-item:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-current {\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-crumb-chevron {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-chevron-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-ref-space-16);\n height: var(--fx-component-control-height-compact);\n padding: 0;\n border: 0;\n border-radius: var(--fx-ref-radius-4);\n background: transparent;\n color: var(--fx-color-text-muted);\n cursor: pointer;\n appearance: none;\n}\n.cb-crumb-chevron-btn > svg {\n transition: transform 0.12s ease;\n}\n.cb-crumb-chevron-btn:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-chevron-btn[aria-expanded=true] {\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-chevron-btn[aria-expanded=true] > svg {\n transform: rotate(90deg);\n}\n.cb-crumb-chevron .fx-menu-dropdown__anchor {\n display: inline-flex;\n}\n.cb-crumb-menu .fx-menu {\n max-height: 320px;\n overflow-y: auto;\n}\n.cb-crumb-menu .fx-menu__item[data-active] {\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-content-body {\n display: flex;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.cb-grid-column {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n min-height: var(--cb-grid-min-height);\n overflow: hidden;\n}\n.cb-filter-bar {\n position: relative;\n flex: 0 0 var(--fx-component-panel-header-height);\n gap: var(--fx-ref-space-6);\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n}\n.cb-filter-bar .is-active {\n color: var(--fx-color-brand-text);\n background: var(--fx-color-brand-soft);\n}\n.cb-search-box {\n flex: 0 0 auto;\n width: var(--fx-component-menu-min-width);\n max-width: 100%;\n}\n.cb-search-box .fx-input {\n height: var(--fx-component-control-height-compact);\n}\n.cb-pop {\n position: absolute;\n z-index: var(--fx-layer-popover);\n}\n.cb-pop-create {\n top: var(--fx-component-panel-header-height);\n left: var(--fx-ref-space-8);\n}\n.cb-pop-import {\n top: var(--fx-component-panel-header-height);\n left: var(--cb-import-pop-left);\n}\n.cb-settings-menu {\n min-width: var(--fx-component-menu-min-width);\n padding: var(--fx-ref-space-6);\n border: var(--fx-border-width) solid var(--fx-color-border-default);\n border-radius: var(--fx-radius-popover);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-primary);\n box-shadow: var(--fx-shadow-floating);\n}\n.cb-settings-menu > strong {\n display: block;\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-menu-title {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-ref-space-6);\n}\n.cb-menu-icon-placeholder {\n width: var(--fx-component-control-icon-size);\n}\n.cb-layout-switch {\n padding: var(--fx-ref-space-2) var(--fx-ref-space-8) var(--fx-ref-space-6);\n}\n.cb-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-ref-space-12);\n padding: var(--fx-ref-space-8);\n}\n.cb-settings-label {\n color: var(--fx-color-text-secondary);\n white-space: nowrap;\n}\n.cb-settings-range {\n flex: 0 0 auto;\n}\n.cb-settings-switch-row {\n margin: 0 var(--fx-ref-space-2);\n}\n.cb-grid-view {\n position: relative;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n}\n.cb-fe-grid {\n display: grid;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n grid-template-columns: repeat(auto-fill, minmax(0, var(--cb-thumb, 88px)));\n grid-auto-rows: max-content;\n align-content: start;\n justify-content: start;\n gap: calc(var(--fx-ref-space-8) + var(--cb-group-stack));\n padding: var(--fx-ref-space-12);\n overflow-x: hidden;\n}\n.cb-grid-item {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n overflow: hidden;\n padding: var(--fx-ref-space-4);\n border: var(--fx-border-width) solid transparent;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-secondary);\n cursor: pointer;\n}\n.cb-grid-folder {\n background: transparent;\n}\n.cb-grid-item:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-grid-item.sel {\n border-color: var(--fx-color-selection-indicator);\n background: var(--fx-color-selection-bg);\n box-shadow: 0 0 0 var(--fx-border-width) var(--fx-color-selection-indicator);\n}\n.cb-grid-item:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: var(--fx-border-focus-offset);\n}\n.cb-grid-item.is-busy,\n.cb-details-row.is-busy {\n cursor: progress;\n}\n.cb-item-busy-veil {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--fx-color-surface-raised) 55%, transparent);\n pointer-events: none;\n}\n.cb-item-busy-veil .fx-spinner,\n.cb-item-busy-inline {\n width: 14px;\n height: 14px;\n flex: 0 0 14px;\n}\n.cb-grid-view.cb-layout-list .cb-item-busy-inline {\n margin-left: auto;\n}\n.cb-grid-thumb {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n aspect-ratio: 1;\n overflow: hidden;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n}\n.cb-grid-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: var(--fx-color-text-muted);\n}\n.cb-grid-icon svg {\n width: 46%;\n height: 46%;\n}\n.cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-preview-ico.cb-folder-icon,\n.cb-preview-list-ico.cb-folder-icon,\n.cb-details-icon.cb-folder-icon,\n.cb-filter-option-icon.cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-grid-thumb .cb-folder-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n.cb-grid-thumb .cb-folder-icon svg {\n width: 86%;\n height: 86%;\n color: currentColor;\n}\n.cb-grid-label {\n width: 100%;\n margin-top: var(--fx-ref-space-4);\n overflow: hidden;\n text-align: center;\n}\n.cb-fe-label {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n width: 100%;\n flex: 0 0 auto;\n height: calc(var(--cb-card-line) * 4);\n overflow: hidden;\n}\n.cb-fe-name {\n display: -webkit-box;\n margin-top: 0;\n overflow: hidden;\n color: var(--fx-color-text-primary);\n font-size: var(--fx-type-caption-font-size);\n line-height: var(--cb-card-line);\n text-align: center;\n white-space: normal;\n word-break: normal;\n overflow-wrap: anywhere;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 4;\n}\n.cb-fe-label.has-kind .cb-fe-name {\n min-height: calc(var(--cb-card-line) * 3);\n -webkit-line-clamp: 3;\n}\n.cb-card-meta,\n.cb-card-kind {\n width: 100%;\n flex: 0 0 auto;\n margin-top: auto;\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n line-height: var(--cb-card-line);\n text-align: center;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-fe-grid.cb-layout-list {\n grid-template-columns: minmax(0, 1fr);\n grid-auto-rows: auto;\n gap: var(--fx-ref-space-2);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-6);\n overflow-x: hidden;\n}\n.cb-grid-view.cb-layout-list .cb-grid-item {\n flex-direction: row;\n align-items: center;\n width: 100%;\n min-width: 0;\n min-height: 40px;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n gap: var(--fx-ref-space-8);\n overflow: hidden;\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-grid-item.sel {\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-grid-thumb {\n width: 32px;\n height: 32px;\n flex: 0 0 32px;\n aspect-ratio: auto;\n overflow: visible;\n}\n.cb-grid-view.cb-layout-list .cb-file-thumb {\n position: static;\n}\n.cb-grid-view.cb-layout-list .cb-grid-thumb svg {\n width: 20px;\n height: 20px;\n}\n.cb-layout-list .cb-fe-label {\n flex: 1 1 auto;\n min-width: 0;\n height: auto;\n overflow: hidden;\n flex-direction: row;\n align-items: center;\n text-align: left;\n}\n.cb-layout-list .cb-fe-name,\n.cb-layout-list .cb-fe-label.has-kind .cb-fe-name {\n display: block;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n text-align: left;\n white-space: nowrap;\n text-overflow: ellipsis;\n -webkit-line-clamp: unset;\n}\n.cb-layout-list .cb-card-meta,\n.cb-layout-list .cb-card-kind {\n margin-top: 0;\n}\n.cb-layout-list .cb-card-meta {\n display: none;\n}\n.cb-grid-view.cb-layout-list .cb-group-card:not(.cb-pack-expanded),\n.cb-grid-view.cb-layout-list .cb-group-card.sel:not(.cb-pack-expanded) {\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-group-member:not(.sel) {\n background: transparent;\n}\n.cb-grid-view.cb-layout-list .cb-group-member:not(.sel):hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-grid-view.cb-layout-list .cb-group-card,\n.cb-grid-view.cb-layout-list .cb-pack-expanded {\n padding-right: calc(var(--fx-ref-space-8) + 44px);\n}\n.cb-grid-view.cb-layout-list .cb-group-badge {\n top: 50%;\n right: var(--fx-ref-space-8);\n bottom: auto;\n transform: translateY(-50%);\n}\n.cb-details-view {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-height: 0;\n overflow: auto;\n padding: 0 var(--fx-ref-space-6) var(--fx-ref-space-8);\n --cb-details-cols: minmax(160px, 1fr) 160px 90px 130px;\n}\n.cb-details-header,\n.cb-details-row {\n display: grid;\n grid-template-columns: var(--cb-details-cols);\n align-items: center;\n}\n.cb-details-header {\n position: sticky;\n z-index: var(--fx-layer-sticky);\n top: 0;\n min-height: var(--fx-component-editor-row-height);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-default);\n background: var(--fx-color-surface-canvas);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-details-th {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-4);\n height: 100%;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n}\n.cb-details-th:hover {\n color: var(--fx-color-text-primary);\n}\n.cb-details-th.active,\n.cb-details-th[data-active=true] {\n color: var(--fx-color-brand-text);\n}\n.cb-details-sort-arrow {\n font-size: 9px;\n}\n.cb-details-body {\n display: flex;\n flex-direction: column;\n}\n.cb-details-row {\n position: relative;\n width: 100%;\n border-radius: var(--fx-ref-radius-4);\n color: var(--fx-color-text-secondary);\n cursor: pointer;\n}\n.cb-details-row:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-details-row.sel {\n background: var(--fx-color-selection-bg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-details-row:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: -2px;\n}\n.cb-details-cell {\n min-width: 0;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-details-name {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-6);\n}\n.cb-details-icon {\n display: inline-flex;\n width: 16px;\n height: 16px;\n flex: 0 0 16px;\n}\n.cb-details-icon svg {\n width: 16px;\n height: 16px;\n}\n.cb-details-type,\n.cb-details-size,\n.cb-details-modified {\n color: var(--fx-color-text-muted);\n}\n.cb-empty {\n grid-column: 1 / -1;\n color: var(--fx-color-text-muted);\n}\n.cb-status {\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex: 0 0 var(--fx-component-editor-row-height);\n min-height: var(--fx-component-editor-row-height);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border-top: var(--fx-border-width) solid var(--fx-color-border-subtle);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-file-thumb {\n position: relative;\n}\n.cb-group-card:not(.cb-pack-expanded) {\n box-shadow:\n 3px 3px 0 0 var(--fx-color-surface-raised),\n 3px 3px 0 1px var(--fx-color-border-subtle),\n 6px 6px 0 0 var(--fx-color-surface-raised),\n 6px 6px 0 1px var(--fx-color-border-subtle);\n}\n.cb-group-card.sel:not(.cb-pack-expanded) {\n box-shadow:\n 0 0 0 var(--fx-border-width) var(--fx-color-selection-indicator),\n 3px 3px 0 0 var(--fx-color-surface-raised),\n 3px 3px 0 1px var(--fx-color-border-subtle),\n 6px 6px 0 0 var(--fx-color-surface-raised),\n 6px 6px 0 1px var(--fx-color-border-subtle);\n}\n.cb-group-badge {\n position: absolute;\n right: 3px;\n bottom: 3px;\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 3px;\n height: 22px;\n padding: 0 8px;\n border: 0;\n border-radius: 11px;\n background: color-mix(in srgb, var(--fx-color-surface-canvas) 62%, transparent);\n color: var(--fx-color-text-inverse);\n font-size: 11px;\n font-weight: var(--fx-ref-font-weight-600);\n cursor: pointer;\n}\n.cb-group-badge:hover {\n background: rgb(0 0 0 / 62%);\n color: #fff;\n}\n.cb-group-badge svg {\n width: 11px;\n height: 11px;\n transition: transform var(--fx-motion-hover) var(--fx-motion-ease);\n}\n.cb-group-backdrops {\n position: absolute;\n inset: 0;\n z-index: 0;\n pointer-events: none;\n}\n.cb-group-backdrop {\n position: absolute;\n background: rgb(0 0 0 / 10%);\n}\n.cb-group-member {\n z-index: 1;\n}\n.cb-group-badge.open svg {\n transform: rotate(180deg);\n}\n.cb-preview-panel {\n display: flex;\n flex: 0 0 var(--cb-preview-w, 280px);\n flex-direction: column;\n width: var(--cb-preview-w, 280px);\n min-width: 180px;\n max-width: 560px;\n min-height: 0;\n overflow: hidden;\n border-left: var(--fx-border-width) solid var(--fx-color-border-subtle);\n background: var(--fx-color-surface-canvas);\n}\n.cb-preview-head {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n padding: var(--fx-ref-space-8) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n flex-shrink: 0;\n}\n.cb-preview-mode-bar {\n display: flex;\n gap: var(--fx-ref-space-4);\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n flex-shrink: 0;\n}\n.cb-preview-mode-btn {\n flex: 1 1 0;\n min-width: 0;\n height: 24px;\n padding: 0 var(--fx-ref-space-8);\n overflow: hidden;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: transparent;\n color: var(--fx-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n.cb-preview-mode-btn:hover {\n color: var(--fx-color-text-primary);\n background: var(--fx-color-surface-hover);\n}\n.cb-preview-mode-btn.is-active {\n color: var(--fx-color-text-primary);\n border-color: var(--fx-color-border-strong);\n background: var(--fx-color-surface-raised);\n}\n.cb-preview-ico,\n.cb-preview-list-ico {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 28px;\n width: 28px;\n height: 28px;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-muted);\n}\n.cb-preview-list-ico {\n flex-basis: 16px;\n width: 16px;\n height: 16px;\n background: transparent;\n}\n.cb-preview-title {\n flex: 1;\n min-width: 0;\n}\n.cb-preview-title .name {\n overflow: hidden;\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-title .meta {\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-close {\n width: 24px;\n height: 24px;\n}\n.cb-preview-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: var(--fx-ref-space-8);\n}\n.cb-preview-empty .cb-preview-body {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: var(--fx-color-text-muted);\n}\n.cb-preview-list,\n.cb-preview-asset-list {\n display: flex;\n flex-direction: column;\n gap: var(--fx-ref-space-4);\n}\n.cb-preview-list-item,\n.cb-preview-asset-row {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n min-width: 0;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-6);\n border-radius: var(--fx-radius-control);\n color: var(--fx-color-text-secondary);\n}\n.cb-preview-list-item:hover,\n.cb-preview-asset-row:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-preview-list-item > span:nth-of-type(2) {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-list-item .sub,\n.cb-preview-asset-row .guid,\n.cb-preview-asset-row .kind {\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n}\n.cb-preview-list-item .sub {\n flex: 0 1 38%;\n min-width: 0;\n margin-left: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-row > .cb-preview-asset-main {\n flex: 1 1 0;\n min-width: 0;\n}\n.cb-preview-asset-row > .cb-preview-asset-main > div {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-row .kind {\n margin-left: 0;\n}\n.cb-preview-asset-row .guid {\n flex: 0 1 auto;\n max-width: 42%;\n min-width: 0;\n margin-left: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-media {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--fx-ref-space-8);\n min-height: 0;\n}\n.cb-preview-image {\n display: block;\n max-width: 100%;\n max-height: min(360px, 50vh);\n object-fit: contain;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n}\n.cb-preview-audio,\n.cb-preview-video {\n width: 100%;\n max-width: 100%;\n}\n.cb-preview-video {\n max-height: min(360px, 50vh);\n border-radius: var(--fx-radius-control);\n background: #000;\n}\n.cb-preview-font-sample {\n margin: 0;\n color: var(--fx-color-text-primary);\n font-size: 1.35rem;\n line-height: 1.4;\n text-align: center;\n}\n.cb-preview-font-meta {\n margin: 0;\n max-width: 100%;\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font: var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n text-align: center;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-hero {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--fx-ref-space-16) 0 var(--fx-ref-space-8);\n}\n.cb-preview-hero-icon {\n color: var(--fx-color-text-muted);\n}\n.cb-preview-note {\n padding: var(--fx-ref-space-8) var(--fx-ref-space-8);\n overflow: hidden;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-source {\n margin: 0;\n padding: var(--fx-ref-space-8);\n overflow: auto;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n color: var(--fx-color-text-secondary);\n font: var(--fx-type-code-font-weight) var(--fx-type-code-font-size)/var(--fx-type-code-line-height) var(--fx-type-code-font-family);\n white-space: pre-wrap;\n}\n.cb-preview-code {\n display: flex;\n flex-direction: column;\n gap: var(--fx-ref-space-6);\n min-height: 0;\n}\n.cb-preview-code-head {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n}\n.cb-preview-code-badge {\n padding: var(--fx-ref-space-2) var(--fx-ref-space-8);\n border-radius: var(--fx-radius-pill);\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n}\n.cb-preview-code-hint {\n color: var(--fx-color-text-disabled);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-preview-code-plain,\n.cb-preview-code-shiki pre.shiki {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n font: var(--fx-type-code-font-weight) var(--fx-type-code-font-size)/var(--fx-type-code-line-height) var(--fx-type-code-font-family);\n}\n.cb-preview-code-plain {\n color: var(--fx-color-text-secondary);\n}\n.cb-preview-code-shiki pre.shiki {\n background: transparent !important;\n}\n.cb-preview-code-shiki pre.shiki code {\n background: transparent;\n padding: 0;\n border-radius: 0;\n color: inherit;\n font-size: inherit;\n}\n.cb-filter-menu-body {\n min-width: 220px;\n padding: var(--fx-ref-space-4);\n border: var(--fx-border-width) solid var(--fx-color-border-default);\n border-radius: var(--fx-radius-popover);\n background: var(--fx-color-surface-raised);\n box-shadow: var(--fx-shadow-floating);\n}\n.cb-filter-section {\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8) var(--fx-ref-space-4);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-filter-separator {\n height: var(--fx-border-width);\n margin: var(--fx-ref-space-4) 0;\n background: var(--fx-color-border-subtle);\n}\n.cb-filter-option {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n width: 100%;\n min-height: var(--fx-component-editor-row-height);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border: 0;\n border-radius: var(--fx-radius-control);\n background: transparent;\n color: var(--fx-color-text-secondary);\n text-align: left;\n cursor: pointer;\n}\n.cb-filter-option:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-filter-option:disabled {\n color: var(--fx-color-text-disabled);\n cursor: default;\n}\n.cb-filter-option-icon {\n flex: 0 0 14px;\n width: 14px;\n color: var(--fx-color-text-muted);\n}\n.cb-filter-check {\n flex: 0 0 var(--fx-component-choice-size);\n pointer-events: none;\n}\n.cb-filter-check.fx-choice {\n align-items: center;\n gap: 0;\n min-height: 0;\n margin: 0;\n}\n.cb-filter-check .fx-choice__text {\n display: none;\n}\n.cb-radio-menu {\n width: 14px;\n height: 14px;\n flex: 0 0 14px;\n border: var(--fx-border-width) solid var(--fx-color-border-control);\n border-radius: 999px;\n}\n.cb-radio-menu[data-checked=true] {\n border-color: var(--fx-color-choice-indicator);\n background:\n radial-gradient(\n circle at center,\n var(--fx-color-choice-indicator) 4px,\n transparent 5px);\n}\n@container (max-width: 660px) {\n .cb-toolbar-group .fx-button {\n width: var(--fx-component-control-height-compact);\n padding: 0;\n font-size: 0;\n }\n}\n';
|
|
2455
|
-
if (typeof document !== "undefined" && !document.querySelector('style[data-forgeax-css="
|
|
2459
|
+
var css2 = '/* packages/view-plugin-content-browser/src/ui/content-browser.css */\n.cb-root {\n --cb-layout-min-width: calc(var(--fx-component-panel-chat-min-width) + var(--fx-ref-space-48) + var(--fx-ref-space-32));\n --cb-source-width: calc(var(--fx-component-panel-config-min-width) - var(--fx-ref-space-48));\n --cb-source-min-width: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-32) + var(--fx-ref-space-12));\n --cb-resize-width: var(--fx-ref-space-6);\n --cb-resize-offset: calc(-1 * var(--fx-ref-space-2));\n --cb-grid-min-width: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-24));\n --cb-grid-min-height: calc(var(--fx-ref-space-48) * 2 + var(--fx-ref-space-32) + var(--fx-ref-space-12));\n --cb-thumb-default: calc(var(--fx-ref-space-48) + var(--fx-ref-space-32) + var(--fx-ref-space-8));\n --cb-import-pop-left: calc(var(--fx-component-navigation-rail-width) + var(--fx-ref-space-12));\n --cb-card-line: 13px;\n --cb-folder-icon: #c9a86a;\n --cb-group-stack: 6px;\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-width: var(--cb-layout-min-width);\n min-height: 0;\n overflow: hidden;\n background: var(--fx-color-surface-canvas);\n color: var(--fx-color-text-secondary);\n font: var(--fx-type-compact-font-weight) var(--fx-type-compact-font-size)/var(--fx-type-compact-line-height) var(--fx-type-compact-font-family);\n user-select: none;\n}\n.cb-root * {\n box-sizing: border-box;\n}\n.cb-root :where(input, textarea, .cb-address, .cb-address-input, .cb-preview-source, .cb-preview-code-plain, .cb-preview-code-shiki) {\n user-select: text;\n}\n.cb-root button,\n.cb-root input {\n font: inherit;\n}\n.cb-split {\n display: flex;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n.cb-source-panel {\n flex: 0 0 var(--cb-src-w, var(--cb-source-width));\n width: var(--cb-src-w, var(--cb-source-width));\n min-width: var(--cb-source-min-width);\n overflow: auto;\n background: var(--fx-color-surface-sidebar);\n}\n.cb-resize-handle {\n position: relative;\n z-index: var(--fx-layer-sticky);\n flex: 0 0 var(--cb-resize-width);\n width: var(--cb-resize-width);\n margin: 0 var(--cb-resize-offset);\n cursor: col-resize;\n}\n.cb-resize-handle::after {\n content: "";\n position: absolute;\n inset: 0 auto 0 50%;\n width: var(--fx-border-width);\n background: var(--fx-color-border-subtle);\n}\n.cb-resize-handle:hover::after {\n background: var(--fx-color-border-focus);\n}\n.cb-source-tree {\n min-width: 0;\n padding: var(--fx-ref-space-4) 0;\n}\n.cb-source-group + .cb-source-group {\n margin-top: var(--fx-ref-space-2);\n}\n.cb-source-group-head,\n.cb-source-row {\n appearance: none;\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n border: var(--fx-border-width) solid transparent;\n text-align: left;\n cursor: pointer;\n}\n.cb-source-group-head {\n gap: var(--fx-ref-space-4);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-secondary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-source-group-head:hover,\n.cb-source-row:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-source-group-head.collapsed .cb-source-chev,\n.cb-source-chev.collapsed {\n transform: rotate(-90deg);\n}\n.cb-source-group-icon {\n flex: 0 0 auto;\n color: var(--fx-color-text-muted);\n}\n.cb-source-group-head .cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-source-group-name,\n.cb-source-name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-source-group-body {\n padding: var(--fx-ref-space-2) 0 var(--fx-ref-space-4);\n}\n.cb-source-empty {\n padding: var(--fx-ref-space-4) var(--fx-ref-space-12) var(--fx-ref-space-4) var(--fx-ref-space-16);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n font-style: italic;\n}\n.cb-source-row {\n gap: var(--fx-ref-space-4);\n min-height: var(--fx-component-editor-row-height);\n padding-top: var(--fx-ref-space-4);\n padding-right: var(--fx-ref-space-12);\n padding-bottom: var(--fx-ref-space-4);\n background: transparent;\n color: var(--fx-color-text-secondary);\n}\n.cb-source-row.is-path:not(.is-sel) {\n background: color-mix(in srgb, var(--fx-color-surface-hover) 55%, transparent);\n color: var(--fx-color-text-primary);\n}\n.cb-source-group-head.is-sel,\n.cb-source-row.is-sel {\n background: var(--fx-color-selection-bg);\n color: var(--fx-color-selection-fg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-source-row:focus-visible,\n.cb-source-group-head:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: calc(-1 * var(--fx-border-focus-width));\n}\n.cb-source-chev {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-component-choice-size);\n height: var(--fx-component-choice-size);\n flex: 0 0 var(--fx-component-choice-size);\n color: var(--fx-color-text-muted);\n transition: transform var(--fx-motion-hover) var(--fx-motion-ease);\n}\n.cb-source-chev.hidden {\n visibility: hidden;\n}\n.cb-source-icon {\n display: inline-flex;\n flex: 0 0 var(--fx-component-choice-size);\n color: var(--cb-folder-icon);\n}\n.cb-source-name {\n flex: 1;\n}\n.cb-source-fav-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-width: 0;\n}\n.cb-source-fav-row .cb-source-row {\n flex: 1;\n width: auto;\n min-width: 0;\n padding-right: var(--fx-ref-space-4);\n background: transparent;\n box-shadow: none;\n}\n.cb-source-fav-row .cb-source-row:hover {\n background: transparent;\n}\n.cb-source-fav-row:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-source-fav-row.is-path:not(.is-sel) {\n background: color-mix(in srgb, var(--fx-color-surface-hover) 55%, transparent);\n color: var(--fx-color-text-primary);\n}\n.cb-source-fav-row.is-sel {\n background: var(--fx-color-selection-bg);\n color: var(--fx-color-selection-fg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-source-unfav {\n appearance: none;\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-component-control-icon-hit-area-min);\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--fx-color-status-warning-fg);\n cursor: pointer;\n}\n.cb-source-unfav:hover {\n color: var(--fx-color-text-primary);\n}\n.cb-asset-view {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.cb-toolbar {\n position: relative;\n flex: 0 0 var(--fx-component-panel-header-height);\n gap: var(--fx-ref-space-6);\n min-width: 0;\n padding: 0 var(--fx-ref-space-8);\n}\n.cb-toolbar > .fx-panel-header__slot:nth-child(2) {\n flex: 1 1 auto;\n}\n.cb-toolbar-group {\n flex: 0 0 auto;\n}\n.cb-navigation-bar {\n flex: 1 1 auto;\n min-width: calc(var(--fx-ref-space-48) + var(--fx-ref-space-32));\n overflow: hidden;\n}\n.cb-address-slot {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-width: var(--fx-ref-space-32);\n height: var(--fx-component-control-height-compact);\n}\n.cb-address,\n.cb-address-input {\n width: 100%;\n height: var(--fx-component-control-height-compact);\n min-width: 0;\n box-sizing: border-box;\n border: var(--fx-border-width) solid var(--fx-color-border-control);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n color: var(--fx-color-text-secondary);\n}\n.cb-address {\n display: flex;\n align-items: center;\n padding: 0 var(--fx-ref-space-8);\n overflow: hidden;\n cursor: text;\n}\n.cb-address-crumb {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n min-width: 0;\n overflow: hidden;\n}\n.cb-root .cb-address:focus-visible,\n.cb-root .cb-address-input:focus,\n.cb-root .cb-address-input:focus-visible {\n outline: none;\n border-color: var(--fx-color-border-essential);\n}\n.cb-address-input {\n margin: 0;\n padding: 0 var(--fx-ref-space-8);\n font: inherit;\n color: var(--fx-color-text-primary);\n appearance: none;\n outline: none;\n}\n.cb-address-input::placeholder {\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-segment {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-item {\n min-width: 0;\n padding: var(--fx-ref-space-2) var(--fx-ref-space-4);\n border: 0;\n border-radius: var(--fx-ref-radius-4);\n background: transparent;\n color: var(--fx-color-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n.cb-crumb-item:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-current {\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-crumb-chevron {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--fx-color-text-muted);\n}\n.cb-crumb-chevron-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--fx-ref-space-16);\n height: var(--fx-component-control-height-compact);\n padding: 0;\n border: 0;\n border-radius: var(--fx-ref-radius-4);\n background: transparent;\n color: var(--fx-color-text-muted);\n cursor: pointer;\n appearance: none;\n}\n.cb-crumb-chevron-btn > svg {\n transition: transform 0.12s ease;\n}\n.cb-crumb-chevron-btn:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-chevron-btn[aria-expanded=true] {\n color: var(--fx-color-text-primary);\n}\n.cb-crumb-chevron-btn[aria-expanded=true] > svg {\n transform: rotate(90deg);\n}\n.cb-crumb-chevron .fx-menu-dropdown__anchor {\n display: inline-flex;\n}\n.cb-crumb-menu .fx-menu {\n max-height: 320px;\n overflow-y: auto;\n}\n.cb-crumb-menu .fx-menu__item[data-active] {\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-content-body {\n display: flex;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.cb-grid-column {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n min-height: var(--cb-grid-min-height);\n overflow: hidden;\n}\n.cb-filter-bar {\n position: relative;\n flex: 0 0 var(--fx-component-panel-header-height);\n gap: var(--fx-ref-space-6);\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n}\n.cb-filter-bar .is-active {\n color: var(--fx-color-brand-text);\n background: var(--fx-color-brand-soft);\n}\n.cb-search-box {\n flex: 0 0 auto;\n width: var(--fx-component-menu-min-width);\n max-width: 100%;\n}\n.cb-search-box .fx-input {\n height: var(--fx-component-control-height-compact);\n}\n.cb-pop {\n position: absolute;\n z-index: var(--fx-layer-popover);\n}\n.cb-pop-create {\n top: var(--fx-component-panel-header-height);\n left: var(--fx-ref-space-8);\n}\n.cb-pop-import {\n top: var(--fx-component-panel-header-height);\n left: var(--cb-import-pop-left);\n}\n.cb-settings-menu {\n min-width: var(--fx-component-menu-min-width);\n padding: var(--fx-ref-space-6);\n border: var(--fx-border-width) solid var(--fx-color-border-default);\n border-radius: var(--fx-radius-popover);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-primary);\n box-shadow: var(--fx-shadow-floating);\n}\n.cb-settings-menu > strong {\n display: block;\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-menu-title {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-ref-space-6);\n}\n.cb-menu-icon-placeholder {\n width: var(--fx-component-control-icon-size);\n}\n.cb-layout-switch {\n padding: var(--fx-ref-space-2) var(--fx-ref-space-8) var(--fx-ref-space-6);\n}\n.cb-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-ref-space-12);\n padding: var(--fx-ref-space-8);\n}\n.cb-settings-label {\n color: var(--fx-color-text-secondary);\n white-space: nowrap;\n}\n.cb-settings-range {\n flex: 0 0 auto;\n}\n.cb-settings-switch-row {\n margin: 0 var(--fx-ref-space-2);\n}\n.cb-grid-view {\n position: relative;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n}\n.cb-fe-grid {\n display: grid;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n grid-template-columns: repeat(auto-fill, minmax(0, var(--cb-thumb, 88px)));\n grid-auto-rows: max-content;\n align-content: start;\n justify-content: start;\n gap: calc(var(--fx-ref-space-8) + var(--cb-group-stack));\n padding: var(--fx-ref-space-12);\n overflow-x: hidden;\n}\n.cb-grid-item {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n overflow: hidden;\n padding: var(--fx-ref-space-4);\n border: var(--fx-border-width) solid transparent;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-secondary);\n cursor: pointer;\n}\n.cb-grid-folder {\n background: transparent;\n}\n.cb-grid-item:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-grid-item.sel {\n border-color: var(--fx-color-selection-indicator);\n background: var(--fx-color-selection-bg);\n box-shadow: 0 0 0 var(--fx-border-width) var(--fx-color-selection-indicator);\n}\n.cb-grid-item:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: var(--fx-border-focus-offset);\n}\n.cb-grid-item.is-busy,\n.cb-details-row.is-busy {\n cursor: progress;\n}\n.cb-item-busy-veil {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--fx-color-surface-raised) 55%, transparent);\n pointer-events: none;\n}\n.cb-item-busy-veil .fx-spinner,\n.cb-item-busy-inline {\n width: 14px;\n height: 14px;\n flex: 0 0 14px;\n}\n.cb-grid-view.cb-layout-list .cb-item-busy-inline {\n margin-left: auto;\n}\n.cb-grid-thumb {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n aspect-ratio: 1;\n overflow: hidden;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n}\n.cb-grid-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: var(--fx-color-text-muted);\n}\n.cb-grid-icon svg {\n width: 46%;\n height: 46%;\n}\n.cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-preview-ico.cb-folder-icon,\n.cb-preview-list-ico.cb-folder-icon,\n.cb-details-icon.cb-folder-icon,\n.cb-filter-option-icon.cb-folder-icon {\n color: var(--cb-folder-icon);\n}\n.cb-grid-thumb .cb-folder-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n.cb-grid-thumb .cb-folder-icon svg {\n width: 86%;\n height: 86%;\n color: currentColor;\n}\n.cb-grid-label {\n width: 100%;\n margin-top: var(--fx-ref-space-4);\n overflow: hidden;\n text-align: center;\n}\n.cb-fe-label {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n width: 100%;\n flex: 0 0 auto;\n height: calc(var(--cb-card-line) * 4);\n overflow: hidden;\n}\n.cb-fe-name {\n display: -webkit-box;\n margin-top: 0;\n overflow: hidden;\n color: var(--fx-color-text-primary);\n font-size: var(--fx-type-caption-font-size);\n line-height: var(--cb-card-line);\n text-align: center;\n white-space: normal;\n word-break: normal;\n overflow-wrap: anywhere;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 4;\n}\n.cb-fe-label.has-kind .cb-fe-name {\n min-height: calc(var(--cb-card-line) * 3);\n -webkit-line-clamp: 3;\n}\n.cb-card-meta,\n.cb-card-kind {\n width: 100%;\n flex: 0 0 auto;\n margin-top: auto;\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n line-height: var(--cb-card-line);\n text-align: center;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-fe-grid.cb-layout-list {\n grid-template-columns: minmax(0, 1fr);\n grid-auto-rows: auto;\n gap: var(--fx-ref-space-2);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-6);\n overflow-x: hidden;\n}\n.cb-grid-view.cb-layout-list .cb-grid-item {\n flex-direction: row;\n align-items: center;\n width: 100%;\n min-width: 0;\n min-height: 40px;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n gap: var(--fx-ref-space-8);\n overflow: hidden;\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-grid-item.sel {\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-grid-thumb {\n width: 32px;\n height: 32px;\n flex: 0 0 32px;\n aspect-ratio: auto;\n overflow: visible;\n}\n.cb-grid-view.cb-layout-list .cb-file-thumb {\n position: static;\n}\n.cb-grid-view.cb-layout-list .cb-grid-thumb svg {\n width: 20px;\n height: 20px;\n}\n.cb-layout-list .cb-fe-label {\n flex: 1 1 auto;\n min-width: 0;\n height: auto;\n overflow: hidden;\n flex-direction: row;\n align-items: center;\n text-align: left;\n}\n.cb-layout-list .cb-fe-name,\n.cb-layout-list .cb-fe-label.has-kind .cb-fe-name {\n display: block;\n flex: 1;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n text-align: left;\n white-space: nowrap;\n text-overflow: ellipsis;\n -webkit-line-clamp: unset;\n}\n.cb-layout-list .cb-card-meta,\n.cb-layout-list .cb-card-kind {\n margin-top: 0;\n}\n.cb-layout-list .cb-card-meta {\n display: none;\n}\n.cb-grid-view.cb-layout-list .cb-group-card:not(.cb-pack-expanded),\n.cb-grid-view.cb-layout-list .cb-group-card.sel:not(.cb-pack-expanded) {\n box-shadow: none;\n}\n.cb-grid-view.cb-layout-list .cb-group-member:not(.sel) {\n background: transparent;\n}\n.cb-grid-view.cb-layout-list .cb-group-member:not(.sel):hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-grid-view.cb-layout-list .cb-group-card,\n.cb-grid-view.cb-layout-list .cb-pack-expanded {\n padding-right: calc(var(--fx-ref-space-8) + 44px);\n}\n.cb-grid-view.cb-layout-list .cb-group-badge {\n top: 50%;\n right: var(--fx-ref-space-8);\n bottom: auto;\n transform: translateY(-50%);\n}\n.cb-details-view {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-height: 0;\n overflow: auto;\n padding: 0 var(--fx-ref-space-6) var(--fx-ref-space-8);\n --cb-details-cols: minmax(160px, 1fr) 160px 90px 130px;\n}\n.cb-details-header,\n.cb-details-row {\n display: grid;\n grid-template-columns: var(--cb-details-cols);\n align-items: center;\n}\n.cb-details-header {\n position: sticky;\n z-index: var(--fx-layer-sticky);\n top: 0;\n min-height: var(--fx-component-editor-row-height);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-default);\n background: var(--fx-color-surface-canvas);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-details-th {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-4);\n height: 100%;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n}\n.cb-details-th:hover {\n color: var(--fx-color-text-primary);\n}\n.cb-details-th.active,\n.cb-details-th[data-active=true] {\n color: var(--fx-color-brand-text);\n}\n.cb-details-sort-arrow {\n font-size: 9px;\n}\n.cb-details-body {\n display: flex;\n flex-direction: column;\n}\n.cb-details-row {\n position: relative;\n width: 100%;\n border-radius: var(--fx-ref-radius-4);\n color: var(--fx-color-text-secondary);\n cursor: pointer;\n}\n.cb-details-row:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-details-row.sel {\n background: var(--fx-color-selection-bg);\n box-shadow: inset var(--fx-border-focus-width) 0 0 var(--fx-color-selection-indicator);\n}\n.cb-details-row:focus-visible {\n outline: var(--fx-border-focus-width) solid var(--fx-color-border-focus);\n outline-offset: -2px;\n}\n.cb-details-cell {\n min-width: 0;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-details-name {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-6);\n}\n.cb-details-icon {\n display: inline-flex;\n width: 16px;\n height: 16px;\n flex: 0 0 16px;\n}\n.cb-details-icon svg {\n width: 16px;\n height: 16px;\n}\n.cb-details-type,\n.cb-details-size,\n.cb-details-modified {\n color: var(--fx-color-text-muted);\n}\n.cb-empty {\n grid-column: 1 / -1;\n color: var(--fx-color-text-muted);\n}\n.cb-status {\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex: 0 0 var(--fx-component-editor-row-height);\n min-height: var(--fx-component-editor-row-height);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border-top: var(--fx-border-width) solid var(--fx-color-border-subtle);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-file-thumb {\n position: relative;\n}\n.cb-group-card:not(.cb-pack-expanded) {\n box-shadow:\n 3px 3px 0 0 var(--fx-color-surface-raised),\n 3px 3px 0 1px var(--fx-color-border-subtle),\n 6px 6px 0 0 var(--fx-color-surface-raised),\n 6px 6px 0 1px var(--fx-color-border-subtle);\n}\n.cb-group-card.sel:not(.cb-pack-expanded) {\n box-shadow:\n 0 0 0 var(--fx-border-width) var(--fx-color-selection-indicator),\n 3px 3px 0 0 var(--fx-color-surface-raised),\n 3px 3px 0 1px var(--fx-color-border-subtle),\n 6px 6px 0 0 var(--fx-color-surface-raised),\n 6px 6px 0 1px var(--fx-color-border-subtle);\n}\n.cb-group-badge {\n position: absolute;\n right: 3px;\n bottom: 3px;\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 3px;\n height: 22px;\n padding: 0 8px;\n border: 0;\n border-radius: 11px;\n background: color-mix(in srgb, var(--fx-color-surface-canvas) 62%, transparent);\n color: var(--fx-color-text-inverse);\n font-size: 11px;\n font-weight: var(--fx-ref-font-weight-600);\n cursor: pointer;\n}\n.cb-group-badge:hover {\n background: rgb(0 0 0 / 62%);\n color: #fff;\n}\n.cb-group-badge svg {\n width: 11px;\n height: 11px;\n transition: transform var(--fx-motion-hover) var(--fx-motion-ease);\n}\n.cb-group-backdrops {\n position: absolute;\n inset: 0;\n z-index: 0;\n pointer-events: none;\n}\n.cb-group-backdrop {\n position: absolute;\n background: rgb(0 0 0 / 10%);\n}\n.cb-group-member {\n z-index: 1;\n}\n.cb-group-badge.open svg {\n transform: rotate(180deg);\n}\n.cb-preview-panel {\n display: flex;\n flex: 0 0 var(--cb-preview-w, 280px);\n flex-direction: column;\n width: var(--cb-preview-w, 280px);\n min-width: 180px;\n max-width: 560px;\n min-height: 0;\n overflow: hidden;\n border-left: var(--fx-border-width) solid var(--fx-color-border-subtle);\n background: var(--fx-color-surface-canvas);\n}\n.cb-preview-head {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n padding: var(--fx-ref-space-8) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n flex-shrink: 0;\n}\n.cb-preview-mode-bar {\n display: flex;\n gap: var(--fx-ref-space-4);\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8);\n border-bottom: var(--fx-border-width) solid var(--fx-color-border-subtle);\n flex-shrink: 0;\n}\n.cb-preview-mode-btn {\n flex: 1 1 0;\n min-width: 0;\n height: 24px;\n padding: 0 var(--fx-ref-space-8);\n overflow: hidden;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: transparent;\n color: var(--fx-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n.cb-preview-mode-btn:hover {\n color: var(--fx-color-text-primary);\n background: var(--fx-color-surface-hover);\n}\n.cb-preview-mode-btn.is-active {\n color: var(--fx-color-text-primary);\n border-color: var(--fx-color-border-strong);\n background: var(--fx-color-surface-raised);\n}\n.cb-preview-ico,\n.cb-preview-list-ico {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 28px;\n width: 28px;\n height: 28px;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-muted);\n}\n.cb-preview-list-ico {\n flex-basis: 16px;\n width: 16px;\n height: 16px;\n background: transparent;\n}\n.cb-preview-title {\n flex: 1;\n min-width: 0;\n}\n.cb-preview-title .name {\n overflow: hidden;\n color: var(--fx-color-text-primary);\n font-weight: var(--fx-ref-font-weight-600);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-title .meta {\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-close {\n width: 24px;\n height: 24px;\n}\n.cb-preview-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: var(--fx-ref-space-8);\n}\n.cb-preview-empty .cb-preview-body {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: var(--fx-color-text-muted);\n}\n.cb-preview-list,\n.cb-preview-asset-list {\n display: flex;\n flex-direction: column;\n gap: var(--fx-ref-space-4);\n}\n.cb-preview-list-item,\n.cb-preview-asset-row {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n min-width: 0;\n padding: var(--fx-ref-space-4) var(--fx-ref-space-6);\n border-radius: var(--fx-radius-control);\n color: var(--fx-color-text-secondary);\n}\n.cb-preview-list-item:hover,\n.cb-preview-asset-row:hover {\n background: var(--fx-color-surface-hover);\n}\n.cb-preview-list-item > span:nth-of-type(2) {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-list-item .sub,\n.cb-preview-asset-row .guid,\n.cb-preview-asset-row .kind {\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n}\n.cb-preview-list-item .sub {\n flex: 0 1 38%;\n min-width: 0;\n margin-left: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-row > .cb-preview-asset-main {\n flex: 1 1 0;\n min-width: 0;\n}\n.cb-preview-asset-row > .cb-preview-asset-main > div {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-row .kind {\n margin-left: 0;\n}\n.cb-preview-asset-row .guid {\n flex: 0 1 auto;\n max-width: 42%;\n min-width: 0;\n margin-left: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-media {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--fx-ref-space-8);\n min-height: 0;\n}\n.cb-preview-image {\n display: block;\n max-width: 100%;\n max-height: min(360px, 50vh);\n object-fit: contain;\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n}\n.cb-preview-audio,\n.cb-preview-video {\n width: 100%;\n max-width: 100%;\n}\n.cb-preview-video {\n max-height: min(360px, 50vh);\n border-radius: var(--fx-radius-control);\n background: #000;\n}\n.cb-preview-font-sample {\n margin: 0;\n color: var(--fx-color-text-primary);\n font-size: 1.35rem;\n line-height: 1.4;\n text-align: center;\n}\n.cb-preview-font-meta {\n margin: 0;\n max-width: 100%;\n overflow: hidden;\n color: var(--fx-color-text-muted);\n font: var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n text-align: center;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-asset-hero {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--fx-ref-space-16) 0 var(--fx-ref-space-8);\n}\n.cb-preview-hero-icon {\n color: var(--fx-color-text-muted);\n}\n.cb-preview-note {\n padding: var(--fx-ref-space-8) var(--fx-ref-space-8);\n overflow: hidden;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-raised);\n color: var(--fx-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cb-preview-source {\n margin: 0;\n padding: var(--fx-ref-space-8);\n overflow: auto;\n border: var(--fx-border-width) solid var(--fx-color-border-subtle);\n border-radius: var(--fx-radius-control);\n background: var(--fx-color-surface-inset);\n color: var(--fx-color-text-secondary);\n font: var(--fx-type-code-font-weight) var(--fx-type-code-font-size)/var(--fx-type-code-line-height) var(--fx-type-code-font-family);\n white-space: pre-wrap;\n}\n.cb-preview-code {\n display: flex;\n flex-direction: column;\n gap: var(--fx-ref-space-6);\n min-height: 0;\n}\n.cb-preview-code-head {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n}\n.cb-preview-code-badge {\n padding: var(--fx-ref-space-2) var(--fx-ref-space-8);\n border-radius: var(--fx-radius-pill);\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-muted);\n font: var(--fx-type-code-font-weight) var(--fx-type-caption-font-size)/var(--fx-type-caption-line-height) var(--fx-type-code-font-family);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n}\n.cb-preview-code-hint {\n color: var(--fx-color-text-disabled);\n font-size: var(--fx-type-caption-font-size);\n}\n.cb-preview-code-plain,\n.cb-preview-code-shiki pre.shiki {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n font: var(--fx-type-code-font-weight) var(--fx-type-code-font-size)/var(--fx-type-code-line-height) var(--fx-type-code-font-family);\n}\n.cb-preview-code-plain {\n color: var(--fx-color-text-secondary);\n}\n.cb-preview-code-shiki pre.shiki {\n background: transparent !important;\n}\n.cb-preview-code-shiki pre.shiki code {\n background: transparent;\n padding: 0;\n border-radius: 0;\n color: inherit;\n font-size: inherit;\n}\n.cb-filter-menu-body {\n min-width: 220px;\n padding: var(--fx-ref-space-4);\n border: var(--fx-border-width) solid var(--fx-color-border-default);\n border-radius: var(--fx-radius-popover);\n background: var(--fx-color-surface-raised);\n box-shadow: var(--fx-shadow-floating);\n}\n.cb-filter-section {\n padding: var(--fx-ref-space-6) var(--fx-ref-space-8) var(--fx-ref-space-4);\n color: var(--fx-color-text-muted);\n font-size: var(--fx-type-caption-font-size);\n font-weight: var(--fx-ref-font-weight-600);\n}\n.cb-filter-separator {\n height: var(--fx-border-width);\n margin: var(--fx-ref-space-4) 0;\n background: var(--fx-color-border-subtle);\n}\n.cb-filter-option {\n display: flex;\n align-items: center;\n gap: var(--fx-ref-space-8);\n width: 100%;\n min-height: var(--fx-component-editor-row-height);\n padding: var(--fx-ref-space-4) var(--fx-ref-space-8);\n border: 0;\n border-radius: var(--fx-radius-control);\n background: transparent;\n color: var(--fx-color-text-secondary);\n text-align: left;\n cursor: pointer;\n}\n.cb-filter-option:hover {\n background: var(--fx-color-surface-hover);\n color: var(--fx-color-text-primary);\n}\n.cb-filter-option:disabled {\n color: var(--fx-color-text-disabled);\n cursor: default;\n}\n.cb-filter-option-icon {\n flex: 0 0 14px;\n width: 14px;\n color: var(--fx-color-text-muted);\n}\n.cb-filter-check {\n flex: 0 0 var(--fx-component-choice-size);\n pointer-events: none;\n}\n.cb-filter-check.fx-choice {\n align-items: center;\n gap: 0;\n min-height: 0;\n margin: 0;\n}\n.cb-filter-check .fx-choice__text {\n display: none;\n}\n.cb-radio-menu {\n width: 14px;\n height: 14px;\n flex: 0 0 14px;\n border: var(--fx-border-width) solid var(--fx-color-border-control);\n border-radius: 999px;\n}\n.cb-radio-menu[data-checked=true] {\n border-color: var(--fx-color-choice-indicator);\n background:\n radial-gradient(\n circle at center,\n var(--fx-color-choice-indicator) 4px,\n transparent 5px);\n}\n@container (max-width: 660px) {\n .cb-toolbar-group .fx-button {\n width: var(--fx-component-control-height-compact);\n padding: 0;\n font-size: 0;\n }\n}\n';
|
|
2460
|
+
if (typeof document !== "undefined" && !document.querySelector('style[data-forgeax-css="1ef84f0e3fb40317f76b77e1b83b84b7c994dc8c43c3bc38be5abb2446c7740b"]')) {
|
|
2456
2461
|
const style = document.createElement("style");
|
|
2457
|
-
style.dataset.forgeaxCss = "
|
|
2462
|
+
style.dataset.forgeaxCss = "1ef84f0e3fb40317f76b77e1b83b84b7c994dc8c43c3bc38be5abb2446c7740b";
|
|
2458
2463
|
style.textContent = css2;
|
|
2459
2464
|
document.head.append(style);
|
|
2460
2465
|
}
|
|
@@ -2478,4 +2483,4 @@ async function mountContentBrowserPanel(props) {
|
|
|
2478
2483
|
export {
|
|
2479
2484
|
mountContentBrowserPanel
|
|
2480
2485
|
};
|
|
2481
|
-
//# sourceMappingURL=chunk-
|
|
2486
|
+
//# sourceMappingURL=chunk-VKNIYV2W.mjs.map
|