@xopcai/xopc 0.0.6 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BdC4Y-HX.js} +2 -2
- package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-C-oaSHkm.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-C-oaSHkm.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-BqEUppPO.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-BqEUppPO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-BhqjQ7iL.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-Cli49RKR.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-Cli49RKR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Dkj-Ldpf.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
- package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js +2 -0
- package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js.map +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-BMcZlaxF.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-D5HEF0KR.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-CXdCSSPl.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-CXdCSSPl.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-DZCZgQE2.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-CX6STpx3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Cph02QZn.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js +16 -0
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-B9O5l3I8.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-B9O5l3I8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-BLiY_O25.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-BLiY_O25.js.map} +1 -1
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
- package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-BFpnFTed.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-DEhQXWJg.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-DEhQXWJg.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-DKOtWs3-.js} +3 -3
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-BYlWP1ep.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-DWrn8Az0.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-DWrn8Az0.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-C59WQpM1.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-C59WQpM1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CywXkKml.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CywXkKml.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
- package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-CACj0DBJ.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
- package/dist/gateway/static/root/index.html +16 -16
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +17 -9
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +2 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/child-agent-factory.js +2 -0
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
- package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
- package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/image/index.d.ts +0 -1
- package/dist/src/agent/image/index.js +1 -2
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +38 -11
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +13 -0
- package/dist/src/agent/messaging/command-handler.js +14 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/service.d.ts +16 -1
- package/dist/src/agent/service.js +175 -17
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
- package/dist/src/agent/skills/hub-hash.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/tools/browser/tools.js +2 -2
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/image-tool.js +2 -2
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -1
- package/dist/src/agent/tools/index.js +2 -2
- package/dist/src/agent/tools/read.d.ts +0 -2
- package/dist/src/agent/tools/read.js +1 -3
- package/dist/src/agent/tools/read.js.map +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/index.d.ts +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/pipeline.d.ts +8 -1
- package/dist/src/channels/pipeline.js +49 -4
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/channels/plugin-types.d.ts +14 -0
- package/dist/src/chat-commands/builtins/config.d.ts +4 -0
- package/dist/src/chat-commands/builtins/config.js +197 -0
- package/dist/src/chat-commands/builtins/config.js.map +1 -0
- package/dist/src/chat-commands/builtins/context.d.ts +4 -0
- package/dist/src/chat-commands/builtins/context.js +44 -0
- package/dist/src/chat-commands/builtins/context.js.map +1 -0
- package/dist/src/chat-commands/builtins/session.js +111 -0
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +49 -21
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.d.ts +10 -0
- package/dist/src/chat-commands/config-paths.js +45 -0
- package/dist/src/chat-commands/config-paths.js.map +1 -0
- package/dist/src/chat-commands/config-value.d.ts +12 -0
- package/dist/src/chat-commands/config-value.js +53 -0
- package/dist/src/chat-commands/config-value.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +24 -1
- package/dist/src/chat-commands/context.js +41 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +2 -0
- package/dist/src/chat-commands/index.js +5 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +33 -1
- package/dist/src/cli/commands/agent/interactive.js +1 -1
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent.js +21 -9
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
- package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
- package/dist/src/cli/commands/doctor/flow.js +51 -0
- package/dist/src/cli/commands/doctor/flow.js.map +1 -0
- package/dist/src/cli/commands/doctor/format.d.ts +6 -0
- package/dist/src/cli/commands/doctor/format.js +61 -0
- package/dist/src/cli/commands/doctor/format.js.map +1 -0
- package/dist/src/cli/commands/doctor/index.js +44 -0
- package/dist/src/cli/commands/doctor/index.js.map +1 -0
- package/dist/src/cli/commands/doctor/types.d.ts +20 -0
- package/dist/src/cli/commands/doctor/types.js +1 -0
- package/dist/src/cli/commands/extension.js +10 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/init.js +1 -2
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts +1 -0
- package/dist/src/cli/commands/update.js +171 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -2
- package/dist/src/cli/index.js +4 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/init-workspace.js +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +4 -3
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +15 -15
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +1 -1
- package/dist/src/config/runtime-overrides.d.ts +8 -0
- package/dist/src/config/runtime-overrides.js +40 -0
- package/dist/src/config/runtime-overrides.js.map +1 -0
- package/dist/src/config/schema.d.ts +34 -104
- package/dist/src/config/schema.js +18 -39
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +1 -1
- package/dist/src/extensions/loader.js +5 -8
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +6 -1
- package/dist/src/extensions/sdk/index.js.map +1 -0
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
- package/dist/src/gateway/hono/routes/doctor.js +35 -0
- package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
- package/dist/src/gateway/hono/routes/index.js +4 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +64 -11
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.d.ts +3 -0
- package/dist/src/gateway/hono/routes/update.js +141 -0
- package/dist/src/gateway/hono/routes/update.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +82 -2
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +98 -3
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/service.d.ts +1 -4
- package/dist/src/gateway/service.js +13 -20
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
- package/dist/src/gateway/workspace-fs-file-list.js +56 -0
- package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
- package/dist/src/gateway/workspace-ripgrep.js +88 -4
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/infra/update-channels.d.ts +14 -0
- package/dist/src/infra/update-channels.js +30 -0
- package/dist/src/infra/update-channels.js.map +1 -0
- package/dist/src/infra/update-check.d.ts +53 -0
- package/dist/src/infra/update-check.js +155 -0
- package/dist/src/infra/update-check.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +18 -0
- package/dist/src/infra/update-runner.js +112 -0
- package/dist/src/infra/update-runner.js.map +1 -0
- package/dist/src/infra/update-startup.d.ts +20 -0
- package/dist/src/infra/update-startup.js +246 -0
- package/dist/src/infra/update-startup.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
- package/dist/src/providers/extension-stream-bridge.js +239 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -0
- package/dist/src/providers/index.d.ts +7 -2
- package/dist/src/providers/index.js +77 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/plugin-registry.js +92 -87
- package/dist/src/providers/plugin-registry.js.map +1 -1
- package/dist/src/routing/bindings.js +1 -1
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +2 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/session-key.d.ts +0 -5
- package/dist/src/routing/session-key.js +1 -27
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/chat-export.d.ts +5 -0
- package/dist/src/session/chat-export.js +35 -0
- package/dist/src/session/chat-export.js.map +1 -0
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/store.d.ts +1 -1
- package/dist/src/session/store.js +3 -7
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/types.d.ts +0 -10
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agents-B6s2BvpH.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
- package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
- package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
- package/dist/src/acp/commands.d.ts +0 -11
- package/dist/src/acp/commands.js +0 -17
- package/dist/src/acp/commands.js.map +0 -1
- package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
- package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
- package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
- package/dist/src/acp/control-plane/index.d.ts +0 -10
- package/dist/src/acp/control-plane/index.js +0 -6
- package/dist/src/acp/control-plane/manager.d.ts +0 -86
- package/dist/src/acp/control-plane/manager.js +0 -502
- package/dist/src/acp/control-plane/manager.js.map +0 -1
- package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
- package/dist/src/acp/control-plane/manager.types.js +0 -14
- package/dist/src/acp/control-plane/manager.types.js.map +0 -1
- package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
- package/dist/src/acp/control-plane/manager.utils.js +0 -46
- package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
- package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
- package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
- package/dist/src/acp/control-plane/runtime-cache.js +0 -58
- package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
- package/dist/src/acp/control-plane/runtime-options.js +0 -92
- package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
- package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
- package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
- package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
- package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
- package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
- package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
- package/dist/src/acp/control-plane/session-store.d.ts +0 -39
- package/dist/src/acp/control-plane/session-store.js +0 -149
- package/dist/src/acp/control-plane/session-store.js.map +0 -1
- package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
- package/dist/src/acp/control-plane/turn-manager.js +0 -134
- package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
- package/dist/src/acp/event-mapper.d.ts +0 -48
- package/dist/src/acp/event-mapper.js +0 -94
- package/dist/src/acp/event-mapper.js.map +0 -1
- package/dist/src/acp/index.d.ts +0 -10
- package/dist/src/acp/index.js +0 -5
- package/dist/src/acp/meta.d.ts +0 -15
- package/dist/src/acp/meta.js +0 -36
- package/dist/src/acp/meta.js.map +0 -1
- package/dist/src/acp/routing-integration.d.ts +0 -37
- package/dist/src/acp/routing-integration.js +0 -58
- package/dist/src/acp/routing-integration.js.map +0 -1
- package/dist/src/acp/runtime/backends/index.d.ts +0 -4
- package/dist/src/acp/runtime/backends/index.js +0 -2
- package/dist/src/acp/runtime/backends/local.d.ts +0 -136
- package/dist/src/acp/runtime/backends/local.js +0 -603
- package/dist/src/acp/runtime/backends/local.js.map +0 -1
- package/dist/src/acp/runtime/error-text.d.ts +0 -16
- package/dist/src/acp/runtime/error-text.js +0 -40
- package/dist/src/acp/runtime/error-text.js.map +0 -1
- package/dist/src/acp/runtime/errors.d.ts +0 -31
- package/dist/src/acp/runtime/errors.js +0 -47
- package/dist/src/acp/runtime/errors.js.map +0 -1
- package/dist/src/acp/runtime/index.d.ts +0 -7
- package/dist/src/acp/runtime/index.js +0 -4
- package/dist/src/acp/runtime/registry.d.ts +0 -35
- package/dist/src/acp/runtime/registry.js +0 -85
- package/dist/src/acp/runtime/registry.js.map +0 -1
- package/dist/src/acp/runtime/session-identity.d.ts +0 -35
- package/dist/src/acp/runtime/session-identity.js +0 -134
- package/dist/src/acp/runtime/session-identity.js.map +0 -1
- package/dist/src/acp/runtime/types.d.ts +0 -214
- package/dist/src/acp/secret-file.d.ts +0 -7
- package/dist/src/acp/secret-file.js +0 -19
- package/dist/src/acp/secret-file.js.map +0 -1
- package/dist/src/acp/server.d.ts +0 -48
- package/dist/src/acp/server.js +0 -300
- package/dist/src/acp/server.js.map +0 -1
- package/dist/src/acp/session.d.ts +0 -29
- package/dist/src/acp/session.js +0 -30
- package/dist/src/acp/session.js.map +0 -1
- package/dist/src/acp/types.d.ts +0 -39
- package/dist/src/acp/types.js +0 -13
- package/dist/src/acp/types.js.map +0 -1
- package/dist/src/agent/image/describe-images.d.ts +0 -18
- package/dist/src/agent/image/describe-images.js +0 -19
- package/dist/src/agent/image/describe-images.js.map +0 -1
- package/dist/src/cli/commands/acp.d.ts +0 -4
- package/dist/src/cli/commands/acp.js +0 -200
- package/dist/src/cli/commands/acp.js.map +0 -1
- /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./vendor-react-QAsRxa6t.js";import{c as t,l as n,s as r}from"./url-D7yWllI8.js";import{r as i,t as a}from"./cn-DPF56z7S.js";import{o}from"./form-field-width-BlpNwrfn.js";import{a as s,i as c}from"./theme-store-CywXkKml.js";var l=i(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),u=i(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]),d=i(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),f=i(`heart`,[[`path`,{d:`M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5`,key:`mvr1a0`}]]),p=i(`key-round`,[[`path`,{d:`M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z`,key:`1s6t7t`}],[`circle`,{cx:`16.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`w0ekpg`}]]),m=i(`log-in`,[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]),h=i(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),g=i(`mic`,[[`path`,{d:`M12 19v3`,key:`npa21l`}],[`path`,{d:`M19 10v2a7 7 0 0 1-14 0v-2`,key:`1vc78b`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`13`,rx:`3`,key:`s6n7sd`}]]),_=i(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]),v=i(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]),y=i(`shield`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}]]),b=i(`volume-2`,[[`path`,{d:`M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z`,key:`uqj9uw`}],[`path`,{d:`M16 9a5 5 0 0 1 0 6`,key:`1q6k2b`}],[`path`,{d:`M19.364 18.364a9 9 0 0 0 0-12.728`,key:`ijwkga`}]]),x=[`compact`,`default`,`large`];function S(e){document.documentElement.dataset.fontScale=e}function C(){try{let e=localStorage.getItem(`xopc-web-font-scale`),t=`default`;if(e){let n=JSON.parse(e).state?.preference;n&&x.includes(n)&&(t=n)}S(t)}catch{S(`default`)}}var w=t(s(e=>({preference:`default`,setPreference:t=>{S(t),e({preference:t})}}),{name:`xopc-web-font-scale`}));function T(){S(w.getState().preference)}var E=e(),D=[`language`,`theme`,`font`];function O({variant:e,sections:t=D}){let i=r(e=>e.language),s=r(e=>e.setLanguage),l=c(e=>e.preference),u=c(e=>e.setPreference),d=w(e=>e.preference),f=w(e=>e.setPreference),p=n(i).appearanceSettings,m=a(o,e===`page`?`w-full max-w-[min(100%,12rem)] shrink-0 sm:ml-auto sm:w-auto sm:max-w-[11rem]`:e===`toolbar`?`w-auto min-w-[6.5rem] max-w-[10rem] shrink-0 sm:min-w-[7.5rem]`:`w-full`),h=e===`page`?`flex flex-col gap-2 border-b border-edge-subtle py-3.5 last:border-b-0 sm:flex-row sm:items-center sm:justify-between sm:gap-6 sm:py-4`:`flex flex-col gap-1.5`,g=e=>t.includes(e);return e===`toolbar`?(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[g(`language`)?(0,E.jsxs)(`select`,{className:m,value:i,"aria-label":p.languageTitle,onChange:e=>s(e.target.value),children:[(0,E.jsx)(`option`,{value:`en`,children:p.langOptionEn}),(0,E.jsx)(`option`,{value:`zh`,children:p.langOptionZh})]}):null,g(`theme`)?(0,E.jsxs)(`select`,{className:m,value:l,"aria-label":p.themeTitle,onChange:e=>u(e.target.value),children:[(0,E.jsx)(`option`,{value:`light`,children:p.themeOptionLight}),(0,E.jsx)(`option`,{value:`dark`,children:p.themeOptionDark}),(0,E.jsx)(`option`,{value:`system`,children:p.themeOptionSystem})]}):null,g(`font`)?(0,E.jsxs)(`select`,{className:m,value:d,"aria-label":p.fontScaleTitle,onChange:e=>f(e.target.value),children:[(0,E.jsx)(`option`,{value:`compact`,children:p.fontScaleCompact}),(0,E.jsx)(`option`,{value:`default`,children:p.fontScaleDefault}),(0,E.jsx)(`option`,{value:`large`,children:p.fontScaleLarge})]}):null]}):(0,E.jsxs)(`div`,{className:a(e===`sidebar`&&`flex flex-col gap-4`),children:[g(`language`)?(0,E.jsxs)(`div`,{className:h,children:[(0,E.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,E.jsx)(`div`,{className:`text-sm font-semibold text-fg`,children:p.languageTitle}),(0,E.jsx)(`p`,{className:`mt-0.5 text-xs text-fg-muted`,children:p.languageDescription})]}),(0,E.jsxs)(`select`,{className:m,value:i,"aria-label":p.languageTitle,onChange:e=>s(e.target.value),children:[(0,E.jsx)(`option`,{value:`en`,children:p.langOptionEn}),(0,E.jsx)(`option`,{value:`zh`,children:p.langOptionZh})]})]}):null,g(`theme`)?(0,E.jsxs)(`div`,{className:h,children:[(0,E.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,E.jsx)(`div`,{className:`text-sm font-semibold text-fg`,children:p.themeTitle}),(0,E.jsx)(`p`,{className:`mt-0.5 text-xs text-fg-muted`,children:p.themeDescription})]}),(0,E.jsxs)(`select`,{className:m,value:l,"aria-label":p.themeTitle,onChange:e=>u(e.target.value),children:[(0,E.jsx)(`option`,{value:`light`,children:p.themeOptionLight}),(0,E.jsx)(`option`,{value:`dark`,children:p.themeOptionDark}),(0,E.jsx)(`option`,{value:`system`,children:p.themeOptionSystem})]})]}):null,g(`font`)?(0,E.jsxs)(`div`,{className:h,children:[(0,E.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,E.jsx)(`div`,{className:`text-sm font-semibold text-fg`,children:p.fontScaleTitle}),(0,E.jsx)(`p`,{className:`mt-0.5 text-xs text-fg-muted`,children:p.fontScaleDescription})]}),(0,E.jsxs)(`select`,{className:m,value:d,"aria-label":p.fontScaleTitle,onChange:e=>f(e.target.value),children:[(0,E.jsx)(`option`,{value:`compact`,children:p.fontScaleCompact}),(0,E.jsx)(`option`,{value:`default`,children:p.fontScaleDefault}),(0,E.jsx)(`option`,{value:`large`,children:p.fontScaleLarge})]})]}):null]})}function k(){return typeof window<`u`&&!!window.electronAPI}export{l as _,w as a,v as c,h as d,m as f,u as g,d as h,T as i,_ as l,f as m,O as n,b as o,p,C as r,y as s,k as t,g as u};
|
|
2
|
+
//# sourceMappingURL=electron-env-BDtJw9AY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electron-env-BDtJw9AY.js","names":["__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode"],"sources":["../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/box.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/circle-alert.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/circle-check.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/heart.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/key-round.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/log-in.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/log-out.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/mic.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/play.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/server.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/shield.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/volume-2.js","../../../../../web/src/stores/font-scale-store.ts","../../../../../web/src/components/shell/preference-select-fields.tsx","../../../../../web/src/lib/electron-env.ts"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\",\n key: \"hh9hay\"\n }\n ],\n [\"path\", { d: \"m3.3 7 8.7 5 8.7-5\", key: \"g66t2b\" }],\n [\"path\", { d: \"M12 22V12\", key: \"d0xqtd\" }]\n];\nconst Box = createLucideIcon(\"box\", __iconNode);\n\nexport { __iconNode, Box as default };\n//# sourceMappingURL=box.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"line\", { x1: \"12\", x2: \"12\", y1: \"8\", y2: \"12\", key: \"1pkeuh\" }],\n [\"line\", { x1: \"12\", x2: \"12.01\", y1: \"16\", y2: \"16\", key: \"4dfq90\" }]\n];\nconst CircleAlert = createLucideIcon(\"circle-alert\", __iconNode);\n\nexport { __iconNode, CircleAlert as default };\n//# sourceMappingURL=circle-alert.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"path\", { d: \"m9 12 2 2 4-4\", key: \"dzmm74\" }]\n];\nconst CircleCheck = createLucideIcon(\"circle-check\", __iconNode);\n\nexport { __iconNode, CircleCheck as default };\n//# sourceMappingURL=circle-check.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5\",\n key: \"mvr1a0\"\n }\n ]\n];\nconst Heart = createLucideIcon(\"heart\", __iconNode);\n\nexport { __iconNode, Heart as default };\n//# sourceMappingURL=heart.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z\",\n key: \"1s6t7t\"\n }\n ],\n [\"circle\", { cx: \"16.5\", cy: \"7.5\", r: \".5\", fill: \"currentColor\", key: \"w0ekpg\" }]\n];\nconst KeyRound = createLucideIcon(\"key-round\", __iconNode);\n\nexport { __iconNode, KeyRound as default };\n//# sourceMappingURL=key-round.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"m10 17 5-5-5-5\", key: \"1bsop3\" }],\n [\"path\", { d: \"M15 12H3\", key: \"6jk70r\" }],\n [\"path\", { d: \"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4\", key: \"u53s6r\" }]\n];\nconst LogIn = createLucideIcon(\"log-in\", __iconNode);\n\nexport { __iconNode, LogIn as default };\n//# sourceMappingURL=log-in.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"m16 17 5-5-5-5\", key: \"1bji2h\" }],\n [\"path\", { d: \"M21 12H9\", key: \"dn1m92\" }],\n [\"path\", { d: \"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\", key: \"1uf3rs\" }]\n];\nconst LogOut = createLucideIcon(\"log-out\", __iconNode);\n\nexport { __iconNode, LogOut as default };\n//# sourceMappingURL=log-out.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M12 19v3\", key: \"npa21l\" }],\n [\"path\", { d: \"M19 10v2a7 7 0 0 1-14 0v-2\", key: \"1vc78b\" }],\n [\"rect\", { x: \"9\", y: \"2\", width: \"6\", height: \"13\", rx: \"3\", key: \"s6n7sd\" }]\n];\nconst Mic = createLucideIcon(\"mic\", __iconNode);\n\nexport { __iconNode, Mic as default };\n//# sourceMappingURL=mic.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\",\n key: \"10ikf1\"\n }\n ]\n];\nconst Play = createLucideIcon(\"play\", __iconNode);\n\nexport { __iconNode, Play as default };\n//# sourceMappingURL=play.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"rect\", { width: \"20\", height: \"8\", x: \"2\", y: \"2\", rx: \"2\", ry: \"2\", key: \"ngkwjq\" }],\n [\"rect\", { width: \"20\", height: \"8\", x: \"2\", y: \"14\", rx: \"2\", ry: \"2\", key: \"iecqi9\" }],\n [\"line\", { x1: \"6\", x2: \"6.01\", y1: \"6\", y2: \"6\", key: \"16zg32\" }],\n [\"line\", { x1: \"6\", x2: \"6.01\", y1: \"18\", y2: \"18\", key: \"nzw8ys\" }]\n];\nconst Server = createLucideIcon(\"server\", __iconNode);\n\nexport { __iconNode, Server as default };\n//# sourceMappingURL=server.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\",\n key: \"oel41y\"\n }\n ]\n];\nconst Shield = createLucideIcon(\"shield\", __iconNode);\n\nexport { __iconNode, Shield as default };\n//# sourceMappingURL=shield.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\",\n key: \"uqj9uw\"\n }\n ],\n [\"path\", { d: \"M16 9a5 5 0 0 1 0 6\", key: \"1q6k2b\" }],\n [\"path\", { d: \"M19.364 18.364a9 9 0 0 0 0-12.728\", key: \"ijwkga\" }]\n];\nconst Volume2 = createLucideIcon(\"volume-2\", __iconNode);\n\nexport { __iconNode, Volume2 as default };\n//# sourceMappingURL=volume-2.js.map\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\nexport type FontScalePreference = 'compact' | 'default' | 'large';\n\nconst VALID: readonly FontScalePreference[] = ['compact', 'default', 'large'];\n\nfunction applyDomFontScale(pref: FontScalePreference) {\n document.documentElement.dataset.fontScale = pref;\n}\n\n/** Sync DOM from localStorage before React paint (zustand persist hydrates async). */\nexport function bootstrapFontScale() {\n try {\n const raw = localStorage.getItem('xopc-web-font-scale');\n let pref: FontScalePreference = 'default';\n if (raw) {\n const parsed = JSON.parse(raw) as { state?: { preference?: FontScalePreference } };\n const p = parsed.state?.preference;\n if (p && (VALID as readonly string[]).includes(p)) pref = p;\n }\n applyDomFontScale(pref);\n } catch {\n applyDomFontScale('default');\n }\n}\n\ntype FontScaleState = {\n preference: FontScalePreference;\n setPreference: (p: FontScalePreference) => void;\n};\n\nexport const useFontScaleStore = create(\n persist<FontScaleState>(\n (set) => ({\n preference: 'default',\n\n setPreference: (preference) => {\n applyDomFontScale(preference);\n set({ preference });\n },\n }),\n {\n name: 'xopc-web-font-scale',\n },\n ),\n);\n\nexport function syncFontScaleAfterHydration() {\n applyDomFontScale(useFontScaleStore.getState().preference);\n}\n","import type { StoredLanguage } from '@/lib/storage';\nimport { selectControlBaseClass } from '@/lib/form-field-width';\nimport { cn } from '@/lib/cn';\nimport { messages } from '@/i18n/messages';\nimport { useLocaleStore } from '@/stores/locale-store';\nimport { type FontScalePreference, useFontScaleStore } from '@/stores/font-scale-store';\nimport { type ThemePreference, useThemeStore } from '@/stores/theme-store';\n\ntype Variant = 'page' | 'sidebar' | 'toolbar';\n\nexport type PreferenceSection = 'language' | 'theme' | 'font';\n\nconst ALL_SECTIONS: readonly PreferenceSection[] = ['language', 'theme', 'font'];\n\nexport function PreferenceSelectFields({\n variant,\n sections = ALL_SECTIONS,\n}: {\n variant: Variant;\n sections?: readonly PreferenceSection[];\n}) {\n const language = useLocaleStore((s) => s.language);\n const setLanguage = useLocaleStore((s) => s.setLanguage);\n const themePref = useThemeStore((s) => s.preference);\n const setThemePref = useThemeStore((s) => s.setPreference);\n const fontPref = useFontScaleStore((s) => s.preference);\n const setFontPref = useFontScaleStore((s) => s.setPreference);\n\n const m = messages(language);\n const a = m.appearanceSettings;\n\n /** Page layout: short control width (labels are short); avoid `w-full` stretching on wide rows. */\n const selectClass = cn(\n selectControlBaseClass,\n variant === 'page'\n ? 'w-full max-w-[min(100%,12rem)] shrink-0 sm:ml-auto sm:w-auto sm:max-w-[11rem]'\n : variant === 'toolbar'\n ? 'w-auto min-w-[6.5rem] max-w-[10rem] shrink-0 sm:min-w-[7.5rem]'\n : 'w-full',\n );\n\n const rowClass =\n variant === 'page'\n ? 'flex flex-col gap-2 border-b border-edge-subtle py-3.5 last:border-b-0 sm:flex-row sm:items-center sm:justify-between sm:gap-6 sm:py-4'\n : 'flex flex-col gap-1.5';\n\n const show = (id: PreferenceSection) => sections.includes(id);\n\n if (variant === 'toolbar') {\n return (\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {show('language') ? (\n <select\n className={selectClass}\n value={language}\n aria-label={a.languageTitle}\n onChange={(e) => setLanguage(e.target.value as StoredLanguage)}\n >\n <option value=\"en\">{a.langOptionEn}</option>\n <option value=\"zh\">{a.langOptionZh}</option>\n </select>\n ) : null}\n {show('theme') ? (\n <select\n className={selectClass}\n value={themePref}\n aria-label={a.themeTitle}\n onChange={(e) => setThemePref(e.target.value as ThemePreference)}\n >\n <option value=\"light\">{a.themeOptionLight}</option>\n <option value=\"dark\">{a.themeOptionDark}</option>\n <option value=\"system\">{a.themeOptionSystem}</option>\n </select>\n ) : null}\n {show('font') ? (\n <select\n className={selectClass}\n value={fontPref}\n aria-label={a.fontScaleTitle}\n onChange={(e) => setFontPref(e.target.value as FontScalePreference)}\n >\n <option value=\"compact\">{a.fontScaleCompact}</option>\n <option value=\"default\">{a.fontScaleDefault}</option>\n <option value=\"large\">{a.fontScaleLarge}</option>\n </select>\n ) : null}\n </div>\n );\n }\n\n return (\n <div className={cn(variant === 'sidebar' && 'flex flex-col gap-4')}>\n {show('language') ? (\n <div className={rowClass}>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-semibold text-fg\">{a.languageTitle}</div>\n <p className=\"mt-0.5 text-xs text-fg-muted\">{a.languageDescription}</p>\n </div>\n <select\n className={selectClass}\n value={language}\n aria-label={a.languageTitle}\n onChange={(e) => setLanguage(e.target.value as StoredLanguage)}\n >\n <option value=\"en\">{a.langOptionEn}</option>\n <option value=\"zh\">{a.langOptionZh}</option>\n </select>\n </div>\n ) : null}\n\n {show('theme') ? (\n <div className={rowClass}>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-semibold text-fg\">{a.themeTitle}</div>\n <p className=\"mt-0.5 text-xs text-fg-muted\">{a.themeDescription}</p>\n </div>\n <select\n className={selectClass}\n value={themePref}\n aria-label={a.themeTitle}\n onChange={(e) => setThemePref(e.target.value as ThemePreference)}\n >\n <option value=\"light\">{a.themeOptionLight}</option>\n <option value=\"dark\">{a.themeOptionDark}</option>\n <option value=\"system\">{a.themeOptionSystem}</option>\n </select>\n </div>\n ) : null}\n\n {show('font') ? (\n <div className={rowClass}>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-semibold text-fg\">{a.fontScaleTitle}</div>\n <p className=\"mt-0.5 text-xs text-fg-muted\">{a.fontScaleDescription}</p>\n </div>\n <select\n className={selectClass}\n value={fontPref}\n aria-label={a.fontScaleTitle}\n onChange={(e) => setFontPref(e.target.value as FontScalePreference)}\n >\n <option value=\"compact\">{a.fontScaleCompact}</option>\n <option value=\"default\">{a.fontScaleDefault}</option>\n <option value=\"large\">{a.fontScaleLarge}</option>\n </select>\n </div>\n ) : null}\n </div>\n );\n}\n","/** True when running inside Electron with preload bridge (not gateway-only web). */\nexport function isElectron(): boolean {\n return typeof window !== 'undefined' && Boolean(window.electronAPI);\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{t as r}from"./useTranslation-
|
|
2
|
-
//# sourceMappingURL=extension-debug-page-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{t as r}from"./useTranslation-CACj0DBJ.js";import{i}from"./extension-provider-DZCZgQE2.js";var a=e(t(),1),o=n(),s=`xopc.extensionUiGrants.v1`;function c(){let{t:e}=r(),t=i(),[n,s]=(0,a.useState)(()=>l()),c=(0,a.useMemo)(()=>[...t].sort((e,t)=>e.id.localeCompare(t.id)),[t]);return(0,o.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-6 px-4 py-8`,children:[(0,o.jsxs)(`div`,{children:[(0,o.jsx)(`h1`,{className:`text-lg font-semibold text-fg`,children:e(`extensionDebug.title`)}),(0,o.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:e(`extensionDebug.subtitle`)})]}),(0,o.jsxs)(`section`,{className:`rounded-xl border border-edge bg-surface-base p-4`,children:[(0,o.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:e(`extensionDebug.grantsHeading`)}),(0,o.jsx)(`p`,{className:`mt-1 text-xs text-fg-muted`,children:e(`extensionDebug.grantsHint`)}),(0,o.jsx)(`pre`,{className:`mt-3 max-h-48 overflow-auto rounded-lg border border-edge-subtle bg-surface-muted p-3 font-mono text-xs text-fg`,children:n}),(0,o.jsx)(`button`,{type:`button`,className:`mt-3 rounded-lg border border-edge px-3 py-1.5 text-xs font-medium text-fg hover:bg-surface-hover`,onClick:()=>s(l()),children:e(`extensionDebug.refresh`)})]}),(0,o.jsxs)(`section`,{className:`rounded-xl border border-edge bg-surface-base p-4`,children:[(0,o.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:e(`extensionDebug.listHeading`)}),(0,o.jsx)(`div`,{className:`mt-3 overflow-x-auto`,children:(0,o.jsxs)(`table`,{className:`w-full min-w-[32rem] border-collapse text-left text-sm`,children:[(0,o.jsx)(`thead`,{children:(0,o.jsxs)(`tr`,{className:`border-b border-edge text-xs uppercase text-fg-muted`,children:[(0,o.jsx)(`th`,{className:`py-2 pr-2`,children:e(`extensionDebug.colId`)}),(0,o.jsx)(`th`,{className:`py-2 pr-2`,children:e(`extensionDebug.colName`)}),(0,o.jsx)(`th`,{className:`py-2`,children:e(`extensionDebug.colPermissions`)})]})}),(0,o.jsx)(`tbody`,{children:c.map(e=>(0,o.jsxs)(`tr`,{className:`border-b border-edge-subtle`,children:[(0,o.jsx)(`td`,{className:`py-2 pr-2 font-mono text-xs text-fg`,children:e.id}),(0,o.jsx)(`td`,{className:`py-2 pr-2 text-fg`,children:e.name}),(0,o.jsx)(`td`,{className:`py-2 font-mono text-xs text-fg-muted`,children:(e.ui?.permissions??[]).join(`, `)||`—`})]},e.id))})]})})]}),(0,o.jsxs)(`section`,{className:`rounded-xl border border-dashed border-edge-subtle bg-surface-muted/50 p-4 text-sm text-fg-muted`,children:[(0,o.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:e(`extensionDebug.futureHeading`)}),(0,o.jsx)(`p`,{className:`mt-2`,children:e(`extensionDebug.futureBody`)})]})]})}function l(){try{let e=localStorage.getItem(s);if(!e)return`{}`;let t=JSON.parse(e);return JSON.stringify(t,null,2)}catch(e){return e instanceof Error?e.message:String(e)}}export{c as ExtensionDebugPage};
|
|
2
|
+
//# sourceMappingURL=extension-debug-page-BMcZlaxF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-debug-page-
|
|
1
|
+
{"version":3,"file":"extension-debug-page-BMcZlaxF.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-debug-page.tsx"],"sourcesContent":["/**\n * Extension debug — Settings panel: extension list, declared permissions, UI grant fingerprints.\n */\n\nimport { useMemo, useState } from 'react';\n\nimport { useTranslation } from 'react-i18next';\n\nimport { useExtensions } from '@/features/extensions/extension-provider';\n\nconst GRANTS_STORAGE_KEY = 'xopc.extensionUiGrants.v1';\n\nexport function ExtensionDebugPage() {\n const { t } = useTranslation();\n const extensions = useExtensions();\n const [grantsRaw, setGrantsRaw] = useState(() => readGrantsSafe());\n\n const sorted = useMemo(\n () => [...extensions].sort((a, b) => a.id.localeCompare(b.id)),\n [extensions],\n );\n\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-6 px-4 py-8\">\n <div>\n <h1 className=\"text-lg font-semibold text-fg\">{t('extensionDebug.title')}</h1>\n <p className=\"mt-1 text-sm text-fg-muted\">{t('extensionDebug.subtitle')}</p>\n </div>\n\n <section className=\"rounded-xl border border-edge bg-surface-base p-4\">\n <h2 className=\"text-sm font-semibold text-fg\">{t('extensionDebug.grantsHeading')}</h2>\n <p className=\"mt-1 text-xs text-fg-muted\">{t('extensionDebug.grantsHint')}</p>\n <pre className=\"mt-3 max-h-48 overflow-auto rounded-lg border border-edge-subtle bg-surface-muted p-3 font-mono text-xs text-fg\">\n {grantsRaw}\n </pre>\n <button\n type=\"button\"\n className=\"mt-3 rounded-lg border border-edge px-3 py-1.5 text-xs font-medium text-fg hover:bg-surface-hover\"\n onClick={() => setGrantsRaw(readGrantsSafe())}\n >\n {t('extensionDebug.refresh')}\n </button>\n </section>\n\n <section className=\"rounded-xl border border-edge bg-surface-base p-4\">\n <h2 className=\"text-sm font-semibold text-fg\">{t('extensionDebug.listHeading')}</h2>\n <div className=\"mt-3 overflow-x-auto\">\n <table className=\"w-full min-w-[32rem] border-collapse text-left text-sm\">\n <thead>\n <tr className=\"border-b border-edge text-xs uppercase text-fg-muted\">\n <th className=\"py-2 pr-2\">{t('extensionDebug.colId')}</th>\n <th className=\"py-2 pr-2\">{t('extensionDebug.colName')}</th>\n <th className=\"py-2\">{t('extensionDebug.colPermissions')}</th>\n </tr>\n </thead>\n <tbody>\n {sorted.map((e) => (\n <tr key={e.id} className=\"border-b border-edge-subtle\">\n <td className=\"py-2 pr-2 font-mono text-xs text-fg\">{e.id}</td>\n <td className=\"py-2 pr-2 text-fg\">{e.name}</td>\n <td className=\"py-2 font-mono text-xs text-fg-muted\">\n {(e.ui?.permissions ?? []).join(', ') || '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </section>\n\n <section className=\"rounded-xl border border-dashed border-edge-subtle bg-surface-muted/50 p-4 text-sm text-fg-muted\">\n <h2 className=\"text-sm font-semibold text-fg\">{t('extensionDebug.futureHeading')}</h2>\n <p className=\"mt-2\">{t('extensionDebug.futureBody')}</p>\n </section>\n </div>\n );\n}\n\nfunction readGrantsSafe(): string {\n try {\n const raw = localStorage.getItem(GRANTS_STORAGE_KEY);\n if (!raw) return '{}';\n const parsed = JSON.parse(raw) as unknown;\n return JSON.stringify(parsed, null, 2);\n } catch (e) {\n return e instanceof Error ? e.message : String(e);\n }\n}\n"],"mappings":"+NAUM,EAAqB,4BAE3B,SAAgB,GAAqB,CACnC,GAAM,CAAE,KAAM,GAAgB,CACxB,EAAa,GAAe,CAC5B,CAAC,EAAW,IAAA,EAAA,EAAA,cAA+B,GAAgB,CAAC,CAE5D,GAAA,EAAA,EAAA,aACE,CAAC,GAAG,EAAW,CAAC,MAAM,EAAG,IAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC,CAC9D,CAAC,EAAW,CACb,CAED,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uEAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAE,uBAAuB,CAAM,CAAA,EAC9E,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAE,0BAA0B,CAAK,CAAA,CACxE,CAAA,CAAA,EAEN,EAAA,EAAA,MAAC,UAAD,CAAS,UAAU,6DAAnB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAE,+BAA+B,CAAM,CAAA,EACtF,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAE,4BAA4B,CAAK,CAAA,EAC9E,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,2HACZ,EACG,CAAA,EACN,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,UAAU,oGACV,YAAe,EAAa,GAAgB,CAAC,UAE5C,EAAE,yBAAyB,CACrB,CAAA,CACD,IAEV,EAAA,EAAA,MAAC,UAAD,CAAS,UAAU,6DAAnB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAE,6BAA6B,CAAM,CAAA,EACpF,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,iCACb,EAAA,EAAA,MAAC,QAAD,CAAO,UAAU,kEAAjB,EACE,EAAA,EAAA,KAAC,QAAD,CAAA,UACE,EAAA,EAAA,MAAC,KAAD,CAAI,UAAU,gEAAd,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,qBAAa,EAAE,uBAAuB,CAAM,CAAA,EAC1D,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,qBAAa,EAAE,yBAAyB,CAAM,CAAA,EAC5D,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,gBAAQ,EAAE,gCAAgC,CAAM,CAAA,CAC3D,GACC,CAAA,EACR,EAAA,EAAA,KAAC,QAAD,CAAA,SACG,EAAO,IAAK,IACX,EAAA,EAAA,MAAC,KAAD,CAAe,UAAU,uCAAzB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,+CAAuC,EAAE,GAAQ,CAAA,EAC/D,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,6BAAqB,EAAE,KAAU,CAAA,EAC/C,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iDACV,EAAE,IAAI,aAAe,EAAE,EAAE,KAAK,KAAK,EAAI,IACtC,CAAA,CACF,EANI,EAAE,GAMN,CACL,CACI,CAAA,CACF,GACJ,CAAA,CACE,IAEV,EAAA,EAAA,MAAC,UAAD,CAAS,UAAU,4GAAnB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAE,+BAA+B,CAAM,CAAA,EACtF,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,gBAAQ,EAAE,4BAA4B,CAAK,CAAA,CAChD,GACN,GAIV,SAAS,GAAyB,CAChC,GAAI,CACF,IAAM,EAAM,aAAa,QAAQ,EAAmB,CACpD,GAAI,CAAC,EAAK,MAAO,KACjB,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,OAAO,KAAK,UAAU,EAAQ,KAAM,EAAE,OAC/B,EAAG,CACV,OAAO,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{o as r,s as i,t as a}from"./url-
|
|
2
|
-
//# sourceMappingURL=extension-iframe-host-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{o as r,s as i,t as a}from"./url-D7yWllI8.js";import{t as o}from"./useTranslation-CACj0DBJ.js";import{a as s,i as c,n as l,o as u,s as d,t as f}from"./dist-Db1dcLr9.js";import{i as p}from"./theme-store-CywXkKml.js";import{r as m,s as h}from"./extension-provider-DZCZgQE2.js";var g=e(t(),1),_=`xopc.extensionUiGrants.v1`;function v(e){return[...e].map(e=>e.trim()).filter(Boolean).sort().join(`\0`)}function y(e,t){try{let n=localStorage.getItem(_);return(n?JSON.parse(n):{})[e]===v(t)}catch{return!1}}function b(e,t){let n=localStorage.getItem(_),r=(n?JSON.parse(n):{})??{};r[e]=v(t),localStorage.setItem(_,JSON.stringify(r))}var x={theme:`Read theme (light / dark)`,"agent.send":`Send messages to the assistant`,"agent.subscribe":`Receive live agent stream events for a chat`,"session.read":`List and open chat sessions`,"session.write":`Modify chat sessions`,"config.read":`Read extension configuration`,"config.write":`Write extension configuration`,storage:`Read and write extension storage`,notification:`Show in-app notifications`,clipboard:`Use the clipboard`,"workspace.read":`Read workspace files`,"workspace.write":`Write workspace files`},S={theme:`读取主题(浅色/深色)`,"agent.send":`向助手发送消息`,"agent.subscribe":`接收会话的实时助手流式事件`,"session.read":`列出并打开聊天会话`,"session.write":`修改聊天会话`,"config.read":`读取扩展配置`,"config.write":`写入扩展配置`,storage:`读写扩展存储`,notification:`显示应用内通知`,clipboard:`使用剪贴板`,"workspace.read":`读取工作区文件`,"workspace.write":`写入工作区文件`};function C(e,t){let n=x[e];return t===`zh`&&S[e]?S[e]:n??e}var w=n();function T({open:e,onOpenChange:t,extensionId:n,extensionName:r,permissions:a,onConfirm:p}){let{t:m}=o(),h=i(e=>e.language);return(0,w.jsx)(u,{open:e,onOpenChange:t,children:(0,w.jsxs)(s,{children:[(0,w.jsx)(c,{className:`xopc-dialog-overlay fixed inset-0 z-[130] bg-scrim`}),(0,w.jsxs)(l,{className:`fixed left-1/2 top-1/2 z-[131] w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-edge bg-surface-panel p-5 shadow-elevated`,onOpenAutoFocus:e=>e.preventDefault(),children:[(0,w.jsx)(d,{className:`text-base font-semibold text-fg`,children:m(`extensionUi.permissionTitle`,{name:r})}),(0,w.jsxs)(`p`,{className:`mt-2 text-sm text-fg-muted`,children:[m(`extensionUi.permissionSubtitle`),` `,(0,w.jsxs)(`span`,{className:`font-mono text-xs text-fg-muted`,title:n,children:[`(`,n,`)`]})]}),a.length===0?(0,w.jsx)(`p`,{className:`mt-4 text-sm text-fg-muted`,children:m(`extensionUi.permissionsNone`)}):(0,w.jsx)(`ul`,{className:`mt-4 max-h-48 list-inside list-disc space-y-1 overflow-y-auto text-sm text-fg`,children:a.map(e=>(0,w.jsx)(`li`,{children:C(e,h)},e))}),(0,w.jsxs)(`div`,{className:`mt-6 flex justify-end gap-2`,children:[(0,w.jsx)(f,{asChild:!0,children:(0,w.jsx)(`button`,{type:`button`,className:`rounded-lg border border-edge px-3 py-2 text-sm text-fg hover:bg-surface-muted`,children:m(`extensionUi.deny`)})}),(0,w.jsx)(`button`,{type:`button`,className:`rounded-lg bg-accent px-3 py-2 text-sm font-medium text-white hover:opacity-90`,onClick:()=>{p(),t(!1)},children:m(`extensionUi.allow`)})]})]})]})})}var E=48,D=2e3,O=`allow-scripts allow-forms allow-popups`;function k(e){return e.split(`/`).filter(Boolean).map(e=>encodeURIComponent(e)).join(`/`)}function A({extensionId:e,extensionName:t,entrypoint:n,permissions:s,title:c,className:l,fixedHeight:u,maxHeight:d=D,minHeight:f=E,initialData:_}){let{t:v}=o(),x=m(),S=(0,g.useRef)(null),C=r(e=>e.token),A=p(e=>e.resolved),j=t?.trim()||e,M=(0,g.useMemo)(()=>JSON.stringify([...s??[]].sort()),[s]),N=(0,g.useMemo)(()=>JSON.parse(M),[M]),[P,F]=(0,g.useState)(()=>y(e,N)),[I,L]=(0,g.useState)(()=>!y(e,N));(0,g.useEffect)(()=>{let t=y(e,s??[]);F(t),L(!t)},[e,M]);let[R,z]=(0,g.useState)(0),[B,V]=(0,g.useState)(!1),[H,U]=(0,g.useState)(u??Math.min(d,Math.max(f,320))),W=(0,g.useMemo)(()=>{let t=k(n),r=a(`/api/extensions/${encodeURIComponent(e)}/assets/${t}`);if(!C?.trim())return r;let i=new URL(r);return i.searchParams.set(`token`,C.trim()),i.toString()},[e,n,C]);(0,g.useLayoutEffect)(()=>{if(!P)return;let t=S.current;if(t)return x.registerIframe(e,t,N),()=>x.unregisterIframe(e)},[P,e,N,x]),(0,g.useEffect)(()=>{if(P)return x.subscribeExtensionEvents(e,e=>{if(e.event!==`ui.resize`||!e.data||typeof e.data!=`object`||e.data===null)return;let t=Number(e.data.height);Number.isFinite(t)&&u===void 0&&U(Math.min(d,Math.max(f,t)))})},[P,e,u,d,f,x]),(0,g.useEffect)(()=>{if(!P)return;let t=h(A);x.sendEvent(e,`theme.changed`,t)},[P,e,A,x]);let G=u===void 0?{width:`100%`,height:H,border:`none`}:{width:`100%`,height:u,border:`none`};return P?(0,w.jsxs)(`div`,{className:`flex flex-1 min-h-0 min-w-0 flex-col`,children:[B?(0,w.jsxs)(`div`,{className:`mb-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm text-fg`,children:[(0,w.jsx)(`p`,{children:v(`extensionUi.loadFailed`)}),(0,w.jsx)(`p`,{className:`mt-2 text-xs text-fg-muted`,children:v(`extensionUi.loadFailedConnectionHint`)}),(0,w.jsx)(`button`,{type:`button`,className:`mt-2 font-medium text-accent underline-offset-2 hover:underline`,onClick:()=>{V(!1),z(e=>e+1)},children:v(`extensionUi.retryLoad`)})]}):null,(0,w.jsx)(`iframe`,{ref:S,className:l,title:c??`Extension ${e}`,src:W,style:G,sandbox:O,referrerPolicy:`no-referrer`,onError:()=>V(!0),onLoad:()=>{V(!1);let t=i.getState().language;x.sendInit(e,h(p.getState().resolved),t),_!==void 0&&x.sendEvent(e,`widget.data`,_)}},`${e}-${n}-${R}`)]}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(T,{open:I,onOpenChange:L,extensionId:e,extensionName:j,permissions:N,onConfirm:()=>{b(e,N),F(!0)}}),I?null:(0,w.jsxs)(`div`,{className:l?`${l} rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted`:`rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted`,children:[(0,w.jsx)(`p`,{children:v(`extensionUi.deniedHint`)}),(0,w.jsx)(`button`,{type:`button`,className:`mt-2 text-sm font-medium text-accent underline-offset-2 hover:underline`,onClick:()=>L(!0),children:v(`extensionUi.reviewPermissions`)})]})]})}export{A as t};
|
|
2
|
+
//# sourceMappingURL=extension-iframe-host-D5HEF0KR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-iframe-host-PWB-Pw2d.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-permission-grants.ts","../../../../../web/src/features/extensions/extension-permission-dialog.tsx","../../../../../web/src/features/extensions/extension-iframe-host.tsx"],"sourcesContent":["const STORAGE_KEY = 'xopc.extensionUiGrants.v1';\n\nfunction permissionFingerprint(permissions: string[]): string {\n return [...permissions].map((p) => p.trim()).filter(Boolean).sort().join('\\0');\n}\n\nexport function hasUiGrant(extensionId: string, permissions: string[]): boolean {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n const map = (raw ? JSON.parse(raw) : {}) as Record<string, string>;\n return map[extensionId] === permissionFingerprint(permissions);\n } catch {\n return false;\n }\n}\n\nexport function saveUiGrant(extensionId: string, permissions: string[]): void {\n const raw = localStorage.getItem(STORAGE_KEY);\n const map = (raw ? (JSON.parse(raw) as Record<string, string>) : {}) ?? {};\n map[extensionId] = permissionFingerprint(permissions);\n localStorage.setItem(STORAGE_KEY, JSON.stringify(map));\n}\n\n/** Short human labels for manifest `ui.permissions` (EN). */\nconst PERMISSION_LABELS_EN: Record<string, string> = {\n theme: 'Read theme (light / dark)',\n 'agent.send': 'Send messages to the assistant',\n 'agent.subscribe': 'Receive live agent stream events for a chat',\n 'session.read': 'List and open chat sessions',\n 'session.write': 'Modify chat sessions',\n 'config.read': 'Read extension configuration',\n 'config.write': 'Write extension configuration',\n storage: 'Read and write extension storage',\n notification: 'Show in-app notifications',\n clipboard: 'Use the clipboard',\n 'workspace.read': 'Read workspace files',\n 'workspace.write': 'Write workspace files',\n};\n\nconst PERMISSION_LABELS_ZH: Record<string, string> = {\n theme: '读取主题(浅色/深色)',\n 'agent.send': '向助手发送消息',\n 'agent.subscribe': '接收会话的实时助手流式事件',\n 'session.read': '列出并打开聊天会话',\n 'session.write': '修改聊天会话',\n 'config.read': '读取扩展配置',\n 'config.write': '写入扩展配置',\n storage: '读写扩展存储',\n notification: '显示应用内通知',\n clipboard: '使用剪贴板',\n 'workspace.read': '读取工作区文件',\n 'workspace.write': '写入工作区文件',\n};\n\nexport function describePermission(permission: string, language: string): string {\n const en = PERMISSION_LABELS_EN[permission];\n if (language === 'zh' && PERMISSION_LABELS_ZH[permission]) {\n return PERMISSION_LABELS_ZH[permission];\n }\n return en ?? permission;\n}\n","import * as Dialog from '@radix-ui/react-dialog';\nimport { useTranslation } from 'react-i18next';\n\nimport { useLocaleStore } from '@/stores/locale-store';\n\nimport { describePermission } from './extension-permission-grants';\n\ntype ExtensionPermissionDialogProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n extensionId: string;\n extensionName: string;\n permissions: string[];\n onConfirm: () => void;\n};\n\nexport function ExtensionPermissionDialog({\n open,\n onOpenChange,\n extensionId,\n extensionName,\n permissions,\n onConfirm,\n}: ExtensionPermissionDialogProps) {\n const { t } = useTranslation();\n const language = useLocaleStore((s) => s.language);\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"xopc-dialog-overlay fixed inset-0 z-[130] bg-scrim\" />\n <Dialog.Content\n className=\"fixed left-1/2 top-1/2 z-[131] w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-edge bg-surface-panel p-5 shadow-elevated\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <Dialog.Title className=\"text-base font-semibold text-fg\">\n {t('extensionUi.permissionTitle', { name: extensionName })}\n </Dialog.Title>\n <p className=\"mt-2 text-sm text-fg-muted\">\n {t('extensionUi.permissionSubtitle')}{' '}\n <span className=\"font-mono text-xs text-fg-muted\" title={extensionId}>\n ({extensionId})\n </span>\n </p>\n {permissions.length === 0 ? (\n <p className=\"mt-4 text-sm text-fg-muted\">{t('extensionUi.permissionsNone')}</p>\n ) : (\n <ul className=\"mt-4 max-h-48 list-inside list-disc space-y-1 overflow-y-auto text-sm text-fg\">\n {permissions.map((p) => (\n <li key={p}>{describePermission(p, language)}</li>\n ))}\n </ul>\n )}\n <div className=\"mt-6 flex justify-end gap-2\">\n <Dialog.Close asChild>\n <button\n type=\"button\"\n className=\"rounded-lg border border-edge px-3 py-2 text-sm text-fg hover:bg-surface-muted\"\n >\n {t('extensionUi.deny')}\n </button>\n </Dialog.Close>\n <button\n type=\"button\"\n className=\"rounded-lg bg-accent px-3 py-2 text-sm font-medium text-white hover:opacity-90\"\n onClick={() => {\n onConfirm();\n onOpenChange(false);\n }}\n >\n {t('extensionUi.allow')}\n </button>\n </div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import { type CSSProperties, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { useLocaleStore } from '@/stores/locale-store';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useThemeStore } from '@/stores/theme-store';\nimport { apiUrl } from '@/lib/url';\n\nimport { ExtensionPermissionDialog } from './extension-permission-dialog';\nimport { hasUiGrant, saveUiGrant } from './extension-permission-grants';\nimport { useExtensionRouter } from './extension-provider';\nimport { buildThemeInfo } from './theme-bridge';\n\nconst DEFAULT_MIN = 48;\nconst DEFAULT_MAX = 2000;\n\n/** Sandboxed extension UI: no `allow-same-origin` so the document is opaque-isolated from the host origin. */\nconst EXTENSION_IFRAME_SANDBOX = 'allow-scripts allow-forms allow-popups';\n\nexport type ExtensionIframeHostProps = {\n extensionId: string;\n /** Display name for the permission dialog; falls back to `extensionId`. */\n extensionName?: string;\n entrypoint: string;\n permissions?: string[];\n title?: string;\n className?: string;\n fixedHeight?: number;\n maxHeight?: number;\n minHeight?: number;\n initialData?: unknown;\n};\n\nfunction encodeAssetPath(entrypoint: string): string {\n return entrypoint\n .split('/')\n .filter(Boolean)\n .map((seg) => encodeURIComponent(seg))\n .join('/');\n}\n\nexport function ExtensionIframeHost({\n extensionId,\n extensionName,\n entrypoint,\n permissions,\n title,\n className,\n fixedHeight,\n maxHeight = DEFAULT_MAX,\n minHeight = DEFAULT_MIN,\n initialData,\n}: ExtensionIframeHostProps) {\n const { t } = useTranslation();\n const router = useExtensionRouter();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const gatewayToken = useGatewayStore((s) => s.token);\n const resolved = useThemeStore((s) => s.resolved);\n const displayName = extensionName?.trim() || extensionId;\n const permsKey = useMemo(\n () => JSON.stringify([...(permissions ?? [])].sort()),\n [permissions],\n );\n /** Stable list so registerIframe effect does not churn every render (permissions ?? [] is a new []). */\n const permList = useMemo(() => JSON.parse(permsKey) as string[], [permsKey]);\n\n const [allowed, setAllowed] = useState(() => hasUiGrant(extensionId, permList));\n const [dialogOpen, setDialogOpen] = useState(() => !hasUiGrant(extensionId, permList));\n\n useEffect(() => {\n const list = permissions ?? [];\n const ok = hasUiGrant(extensionId, list);\n setAllowed(ok);\n setDialogOpen(!ok);\n }, [extensionId, permsKey]);\n const [reloadKey, setReloadKey] = useState(0);\n const [loadError, setLoadError] = useState(false);\n\n const [dynamicHeight, setDynamicHeight] = useState(\n fixedHeight ?? Math.min(maxHeight, Math.max(minHeight, 320)),\n );\n\n const src = useMemo(() => {\n const rel = encodeAssetPath(entrypoint);\n const base = apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/assets/${rel}`);\n if (!gatewayToken?.trim()) {\n return base;\n }\n const u = new URL(base);\n u.searchParams.set('token', gatewayToken.trim());\n return u.toString();\n }, [extensionId, entrypoint, gatewayToken]);\n\n useLayoutEffect(() => {\n if (!allowed) return;\n const el = iframeRef.current;\n if (!el) return;\n router.registerIframe(extensionId, el, permList);\n return () => router.unregisterIframe(extensionId);\n }, [allowed, extensionId, permList, router]);\n\n useEffect(() => {\n if (!allowed) return;\n return router.subscribeExtensionEvents(extensionId, (msg) => {\n if (msg.event !== 'ui.resize') return;\n if (!msg.data || typeof msg.data !== 'object' || msg.data === null) return;\n const h = Number((msg.data as { height?: unknown }).height);\n if (!Number.isFinite(h)) return;\n const clamped = Math.min(maxHeight, Math.max(minHeight, h));\n if (fixedHeight === undefined) {\n setDynamicHeight(clamped);\n }\n });\n }, [allowed, extensionId, fixedHeight, maxHeight, minHeight, router]);\n\n useEffect(() => {\n if (!allowed) return;\n const th = buildThemeInfo(resolved);\n router.sendEvent(extensionId, 'theme.changed', th);\n }, [allowed, extensionId, resolved, router]);\n\n const style: CSSProperties =\n fixedHeight !== undefined\n ? { width: '100%', height: fixedHeight, border: 'none' }\n : { width: '100%', height: dynamicHeight, border: 'none' };\n\n const handleConfirmGrant = () => {\n saveUiGrant(extensionId, permList);\n setAllowed(true);\n };\n\n if (!allowed) {\n return (\n <>\n <ExtensionPermissionDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n extensionId={extensionId}\n extensionName={displayName}\n permissions={permList}\n onConfirm={handleConfirmGrant}\n />\n {!dialogOpen ? (\n <div\n className={\n className\n ? `${className} rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted`\n : 'rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted'\n }\n >\n <p>{t('extensionUi.deniedHint')}</p>\n <button\n type=\"button\"\n className=\"mt-2 text-sm font-medium text-accent underline-offset-2 hover:underline\"\n onClick={() => setDialogOpen(true)}\n >\n {t('extensionUi.reviewPermissions')}\n </button>\n </div>\n ) : null}\n </>\n );\n }\n\n return (\n <div className=\"flex flex-1 min-h-0 min-w-0 flex-col\">\n {loadError ? (\n <div className=\"mb-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm text-fg\">\n <p>{t('extensionUi.loadFailed')}</p>\n <p className=\"mt-2 text-xs text-fg-muted\">{t('extensionUi.loadFailedConnectionHint')}</p>\n <button\n type=\"button\"\n className=\"mt-2 font-medium text-accent underline-offset-2 hover:underline\"\n onClick={() => {\n setLoadError(false);\n setReloadKey((k) => k + 1);\n }}\n >\n {t('extensionUi.retryLoad')}\n </button>\n </div>\n ) : null}\n <iframe\n key={`${extensionId}-${entrypoint}-${reloadKey}`}\n ref={iframeRef}\n className={className}\n title={title ?? `Extension ${extensionId}`}\n src={src}\n style={style}\n sandbox={EXTENSION_IFRAME_SANDBOX}\n referrerPolicy=\"no-referrer\"\n onError={() => setLoadError(true)}\n onLoad={() => {\n setLoadError(false);\n const locale = useLocaleStore.getState().language;\n router.sendInit(extensionId, buildThemeInfo(useThemeStore.getState().resolved), locale);\n if (initialData !== undefined) {\n router.sendEvent(extensionId, 'widget.data', initialData);\n }\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension-iframe-host-D5HEF0KR.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-permission-grants.ts","../../../../../web/src/features/extensions/extension-permission-dialog.tsx","../../../../../web/src/features/extensions/extension-iframe-host.tsx"],"sourcesContent":["const STORAGE_KEY = 'xopc.extensionUiGrants.v1';\n\nfunction permissionFingerprint(permissions: string[]): string {\n return [...permissions].map((p) => p.trim()).filter(Boolean).sort().join('\\0');\n}\n\nexport function hasUiGrant(extensionId: string, permissions: string[]): boolean {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n const map = (raw ? JSON.parse(raw) : {}) as Record<string, string>;\n return map[extensionId] === permissionFingerprint(permissions);\n } catch {\n return false;\n }\n}\n\nexport function saveUiGrant(extensionId: string, permissions: string[]): void {\n const raw = localStorage.getItem(STORAGE_KEY);\n const map = (raw ? (JSON.parse(raw) as Record<string, string>) : {}) ?? {};\n map[extensionId] = permissionFingerprint(permissions);\n localStorage.setItem(STORAGE_KEY, JSON.stringify(map));\n}\n\n/** Short human labels for manifest `ui.permissions` (EN). */\nconst PERMISSION_LABELS_EN: Record<string, string> = {\n theme: 'Read theme (light / dark)',\n 'agent.send': 'Send messages to the assistant',\n 'agent.subscribe': 'Receive live agent stream events for a chat',\n 'session.read': 'List and open chat sessions',\n 'session.write': 'Modify chat sessions',\n 'config.read': 'Read extension configuration',\n 'config.write': 'Write extension configuration',\n storage: 'Read and write extension storage',\n notification: 'Show in-app notifications',\n clipboard: 'Use the clipboard',\n 'workspace.read': 'Read workspace files',\n 'workspace.write': 'Write workspace files',\n};\n\nconst PERMISSION_LABELS_ZH: Record<string, string> = {\n theme: '读取主题(浅色/深色)',\n 'agent.send': '向助手发送消息',\n 'agent.subscribe': '接收会话的实时助手流式事件',\n 'session.read': '列出并打开聊天会话',\n 'session.write': '修改聊天会话',\n 'config.read': '读取扩展配置',\n 'config.write': '写入扩展配置',\n storage: '读写扩展存储',\n notification: '显示应用内通知',\n clipboard: '使用剪贴板',\n 'workspace.read': '读取工作区文件',\n 'workspace.write': '写入工作区文件',\n};\n\nexport function describePermission(permission: string, language: string): string {\n const en = PERMISSION_LABELS_EN[permission];\n if (language === 'zh' && PERMISSION_LABELS_ZH[permission]) {\n return PERMISSION_LABELS_ZH[permission];\n }\n return en ?? permission;\n}\n","import * as Dialog from '@radix-ui/react-dialog';\nimport { useTranslation } from 'react-i18next';\n\nimport { useLocaleStore } from '@/stores/locale-store';\n\nimport { describePermission } from './extension-permission-grants';\n\ntype ExtensionPermissionDialogProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n extensionId: string;\n extensionName: string;\n permissions: string[];\n onConfirm: () => void;\n};\n\nexport function ExtensionPermissionDialog({\n open,\n onOpenChange,\n extensionId,\n extensionName,\n permissions,\n onConfirm,\n}: ExtensionPermissionDialogProps) {\n const { t } = useTranslation();\n const language = useLocaleStore((s) => s.language);\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"xopc-dialog-overlay fixed inset-0 z-[130] bg-scrim\" />\n <Dialog.Content\n className=\"fixed left-1/2 top-1/2 z-[131] w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-edge bg-surface-panel p-5 shadow-elevated\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <Dialog.Title className=\"text-base font-semibold text-fg\">\n {t('extensionUi.permissionTitle', { name: extensionName })}\n </Dialog.Title>\n <p className=\"mt-2 text-sm text-fg-muted\">\n {t('extensionUi.permissionSubtitle')}{' '}\n <span className=\"font-mono text-xs text-fg-muted\" title={extensionId}>\n ({extensionId})\n </span>\n </p>\n {permissions.length === 0 ? (\n <p className=\"mt-4 text-sm text-fg-muted\">{t('extensionUi.permissionsNone')}</p>\n ) : (\n <ul className=\"mt-4 max-h-48 list-inside list-disc space-y-1 overflow-y-auto text-sm text-fg\">\n {permissions.map((p) => (\n <li key={p}>{describePermission(p, language)}</li>\n ))}\n </ul>\n )}\n <div className=\"mt-6 flex justify-end gap-2\">\n <Dialog.Close asChild>\n <button\n type=\"button\"\n className=\"rounded-lg border border-edge px-3 py-2 text-sm text-fg hover:bg-surface-muted\"\n >\n {t('extensionUi.deny')}\n </button>\n </Dialog.Close>\n <button\n type=\"button\"\n className=\"rounded-lg bg-accent px-3 py-2 text-sm font-medium text-white hover:opacity-90\"\n onClick={() => {\n onConfirm();\n onOpenChange(false);\n }}\n >\n {t('extensionUi.allow')}\n </button>\n </div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import { type CSSProperties, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { useLocaleStore } from '@/stores/locale-store';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useThemeStore } from '@/stores/theme-store';\nimport { apiUrl } from '@/lib/url';\n\nimport { ExtensionPermissionDialog } from './extension-permission-dialog';\nimport { hasUiGrant, saveUiGrant } from './extension-permission-grants';\nimport { useExtensionRouter } from './extension-provider';\nimport { buildThemeInfo } from './theme-bridge';\n\nconst DEFAULT_MIN = 48;\nconst DEFAULT_MAX = 2000;\n\n/** Sandboxed extension UI: no `allow-same-origin` so the document is opaque-isolated from the host origin. */\nconst EXTENSION_IFRAME_SANDBOX = 'allow-scripts allow-forms allow-popups';\n\nexport type ExtensionIframeHostProps = {\n extensionId: string;\n /** Display name for the permission dialog; falls back to `extensionId`. */\n extensionName?: string;\n entrypoint: string;\n permissions?: string[];\n title?: string;\n className?: string;\n fixedHeight?: number;\n maxHeight?: number;\n minHeight?: number;\n initialData?: unknown;\n};\n\nfunction encodeAssetPath(entrypoint: string): string {\n return entrypoint\n .split('/')\n .filter(Boolean)\n .map((seg) => encodeURIComponent(seg))\n .join('/');\n}\n\nexport function ExtensionIframeHost({\n extensionId,\n extensionName,\n entrypoint,\n permissions,\n title,\n className,\n fixedHeight,\n maxHeight = DEFAULT_MAX,\n minHeight = DEFAULT_MIN,\n initialData,\n}: ExtensionIframeHostProps) {\n const { t } = useTranslation();\n const router = useExtensionRouter();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const gatewayToken = useGatewayStore((s) => s.token);\n const resolved = useThemeStore((s) => s.resolved);\n const displayName = extensionName?.trim() || extensionId;\n const permsKey = useMemo(\n () => JSON.stringify([...(permissions ?? [])].sort()),\n [permissions],\n );\n /** Stable list so registerIframe effect does not churn every render (permissions ?? [] is a new []). */\n const permList = useMemo(() => JSON.parse(permsKey) as string[], [permsKey]);\n\n const [allowed, setAllowed] = useState(() => hasUiGrant(extensionId, permList));\n const [dialogOpen, setDialogOpen] = useState(() => !hasUiGrant(extensionId, permList));\n\n useEffect(() => {\n const list = permissions ?? [];\n const ok = hasUiGrant(extensionId, list);\n setAllowed(ok);\n setDialogOpen(!ok);\n }, [extensionId, permsKey]);\n const [reloadKey, setReloadKey] = useState(0);\n const [loadError, setLoadError] = useState(false);\n\n const [dynamicHeight, setDynamicHeight] = useState(\n fixedHeight ?? Math.min(maxHeight, Math.max(minHeight, 320)),\n );\n\n const src = useMemo(() => {\n const rel = encodeAssetPath(entrypoint);\n const base = apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/assets/${rel}`);\n if (!gatewayToken?.trim()) {\n return base;\n }\n const u = new URL(base);\n u.searchParams.set('token', gatewayToken.trim());\n return u.toString();\n }, [extensionId, entrypoint, gatewayToken]);\n\n useLayoutEffect(() => {\n if (!allowed) return;\n const el = iframeRef.current;\n if (!el) return;\n router.registerIframe(extensionId, el, permList);\n return () => router.unregisterIframe(extensionId);\n }, [allowed, extensionId, permList, router]);\n\n useEffect(() => {\n if (!allowed) return;\n return router.subscribeExtensionEvents(extensionId, (msg) => {\n if (msg.event !== 'ui.resize') return;\n if (!msg.data || typeof msg.data !== 'object' || msg.data === null) return;\n const h = Number((msg.data as { height?: unknown }).height);\n if (!Number.isFinite(h)) return;\n const clamped = Math.min(maxHeight, Math.max(minHeight, h));\n if (fixedHeight === undefined) {\n setDynamicHeight(clamped);\n }\n });\n }, [allowed, extensionId, fixedHeight, maxHeight, minHeight, router]);\n\n useEffect(() => {\n if (!allowed) return;\n const th = buildThemeInfo(resolved);\n router.sendEvent(extensionId, 'theme.changed', th);\n }, [allowed, extensionId, resolved, router]);\n\n const style: CSSProperties =\n fixedHeight !== undefined\n ? { width: '100%', height: fixedHeight, border: 'none' }\n : { width: '100%', height: dynamicHeight, border: 'none' };\n\n const handleConfirmGrant = () => {\n saveUiGrant(extensionId, permList);\n setAllowed(true);\n };\n\n if (!allowed) {\n return (\n <>\n <ExtensionPermissionDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n extensionId={extensionId}\n extensionName={displayName}\n permissions={permList}\n onConfirm={handleConfirmGrant}\n />\n {!dialogOpen ? (\n <div\n className={\n className\n ? `${className} rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted`\n : 'rounded-lg border border-edge border-dashed bg-surface-base p-4 text-sm text-fg-muted'\n }\n >\n <p>{t('extensionUi.deniedHint')}</p>\n <button\n type=\"button\"\n className=\"mt-2 text-sm font-medium text-accent underline-offset-2 hover:underline\"\n onClick={() => setDialogOpen(true)}\n >\n {t('extensionUi.reviewPermissions')}\n </button>\n </div>\n ) : null}\n </>\n );\n }\n\n return (\n <div className=\"flex flex-1 min-h-0 min-w-0 flex-col\">\n {loadError ? (\n <div className=\"mb-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm text-fg\">\n <p>{t('extensionUi.loadFailed')}</p>\n <p className=\"mt-2 text-xs text-fg-muted\">{t('extensionUi.loadFailedConnectionHint')}</p>\n <button\n type=\"button\"\n className=\"mt-2 font-medium text-accent underline-offset-2 hover:underline\"\n onClick={() => {\n setLoadError(false);\n setReloadKey((k) => k + 1);\n }}\n >\n {t('extensionUi.retryLoad')}\n </button>\n </div>\n ) : null}\n <iframe\n key={`${extensionId}-${entrypoint}-${reloadKey}`}\n ref={iframeRef}\n className={className}\n title={title ?? `Extension ${extensionId}`}\n src={src}\n style={style}\n sandbox={EXTENSION_IFRAME_SANDBOX}\n referrerPolicy=\"no-referrer\"\n onError={() => setLoadError(true)}\n onLoad={() => {\n setLoadError(false);\n const locale = useLocaleStore.getState().language;\n router.sendInit(extensionId, buildThemeInfo(useThemeStore.getState().resolved), locale);\n if (initialData !== undefined) {\n router.sendEvent(extensionId, 'widget.data', initialData);\n }\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-CXdCSSPl.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{u as r}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{t as i}from"./page-header-store-
|
|
2
|
-
//# sourceMappingURL=extension-page-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{u as r}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{t as i}from"./page-header-store-BFpnFTed.js";import{o as a}from"./extension-provider-DZCZgQE2.js";import{t as o}from"./extension-iframe-host-D5HEF0KR.js";var s=e(t(),1),c=n();function l(){let{extensionId:e,pageId:t}=r(),n=a(),l=i(e=>e.setPageHeader),d=i(e=>e.clearPageHeader),f=e?n.find(t=>t.id===e):void 0,p=f?.ui?.contributions?.pages,m=e&&p?.length?t?p.find(n=>n.id===t||n.id===`${e}.${t}`):p[0]:void 0;return(0,s.useLayoutEffect)(()=>{if(!e||!f||!m)return d(),()=>d();let t=m.title?.trim()||f.name||e;return l({startExtra:null,main:(0,c.jsx)(`div`,{className:`w-full min-w-0 px-3 sm:px-5 xl:px-6`,children:(0,c.jsx)(`h1`,{className:`min-w-0 truncate text-base font-semibold tracking-tight text-fg`,title:t,children:t})}),end:null}),()=>d()},[d,f,e,m,l]),e?f?p?.length?m?(0,c.jsx)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:(0,c.jsx)(o,{extensionId:e,extensionName:f.name,entrypoint:m.entrypoint,permissions:f.ui?.permissions,title:m.title,className:`min-h-0 flex-1`,fixedHeight:void 0,maxHeight:99999})}):(0,c.jsx)(u,{message:`Page "${t}" not found in extension "${e}".`}):(0,c.jsx)(u,{message:`Extension "${e}" has no page contributions.`}):(0,c.jsx)(u,{message:`Extension "${e}" not found or has no UI.`}):(0,c.jsx)(u,{message:`No extension ID provided.`})}function u({message:e}){return(0,c.jsx)(`div`,{className:`flex min-h-[min(40vh,16rem)] flex-1 items-center justify-center`,children:(0,c.jsx)(`div`,{className:`text-center`,children:(0,c.jsx)(`p`,{className:`text-sm text-fg-muted`,children:e})})})}export{l as ExtensionPage};
|
|
2
|
+
//# sourceMappingURL=extension-page-CXdCSSPl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-page-
|
|
1
|
+
{"version":3,"file":"extension-page-CXdCSSPl.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-page.tsx"],"sourcesContent":["/**\n * ExtensionPage — renders a full-page extension UI via ExtensionIframeHost.\n *\n * Mounted at /apps/:extensionId (or /apps/:extensionId/:pageId for multi-page extensions).\n */\n\nimport { useLayoutEffect } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { usePageHeaderStore } from '@/stores/page-header-store';\n\nimport { ExtensionIframeHost } from './extension-iframe-host';\nimport { useUiExtensions } from './extension-provider';\n\nexport function ExtensionPage() {\n const { extensionId, pageId } = useParams<{ extensionId: string; pageId?: string }>();\n const uiExtensions = useUiExtensions();\n const setPageHeader = usePageHeaderStore((s) => s.setPageHeader);\n const clearPageHeader = usePageHeaderStore((s) => s.clearPageHeader);\n\n const extension = extensionId ? uiExtensions.find((ext) => ext.id === extensionId) : undefined;\n const pages = extension?.ui?.contributions?.pages;\n const page =\n extensionId && pages?.length\n ? pageId\n ? pages.find((p) => p.id === pageId || p.id === `${extensionId}.${pageId}`)\n : pages[0]\n : undefined;\n\n useLayoutEffect(() => {\n if (!extensionId || !extension || !page) {\n clearPageHeader();\n return () => clearPageHeader();\n }\n const headline = page.title?.trim() || extension.name || extensionId;\n setPageHeader({\n startExtra: null,\n main: (\n <div className=\"w-full min-w-0 px-3 sm:px-5 xl:px-6\">\n <h1\n className=\"min-w-0 truncate text-base font-semibold tracking-tight text-fg\"\n title={headline}\n >\n {headline}\n </h1>\n </div>\n ),\n end: null,\n });\n return () => clearPageHeader();\n }, [clearPageHeader, extension, extensionId, page, setPageHeader]);\n\n if (!extensionId) {\n return <ExtensionPageNotFound message=\"No extension ID provided.\" />;\n }\n\n if (!extension) {\n return (\n <ExtensionPageNotFound message={`Extension \"${extensionId}\" not found or has no UI.`} />\n );\n }\n\n if (!pages?.length) {\n return (\n <ExtensionPageNotFound message={`Extension \"${extensionId}\" has no page contributions.`} />\n );\n }\n\n if (!page) {\n return (\n <ExtensionPageNotFound message={`Page \"${pageId}\" not found in extension \"${extensionId}\".`} />\n );\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <ExtensionIframeHost\n extensionId={extensionId}\n extensionName={extension.name}\n entrypoint={page.entrypoint}\n permissions={extension.ui?.permissions}\n title={page.title}\n className=\"min-h-0 flex-1\"\n fixedHeight={undefined}\n maxHeight={99999}\n />\n </div>\n );\n}\n\nfunction ExtensionPageNotFound({ message }: { message: string }) {\n return (\n <div className=\"flex min-h-[min(40vh,16rem)] flex-1 items-center justify-center\">\n <div className=\"text-center\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n </div>\n );\n}\n"],"mappings":"gVAcA,SAAgB,GAAgB,CAC9B,GAAM,CAAE,cAAa,UAAW,GAAqD,CAC/E,EAAe,GAAiB,CAChC,EAAgB,EAAoB,GAAM,EAAE,cAAc,CAC1D,EAAkB,EAAoB,GAAM,EAAE,gBAAgB,CAE9D,EAAY,EAAc,EAAa,KAAM,GAAQ,EAAI,KAAO,EAAY,CAAG,IAAA,GAC/E,EAAQ,GAAW,IAAI,eAAe,MACtC,EACJ,GAAe,GAAO,OAClB,EACE,EAAM,KAAM,GAAM,EAAE,KAAO,GAAU,EAAE,KAAO,GAAG,EAAY,GAAG,IAAS,CACzE,EAAM,GACR,IAAA,GA+CN,OA7CA,EAAA,EAAA,qBAAsB,CACpB,GAAI,CAAC,GAAe,CAAC,GAAa,CAAC,EAEjC,OADA,GAAiB,KACJ,GAAiB,CAEhC,IAAM,EAAW,EAAK,OAAO,MAAM,EAAI,EAAU,MAAQ,EAezD,OAdA,EAAc,CACZ,WAAY,KACZ,MACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gDACb,EAAA,EAAA,KAAC,KAAD,CACE,UAAU,kEACV,MAAO,WAEN,EACE,CAAA,CACD,CAAA,CAER,IAAK,KACN,CAAC,KACW,GAAiB,EAC7B,CAAC,EAAiB,EAAW,EAAa,EAAM,EAAc,CAAC,CAE7D,EAIA,EAMA,GAAO,OAMP,GAOH,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,yCACb,EAAA,EAAA,KAAC,EAAD,CACe,cACb,cAAe,EAAU,KACzB,WAAY,EAAK,WACjB,YAAa,EAAU,IAAI,YAC3B,MAAO,EAAK,MACZ,UAAU,iBACV,YAAa,IAAA,GACb,UAAW,MACX,CAAA,CACE,CAAA,EAhBJ,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,SAAS,EAAO,4BAA4B,EAAY,IAAO,CAAA,EAN/F,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,8BAAiC,CAAA,EAN3F,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,2BAA8B,CAAA,EALnF,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAQ,4BAA8B,CAAA,CAqCxE,SAAS,EAAsB,CAAE,WAAgC,CAC/D,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,4EACb,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wBACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAyB,EAAY,CAAA,CAC9C,CAAA,CACF,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{n as r,o as i,r as a,t as o}from"./url-QmwQTJ-j.js";import{r as s}from"./vendor-swr-8rdcElid.js";import{i as c}from"./theme-store-CPTH77BE.js";var l=function(e){return e[e.PermissionDenied=4001]=`PermissionDenied`,e[e.InvalidRequest=4002]=`InvalidRequest`,e[e.MethodNotFound=4003]=`MethodNotFound`,e[e.InternalError=4004]=`InternalError`,e[e.Timeout=4005]=`Timeout`,e[e.ExtensionNotFound=4006]=`ExtensionNotFound`,e[e.RateLimited=4007]=`RateLimited`,e}({}),u=e(t(),1),d=[`--color-surface-base`,`--color-surface-panel`,`--color-surface-hover`,`--color-fg`,`--color-fg-muted`,`--color-fg-subtle`,`--color-accent`,`--color-accent-hover`,`--color-accent-soft`,`--color-edge`,`--color-edge-subtle`,`--color-danger`,`--color-success`,`--color-warning`,`--radius-sm`,`--radius-lg`,`--radius-xl`];function f(e){let t=getComputedStyle(document.documentElement),n={};for(let e of d){let r=t.getPropertyValue(e).trim();r&&(n[e]=r)}return{mode:e,tokens:n,fontFamily:t.getPropertyValue(`font-family`).trim(),fontFamilyMono:t.getPropertyValue(`--font-mono`).trim()}}var p={"agent.sendMessage":`agent.send`,"session.list":`session.read`,"session.navigate":`session.read`,"config.get":`config.read`,"config.set":`config.write`,"storage.get":`storage`,"storage.set":`storage`,"storage.remove":`storage`,"storage.keys":`storage`,"ui.notification":`notification`,"ui.navigate":`theme`,"theme.get":`theme`},m=class{iframes=new Map;byContentWindow=new WeakMap;handlers=new Map;extensionPermissions=new Map;eventSubscribers=new Map;agentStreamSubscriptions=new Map;boundListener=e=>{this.onWindowMessage(e)};constructor(){window.addEventListener(`message`,this.boundListener)}dispose(){window.removeEventListener(`message`,this.boundListener),this.iframes.clear(),this.handlers.clear(),this.extensionPermissions.clear(),this.eventSubscribers.clear(),this.agentStreamSubscriptions.clear()}registerIframe(e,t,n){let r=this.iframes.get(e);r&&r!==t&&r.contentWindow&&this.byContentWindow.delete(r.contentWindow),this.iframes.set(e,t),this.extensionPermissions.set(e,new Set(n)),this.rememberIframeWindow(e,t)}unregisterIframe(e){let t=this.iframes.get(e);t?.contentWindow&&this.byContentWindow.delete(t.contentWindow),this.iframes.delete(e),this.extensionPermissions.delete(e),this.agentStreamSubscriptions.delete(e)}rememberIframeWindow(e,t){let n=t.contentWindow;n&&this.byContentWindow.set(n,{extensionId:e,iframe:t})}subscribeAgentStream(e,t){let n=this.agentStreamSubscriptions.get(e);n||(n=new Set,this.agentStreamSubscriptions.set(e,n)),n.add(t)}unsubscribeAgentStream(e,t){this.agentStreamSubscriptions.get(e)?.delete(t)}forwardAgentStreamEvent(e,t){for(let[n,r]of this.agentStreamSubscriptions)r.has(e)&&this.sendEvent(n,`agent.stream.${e}`,t)}broadcastExtensionEvent(e,t,n){let r=t.startsWith(`ext.`)?t:`ext.${t}`;for(let[t]of this.iframes)t!==e&&this.sendEvent(t,r,n)}handleExtensionEvent(e,t,n){if(t.startsWith(`ext.`)){let r=t.slice(4);this.broadcastExtensionEvent(e,r,n);return}if(t===`agent.subscribe`||t===`agent.unsubscribe`){if(!(this.extensionPermissions.get(e)??new Set).has(`agent.subscribe`))return;let{sessionKey:r}=n??{};if(typeof r!=`string`||!r.trim())return;let i=r.trim();t===`agent.subscribe`?this.subscribeAgentStream(e,i):this.unsubscribeAgentStream(e,i)}}registerMethod(e,t){this.handlers.set(e,t)}subscribeExtensionEvents(e,t){let n=this.eventSubscribers.get(e);return n||(n=new Set,this.eventSubscribers.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&this.eventSubscribers.delete(e)}}sendInit(e,t,n){let r=this.iframes.get(e);if(!r?.contentWindow)return;this.rememberIframeWindow(e,r);let i=[...this.extensionPermissions.get(e)??[]];r.contentWindow.postMessage({source:`xopc-host`,type:`init`,extensionId:e,permissions:i,theme:t,locale:n},`*`)}sendEvent(e,t,n){let r=this.iframes.get(e);r?.contentWindow&&r.contentWindow.postMessage({source:`xopc-host`,type:`event`,event:t,data:n},`*`)}broadcastEvent(e,t){for(let[,n]of this.iframes)n.contentWindow?.postMessage({source:`xopc-host`,type:`event`,event:e,data:t},`*`)}postResponse(e,t,n,r,i){let a=e.contentWindow??(t?.source instanceof Window?t.source:null),o={source:`xopc-host`,type:`response`,requestId:n,result:r,error:i};a&&a.postMessage(o,`*`)}isTrustedExtensionSource(e,t){if(t.source===null)return!0;let n=e.contentWindow;if(t.source===n)return!0;if(n&&typeof Window<`u`&&t.source instanceof Window)try{return t.source.frameElement===e}catch{}return!1}async onWindowMessage(e){let t=e.data;if(!t||t.source!==`xopc-extension`)return;let n,r=t.extensionId;if(e.source instanceof Window){let t=this.byContentWindow.get(e.source);t&&(n=t.iframe,r=t.extensionId)}if(n||(n=this.iframes.get(t.extensionId),r=t.extensionId),!n||!(e.source instanceof Window&&this.byContentWindow.has(e.source))&&!this.isTrustedExtensionSource(n,e))return;if(t.type===`event`){this.handleExtensionEvent(r,t.event,t.data);let e=this.eventSubscribers.get(r);if(e){let n={event:t.event,data:t.data};for(let t of e)try{t(n)}catch{}}return}if(t.type!==`request`)return;let{requestId:i,method:a,params:o}=t,s=this.handlers.get(a);if(!s){this.postResponse(n,e,i,void 0,{code:l.MethodNotFound,message:`Unknown method: ${a}`});return}let c=p[a],u=this.extensionPermissions.get(r)??new Set;if(c&&!u.has(c)){this.postResponse(n,e,i,void 0,{code:l.PermissionDenied,message:`Missing permission: ${c}`});return}try{let t=await s(r,o);this.postResponse(n,e,i,t)}catch(t){let r=t instanceof Error?t.message:String(t);this.postResponse(n,e,i,void 0,{code:l.InternalError,message:r})}}};function h(e){e.registerMethod(`theme.get`,async()=>f(c.getState().resolved)),e.registerMethod(`ui.navigate`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`path`in t?String(t.path??``):``;n&&window.dispatchEvent(new CustomEvent(`extension-navigate`,{detail:{path:n}}))}),e.registerMethod(`ui.notification`,async(e,t)=>{window.dispatchEvent(new CustomEvent(`extension-notification`,{detail:t}))}),e.registerMethod(`session.navigate`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`sessionKey`in t?String(t.sessionKey??``):``;n&&window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:n},bubbles:!0}))}),e.registerMethod(`session.list`,async()=>{let e=await r(o(`/api/sessions`));if(!e.ok)throw Error(`Failed to list sessions: ${e.status}`);return((await e.json()).items??[]).map(e=>({sessionKey:e.key,title:e.name,lastMessageAt:e.updatedAt??e.lastAccessedAt,messageCount:e.messageCount}))}),e.registerMethod(`agent.sendMessage`,async(e,t)=>{let{message:n,sessionKey:i,newSession:a}=t,s=await r(o(`/api/agent`),{method:`POST`,headers:{Accept:`application/json`},body:JSON.stringify({message:n,channel:`webchat`,sessionKey:a?void 0:i,newSession:!!a})});if(!s.ok)throw Error(`Agent request failed: ${s.status}`);let c=await s.json();return{sessionKey:c.payload?.sessionKey??c.payload?.key??c.sessionKey??i??``}}),e.registerMethod(`config.get`,async e=>{let t=await r(o(`/api/extensions/${encodeURIComponent(e)}/config`));if(!t.ok)throw Error(`Failed to get config: ${t.status}`);return t.json()}),e.registerMethod(`config.set`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&!Array.isArray(t)?t:{},i=await r(o(`/api/extensions/${encodeURIComponent(e)}/config`),{method:`PATCH`,body:JSON.stringify(n)});if(!i.ok)throw Error(`Failed to set config: ${i.status}`)}),e.registerMethod(`storage.get`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`key`in t?String(t.key??``):``,i=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(n)}`));if(i.status!==404){if(!i.ok)throw Error(`Failed to get storage key: ${i.status}`);return(await i.json()).value}}),e.registerMethod(`storage.set`,async(e,t)=>{let n=t,i=n&&typeof n.key==`string`?n.key:``,a=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(i)}`),{method:`PUT`,body:JSON.stringify({value:n?.value})});if(!a.ok)throw Error(`Failed to set storage key: ${a.status}`)}),e.registerMethod(`storage.remove`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`key`in t?String(t.key??``):``,i=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(n)}`),{method:`DELETE`});if(i.status!==404&&!i.ok)throw Error(`Failed to remove storage key: ${i.status}`)}),e.registerMethod(`storage.keys`,async e=>{let t=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage`));if(!t.ok)throw Error(`Failed to list storage keys: ${t.status}`);return(await t.json()).keys??[]})}var g=n(),_=(0,u.createContext)(null);function v({children:e}){let t=(0,u.useRef)(null);if(!t.current){let e=new m;h(e),t.current=e}let n=t.current,{data:r,isLoading:l}=s(i(e=>!!e.token)?`gateway-extensions-list`:null,()=>a(o(`/api/extensions`)),{revalidateOnFocus:!1}),d=r?.extensions??[],p=c(e=>e.resolved),v=(0,u.useCallback)(e=>{let t=e.detail;t?.sessionKey&&n.forwardAgentStreamEvent(t.sessionKey,t.event??t)},[n]);(0,u.useEffect)(()=>(window.addEventListener(`agent-stream-event`,v),()=>{window.removeEventListener(`agent-stream-event`,v)}),[v]),(0,u.useEffect)(()=>{let e=f(p);n.broadcastEvent(`theme.changed`,e)},[p,n]);let y=(0,u.useRef)(n);y.current=n,(0,u.useEffect)(()=>()=>{y.current.dispose(),t.current=null},[]);let b=(0,u.useMemo)(()=>({router:n,extensions:d,loading:l}),[n,d,l]);return(0,g.jsx)(_.Provider,{value:b,children:e})}function y(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensionRouter must be used within ExtensionProvider`);return e.router}function b(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensions must be used within ExtensionProvider`);return e.extensions}function x(e){return e.active||e.activationEligible===!0}function S(e){let t=e.ui?.contributions;return t?Array.isArray(t.pages)&&t.pages.length>0||Array.isArray(t.settingsPanels)&&t.settingsPanels.length>0||Array.isArray(t.chatWidgets)&&t.chatWidgets.length>0:!1}function C(e){return!e.hasUi||!x(e)?!1:S(e)}function w(){let e=b();return(0,u.useMemo)(()=>e.filter(C),[e])}function T(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensionsLoading must be used within ExtensionProvider`);return e.loading}export{T as a,b as i,C as n,w as o,y as r,f as s,v as t};
|
|
2
|
-
//# sourceMappingURL=extension-provider-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{n as r,o as i,r as a,t as o}from"./url-D7yWllI8.js";import{r as s}from"./vendor-swr-8rdcElid.js";import{i as c}from"./theme-store-CywXkKml.js";var l=function(e){return e[e.PermissionDenied=4001]=`PermissionDenied`,e[e.InvalidRequest=4002]=`InvalidRequest`,e[e.MethodNotFound=4003]=`MethodNotFound`,e[e.InternalError=4004]=`InternalError`,e[e.Timeout=4005]=`Timeout`,e[e.ExtensionNotFound=4006]=`ExtensionNotFound`,e[e.RateLimited=4007]=`RateLimited`,e}({}),u=e(t(),1),d=[`--color-surface-base`,`--color-surface-panel`,`--color-surface-hover`,`--color-fg`,`--color-fg-muted`,`--color-fg-subtle`,`--color-accent`,`--color-accent-hover`,`--color-accent-soft`,`--color-edge`,`--color-edge-subtle`,`--color-danger`,`--color-success`,`--color-warning`,`--radius-sm`,`--radius-lg`,`--radius-xl`];function f(e){let t=getComputedStyle(document.documentElement),n={};for(let e of d){let r=t.getPropertyValue(e).trim();r&&(n[e]=r)}return{mode:e,tokens:n,fontFamily:t.getPropertyValue(`font-family`).trim(),fontFamilyMono:t.getPropertyValue(`--font-mono`).trim()}}var p={"agent.sendMessage":`agent.send`,"session.list":`session.read`,"session.navigate":`session.read`,"config.get":`config.read`,"config.set":`config.write`,"storage.get":`storage`,"storage.set":`storage`,"storage.remove":`storage`,"storage.keys":`storage`,"ui.notification":`notification`,"ui.navigate":`theme`,"theme.get":`theme`},m=class{iframes=new Map;byContentWindow=new WeakMap;handlers=new Map;extensionPermissions=new Map;eventSubscribers=new Map;agentStreamSubscriptions=new Map;boundListener=e=>{this.onWindowMessage(e)};constructor(){window.addEventListener(`message`,this.boundListener)}dispose(){window.removeEventListener(`message`,this.boundListener),this.iframes.clear(),this.handlers.clear(),this.extensionPermissions.clear(),this.eventSubscribers.clear(),this.agentStreamSubscriptions.clear()}registerIframe(e,t,n){let r=this.iframes.get(e);r&&r!==t&&r.contentWindow&&this.byContentWindow.delete(r.contentWindow),this.iframes.set(e,t),this.extensionPermissions.set(e,new Set(n)),this.rememberIframeWindow(e,t)}unregisterIframe(e){let t=this.iframes.get(e);t?.contentWindow&&this.byContentWindow.delete(t.contentWindow),this.iframes.delete(e),this.extensionPermissions.delete(e),this.agentStreamSubscriptions.delete(e)}rememberIframeWindow(e,t){let n=t.contentWindow;n&&this.byContentWindow.set(n,{extensionId:e,iframe:t})}subscribeAgentStream(e,t){let n=this.agentStreamSubscriptions.get(e);n||(n=new Set,this.agentStreamSubscriptions.set(e,n)),n.add(t)}unsubscribeAgentStream(e,t){this.agentStreamSubscriptions.get(e)?.delete(t)}forwardAgentStreamEvent(e,t){for(let[n,r]of this.agentStreamSubscriptions)r.has(e)&&this.sendEvent(n,`agent.stream.${e}`,t)}broadcastExtensionEvent(e,t,n){let r=t.startsWith(`ext.`)?t:`ext.${t}`;for(let[t]of this.iframes)t!==e&&this.sendEvent(t,r,n)}handleExtensionEvent(e,t,n){if(t.startsWith(`ext.`)){let r=t.slice(4);this.broadcastExtensionEvent(e,r,n);return}if(t===`agent.subscribe`||t===`agent.unsubscribe`){if(!(this.extensionPermissions.get(e)??new Set).has(`agent.subscribe`))return;let{sessionKey:r}=n??{};if(typeof r!=`string`||!r.trim())return;let i=r.trim();t===`agent.subscribe`?this.subscribeAgentStream(e,i):this.unsubscribeAgentStream(e,i)}}registerMethod(e,t){this.handlers.set(e,t)}subscribeExtensionEvents(e,t){let n=this.eventSubscribers.get(e);return n||(n=new Set,this.eventSubscribers.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&this.eventSubscribers.delete(e)}}sendInit(e,t,n){let r=this.iframes.get(e);if(!r?.contentWindow)return;this.rememberIframeWindow(e,r);let i=[...this.extensionPermissions.get(e)??[]];r.contentWindow.postMessage({source:`xopc-host`,type:`init`,extensionId:e,permissions:i,theme:t,locale:n},`*`)}sendEvent(e,t,n){let r=this.iframes.get(e);r?.contentWindow&&r.contentWindow.postMessage({source:`xopc-host`,type:`event`,event:t,data:n},`*`)}broadcastEvent(e,t){for(let[,n]of this.iframes)n.contentWindow?.postMessage({source:`xopc-host`,type:`event`,event:e,data:t},`*`)}postResponse(e,t,n,r,i){let a=e.contentWindow??(t?.source instanceof Window?t.source:null),o={source:`xopc-host`,type:`response`,requestId:n,result:r,error:i};a&&a.postMessage(o,`*`)}isTrustedExtensionSource(e,t){if(t.source===null)return!0;let n=e.contentWindow;if(t.source===n)return!0;if(n&&typeof Window<`u`&&t.source instanceof Window)try{return t.source.frameElement===e}catch{}return!1}async onWindowMessage(e){let t=e.data;if(!t||t.source!==`xopc-extension`)return;let n,r=t.extensionId;if(e.source instanceof Window){let t=this.byContentWindow.get(e.source);t&&(n=t.iframe,r=t.extensionId)}if(n||(n=this.iframes.get(t.extensionId),r=t.extensionId),!n||!(e.source instanceof Window&&this.byContentWindow.has(e.source))&&!this.isTrustedExtensionSource(n,e))return;if(t.type===`event`){this.handleExtensionEvent(r,t.event,t.data);let e=this.eventSubscribers.get(r);if(e){let n={event:t.event,data:t.data};for(let t of e)try{t(n)}catch{}}return}if(t.type!==`request`)return;let{requestId:i,method:a,params:o}=t,s=this.handlers.get(a);if(!s){this.postResponse(n,e,i,void 0,{code:l.MethodNotFound,message:`Unknown method: ${a}`});return}let c=p[a],u=this.extensionPermissions.get(r)??new Set;if(c&&!u.has(c)){this.postResponse(n,e,i,void 0,{code:l.PermissionDenied,message:`Missing permission: ${c}`});return}try{let t=await s(r,o);this.postResponse(n,e,i,t)}catch(t){let r=t instanceof Error?t.message:String(t);this.postResponse(n,e,i,void 0,{code:l.InternalError,message:r})}}};function h(e){e.registerMethod(`theme.get`,async()=>f(c.getState().resolved)),e.registerMethod(`ui.navigate`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`path`in t?String(t.path??``):``;n&&window.dispatchEvent(new CustomEvent(`extension-navigate`,{detail:{path:n}}))}),e.registerMethod(`ui.notification`,async(e,t)=>{window.dispatchEvent(new CustomEvent(`extension-notification`,{detail:t}))}),e.registerMethod(`session.navigate`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`sessionKey`in t?String(t.sessionKey??``):``;n&&window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:n},bubbles:!0}))}),e.registerMethod(`session.list`,async()=>{let e=await r(o(`/api/sessions`));if(!e.ok)throw Error(`Failed to list sessions: ${e.status}`);return((await e.json()).items??[]).map(e=>({sessionKey:e.key,title:e.name,lastMessageAt:e.updatedAt??e.lastAccessedAt,messageCount:e.messageCount}))}),e.registerMethod(`agent.sendMessage`,async(e,t)=>{let{message:n,sessionKey:i,newSession:a}=t,s=await r(o(`/api/agent`),{method:`POST`,headers:{Accept:`application/json`},body:JSON.stringify({message:n,channel:`webchat`,sessionKey:a?void 0:i,newSession:!!a})});if(!s.ok)throw Error(`Agent request failed: ${s.status}`);let c=await s.json();return{sessionKey:c.payload?.sessionKey??c.payload?.key??c.sessionKey??i??``}}),e.registerMethod(`config.get`,async e=>{let t=await r(o(`/api/extensions/${encodeURIComponent(e)}/config`));if(!t.ok)throw Error(`Failed to get config: ${t.status}`);return t.json()}),e.registerMethod(`config.set`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&!Array.isArray(t)?t:{},i=await r(o(`/api/extensions/${encodeURIComponent(e)}/config`),{method:`PATCH`,body:JSON.stringify(n)});if(!i.ok)throw Error(`Failed to set config: ${i.status}`)}),e.registerMethod(`storage.get`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`key`in t?String(t.key??``):``,i=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(n)}`));if(i.status!==404){if(!i.ok)throw Error(`Failed to get storage key: ${i.status}`);return(await i.json()).value}}),e.registerMethod(`storage.set`,async(e,t)=>{let n=t,i=n&&typeof n.key==`string`?n.key:``,a=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(i)}`),{method:`PUT`,body:JSON.stringify({value:n?.value})});if(!a.ok)throw Error(`Failed to set storage key: ${a.status}`)}),e.registerMethod(`storage.remove`,async(e,t)=>{let n=t&&typeof t==`object`&&t&&`key`in t?String(t.key??``):``,i=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage/${encodeURIComponent(n)}`),{method:`DELETE`});if(i.status!==404&&!i.ok)throw Error(`Failed to remove storage key: ${i.status}`)}),e.registerMethod(`storage.keys`,async e=>{let t=await r(o(`/api/extensions/${encodeURIComponent(e)}/storage`));if(!t.ok)throw Error(`Failed to list storage keys: ${t.status}`);return(await t.json()).keys??[]})}var g=n(),_=(0,u.createContext)(null);function v({children:e}){let t=(0,u.useRef)(null);if(!t.current){let e=new m;h(e),t.current=e}let n=t.current,{data:r,isLoading:l}=s(i(e=>!!e.token)?`gateway-extensions-list`:null,()=>a(o(`/api/extensions`)),{revalidateOnFocus:!1}),d=r?.extensions??[],p=c(e=>e.resolved),v=(0,u.useCallback)(e=>{let t=e.detail;t?.sessionKey&&n.forwardAgentStreamEvent(t.sessionKey,t.event??t)},[n]);(0,u.useEffect)(()=>(window.addEventListener(`agent-stream-event`,v),()=>{window.removeEventListener(`agent-stream-event`,v)}),[v]),(0,u.useEffect)(()=>{let e=f(p);n.broadcastEvent(`theme.changed`,e)},[p,n]);let y=(0,u.useRef)(n);y.current=n,(0,u.useEffect)(()=>()=>{y.current.dispose(),t.current=null},[]);let b=(0,u.useMemo)(()=>({router:n,extensions:d,loading:l}),[n,d,l]);return(0,g.jsx)(_.Provider,{value:b,children:e})}function y(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensionRouter must be used within ExtensionProvider`);return e.router}function b(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensions must be used within ExtensionProvider`);return e.extensions}function x(e){return e.active||e.activationEligible===!0}function S(e){let t=e.ui?.contributions;return t?Array.isArray(t.pages)&&t.pages.length>0||Array.isArray(t.settingsPanels)&&t.settingsPanels.length>0||Array.isArray(t.chatWidgets)&&t.chatWidgets.length>0:!1}function C(e){return!e.hasUi||!x(e)?!1:S(e)}function w(){let e=b();return(0,u.useMemo)(()=>e.filter(C),[e])}function T(){let e=(0,u.useContext)(_);if(!e)throw Error(`useExtensionsLoading must be used within ExtensionProvider`);return e.loading}export{T as a,b as i,C as n,w as o,y as r,f as s,v as t};
|
|
2
|
+
//# sourceMappingURL=extension-provider-DZCZgQE2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-provider-BpHodVRj.js","names":[],"sources":["../../../../../packages/extension-ui-sdk/src/types.ts","../../../../../web/src/features/extensions/theme-bridge.ts","../../../../../web/src/features/extensions/extension-message-router.ts","../../../../../web/src/features/extensions/extension-provider.tsx"],"sourcesContent":["export enum ExtensionErrorCode {\n PermissionDenied = 4001,\n InvalidRequest = 4002,\n MethodNotFound = 4003,\n InternalError = 4004,\n Timeout = 4005,\n ExtensionNotFound = 4006,\n RateLimited = 4007,\n}\n\nexport interface ThemeInfo {\n mode: 'light' | 'dark';\n tokens: Record<string, string>;\n fontFamily?: string;\n fontFamilyMono?: string;\n}\n\nexport interface ExtensionRequest {\n source: 'xopc-extension';\n extensionId: string;\n type: 'request';\n requestId: string;\n method: string;\n params?: unknown;\n}\n\nexport interface ExtensionEventMessage {\n source: 'xopc-extension';\n extensionId: string;\n type: 'event';\n event: string;\n data?: unknown;\n}\n\nexport interface HostInit {\n source: 'xopc-host';\n type: 'init';\n extensionId: string;\n permissions: string[];\n theme: ThemeInfo;\n locale: string;\n}\n\nexport interface HostResponse {\n source: 'xopc-host';\n type: 'response';\n requestId: string;\n result?: unknown;\n error?: { code: number; message: string };\n}\n\nexport interface HostEventMessage {\n source: 'xopc-host';\n type: 'event';\n event: string;\n data?: unknown;\n}\n\nexport type HostToExtensionMessage = HostInit | HostResponse | HostEventMessage;\nexport type ExtensionToHostMessage = ExtensionRequest | ExtensionEventMessage;\n\nexport type StreamHandler = (payload: unknown) => void;\n\nexport interface ExtensionClient {\n whenReady(): Promise<void>;\n theme: {\n getTheme(): Promise<ThemeInfo>;\n onThemeChange(handler: (t: ThemeInfo) => void): () => void;\n };\n agent: {\n sendMessage(\n message: string,\n options?: { sessionKey?: string; newSession?: boolean },\n ): Promise<{ sessionKey: string }>;\n onStreamEvent(sessionKey: string, handler: StreamHandler): () => void;\n };\n session: {\n listSessions(): Promise<unknown[]>;\n navigateToSession(sessionKey: string): Promise<void>;\n };\n config: {\n getExtensionConfig<T = Record<string, unknown>>(): Promise<T>;\n setExtensionConfig(patch: Record<string, unknown>): Promise<void>;\n };\n storage: {\n get<T = unknown>(key: string): Promise<T | undefined>;\n set(key: string, value: unknown): Promise<void>;\n remove(key: string): Promise<void>;\n keys(): Promise<string[]>;\n };\n ui: {\n resize(height: number): void;\n showNotification(options: {\n type?: 'success' | 'error' | 'info';\n title: string;\n message?: string;\n }): Promise<void>;\n closePanel(): void;\n navigate(path: string): Promise<void>;\n /** Chat/tool widget iframe: host sends the tool result via `widget.data` after load. */\n onWidgetResult(handler: (data: unknown) => void): () => void;\n };\n events: {\n emit(event: string, data?: unknown): void;\n on(event: string, handler: (data: unknown) => void): () => void;\n };\n onDispose(handler: () => void): () => void;\n onDidChangeVisibility(handler: (visible: boolean) => void): () => void;\n}\n","import type { ThemeInfo } from '@xopcai/extension-ui-sdk';\n\nconst THEME_TOKEN_NAMES = [\n '--color-surface-base',\n '--color-surface-panel',\n '--color-surface-hover',\n '--color-fg',\n '--color-fg-muted',\n '--color-fg-subtle',\n '--color-accent',\n '--color-accent-hover',\n '--color-accent-soft',\n '--color-edge',\n '--color-edge-subtle',\n '--color-danger',\n '--color-success',\n '--color-warning',\n '--radius-sm',\n '--radius-lg',\n '--radius-xl',\n];\n\nexport function buildThemeInfo(mode: 'light' | 'dark'): ThemeInfo {\n const computedStyle = getComputedStyle(document.documentElement);\n const tokens: Record<string, string> = {};\n for (const name of THEME_TOKEN_NAMES) {\n const value = computedStyle.getPropertyValue(name).trim();\n if (value) tokens[name] = value;\n }\n return {\n mode,\n tokens,\n fontFamily: computedStyle.getPropertyValue('font-family').trim(),\n fontFamilyMono: computedStyle.getPropertyValue('--font-mono').trim(),\n };\n}\n","import { ExtensionErrorCode, type ThemeInfo } from '@xopcai/extension-ui-sdk';\n\nimport { apiFetch } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { useThemeStore } from '@/stores/theme-store';\n\nimport { buildThemeInfo } from './theme-bridge';\n\nexport type MethodHandler = (extensionId: string, params: unknown) => Promise<unknown>;\n\nconst METHOD_PERMISSION_MAP: Record<string, string | undefined> = {\n 'agent.sendMessage': 'agent.send',\n 'session.list': 'session.read',\n 'session.navigate': 'session.read',\n 'config.get': 'config.read',\n 'config.set': 'config.write',\n 'storage.get': 'storage',\n 'storage.set': 'storage',\n 'storage.remove': 'storage',\n 'storage.keys': 'storage',\n 'ui.notification': 'notification',\n 'ui.navigate': 'theme',\n 'theme.get': 'theme',\n};\n\ntype ExtensionHostMessage =\n | {\n source: 'xopc-extension';\n extensionId: string;\n type: 'request';\n requestId: string;\n method: string;\n params?: unknown;\n }\n | {\n source: 'xopc-extension';\n extensionId: string;\n type: 'event';\n event: string;\n data?: unknown;\n };\n\nexport class ExtensionMessageRouter {\n private iframes = new Map<string, HTMLIFrameElement>();\n /** Incoming iframe → extensionId (handles contentWindow ready after first register; also disambiguates identity). */\n private byContentWindow = new WeakMap<\n Window,\n { extensionId: string; iframe: HTMLIFrameElement }\n >();\n private handlers = new Map<string, MethodHandler>();\n private extensionPermissions = new Map<string, Set<string>>();\n private eventSubscribers = new Map<string, Set<(e: { event: string; data?: unknown }) => void>>();\n /** extensionId → sessionKeys subscribed for agent stream forwarding */\n private agentStreamSubscriptions = new Map<string, Set<string>>();\n private boundListener = (ev: MessageEvent) => {\n void this.onWindowMessage(ev);\n };\n\n constructor() {\n window.addEventListener('message', this.boundListener);\n }\n\n dispose(): void {\n window.removeEventListener('message', this.boundListener);\n this.iframes.clear();\n this.handlers.clear();\n this.extensionPermissions.clear();\n this.eventSubscribers.clear();\n this.agentStreamSubscriptions.clear();\n }\n\n registerIframe(extensionId: string, iframe: HTMLIFrameElement, permissions: string[]): void {\n const prev = this.iframes.get(extensionId);\n if (prev && prev !== iframe && prev.contentWindow) {\n this.byContentWindow.delete(prev.contentWindow);\n }\n this.iframes.set(extensionId, iframe);\n this.extensionPermissions.set(extensionId, new Set(permissions));\n this.rememberIframeWindow(extensionId, iframe);\n }\n\n unregisterIframe(extensionId: string): void {\n const iframe = this.iframes.get(extensionId);\n if (iframe?.contentWindow) {\n this.byContentWindow.delete(iframe.contentWindow);\n }\n this.iframes.delete(extensionId);\n this.extensionPermissions.delete(extensionId);\n this.agentStreamSubscriptions.delete(extensionId);\n }\n\n private rememberIframeWindow(extensionId: string, iframe: HTMLIFrameElement): void {\n const cw = iframe.contentWindow;\n if (cw) {\n this.byContentWindow.set(cw, { extensionId, iframe });\n }\n }\n\n subscribeAgentStream(extensionId: string, sessionKey: string): void {\n let sessions = this.agentStreamSubscriptions.get(extensionId);\n if (!sessions) {\n sessions = new Set();\n this.agentStreamSubscriptions.set(extensionId, sessions);\n }\n sessions.add(sessionKey);\n }\n\n unsubscribeAgentStream(extensionId: string, sessionKey: string): void {\n this.agentStreamSubscriptions.get(extensionId)?.delete(sessionKey);\n }\n\n forwardAgentStreamEvent(sessionKey: string, event: unknown): void {\n for (const [extensionId, sessions] of this.agentStreamSubscriptions) {\n if (sessions.has(sessionKey)) {\n this.sendEvent(extensionId, `agent.stream.${sessionKey}`, event);\n }\n }\n }\n\n /**\n * Broadcast a fire-and-forget event from one extension to all others (`ext.{name}` on the wire).\n */\n broadcastExtensionEvent(sourceExtensionId: string, bareName: string, data?: unknown): void {\n const outbound = bareName.startsWith('ext.') ? bareName : `ext.${bareName}`;\n for (const [extensionId] of this.iframes) {\n if (extensionId !== sourceExtensionId) {\n this.sendEvent(extensionId, outbound, data);\n }\n }\n }\n\n private handleExtensionEvent(extensionId: string, event: string, data: unknown): void {\n if (event.startsWith('ext.')) {\n const rest = event.slice(4);\n this.broadcastExtensionEvent(extensionId, rest, data);\n return;\n }\n if (event === 'agent.subscribe' || event === 'agent.unsubscribe') {\n const perms = this.extensionPermissions.get(extensionId) ?? new Set<string>();\n if (!perms.has('agent.subscribe')) return;\n const { sessionKey } = (data ?? {}) as { sessionKey?: string };\n if (typeof sessionKey !== 'string' || !sessionKey.trim()) return;\n const sk = sessionKey.trim();\n if (event === 'agent.subscribe') this.subscribeAgentStream(extensionId, sk);\n else this.unsubscribeAgentStream(extensionId, sk);\n }\n }\n\n registerMethod(method: string, handler: MethodHandler): void {\n this.handlers.set(method, handler);\n }\n\n subscribeExtensionEvents(\n extensionId: string,\n fn: (e: { event: string; data?: unknown }) => void,\n ): () => void {\n let subscribers = this.eventSubscribers.get(extensionId);\n if (!subscribers) {\n subscribers = new Set();\n this.eventSubscribers.set(extensionId, subscribers);\n }\n subscribers.add(fn);\n return () => {\n subscribers.delete(fn);\n if (subscribers.size === 0) {\n this.eventSubscribers.delete(extensionId);\n }\n };\n }\n\n sendInit(extensionId: string, theme: ThemeInfo, locale: string): void {\n const iframe = this.iframes.get(extensionId);\n if (!iframe?.contentWindow) return;\n this.rememberIframeWindow(extensionId, iframe);\n const permissions = [...(this.extensionPermissions.get(extensionId) ?? [])];\n iframe.contentWindow.postMessage(\n {\n source: 'xopc-host',\n type: 'init',\n extensionId,\n permissions,\n theme,\n locale,\n },\n '*',\n );\n }\n\n sendEvent(extensionId: string, event: string, data?: unknown): void {\n const iframe = this.iframes.get(extensionId);\n if (!iframe?.contentWindow) return;\n iframe.contentWindow.postMessage(\n {\n source: 'xopc-host',\n type: 'event',\n event,\n data,\n },\n '*',\n );\n }\n\n broadcastEvent(event: string, data?: unknown): void {\n for (const [, iframe] of this.iframes) {\n iframe.contentWindow?.postMessage(\n {\n source: 'xopc-host',\n type: 'event',\n event,\n data,\n },\n '*',\n );\n }\n }\n\n /** Prefer `iframe.contentWindow`; if null (rare), fall back to the request's `event.source`. */\n private postResponse(\n iframe: HTMLIFrameElement,\n event: MessageEvent | undefined,\n requestId: string,\n result?: unknown,\n error?: { code: number; message: string },\n ): void {\n const target =\n iframe.contentWindow ??\n (event?.source instanceof Window ? event.source : null);\n const payload = {\n source: 'xopc-host' as const,\n type: 'response' as const,\n requestId,\n result,\n error,\n };\n if (!target) {\n return;\n }\n target.postMessage(payload, '*');\n }\n\n private isTrustedExtensionSource(iframe: HTMLIFrameElement, ev: MessageEvent): boolean {\n if (ev.source === null) {\n // Sandboxed iframes (no allow-same-origin) may report a null source.\n return true;\n }\n const cw = iframe.contentWindow;\n if (ev.source === cw) {\n return true;\n }\n // Some environments do not keep `===` identity between postMessage `source`\n // and `iframe.contentWindow`; `frameElement` still ties the window to this iframe.\n if (cw && typeof Window !== 'undefined' && ev.source instanceof Window) {\n try {\n return ev.source.frameElement === iframe;\n } catch {\n /* cross-origin access */\n }\n }\n return false;\n }\n\n private async onWindowMessage(event: MessageEvent) {\n const msg = event.data as ExtensionHostMessage | undefined;\n if (!msg || msg.source !== 'xopc-extension') return;\n\n let iframe: HTMLIFrameElement | undefined;\n let effectiveExtensionId = msg.extensionId;\n\n if (event.source instanceof Window) {\n const reg = this.byContentWindow.get(event.source);\n if (reg) {\n iframe = reg.iframe;\n effectiveExtensionId = reg.extensionId;\n }\n }\n\n if (!iframe) {\n iframe = this.iframes.get(msg.extensionId);\n effectiveExtensionId = msg.extensionId;\n }\n\n if (!iframe) {\n return;\n }\n\n const trustedByWindow =\n event.source instanceof Window && this.byContentWindow.has(event.source);\n\n if (!trustedByWindow && !this.isTrustedExtensionSource(iframe, event)) {\n return;\n }\n\n if (msg.type === 'event') {\n this.handleExtensionEvent(effectiveExtensionId, msg.event, msg.data);\n const subs = this.eventSubscribers.get(effectiveExtensionId);\n if (subs) {\n const payload = { event: msg.event, data: msg.data };\n for (const fn of subs) {\n try {\n fn(payload);\n } catch {\n /* ignore */\n }\n }\n }\n return;\n }\n\n if (msg.type !== 'request') return;\n\n const { requestId, method, params } = msg;\n\n const handler = this.handlers.get(method);\n if (!handler) {\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.MethodNotFound,\n message: `Unknown method: ${method}`,\n });\n return;\n }\n\n const required = METHOD_PERMISSION_MAP[method];\n const perms = this.extensionPermissions.get(effectiveExtensionId) ?? new Set<string>();\n if (required && !perms.has(required)) {\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.PermissionDenied,\n message: `Missing permission: ${required}`,\n });\n return;\n }\n\n try {\n const result = await handler(effectiveExtensionId, params);\n this.postResponse(iframe, event, requestId, result);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.InternalError,\n message,\n });\n }\n }\n}\n\nexport function registerBuiltinMethods(router: ExtensionMessageRouter): void {\n router.registerMethod('theme.get', async () =>\n buildThemeInfo(useThemeStore.getState().resolved),\n );\n\n router.registerMethod('ui.navigate', async (_extensionId, params) => {\n const path =\n params && typeof params === 'object' && params !== null && 'path' in params\n ? String((params as { path?: string }).path ?? '')\n : '';\n if (path) {\n window.dispatchEvent(new CustomEvent('extension-navigate', { detail: { path } }));\n }\n });\n\n router.registerMethod('ui.notification', async (_extensionId, params) => {\n window.dispatchEvent(new CustomEvent('extension-notification', { detail: params }));\n });\n\n router.registerMethod('session.navigate', async (_extensionId, params) => {\n const sessionKey =\n params && typeof params === 'object' && params !== null && 'sessionKey' in params\n ? String((params as { sessionKey?: string }).sessionKey ?? '')\n : '';\n if (sessionKey) {\n window.dispatchEvent(\n new CustomEvent('navigate-to-chat', { detail: { sessionKey }, bubbles: true }),\n );\n }\n });\n\n router.registerMethod('session.list', async () => {\n const response = await apiFetch(apiUrl('/api/sessions'));\n if (!response.ok) throw new Error(`Failed to list sessions: ${response.status}`);\n const data = (await response.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n lastAccessedAt?: string;\n messageCount?: number;\n }>;\n };\n return (data.items ?? []).map((s) => ({\n sessionKey: s.key,\n title: s.name,\n lastMessageAt: s.updatedAt ?? s.lastAccessedAt,\n messageCount: s.messageCount,\n }));\n });\n\n router.registerMethod('agent.sendMessage', async (_extensionId, params) => {\n const { message, sessionKey, newSession } = params as {\n message: string;\n sessionKey?: string;\n newSession?: boolean;\n };\n const response = await apiFetch(apiUrl('/api/agent'), {\n method: 'POST',\n headers: { Accept: 'application/json' },\n body: JSON.stringify({\n message,\n channel: 'webchat',\n sessionKey: newSession ? undefined : sessionKey,\n newSession: Boolean(newSession),\n }),\n });\n if (!response.ok) throw new Error(`Agent request failed: ${response.status}`);\n const data = (await response.json()) as {\n payload?: { sessionKey?: string; key?: string };\n sessionKey?: string;\n };\n const fromPayload = data.payload?.sessionKey ?? data.payload?.key;\n return { sessionKey: fromPayload ?? data.sessionKey ?? sessionKey ?? '' };\n });\n\n router.registerMethod('config.get', async (extensionId) => {\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`));\n if (!response.ok) throw new Error(`Failed to get config: ${response.status}`);\n return response.json();\n });\n\n router.registerMethod('config.set', async (extensionId, params) => {\n const patch =\n params && typeof params === 'object' && params !== null && !Array.isArray(params)\n ? (params as Record<string, unknown>)\n : {};\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`), {\n method: 'PATCH',\n body: JSON.stringify(patch),\n });\n if (!response.ok) throw new Error(`Failed to set config: ${response.status}`);\n });\n\n router.registerMethod('storage.get', async (extensionId, params) => {\n const key =\n params && typeof params === 'object' && params !== null && 'key' in params\n ? String((params as { key?: string }).key ?? '')\n : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n );\n if (response.status === 404) return undefined;\n if (!response.ok) throw new Error(`Failed to get storage key: ${response.status}`);\n const data = (await response.json()) as { value: unknown };\n return data.value;\n });\n\n router.registerMethod('storage.set', async (extensionId, params) => {\n const raw = params as { key?: string; value?: unknown } | undefined;\n const key = raw && typeof raw.key === 'string' ? raw.key : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n { method: 'PUT', body: JSON.stringify({ value: raw?.value }) },\n );\n if (!response.ok) throw new Error(`Failed to set storage key: ${response.status}`);\n });\n\n router.registerMethod('storage.remove', async (extensionId, params) => {\n const key =\n params && typeof params === 'object' && params !== null && 'key' in params\n ? String((params as { key?: string }).key ?? '')\n : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n { method: 'DELETE' },\n );\n if (response.status === 404) return;\n if (!response.ok) throw new Error(`Failed to remove storage key: ${response.status}`);\n });\n\n router.registerMethod('storage.keys', async (extensionId) => {\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage`));\n if (!response.ok) throw new Error(`Failed to list storage keys: ${response.status}`);\n const data = (await response.json()) as { keys?: string[] };\n return data.keys ?? [];\n });\n}\n","import { createContext, useCallback, useContext, useEffect, useMemo, useRef } from 'react';\nimport useSWR from 'swr';\n\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { useThemeStore } from '@/stores/theme-store';\n\nimport { ExtensionMessageRouter, registerBuiltinMethods } from './extension-message-router';\nimport type { ExtensionApiRow, ExtensionsListResponse } from './types';\nimport { buildThemeInfo } from './theme-bridge';\n\ntype Ctx = {\n router: ExtensionMessageRouter;\n extensions: ExtensionApiRow[];\n loading: boolean;\n};\n\nconst ExtensionContext = createContext<Ctx | null>(null);\n\nexport function ExtensionProvider({ children }: { children: React.ReactNode }) {\n const routerRef = useRef<ExtensionMessageRouter | null>(null);\n if (!routerRef.current) {\n const newRouter = new ExtensionMessageRouter();\n registerBuiltinMethods(newRouter);\n routerRef.current = newRouter;\n }\n const router = routerRef.current;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const { data, isLoading } = useSWR(\n hasToken ? 'gateway-extensions-list' : null,\n () => fetchJson<ExtensionsListResponse>(apiUrl('/api/extensions')),\n { revalidateOnFocus: false },\n );\n\n const extensions = data?.extensions ?? [];\n const resolved = useThemeStore((s) => s.resolved);\n\n const handleAgentStreamEvent = useCallback(\n (event: Event) => {\n const detail = (event as CustomEvent<{ sessionKey?: string; event?: unknown }>).detail;\n if (!detail?.sessionKey) return;\n router.forwardAgentStreamEvent(detail.sessionKey, detail.event ?? detail);\n },\n [router],\n );\n\n useEffect(() => {\n window.addEventListener('agent-stream-event', handleAgentStreamEvent as EventListener);\n return () => {\n window.removeEventListener('agent-stream-event', handleAgentStreamEvent as EventListener);\n };\n }, [handleAgentStreamEvent]);\n\n useEffect(() => {\n const theme = buildThemeInfo(resolved);\n router.broadcastEvent('theme.changed', theme);\n }, [resolved, router]);\n\n const routerForCleanupRef = useRef(router);\n routerForCleanupRef.current = router;\n useEffect(\n () => () => {\n routerForCleanupRef.current.dispose();\n // React StrictMode (dev) invokes this cleanup, then remounts. `useRef` survives,\n // so we must clear the ref or the next render would reuse a disposed router\n // (no `window` message listener — extension requests time out).\n routerRef.current = null;\n },\n [],\n );\n\n const value = useMemo(\n () => ({ router, extensions, loading: isLoading }),\n [router, extensions, isLoading],\n );\n\n return <ExtensionContext.Provider value={value}>{children}</ExtensionContext.Provider>;\n}\n\nexport function useExtensionRouter(): ExtensionMessageRouter {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensionRouter must be used within ExtensionProvider');\n }\n return ctx.router;\n}\n\nexport function useExtensions(): ExtensionApiRow[] {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensions must be used within ExtensionProvider');\n }\n return ctx.extensions;\n}\n\n/** Loaded in the gateway process (tools/hooks) or marked to load after restart. */\nfunction extensionUiUnlocked(e: ExtensionApiRow): boolean {\n return e.active || e.activationEligible === true;\n}\n\n/** Manifest declares iframe surfaces (served from disk); show nav/routes even when Node side is not active yet. */\nfunction manifestDeclaresGatewayContributions(e: ExtensionApiRow): boolean {\n const c = e.ui?.contributions;\n if (!c) return false;\n return (\n (Array.isArray(c.pages) && c.pages.length > 0) ||\n (Array.isArray(c.settingsPanels) && c.settingsPanels.length > 0) ||\n (Array.isArray(c.chatWidgets) && c.chatWidgets.length > 0)\n );\n}\n\n/** Used by Apps detail links and {@link useUiExtensions}. */\nexport function extensionExposesGatewayShellUi(e: ExtensionApiRow): boolean {\n if (!e.hasUi) return false;\n // Extension must be enabled (active or scheduled to activate after restart) before\n // its UI contributions appear in the shell. `manifestDeclaresGatewayContributions`\n // only determines whether the iframe surfaces are shown *while* the Node-side is still\n // starting up — it must not override a deliberate disable action by the user.\n if (!extensionUiUnlocked(e)) return false;\n return manifestDeclaresGatewayContributions(e);\n}\n\nexport function useUiExtensions(): ExtensionApiRow[] {\n const list = useExtensions();\n return useMemo(() => list.filter(extensionExposesGatewayShellUi), [list]);\n}\n\nexport function useExtensionsLoading(): boolean {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensionsLoading must be used within ExtensionProvider');\n }\n return ctx.loading;\n}\n"],"mappings":"+PAAA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,EAAA,iBAAA,MAAA,mBACA,EAAA,EAAA,eAAA,MAAA,iBACA,EAAA,EAAA,eAAA,MAAA,iBACA,EAAA,EAAA,cAAA,MAAA,gBACA,EAAA,EAAA,QAAA,MAAA,UACA,EAAA,EAAA,kBAAA,MAAA,oBACA,EAAA,EAAA,YAAA,MAAA,oBACD,YCND,EAAA,+TAoBA,SAAA,EAAA,EAAA,uDAGE,IAAA,IAAA,KAAA,EAAA,oCAEE,IAAA,EAAA,GAAA,GAEF,MAAA,8HCnBF,IAAM,EAA4D,CAChE,oBAAqB,aACrB,eAAgB,eAChB,mBAAoB,eACpB,aAAc,cACd,aAAc,eACd,cAAe,UACf,cAAe,UACf,iBAAkB,UAClB,eAAgB,UAChB,kBAAmB,eACnB,cAAe,QACf,YAAa,QACd,CAmBY,EAAb,KAAoC,CAClC,QAAkB,IAAI,IAEtB,gBAA0B,IAAI,QAI9B,SAAmB,IAAI,IACvB,qBAA+B,IAAI,IACnC,iBAA2B,IAAI,IAE/B,yBAAmC,IAAI,IACvC,cAAyB,GAAqB,CACvC,KAAK,gBAAgB,EAAG,EAG/B,aAAc,CACZ,OAAO,iBAAiB,UAAW,KAAK,cAAc,CAGxD,SAAgB,CACd,OAAO,oBAAoB,UAAW,KAAK,cAAc,CACzD,KAAK,QAAQ,OAAO,CACpB,KAAK,SAAS,OAAO,CACrB,KAAK,qBAAqB,OAAO,CACjC,KAAK,iBAAiB,OAAO,CAC7B,KAAK,yBAAyB,OAAO,CAGvC,eAAe,EAAqB,EAA2B,EAA6B,CAC1F,IAAM,EAAO,KAAK,QAAQ,IAAI,EAAY,CACtC,GAAQ,IAAS,GAAU,EAAK,eAClC,KAAK,gBAAgB,OAAO,EAAK,cAAc,CAEjD,KAAK,QAAQ,IAAI,EAAa,EAAO,CACrC,KAAK,qBAAqB,IAAI,EAAa,IAAI,IAAI,EAAY,CAAC,CAChE,KAAK,qBAAqB,EAAa,EAAO,CAGhD,iBAAiB,EAA2B,CAC1C,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CACxC,GAAQ,eACV,KAAK,gBAAgB,OAAO,EAAO,cAAc,CAEnD,KAAK,QAAQ,OAAO,EAAY,CAChC,KAAK,qBAAqB,OAAO,EAAY,CAC7C,KAAK,yBAAyB,OAAO,EAAY,CAGnD,qBAA6B,EAAqB,EAAiC,CACjF,IAAM,EAAK,EAAO,cACd,GACF,KAAK,gBAAgB,IAAI,EAAI,CAAE,cAAa,SAAQ,CAAC,CAIzD,qBAAqB,EAAqB,EAA0B,CAClE,IAAI,EAAW,KAAK,yBAAyB,IAAI,EAAY,CACxD,IACH,EAAW,IAAI,IACf,KAAK,yBAAyB,IAAI,EAAa,EAAS,EAE1D,EAAS,IAAI,EAAW,CAG1B,uBAAuB,EAAqB,EAA0B,CACpE,KAAK,yBAAyB,IAAI,EAAY,EAAE,OAAO,EAAW,CAGpE,wBAAwB,EAAoB,EAAsB,CAChE,IAAK,GAAM,CAAC,EAAa,KAAa,KAAK,yBACrC,EAAS,IAAI,EAAW,EAC1B,KAAK,UAAU,EAAa,gBAAgB,IAAc,EAAM,CAQtE,wBAAwB,EAA2B,EAAkB,EAAsB,CACzF,IAAM,EAAW,EAAS,WAAW,OAAO,CAAG,EAAW,OAAO,IACjE,IAAK,GAAM,CAAC,KAAgB,KAAK,QAC3B,IAAgB,GAClB,KAAK,UAAU,EAAa,EAAU,EAAK,CAKjD,qBAA6B,EAAqB,EAAe,EAAqB,CACpF,GAAI,EAAM,WAAW,OAAO,CAAE,CAC5B,IAAM,EAAO,EAAM,MAAM,EAAE,CAC3B,KAAK,wBAAwB,EAAa,EAAM,EAAK,CACrD,OAEF,GAAI,IAAU,mBAAqB,IAAU,oBAAqB,CAEhE,GAAI,EADU,KAAK,qBAAqB,IAAI,EAAY,EAAI,IAAI,KACrD,IAAI,kBAAkB,CAAE,OACnC,GAAM,CAAE,cAAgB,GAAQ,EAAE,CAClC,GAAI,OAAO,GAAe,UAAY,CAAC,EAAW,MAAM,CAAE,OAC1D,IAAM,EAAK,EAAW,MAAM,CACxB,IAAU,kBAAmB,KAAK,qBAAqB,EAAa,EAAG,CACtE,KAAK,uBAAuB,EAAa,EAAG,EAIrD,eAAe,EAAgB,EAA8B,CAC3D,KAAK,SAAS,IAAI,EAAQ,EAAQ,CAGpC,yBACE,EACA,EACY,CACZ,IAAI,EAAc,KAAK,iBAAiB,IAAI,EAAY,CAMxD,OALK,IACH,EAAc,IAAI,IAClB,KAAK,iBAAiB,IAAI,EAAa,EAAY,EAErD,EAAY,IAAI,EAAG,KACN,CACX,EAAY,OAAO,EAAG,CAClB,EAAY,OAAS,GACvB,KAAK,iBAAiB,OAAO,EAAY,EAK/C,SAAS,EAAqB,EAAkB,EAAsB,CACpE,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CAC5C,GAAI,CAAC,GAAQ,cAAe,OAC5B,KAAK,qBAAqB,EAAa,EAAO,CAC9C,IAAM,EAAc,CAAC,GAAI,KAAK,qBAAqB,IAAI,EAAY,EAAI,EAAE,CAAE,CAC3E,EAAO,cAAc,YACnB,CACE,OAAQ,YACR,KAAM,OACN,cACA,cACA,QACA,SACD,CACD,IACD,CAGH,UAAU,EAAqB,EAAe,EAAsB,CAClE,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CACvC,GAAQ,eACb,EAAO,cAAc,YACnB,CACE,OAAQ,YACR,KAAM,QACN,QACA,OACD,CACD,IACD,CAGH,eAAe,EAAe,EAAsB,CAClD,IAAK,GAAM,EAAG,KAAW,KAAK,QAC5B,EAAO,eAAe,YACpB,CACE,OAAQ,YACR,KAAM,QACN,QACA,OACD,CACD,IACD,CAKL,aACE,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EACJ,EAAO,gBACN,GAAO,kBAAkB,OAAS,EAAM,OAAS,MAC9C,EAAU,CACd,OAAQ,YACR,KAAM,WACN,YACA,SACA,QACD,CACI,GAGL,EAAO,YAAY,EAAS,IAAI,CAGlC,yBAAiC,EAA2B,EAA2B,CACrF,GAAI,EAAG,SAAW,KAEhB,MAAO,GAET,IAAM,EAAK,EAAO,cAClB,GAAI,EAAG,SAAW,EAChB,MAAO,GAIT,GAAI,GAAM,OAAO,OAAW,KAAe,EAAG,kBAAkB,OAC9D,GAAI,CACF,OAAO,EAAG,OAAO,eAAiB,OAC5B,EAIV,MAAO,GAGT,MAAc,gBAAgB,EAAqB,CACjD,IAAM,EAAM,EAAM,KAClB,GAAI,CAAC,GAAO,EAAI,SAAW,iBAAkB,OAE7C,IAAI,EACA,EAAuB,EAAI,YAE/B,GAAI,EAAM,kBAAkB,OAAQ,CAClC,IAAM,EAAM,KAAK,gBAAgB,IAAI,EAAM,OAAO,CAC9C,IACF,EAAS,EAAI,OACb,EAAuB,EAAI,aAgB/B,GAZK,IACH,EAAS,KAAK,QAAQ,IAAI,EAAI,YAAY,CAC1C,EAAuB,EAAI,aAGzB,CAAC,GAOD,EAFF,EAAM,kBAAkB,QAAU,KAAK,gBAAgB,IAAI,EAAM,OAAO,GAElD,CAAC,KAAK,yBAAyB,EAAQ,EAAM,CACnE,OAGF,GAAI,EAAI,OAAS,QAAS,CACxB,KAAK,qBAAqB,EAAsB,EAAI,MAAO,EAAI,KAAK,CACpE,IAAM,EAAO,KAAK,iBAAiB,IAAI,EAAqB,CAC5D,GAAI,EAAM,CACR,IAAM,EAAU,CAAE,MAAO,EAAI,MAAO,KAAM,EAAI,KAAM,CACpD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAG,EAAQ,MACL,GAKZ,OAGF,GAAI,EAAI,OAAS,UAAW,OAE5B,GAAM,CAAE,YAAW,SAAQ,UAAW,EAEhC,EAAU,KAAK,SAAS,IAAI,EAAO,CACzC,GAAI,CAAC,EAAS,CACZ,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,eACzB,QAAS,mBAAmB,IAC7B,CAAC,CACF,OAGF,IAAM,EAAW,EAAsB,GACjC,EAAQ,KAAK,qBAAqB,IAAI,EAAqB,EAAI,IAAI,IACzE,GAAI,GAAY,CAAC,EAAM,IAAI,EAAS,CAAE,CACpC,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,iBACzB,QAAS,uBAAuB,IACjC,CAAC,CACF,OAGF,GAAI,CACF,IAAM,EAAS,MAAM,EAAQ,EAAsB,EAAO,CAC1D,KAAK,aAAa,EAAQ,EAAO,EAAW,EAAO,OAC5C,EAAG,CACV,IAAM,EAAU,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAC1D,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,cACzB,UACD,CAAC,IAKR,SAAgB,EAAuB,EAAsC,CAC3E,EAAO,eAAe,YAAa,SACjC,EAAe,EAAc,UAAU,CAAC,SAAS,CAClD,CAED,EAAO,eAAe,cAAe,MAAO,EAAc,IAAW,CACnE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,SAAU,EACjE,OAAQ,EAA6B,MAAQ,GAAG,CAChD,GACF,GACF,OAAO,cAAc,IAAI,YAAY,qBAAsB,CAAE,OAAQ,CAAE,OAAM,CAAE,CAAC,CAAC,EAEnF,CAEF,EAAO,eAAe,kBAAmB,MAAO,EAAc,IAAW,CACvE,OAAO,cAAc,IAAI,YAAY,yBAA0B,CAAE,OAAQ,EAAQ,CAAC,CAAC,EACnF,CAEF,EAAO,eAAe,mBAAoB,MAAO,EAAc,IAAW,CACxE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,eAAgB,EACvE,OAAQ,EAAmC,YAAc,GAAG,CAC5D,GACF,GACF,OAAO,cACL,IAAI,YAAY,mBAAoB,CAAE,OAAQ,CAAE,aAAY,CAAE,QAAS,GAAM,CAAC,CAC/E,EAEH,CAEF,EAAO,eAAe,eAAgB,SAAY,CAChD,IAAM,EAAW,MAAM,EAAS,EAAO,gBAAgB,CAAC,CACxD,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,4BAA4B,EAAS,SAAS,CAUhF,QATc,MAAM,EAAS,MAAM,EAStB,OAAS,EAAE,EAAE,IAAK,IAAO,CACpC,WAAY,EAAE,IACd,MAAO,EAAE,KACT,cAAe,EAAE,WAAa,EAAE,eAChC,aAAc,EAAE,aACjB,EAAE,EACH,CAEF,EAAO,eAAe,oBAAqB,MAAO,EAAc,IAAW,CACzE,GAAM,CAAE,UAAS,aAAY,cAAe,EAKtC,EAAW,MAAM,EAAS,EAAO,aAAa,CAAE,CACpD,OAAQ,OACR,QAAS,CAAE,OAAQ,mBAAoB,CACvC,KAAM,KAAK,UAAU,CACnB,UACA,QAAS,UACT,WAAY,EAAa,IAAA,GAAY,EACrC,WAAY,EAAQ,EACrB,CAAC,CACH,CAAC,CACF,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,CAC7E,IAAM,EAAQ,MAAM,EAAS,MAAM,CAKnC,MAAO,CAAE,WADW,EAAK,SAAS,YAAc,EAAK,SAAS,KAC1B,EAAK,YAAc,GAAc,GAAI,EACzE,CAEF,EAAO,eAAe,aAAc,KAAO,IAAgB,CACzD,IAAM,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CAAC,CACpG,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,CAC7E,OAAO,EAAS,MAAM,EACtB,CAEF,EAAO,eAAe,aAAc,MAAO,EAAa,IAAW,CACjE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,CAAC,MAAM,QAAQ,EAAO,CAC5E,EACD,EAAE,CACF,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CAAE,CACnG,OAAQ,QACR,KAAM,KAAK,UAAU,EAAM,CAC5B,CAAC,CACF,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,EAC7E,CAEF,EAAO,eAAe,cAAe,MAAO,EAAa,IAAW,CAClE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,QAAS,EAChE,OAAQ,EAA4B,KAAO,GAAG,CAC9C,GACA,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAChG,CACG,KAAS,SAAW,IACxB,IAAI,CAAC,EAAS,GAAI,MAAU,MAAM,8BAA8B,EAAS,SAAS,CAElF,OADc,MAAM,EAAS,MAAM,EACvB,QACZ,CAEF,EAAO,eAAe,cAAe,MAAO,EAAa,IAAW,CAClE,IAAM,EAAM,EACN,EAAM,GAAO,OAAO,EAAI,KAAQ,SAAW,EAAI,IAAM,GACrD,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAC/F,CAAE,OAAQ,MAAO,KAAM,KAAK,UAAU,CAAE,MAAO,GAAK,MAAO,CAAC,CAAE,CAC/D,CACD,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,8BAA8B,EAAS,SAAS,EAClF,CAEF,EAAO,eAAe,iBAAkB,MAAO,EAAa,IAAW,CACrE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,QAAS,EAChE,OAAQ,EAA4B,KAAO,GAAG,CAC9C,GACA,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAC/F,CAAE,OAAQ,SAAU,CACrB,CACG,KAAS,SAAW,KACpB,CAAC,EAAS,GAAI,MAAU,MAAM,iCAAiC,EAAS,SAAS,EACrF,CAEF,EAAO,eAAe,eAAgB,KAAO,IAAgB,CAC3D,IAAM,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,UAAU,CAAC,CACrG,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,gCAAgC,EAAS,SAAS,CAEpF,OADc,MAAM,EAAS,MAAM,EACvB,MAAQ,EAAE,EACtB,WC9cE,GAAA,EAAA,EAAA,eAA6C,KAAK,CAExD,SAAgB,EAAkB,CAAE,YAA2C,CAC7E,IAAM,GAAA,EAAA,EAAA,QAAkD,KAAK,CAC7D,GAAI,CAAC,EAAU,QAAS,CACtB,IAAM,EAAY,IAAI,EACtB,EAAuB,EAAU,CACjC,EAAU,QAAU,EAEtB,IAAM,EAAS,EAAU,QAEnB,CAAE,OAAM,aAAc,EADX,EAAiB,GAAM,EAAQ,EAAE,MAAO,CAE5C,0BAA4B,SACjC,EAAkC,EAAO,kBAAkB,CAAC,CAClE,CAAE,kBAAmB,GAAO,CAC7B,CAEK,EAAa,GAAM,YAAc,EAAE,CACnC,EAAW,EAAe,GAAM,EAAE,SAAS,CAE3C,GAAA,EAAA,EAAA,aACH,GAAiB,CAChB,IAAM,EAAU,EAAgE,OAC3E,GAAQ,YACb,EAAO,wBAAwB,EAAO,WAAY,EAAO,OAAS,EAAO,EAE3E,CAAC,EAAO,CACT,EAED,EAAA,EAAA,gBACE,OAAO,iBAAiB,qBAAsB,EAAwC,KACzE,CACX,OAAO,oBAAoB,qBAAsB,EAAwC,GAE1F,CAAC,EAAuB,CAAC,EAE5B,EAAA,EAAA,eAAgB,CACd,IAAM,EAAQ,EAAe,EAAS,CACtC,EAAO,eAAe,gBAAiB,EAAM,EAC5C,CAAC,EAAU,EAAO,CAAC,CAEtB,IAAM,GAAA,EAAA,EAAA,QAA6B,EAAO,CAC1C,EAAoB,QAAU,GAC9B,EAAA,EAAA,mBACc,CACV,EAAoB,QAAQ,SAAS,CAIrC,EAAU,QAAU,MAEtB,EAAE,CACH,CAED,IAAM,GAAA,EAAA,EAAA,cACG,CAAE,SAAQ,aAAY,QAAS,EAAW,EACjD,CAAC,EAAQ,EAAY,EAAU,CAChC,CAED,OAAO,EAAA,EAAA,KAAC,EAAiB,SAAlB,CAAkC,QAAQ,WAAqC,CAAA,CAGxF,SAAgB,GAA6C,CAC3D,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,2DAA2D,CAE7E,OAAO,EAAI,OAGb,SAAgB,GAAmC,CACjD,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,sDAAsD,CAExE,OAAO,EAAI,WAIb,SAAS,EAAoB,EAA6B,CACxD,OAAO,EAAE,QAAU,EAAE,qBAAuB,GAI9C,SAAS,EAAqC,EAA6B,CACzE,IAAM,EAAI,EAAE,IAAI,cAEhB,OADK,EAEF,MAAM,QAAQ,EAAE,MAAM,EAAI,EAAE,MAAM,OAAS,GAC3C,MAAM,QAAQ,EAAE,eAAe,EAAI,EAAE,eAAe,OAAS,GAC7D,MAAM,QAAQ,EAAE,YAAY,EAAI,EAAE,YAAY,OAAS,EAJ3C,GASjB,SAAgB,EAA+B,EAA6B,CAO1E,MANI,CAAC,EAAE,OAKH,CAAC,EAAoB,EAAE,CAAS,GAC7B,EAAqC,EAAE,CAGhD,SAAgB,GAAqC,CACnD,IAAM,EAAO,GAAe,CAC5B,OAAA,EAAA,EAAA,aAAqB,EAAK,OAAO,EAA+B,CAAE,CAAC,EAAK,CAAC,CAG3E,SAAgB,GAAgC,CAC9C,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,6DAA6D,CAE/E,OAAO,EAAI"}
|
|
1
|
+
{"version":3,"file":"extension-provider-DZCZgQE2.js","names":[],"sources":["../../../../../packages/extension-ui-sdk/src/types.ts","../../../../../web/src/features/extensions/theme-bridge.ts","../../../../../web/src/features/extensions/extension-message-router.ts","../../../../../web/src/features/extensions/extension-provider.tsx"],"sourcesContent":["export enum ExtensionErrorCode {\n PermissionDenied = 4001,\n InvalidRequest = 4002,\n MethodNotFound = 4003,\n InternalError = 4004,\n Timeout = 4005,\n ExtensionNotFound = 4006,\n RateLimited = 4007,\n}\n\nexport interface ThemeInfo {\n mode: 'light' | 'dark';\n tokens: Record<string, string>;\n fontFamily?: string;\n fontFamilyMono?: string;\n}\n\nexport interface ExtensionRequest {\n source: 'xopc-extension';\n extensionId: string;\n type: 'request';\n requestId: string;\n method: string;\n params?: unknown;\n}\n\nexport interface ExtensionEventMessage {\n source: 'xopc-extension';\n extensionId: string;\n type: 'event';\n event: string;\n data?: unknown;\n}\n\nexport interface HostInit {\n source: 'xopc-host';\n type: 'init';\n extensionId: string;\n permissions: string[];\n theme: ThemeInfo;\n locale: string;\n}\n\nexport interface HostResponse {\n source: 'xopc-host';\n type: 'response';\n requestId: string;\n result?: unknown;\n error?: { code: number; message: string };\n}\n\nexport interface HostEventMessage {\n source: 'xopc-host';\n type: 'event';\n event: string;\n data?: unknown;\n}\n\nexport type HostToExtensionMessage = HostInit | HostResponse | HostEventMessage;\nexport type ExtensionToHostMessage = ExtensionRequest | ExtensionEventMessage;\n\nexport type StreamHandler = (payload: unknown) => void;\n\nexport interface ExtensionClient {\n whenReady(): Promise<void>;\n theme: {\n getTheme(): Promise<ThemeInfo>;\n onThemeChange(handler: (t: ThemeInfo) => void): () => void;\n };\n agent: {\n sendMessage(\n message: string,\n options?: { sessionKey?: string; newSession?: boolean },\n ): Promise<{ sessionKey: string }>;\n onStreamEvent(sessionKey: string, handler: StreamHandler): () => void;\n };\n session: {\n listSessions(): Promise<unknown[]>;\n navigateToSession(sessionKey: string): Promise<void>;\n };\n config: {\n getExtensionConfig<T = Record<string, unknown>>(): Promise<T>;\n setExtensionConfig(patch: Record<string, unknown>): Promise<void>;\n };\n storage: {\n get<T = unknown>(key: string): Promise<T | undefined>;\n set(key: string, value: unknown): Promise<void>;\n remove(key: string): Promise<void>;\n keys(): Promise<string[]>;\n };\n ui: {\n resize(height: number): void;\n showNotification(options: {\n type?: 'success' | 'error' | 'info';\n title: string;\n message?: string;\n }): Promise<void>;\n closePanel(): void;\n navigate(path: string): Promise<void>;\n /** Chat/tool widget iframe: host sends the tool result via `widget.data` after load. */\n onWidgetResult(handler: (data: unknown) => void): () => void;\n };\n events: {\n emit(event: string, data?: unknown): void;\n on(event: string, handler: (data: unknown) => void): () => void;\n };\n onDispose(handler: () => void): () => void;\n onDidChangeVisibility(handler: (visible: boolean) => void): () => void;\n}\n","import type { ThemeInfo } from '@xopcai/extension-ui-sdk';\n\nconst THEME_TOKEN_NAMES = [\n '--color-surface-base',\n '--color-surface-panel',\n '--color-surface-hover',\n '--color-fg',\n '--color-fg-muted',\n '--color-fg-subtle',\n '--color-accent',\n '--color-accent-hover',\n '--color-accent-soft',\n '--color-edge',\n '--color-edge-subtle',\n '--color-danger',\n '--color-success',\n '--color-warning',\n '--radius-sm',\n '--radius-lg',\n '--radius-xl',\n];\n\nexport function buildThemeInfo(mode: 'light' | 'dark'): ThemeInfo {\n const computedStyle = getComputedStyle(document.documentElement);\n const tokens: Record<string, string> = {};\n for (const name of THEME_TOKEN_NAMES) {\n const value = computedStyle.getPropertyValue(name).trim();\n if (value) tokens[name] = value;\n }\n return {\n mode,\n tokens,\n fontFamily: computedStyle.getPropertyValue('font-family').trim(),\n fontFamilyMono: computedStyle.getPropertyValue('--font-mono').trim(),\n };\n}\n","import { ExtensionErrorCode, type ThemeInfo } from '@xopcai/extension-ui-sdk';\n\nimport { apiFetch } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { useThemeStore } from '@/stores/theme-store';\n\nimport { buildThemeInfo } from './theme-bridge';\n\nexport type MethodHandler = (extensionId: string, params: unknown) => Promise<unknown>;\n\nconst METHOD_PERMISSION_MAP: Record<string, string | undefined> = {\n 'agent.sendMessage': 'agent.send',\n 'session.list': 'session.read',\n 'session.navigate': 'session.read',\n 'config.get': 'config.read',\n 'config.set': 'config.write',\n 'storage.get': 'storage',\n 'storage.set': 'storage',\n 'storage.remove': 'storage',\n 'storage.keys': 'storage',\n 'ui.notification': 'notification',\n 'ui.navigate': 'theme',\n 'theme.get': 'theme',\n};\n\ntype ExtensionHostMessage =\n | {\n source: 'xopc-extension';\n extensionId: string;\n type: 'request';\n requestId: string;\n method: string;\n params?: unknown;\n }\n | {\n source: 'xopc-extension';\n extensionId: string;\n type: 'event';\n event: string;\n data?: unknown;\n };\n\nexport class ExtensionMessageRouter {\n private iframes = new Map<string, HTMLIFrameElement>();\n /** Incoming iframe → extensionId (handles contentWindow ready after first register; also disambiguates identity). */\n private byContentWindow = new WeakMap<\n Window,\n { extensionId: string; iframe: HTMLIFrameElement }\n >();\n private handlers = new Map<string, MethodHandler>();\n private extensionPermissions = new Map<string, Set<string>>();\n private eventSubscribers = new Map<string, Set<(e: { event: string; data?: unknown }) => void>>();\n /** extensionId → sessionKeys subscribed for agent stream forwarding */\n private agentStreamSubscriptions = new Map<string, Set<string>>();\n private boundListener = (ev: MessageEvent) => {\n void this.onWindowMessage(ev);\n };\n\n constructor() {\n window.addEventListener('message', this.boundListener);\n }\n\n dispose(): void {\n window.removeEventListener('message', this.boundListener);\n this.iframes.clear();\n this.handlers.clear();\n this.extensionPermissions.clear();\n this.eventSubscribers.clear();\n this.agentStreamSubscriptions.clear();\n }\n\n registerIframe(extensionId: string, iframe: HTMLIFrameElement, permissions: string[]): void {\n const prev = this.iframes.get(extensionId);\n if (prev && prev !== iframe && prev.contentWindow) {\n this.byContentWindow.delete(prev.contentWindow);\n }\n this.iframes.set(extensionId, iframe);\n this.extensionPermissions.set(extensionId, new Set(permissions));\n this.rememberIframeWindow(extensionId, iframe);\n }\n\n unregisterIframe(extensionId: string): void {\n const iframe = this.iframes.get(extensionId);\n if (iframe?.contentWindow) {\n this.byContentWindow.delete(iframe.contentWindow);\n }\n this.iframes.delete(extensionId);\n this.extensionPermissions.delete(extensionId);\n this.agentStreamSubscriptions.delete(extensionId);\n }\n\n private rememberIframeWindow(extensionId: string, iframe: HTMLIFrameElement): void {\n const cw = iframe.contentWindow;\n if (cw) {\n this.byContentWindow.set(cw, { extensionId, iframe });\n }\n }\n\n subscribeAgentStream(extensionId: string, sessionKey: string): void {\n let sessions = this.agentStreamSubscriptions.get(extensionId);\n if (!sessions) {\n sessions = new Set();\n this.agentStreamSubscriptions.set(extensionId, sessions);\n }\n sessions.add(sessionKey);\n }\n\n unsubscribeAgentStream(extensionId: string, sessionKey: string): void {\n this.agentStreamSubscriptions.get(extensionId)?.delete(sessionKey);\n }\n\n forwardAgentStreamEvent(sessionKey: string, event: unknown): void {\n for (const [extensionId, sessions] of this.agentStreamSubscriptions) {\n if (sessions.has(sessionKey)) {\n this.sendEvent(extensionId, `agent.stream.${sessionKey}`, event);\n }\n }\n }\n\n /**\n * Broadcast a fire-and-forget event from one extension to all others (`ext.{name}` on the wire).\n */\n broadcastExtensionEvent(sourceExtensionId: string, bareName: string, data?: unknown): void {\n const outbound = bareName.startsWith('ext.') ? bareName : `ext.${bareName}`;\n for (const [extensionId] of this.iframes) {\n if (extensionId !== sourceExtensionId) {\n this.sendEvent(extensionId, outbound, data);\n }\n }\n }\n\n private handleExtensionEvent(extensionId: string, event: string, data: unknown): void {\n if (event.startsWith('ext.')) {\n const rest = event.slice(4);\n this.broadcastExtensionEvent(extensionId, rest, data);\n return;\n }\n if (event === 'agent.subscribe' || event === 'agent.unsubscribe') {\n const perms = this.extensionPermissions.get(extensionId) ?? new Set<string>();\n if (!perms.has('agent.subscribe')) return;\n const { sessionKey } = (data ?? {}) as { sessionKey?: string };\n if (typeof sessionKey !== 'string' || !sessionKey.trim()) return;\n const sk = sessionKey.trim();\n if (event === 'agent.subscribe') this.subscribeAgentStream(extensionId, sk);\n else this.unsubscribeAgentStream(extensionId, sk);\n }\n }\n\n registerMethod(method: string, handler: MethodHandler): void {\n this.handlers.set(method, handler);\n }\n\n subscribeExtensionEvents(\n extensionId: string,\n fn: (e: { event: string; data?: unknown }) => void,\n ): () => void {\n let subscribers = this.eventSubscribers.get(extensionId);\n if (!subscribers) {\n subscribers = new Set();\n this.eventSubscribers.set(extensionId, subscribers);\n }\n subscribers.add(fn);\n return () => {\n subscribers.delete(fn);\n if (subscribers.size === 0) {\n this.eventSubscribers.delete(extensionId);\n }\n };\n }\n\n sendInit(extensionId: string, theme: ThemeInfo, locale: string): void {\n const iframe = this.iframes.get(extensionId);\n if (!iframe?.contentWindow) return;\n this.rememberIframeWindow(extensionId, iframe);\n const permissions = [...(this.extensionPermissions.get(extensionId) ?? [])];\n iframe.contentWindow.postMessage(\n {\n source: 'xopc-host',\n type: 'init',\n extensionId,\n permissions,\n theme,\n locale,\n },\n '*',\n );\n }\n\n sendEvent(extensionId: string, event: string, data?: unknown): void {\n const iframe = this.iframes.get(extensionId);\n if (!iframe?.contentWindow) return;\n iframe.contentWindow.postMessage(\n {\n source: 'xopc-host',\n type: 'event',\n event,\n data,\n },\n '*',\n );\n }\n\n broadcastEvent(event: string, data?: unknown): void {\n for (const [, iframe] of this.iframes) {\n iframe.contentWindow?.postMessage(\n {\n source: 'xopc-host',\n type: 'event',\n event,\n data,\n },\n '*',\n );\n }\n }\n\n /** Prefer `iframe.contentWindow`; if null (rare), fall back to the request's `event.source`. */\n private postResponse(\n iframe: HTMLIFrameElement,\n event: MessageEvent | undefined,\n requestId: string,\n result?: unknown,\n error?: { code: number; message: string },\n ): void {\n const target =\n iframe.contentWindow ??\n (event?.source instanceof Window ? event.source : null);\n const payload = {\n source: 'xopc-host' as const,\n type: 'response' as const,\n requestId,\n result,\n error,\n };\n if (!target) {\n return;\n }\n target.postMessage(payload, '*');\n }\n\n private isTrustedExtensionSource(iframe: HTMLIFrameElement, ev: MessageEvent): boolean {\n if (ev.source === null) {\n // Sandboxed iframes (no allow-same-origin) may report a null source.\n return true;\n }\n const cw = iframe.contentWindow;\n if (ev.source === cw) {\n return true;\n }\n // Some environments do not keep `===` identity between postMessage `source`\n // and `iframe.contentWindow`; `frameElement` still ties the window to this iframe.\n if (cw && typeof Window !== 'undefined' && ev.source instanceof Window) {\n try {\n return ev.source.frameElement === iframe;\n } catch {\n /* cross-origin access */\n }\n }\n return false;\n }\n\n private async onWindowMessage(event: MessageEvent) {\n const msg = event.data as ExtensionHostMessage | undefined;\n if (!msg || msg.source !== 'xopc-extension') return;\n\n let iframe: HTMLIFrameElement | undefined;\n let effectiveExtensionId = msg.extensionId;\n\n if (event.source instanceof Window) {\n const reg = this.byContentWindow.get(event.source);\n if (reg) {\n iframe = reg.iframe;\n effectiveExtensionId = reg.extensionId;\n }\n }\n\n if (!iframe) {\n iframe = this.iframes.get(msg.extensionId);\n effectiveExtensionId = msg.extensionId;\n }\n\n if (!iframe) {\n return;\n }\n\n const trustedByWindow =\n event.source instanceof Window && this.byContentWindow.has(event.source);\n\n if (!trustedByWindow && !this.isTrustedExtensionSource(iframe, event)) {\n return;\n }\n\n if (msg.type === 'event') {\n this.handleExtensionEvent(effectiveExtensionId, msg.event, msg.data);\n const subs = this.eventSubscribers.get(effectiveExtensionId);\n if (subs) {\n const payload = { event: msg.event, data: msg.data };\n for (const fn of subs) {\n try {\n fn(payload);\n } catch {\n /* ignore */\n }\n }\n }\n return;\n }\n\n if (msg.type !== 'request') return;\n\n const { requestId, method, params } = msg;\n\n const handler = this.handlers.get(method);\n if (!handler) {\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.MethodNotFound,\n message: `Unknown method: ${method}`,\n });\n return;\n }\n\n const required = METHOD_PERMISSION_MAP[method];\n const perms = this.extensionPermissions.get(effectiveExtensionId) ?? new Set<string>();\n if (required && !perms.has(required)) {\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.PermissionDenied,\n message: `Missing permission: ${required}`,\n });\n return;\n }\n\n try {\n const result = await handler(effectiveExtensionId, params);\n this.postResponse(iframe, event, requestId, result);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n this.postResponse(iframe, event, requestId, undefined, {\n code: ExtensionErrorCode.InternalError,\n message,\n });\n }\n }\n}\n\nexport function registerBuiltinMethods(router: ExtensionMessageRouter): void {\n router.registerMethod('theme.get', async () =>\n buildThemeInfo(useThemeStore.getState().resolved),\n );\n\n router.registerMethod('ui.navigate', async (_extensionId, params) => {\n const path =\n params && typeof params === 'object' && params !== null && 'path' in params\n ? String((params as { path?: string }).path ?? '')\n : '';\n if (path) {\n window.dispatchEvent(new CustomEvent('extension-navigate', { detail: { path } }));\n }\n });\n\n router.registerMethod('ui.notification', async (_extensionId, params) => {\n window.dispatchEvent(new CustomEvent('extension-notification', { detail: params }));\n });\n\n router.registerMethod('session.navigate', async (_extensionId, params) => {\n const sessionKey =\n params && typeof params === 'object' && params !== null && 'sessionKey' in params\n ? String((params as { sessionKey?: string }).sessionKey ?? '')\n : '';\n if (sessionKey) {\n window.dispatchEvent(\n new CustomEvent('navigate-to-chat', { detail: { sessionKey }, bubbles: true }),\n );\n }\n });\n\n router.registerMethod('session.list', async () => {\n const response = await apiFetch(apiUrl('/api/sessions'));\n if (!response.ok) throw new Error(`Failed to list sessions: ${response.status}`);\n const data = (await response.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n lastAccessedAt?: string;\n messageCount?: number;\n }>;\n };\n return (data.items ?? []).map((s) => ({\n sessionKey: s.key,\n title: s.name,\n lastMessageAt: s.updatedAt ?? s.lastAccessedAt,\n messageCount: s.messageCount,\n }));\n });\n\n router.registerMethod('agent.sendMessage', async (_extensionId, params) => {\n const { message, sessionKey, newSession } = params as {\n message: string;\n sessionKey?: string;\n newSession?: boolean;\n };\n const response = await apiFetch(apiUrl('/api/agent'), {\n method: 'POST',\n headers: { Accept: 'application/json' },\n body: JSON.stringify({\n message,\n channel: 'webchat',\n sessionKey: newSession ? undefined : sessionKey,\n newSession: Boolean(newSession),\n }),\n });\n if (!response.ok) throw new Error(`Agent request failed: ${response.status}`);\n const data = (await response.json()) as {\n payload?: { sessionKey?: string; key?: string };\n sessionKey?: string;\n };\n const fromPayload = data.payload?.sessionKey ?? data.payload?.key;\n return { sessionKey: fromPayload ?? data.sessionKey ?? sessionKey ?? '' };\n });\n\n router.registerMethod('config.get', async (extensionId) => {\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`));\n if (!response.ok) throw new Error(`Failed to get config: ${response.status}`);\n return response.json();\n });\n\n router.registerMethod('config.set', async (extensionId, params) => {\n const patch =\n params && typeof params === 'object' && params !== null && !Array.isArray(params)\n ? (params as Record<string, unknown>)\n : {};\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`), {\n method: 'PATCH',\n body: JSON.stringify(patch),\n });\n if (!response.ok) throw new Error(`Failed to set config: ${response.status}`);\n });\n\n router.registerMethod('storage.get', async (extensionId, params) => {\n const key =\n params && typeof params === 'object' && params !== null && 'key' in params\n ? String((params as { key?: string }).key ?? '')\n : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n );\n if (response.status === 404) return undefined;\n if (!response.ok) throw new Error(`Failed to get storage key: ${response.status}`);\n const data = (await response.json()) as { value: unknown };\n return data.value;\n });\n\n router.registerMethod('storage.set', async (extensionId, params) => {\n const raw = params as { key?: string; value?: unknown } | undefined;\n const key = raw && typeof raw.key === 'string' ? raw.key : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n { method: 'PUT', body: JSON.stringify({ value: raw?.value }) },\n );\n if (!response.ok) throw new Error(`Failed to set storage key: ${response.status}`);\n });\n\n router.registerMethod('storage.remove', async (extensionId, params) => {\n const key =\n params && typeof params === 'object' && params !== null && 'key' in params\n ? String((params as { key?: string }).key ?? '')\n : '';\n const response = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage/${encodeURIComponent(key)}`),\n { method: 'DELETE' },\n );\n if (response.status === 404) return;\n if (!response.ok) throw new Error(`Failed to remove storage key: ${response.status}`);\n });\n\n router.registerMethod('storage.keys', async (extensionId) => {\n const response = await apiFetch(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/storage`));\n if (!response.ok) throw new Error(`Failed to list storage keys: ${response.status}`);\n const data = (await response.json()) as { keys?: string[] };\n return data.keys ?? [];\n });\n}\n","import { createContext, useCallback, useContext, useEffect, useMemo, useRef } from 'react';\nimport useSWR from 'swr';\n\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { useThemeStore } from '@/stores/theme-store';\n\nimport { ExtensionMessageRouter, registerBuiltinMethods } from './extension-message-router';\nimport type { ExtensionApiRow, ExtensionsListResponse } from './types';\nimport { buildThemeInfo } from './theme-bridge';\n\ntype Ctx = {\n router: ExtensionMessageRouter;\n extensions: ExtensionApiRow[];\n loading: boolean;\n};\n\nconst ExtensionContext = createContext<Ctx | null>(null);\n\nexport function ExtensionProvider({ children }: { children: React.ReactNode }) {\n const routerRef = useRef<ExtensionMessageRouter | null>(null);\n if (!routerRef.current) {\n const newRouter = new ExtensionMessageRouter();\n registerBuiltinMethods(newRouter);\n routerRef.current = newRouter;\n }\n const router = routerRef.current;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const { data, isLoading } = useSWR(\n hasToken ? 'gateway-extensions-list' : null,\n () => fetchJson<ExtensionsListResponse>(apiUrl('/api/extensions')),\n { revalidateOnFocus: false },\n );\n\n const extensions = data?.extensions ?? [];\n const resolved = useThemeStore((s) => s.resolved);\n\n const handleAgentStreamEvent = useCallback(\n (event: Event) => {\n const detail = (event as CustomEvent<{ sessionKey?: string; event?: unknown }>).detail;\n if (!detail?.sessionKey) return;\n router.forwardAgentStreamEvent(detail.sessionKey, detail.event ?? detail);\n },\n [router],\n );\n\n useEffect(() => {\n window.addEventListener('agent-stream-event', handleAgentStreamEvent as EventListener);\n return () => {\n window.removeEventListener('agent-stream-event', handleAgentStreamEvent as EventListener);\n };\n }, [handleAgentStreamEvent]);\n\n useEffect(() => {\n const theme = buildThemeInfo(resolved);\n router.broadcastEvent('theme.changed', theme);\n }, [resolved, router]);\n\n const routerForCleanupRef = useRef(router);\n routerForCleanupRef.current = router;\n useEffect(\n () => () => {\n routerForCleanupRef.current.dispose();\n // React StrictMode (dev) invokes this cleanup, then remounts. `useRef` survives,\n // so we must clear the ref or the next render would reuse a disposed router\n // (no `window` message listener — extension requests time out).\n routerRef.current = null;\n },\n [],\n );\n\n const value = useMemo(\n () => ({ router, extensions, loading: isLoading }),\n [router, extensions, isLoading],\n );\n\n return <ExtensionContext.Provider value={value}>{children}</ExtensionContext.Provider>;\n}\n\nexport function useExtensionRouter(): ExtensionMessageRouter {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensionRouter must be used within ExtensionProvider');\n }\n return ctx.router;\n}\n\nexport function useExtensions(): ExtensionApiRow[] {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensions must be used within ExtensionProvider');\n }\n return ctx.extensions;\n}\n\n/** Loaded in the gateway process (tools/hooks) or marked to load after restart. */\nfunction extensionUiUnlocked(e: ExtensionApiRow): boolean {\n return e.active || e.activationEligible === true;\n}\n\n/** Manifest declares iframe surfaces (served from disk); show nav/routes even when Node side is not active yet. */\nfunction manifestDeclaresGatewayContributions(e: ExtensionApiRow): boolean {\n const c = e.ui?.contributions;\n if (!c) return false;\n return (\n (Array.isArray(c.pages) && c.pages.length > 0) ||\n (Array.isArray(c.settingsPanels) && c.settingsPanels.length > 0) ||\n (Array.isArray(c.chatWidgets) && c.chatWidgets.length > 0)\n );\n}\n\n/** Used by Apps detail links and {@link useUiExtensions}. */\nexport function extensionExposesGatewayShellUi(e: ExtensionApiRow): boolean {\n if (!e.hasUi) return false;\n // Extension must be enabled (active or scheduled to activate after restart) before\n // its UI contributions appear in the shell. `manifestDeclaresGatewayContributions`\n // only determines whether the iframe surfaces are shown *while* the Node-side is still\n // starting up — it must not override a deliberate disable action by the user.\n if (!extensionUiUnlocked(e)) return false;\n return manifestDeclaresGatewayContributions(e);\n}\n\nexport function useUiExtensions(): ExtensionApiRow[] {\n const list = useExtensions();\n return useMemo(() => list.filter(extensionExposesGatewayShellUi), [list]);\n}\n\nexport function useExtensionsLoading(): boolean {\n const ctx = useContext(ExtensionContext);\n if (!ctx) {\n throw new Error('useExtensionsLoading must be used within ExtensionProvider');\n }\n return ctx.loading;\n}\n"],"mappings":"+PAAA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,EAAA,iBAAA,MAAA,mBACA,EAAA,EAAA,eAAA,MAAA,iBACA,EAAA,EAAA,eAAA,MAAA,iBACA,EAAA,EAAA,cAAA,MAAA,gBACA,EAAA,EAAA,QAAA,MAAA,UACA,EAAA,EAAA,kBAAA,MAAA,oBACA,EAAA,EAAA,YAAA,MAAA,oBACD,YCND,EAAA,+TAoBA,SAAA,EAAA,EAAA,uDAGE,IAAA,IAAA,KAAA,EAAA,oCAEE,IAAA,EAAA,GAAA,GAEF,MAAA,8HCnBF,IAAM,EAA4D,CAChE,oBAAqB,aACrB,eAAgB,eAChB,mBAAoB,eACpB,aAAc,cACd,aAAc,eACd,cAAe,UACf,cAAe,UACf,iBAAkB,UAClB,eAAgB,UAChB,kBAAmB,eACnB,cAAe,QACf,YAAa,QACd,CAmBY,EAAb,KAAoC,CAClC,QAAkB,IAAI,IAEtB,gBAA0B,IAAI,QAI9B,SAAmB,IAAI,IACvB,qBAA+B,IAAI,IACnC,iBAA2B,IAAI,IAE/B,yBAAmC,IAAI,IACvC,cAAyB,GAAqB,CACvC,KAAK,gBAAgB,EAAG,EAG/B,aAAc,CACZ,OAAO,iBAAiB,UAAW,KAAK,cAAc,CAGxD,SAAgB,CACd,OAAO,oBAAoB,UAAW,KAAK,cAAc,CACzD,KAAK,QAAQ,OAAO,CACpB,KAAK,SAAS,OAAO,CACrB,KAAK,qBAAqB,OAAO,CACjC,KAAK,iBAAiB,OAAO,CAC7B,KAAK,yBAAyB,OAAO,CAGvC,eAAe,EAAqB,EAA2B,EAA6B,CAC1F,IAAM,EAAO,KAAK,QAAQ,IAAI,EAAY,CACtC,GAAQ,IAAS,GAAU,EAAK,eAClC,KAAK,gBAAgB,OAAO,EAAK,cAAc,CAEjD,KAAK,QAAQ,IAAI,EAAa,EAAO,CACrC,KAAK,qBAAqB,IAAI,EAAa,IAAI,IAAI,EAAY,CAAC,CAChE,KAAK,qBAAqB,EAAa,EAAO,CAGhD,iBAAiB,EAA2B,CAC1C,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CACxC,GAAQ,eACV,KAAK,gBAAgB,OAAO,EAAO,cAAc,CAEnD,KAAK,QAAQ,OAAO,EAAY,CAChC,KAAK,qBAAqB,OAAO,EAAY,CAC7C,KAAK,yBAAyB,OAAO,EAAY,CAGnD,qBAA6B,EAAqB,EAAiC,CACjF,IAAM,EAAK,EAAO,cACd,GACF,KAAK,gBAAgB,IAAI,EAAI,CAAE,cAAa,SAAQ,CAAC,CAIzD,qBAAqB,EAAqB,EAA0B,CAClE,IAAI,EAAW,KAAK,yBAAyB,IAAI,EAAY,CACxD,IACH,EAAW,IAAI,IACf,KAAK,yBAAyB,IAAI,EAAa,EAAS,EAE1D,EAAS,IAAI,EAAW,CAG1B,uBAAuB,EAAqB,EAA0B,CACpE,KAAK,yBAAyB,IAAI,EAAY,EAAE,OAAO,EAAW,CAGpE,wBAAwB,EAAoB,EAAsB,CAChE,IAAK,GAAM,CAAC,EAAa,KAAa,KAAK,yBACrC,EAAS,IAAI,EAAW,EAC1B,KAAK,UAAU,EAAa,gBAAgB,IAAc,EAAM,CAQtE,wBAAwB,EAA2B,EAAkB,EAAsB,CACzF,IAAM,EAAW,EAAS,WAAW,OAAO,CAAG,EAAW,OAAO,IACjE,IAAK,GAAM,CAAC,KAAgB,KAAK,QAC3B,IAAgB,GAClB,KAAK,UAAU,EAAa,EAAU,EAAK,CAKjD,qBAA6B,EAAqB,EAAe,EAAqB,CACpF,GAAI,EAAM,WAAW,OAAO,CAAE,CAC5B,IAAM,EAAO,EAAM,MAAM,EAAE,CAC3B,KAAK,wBAAwB,EAAa,EAAM,EAAK,CACrD,OAEF,GAAI,IAAU,mBAAqB,IAAU,oBAAqB,CAEhE,GAAI,EADU,KAAK,qBAAqB,IAAI,EAAY,EAAI,IAAI,KACrD,IAAI,kBAAkB,CAAE,OACnC,GAAM,CAAE,cAAgB,GAAQ,EAAE,CAClC,GAAI,OAAO,GAAe,UAAY,CAAC,EAAW,MAAM,CAAE,OAC1D,IAAM,EAAK,EAAW,MAAM,CACxB,IAAU,kBAAmB,KAAK,qBAAqB,EAAa,EAAG,CACtE,KAAK,uBAAuB,EAAa,EAAG,EAIrD,eAAe,EAAgB,EAA8B,CAC3D,KAAK,SAAS,IAAI,EAAQ,EAAQ,CAGpC,yBACE,EACA,EACY,CACZ,IAAI,EAAc,KAAK,iBAAiB,IAAI,EAAY,CAMxD,OALK,IACH,EAAc,IAAI,IAClB,KAAK,iBAAiB,IAAI,EAAa,EAAY,EAErD,EAAY,IAAI,EAAG,KACN,CACX,EAAY,OAAO,EAAG,CAClB,EAAY,OAAS,GACvB,KAAK,iBAAiB,OAAO,EAAY,EAK/C,SAAS,EAAqB,EAAkB,EAAsB,CACpE,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CAC5C,GAAI,CAAC,GAAQ,cAAe,OAC5B,KAAK,qBAAqB,EAAa,EAAO,CAC9C,IAAM,EAAc,CAAC,GAAI,KAAK,qBAAqB,IAAI,EAAY,EAAI,EAAE,CAAE,CAC3E,EAAO,cAAc,YACnB,CACE,OAAQ,YACR,KAAM,OACN,cACA,cACA,QACA,SACD,CACD,IACD,CAGH,UAAU,EAAqB,EAAe,EAAsB,CAClE,IAAM,EAAS,KAAK,QAAQ,IAAI,EAAY,CACvC,GAAQ,eACb,EAAO,cAAc,YACnB,CACE,OAAQ,YACR,KAAM,QACN,QACA,OACD,CACD,IACD,CAGH,eAAe,EAAe,EAAsB,CAClD,IAAK,GAAM,EAAG,KAAW,KAAK,QAC5B,EAAO,eAAe,YACpB,CACE,OAAQ,YACR,KAAM,QACN,QACA,OACD,CACD,IACD,CAKL,aACE,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EACJ,EAAO,gBACN,GAAO,kBAAkB,OAAS,EAAM,OAAS,MAC9C,EAAU,CACd,OAAQ,YACR,KAAM,WACN,YACA,SACA,QACD,CACI,GAGL,EAAO,YAAY,EAAS,IAAI,CAGlC,yBAAiC,EAA2B,EAA2B,CACrF,GAAI,EAAG,SAAW,KAEhB,MAAO,GAET,IAAM,EAAK,EAAO,cAClB,GAAI,EAAG,SAAW,EAChB,MAAO,GAIT,GAAI,GAAM,OAAO,OAAW,KAAe,EAAG,kBAAkB,OAC9D,GAAI,CACF,OAAO,EAAG,OAAO,eAAiB,OAC5B,EAIV,MAAO,GAGT,MAAc,gBAAgB,EAAqB,CACjD,IAAM,EAAM,EAAM,KAClB,GAAI,CAAC,GAAO,EAAI,SAAW,iBAAkB,OAE7C,IAAI,EACA,EAAuB,EAAI,YAE/B,GAAI,EAAM,kBAAkB,OAAQ,CAClC,IAAM,EAAM,KAAK,gBAAgB,IAAI,EAAM,OAAO,CAC9C,IACF,EAAS,EAAI,OACb,EAAuB,EAAI,aAgB/B,GAZK,IACH,EAAS,KAAK,QAAQ,IAAI,EAAI,YAAY,CAC1C,EAAuB,EAAI,aAGzB,CAAC,GAOD,EAFF,EAAM,kBAAkB,QAAU,KAAK,gBAAgB,IAAI,EAAM,OAAO,GAElD,CAAC,KAAK,yBAAyB,EAAQ,EAAM,CACnE,OAGF,GAAI,EAAI,OAAS,QAAS,CACxB,KAAK,qBAAqB,EAAsB,EAAI,MAAO,EAAI,KAAK,CACpE,IAAM,EAAO,KAAK,iBAAiB,IAAI,EAAqB,CAC5D,GAAI,EAAM,CACR,IAAM,EAAU,CAAE,MAAO,EAAI,MAAO,KAAM,EAAI,KAAM,CACpD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAG,EAAQ,MACL,GAKZ,OAGF,GAAI,EAAI,OAAS,UAAW,OAE5B,GAAM,CAAE,YAAW,SAAQ,UAAW,EAEhC,EAAU,KAAK,SAAS,IAAI,EAAO,CACzC,GAAI,CAAC,EAAS,CACZ,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,eACzB,QAAS,mBAAmB,IAC7B,CAAC,CACF,OAGF,IAAM,EAAW,EAAsB,GACjC,EAAQ,KAAK,qBAAqB,IAAI,EAAqB,EAAI,IAAI,IACzE,GAAI,GAAY,CAAC,EAAM,IAAI,EAAS,CAAE,CACpC,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,iBACzB,QAAS,uBAAuB,IACjC,CAAC,CACF,OAGF,GAAI,CACF,IAAM,EAAS,MAAM,EAAQ,EAAsB,EAAO,CAC1D,KAAK,aAAa,EAAQ,EAAO,EAAW,EAAO,OAC5C,EAAG,CACV,IAAM,EAAU,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAC1D,KAAK,aAAa,EAAQ,EAAO,EAAW,IAAA,GAAW,CACrD,KAAM,EAAmB,cACzB,UACD,CAAC,IAKR,SAAgB,EAAuB,EAAsC,CAC3E,EAAO,eAAe,YAAa,SACjC,EAAe,EAAc,UAAU,CAAC,SAAS,CAClD,CAED,EAAO,eAAe,cAAe,MAAO,EAAc,IAAW,CACnE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,SAAU,EACjE,OAAQ,EAA6B,MAAQ,GAAG,CAChD,GACF,GACF,OAAO,cAAc,IAAI,YAAY,qBAAsB,CAAE,OAAQ,CAAE,OAAM,CAAE,CAAC,CAAC,EAEnF,CAEF,EAAO,eAAe,kBAAmB,MAAO,EAAc,IAAW,CACvE,OAAO,cAAc,IAAI,YAAY,yBAA0B,CAAE,OAAQ,EAAQ,CAAC,CAAC,EACnF,CAEF,EAAO,eAAe,mBAAoB,MAAO,EAAc,IAAW,CACxE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,eAAgB,EACvE,OAAQ,EAAmC,YAAc,GAAG,CAC5D,GACF,GACF,OAAO,cACL,IAAI,YAAY,mBAAoB,CAAE,OAAQ,CAAE,aAAY,CAAE,QAAS,GAAM,CAAC,CAC/E,EAEH,CAEF,EAAO,eAAe,eAAgB,SAAY,CAChD,IAAM,EAAW,MAAM,EAAS,EAAO,gBAAgB,CAAC,CACxD,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,4BAA4B,EAAS,SAAS,CAUhF,QATc,MAAM,EAAS,MAAM,EAStB,OAAS,EAAE,EAAE,IAAK,IAAO,CACpC,WAAY,EAAE,IACd,MAAO,EAAE,KACT,cAAe,EAAE,WAAa,EAAE,eAChC,aAAc,EAAE,aACjB,EAAE,EACH,CAEF,EAAO,eAAe,oBAAqB,MAAO,EAAc,IAAW,CACzE,GAAM,CAAE,UAAS,aAAY,cAAe,EAKtC,EAAW,MAAM,EAAS,EAAO,aAAa,CAAE,CACpD,OAAQ,OACR,QAAS,CAAE,OAAQ,mBAAoB,CACvC,KAAM,KAAK,UAAU,CACnB,UACA,QAAS,UACT,WAAY,EAAa,IAAA,GAAY,EACrC,WAAY,EAAQ,EACrB,CAAC,CACH,CAAC,CACF,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,CAC7E,IAAM,EAAQ,MAAM,EAAS,MAAM,CAKnC,MAAO,CAAE,WADW,EAAK,SAAS,YAAc,EAAK,SAAS,KAC1B,EAAK,YAAc,GAAc,GAAI,EACzE,CAEF,EAAO,eAAe,aAAc,KAAO,IAAgB,CACzD,IAAM,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CAAC,CACpG,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,CAC7E,OAAO,EAAS,MAAM,EACtB,CAEF,EAAO,eAAe,aAAc,MAAO,EAAa,IAAW,CACjE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,CAAC,MAAM,QAAQ,EAAO,CAC5E,EACD,EAAE,CACF,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CAAE,CACnG,OAAQ,QACR,KAAM,KAAK,UAAU,EAAM,CAC5B,CAAC,CACF,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,yBAAyB,EAAS,SAAS,EAC7E,CAEF,EAAO,eAAe,cAAe,MAAO,EAAa,IAAW,CAClE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,QAAS,EAChE,OAAQ,EAA4B,KAAO,GAAG,CAC9C,GACA,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAChG,CACG,KAAS,SAAW,IACxB,IAAI,CAAC,EAAS,GAAI,MAAU,MAAM,8BAA8B,EAAS,SAAS,CAElF,OADc,MAAM,EAAS,MAAM,EACvB,QACZ,CAEF,EAAO,eAAe,cAAe,MAAO,EAAa,IAAW,CAClE,IAAM,EAAM,EACN,EAAM,GAAO,OAAO,EAAI,KAAQ,SAAW,EAAI,IAAM,GACrD,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAC/F,CAAE,OAAQ,MAAO,KAAM,KAAK,UAAU,CAAE,MAAO,GAAK,MAAO,CAAC,CAAE,CAC/D,CACD,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,8BAA8B,EAAS,SAAS,EAClF,CAEF,EAAO,eAAe,iBAAkB,MAAO,EAAa,IAAW,CACrE,IAAM,EACJ,GAAU,OAAO,GAAW,UAAY,GAAmB,QAAS,EAChE,OAAQ,EAA4B,KAAO,GAAG,CAC9C,GACA,EAAW,MAAM,EACrB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,WAAW,mBAAmB,EAAI,GAAG,CAC/F,CAAE,OAAQ,SAAU,CACrB,CACG,KAAS,SAAW,KACpB,CAAC,EAAS,GAAI,MAAU,MAAM,iCAAiC,EAAS,SAAS,EACrF,CAEF,EAAO,eAAe,eAAgB,KAAO,IAAgB,CAC3D,IAAM,EAAW,MAAM,EAAS,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,UAAU,CAAC,CACrG,GAAI,CAAC,EAAS,GAAI,MAAU,MAAM,gCAAgC,EAAS,SAAS,CAEpF,OADc,MAAM,EAAS,MAAM,EACvB,MAAQ,EAAE,EACtB,WC9cE,GAAA,EAAA,EAAA,eAA6C,KAAK,CAExD,SAAgB,EAAkB,CAAE,YAA2C,CAC7E,IAAM,GAAA,EAAA,EAAA,QAAkD,KAAK,CAC7D,GAAI,CAAC,EAAU,QAAS,CACtB,IAAM,EAAY,IAAI,EACtB,EAAuB,EAAU,CACjC,EAAU,QAAU,EAEtB,IAAM,EAAS,EAAU,QAEnB,CAAE,OAAM,aAAc,EADX,EAAiB,GAAM,EAAQ,EAAE,MAAO,CAE5C,0BAA4B,SACjC,EAAkC,EAAO,kBAAkB,CAAC,CAClE,CAAE,kBAAmB,GAAO,CAC7B,CAEK,EAAa,GAAM,YAAc,EAAE,CACnC,EAAW,EAAe,GAAM,EAAE,SAAS,CAE3C,GAAA,EAAA,EAAA,aACH,GAAiB,CAChB,IAAM,EAAU,EAAgE,OAC3E,GAAQ,YACb,EAAO,wBAAwB,EAAO,WAAY,EAAO,OAAS,EAAO,EAE3E,CAAC,EAAO,CACT,EAED,EAAA,EAAA,gBACE,OAAO,iBAAiB,qBAAsB,EAAwC,KACzE,CACX,OAAO,oBAAoB,qBAAsB,EAAwC,GAE1F,CAAC,EAAuB,CAAC,EAE5B,EAAA,EAAA,eAAgB,CACd,IAAM,EAAQ,EAAe,EAAS,CACtC,EAAO,eAAe,gBAAiB,EAAM,EAC5C,CAAC,EAAU,EAAO,CAAC,CAEtB,IAAM,GAAA,EAAA,EAAA,QAA6B,EAAO,CAC1C,EAAoB,QAAU,GAC9B,EAAA,EAAA,mBACc,CACV,EAAoB,QAAQ,SAAS,CAIrC,EAAU,QAAU,MAEtB,EAAE,CACH,CAED,IAAM,GAAA,EAAA,EAAA,cACG,CAAE,SAAQ,aAAY,QAAS,EAAW,EACjD,CAAC,EAAQ,EAAY,EAAU,CAChC,CAED,OAAO,EAAA,EAAA,KAAC,EAAiB,SAAlB,CAAkC,QAAQ,WAAqC,CAAA,CAGxF,SAAgB,GAA6C,CAC3D,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,2DAA2D,CAE7E,OAAO,EAAI,OAGb,SAAgB,GAAmC,CACjD,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,sDAAsD,CAExE,OAAO,EAAI,WAIb,SAAS,EAAoB,EAA6B,CACxD,OAAO,EAAE,QAAU,EAAE,qBAAuB,GAI9C,SAAS,EAAqC,EAA6B,CACzE,IAAM,EAAI,EAAE,IAAI,cAEhB,OADK,EAEF,MAAM,QAAQ,EAAE,MAAM,EAAI,EAAE,MAAM,OAAS,GAC3C,MAAM,QAAQ,EAAE,eAAe,EAAI,EAAE,eAAe,OAAS,GAC7D,MAAM,QAAQ,EAAE,YAAY,EAAI,EAAE,YAAY,OAAS,EAJ3C,GASjB,SAAgB,EAA+B,EAA6B,CAO1E,MANI,CAAC,EAAE,OAKH,CAAC,EAAoB,EAAE,CAAS,GAC7B,EAAqC,EAAE,CAGhD,SAAgB,GAAqC,CACnD,IAAM,EAAO,GAAe,CAC5B,OAAA,EAAA,EAAA,aAAqB,EAAK,OAAO,EAA+B,CAAE,CAAC,EAAK,CAAC,CAG3E,SAAgB,GAAgC,CAC9C,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAiB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,6DAA6D,CAE/E,OAAO,EAAI"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./vendor-react-QAsRxa6t.js";import{u as t}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{o as n}from"./extension-provider-
|
|
2
|
-
//# sourceMappingURL=extension-settings-page-
|
|
1
|
+
import{t as e}from"./vendor-react-QAsRxa6t.js";import{u as t}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{o as n}from"./extension-provider-DZCZgQE2.js";import{t as r}from"./extension-iframe-host-D5HEF0KR.js";var i=e();function a(){let{extensionId:e,panelId:a}=t(),s=n();if(!e)return(0,i.jsx)(o,{message:`No extension ID provided.`});let c=s.find(t=>t.id===e);if(!c)return(0,i.jsx)(o,{message:`Extension "${e}" not found or has no UI.`});let l=c.ui?.contributions?.settingsPanels;if(!l?.length)return(0,i.jsx)(o,{message:`Extension "${e}" has no settings panels.`});let u=a?l.find(t=>t.id===a||t.id===`${e}.${a}`):l[0];return u?(0,i.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:[(0,i.jsx)(`h1`,{className:`text-lg font-semibold text-fg`,children:u.title}),(0,i.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-edge bg-surface-base`,children:(0,i.jsx)(r,{extensionId:e,extensionName:c.name,entrypoint:u.entrypoint,permissions:c.ui?.permissions,title:u.title,className:`w-full`,minHeight:120,maxHeight:2e3})})]}):(0,i.jsx)(o,{message:`Settings panel "${a}" not found.`})}function o({message:e}){return(0,i.jsx)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:(0,i.jsx)(`p`,{className:`text-sm text-fg-muted`,children:e})})}export{a as ExtensionSettingsPage};
|
|
2
|
+
//# sourceMappingURL=extension-settings-page-CX6STpx3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-settings-page-
|
|
1
|
+
{"version":3,"file":"extension-settings-page-CX6STpx3.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-settings-page.tsx"],"sourcesContent":["/**\n * ExtensionSettingsPage — renders an extension's settings panel via ExtensionIframeHost.\n *\n * Mounted at /settings/ext/:extensionId/:panelId within the settings layout.\n */\n\nimport { useParams } from 'react-router-dom';\n\nimport { ExtensionIframeHost } from './extension-iframe-host';\nimport { useUiExtensions } from './extension-provider';\n\nexport function ExtensionSettingsPage() {\n const { extensionId, panelId } = useParams<{ extensionId: string; panelId?: string }>();\n const uiExtensions = useUiExtensions();\n\n if (!extensionId) {\n return <SettingsPanelNotFound message=\"No extension ID provided.\" />;\n }\n\n const extension = uiExtensions.find((ext) => ext.id === extensionId);\n if (!extension) {\n return (\n <SettingsPanelNotFound message={`Extension \"${extensionId}\" not found or has no UI.`} />\n );\n }\n\n const panels = extension.ui?.contributions?.settingsPanels;\n if (!panels?.length) {\n return (\n <SettingsPanelNotFound message={`Extension \"${extensionId}\" has no settings panels.`} />\n );\n }\n\n const panel = panelId\n ? panels.find((p) => p.id === panelId || p.id === `${extensionId}.${panelId}`)\n : panels[0];\n\n if (!panel) {\n return <SettingsPanelNotFound message={`Settings panel \"${panelId}\" not found.`} />;\n }\n\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <h1 className=\"text-lg font-semibold text-fg\">{panel.title}</h1>\n <div className=\"overflow-hidden rounded-xl border border-edge bg-surface-base\">\n <ExtensionIframeHost\n extensionId={extensionId}\n extensionName={extension.name}\n entrypoint={panel.entrypoint}\n permissions={extension.ui?.permissions}\n title={panel.title}\n className=\"w-full\"\n minHeight={120}\n maxHeight={2000}\n />\n </div>\n </div>\n );\n}\n\nfunction SettingsPanelNotFound({ message }: { message: string }) {\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n );\n}\n"],"mappings":"uNAWA,SAAgB,GAAwB,CACtC,GAAM,CAAE,cAAa,WAAY,GAAsD,CACjF,EAAe,GAAiB,CAEtC,GAAI,CAAC,EACH,OAAO,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAQ,4BAA8B,CAAA,CAGtE,IAAM,EAAY,EAAa,KAAM,GAAQ,EAAI,KAAO,EAAY,CACpE,GAAI,CAAC,EACH,OACE,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,2BAA8B,CAAA,CAI5F,IAAM,EAAS,EAAU,IAAI,eAAe,eAC5C,GAAI,CAAC,GAAQ,OACX,OACE,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,2BAA8B,CAAA,CAI5F,IAAM,EAAQ,EACV,EAAO,KAAM,GAAM,EAAE,KAAO,GAAW,EAAE,KAAO,GAAG,EAAY,GAAG,IAAU,CAC5E,EAAO,GAMX,OAJK,GAKH,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uEAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAM,MAAW,CAAA,EAChE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,0EACb,EAAA,EAAA,KAAC,EAAD,CACe,cACb,cAAe,EAAU,KACzB,WAAY,EAAM,WAClB,YAAa,EAAU,IAAI,YAC3B,MAAO,EAAM,MACb,UAAU,SACV,UAAW,IACX,UAAW,IACX,CAAA,CACE,CAAA,CACF,IAlBC,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,mBAAmB,EAAQ,cAAiB,CAAA,CAsBvF,SAAS,EAAsB,CAAE,WAAgC,CAC/D,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wEACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAyB,EAAY,CAAA,CAC9C,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t}from"./vendor-react-QAsRxa6t.js";import{r as n,t as r}from"./url-
|
|
2
|
-
//# sourceMappingURL=gateway-config-swr-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t}from"./vendor-react-QAsRxa6t.js";import{r as n,t as r}from"./url-D7yWllI8.js";import{a as i,r as a}from"./vendor-swr-8rdcElid.js";import{r as o}from"./cn-DPF56z7S.js";var s=o(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),c=e(t(),1);function l(){return r(`/api/config`)}var u=null;async function d(){return u||(u=n(l()).finally(()=>{u=null}),u)}function f(e){let t=a(e?l():null,d,{revalidateOnFocus:!1});return(0,c.useEffect)(()=>{if(!e)return;let t=l(),n=()=>{i(t)};return window.addEventListener(`config-reload`,n),()=>window.removeEventListener(`config-reload`,n)},[e]),t}function p(){return i(l())}export{s as i,p as n,f as r,d as t};
|
|
2
|
+
//# sourceMappingURL=gateway-config-swr-Cph02QZn.js.map
|