aimeat 2.1.0 → 2.2.0
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/.env.example +9 -4
- package/dist/locales/en.json +28 -0
- package/dist/locales/fi.json +28 -0
- package/dist/public/cortex-bundled/aimeat-charts.js +158 -10
- package/dist/public/cortex-bundled/aimeat-charts.yaml +1 -1
- package/dist/public/cortex-bundled/aimeat-dag.js +515 -173
- package/dist/public/cortex-bundled/aimeat-dag.yaml +1 -1
- package/dist/public/cortex-bundled/aimeat-i18n.js +6 -1
- package/dist/public/cortex-bundled/aimeat-i18n.yaml +1 -1
- package/dist/public/cortex-bundled/aimeat-surface.js +577 -0
- package/dist/public/cortex-bundled/aimeat-surface.yaml +107 -0
- package/dist/public/cortex-bundled/aimeat-viewport.js +405 -0
- package/dist/public/cortex-bundled/aimeat-viewport.yaml +100 -0
- package/dist/public/css/views/inbox.css +1 -1
- package/dist/public/css/views/profile.css +9 -0
- package/dist/public/lib/VENDORED.md +6 -3
- package/dist/public/lib/aimeat-daisyui-bridge.css +39 -2
- package/dist/public/lib/aimeat-theme.css +538 -0
- package/dist/public/lib/fonts/LICENSE.md +11 -4
- package/dist/public/lib/fonts/fraunces-var-latin-ext.woff2 +0 -0
- package/dist/public/lib/fonts/fraunces-var-latin.woff2 +0 -0
- package/dist/public/lib/fonts/inter-var-latin-ext.woff2 +0 -0
- package/dist/public/lib/fonts/inter-var-latin.woff2 +0 -0
- package/dist/public/lib/fonts/jetbrains-mono-var-latin-ext.woff2 +0 -0
- package/dist/public/lib/fonts/jetbrains-mono-var-latin.woff2 +0 -0
- package/dist/public/lib/fonts/space-grotesk-var-latin-ext.woff2 +0 -0
- package/dist/public/lib/fonts/space-grotesk-var-latin.woff2 +0 -0
- package/dist/public/lib/samples/theme-check.html +92 -0
- package/dist/public/lib/samples/themes-boot.js +20 -0
- package/dist/public/lib/samples/themes.html +154 -0
- package/dist/public/lib/samples/themes.js +60 -0
- package/dist/public/views/profile/access-tab/connected-apps.js +81 -5
- package/dist/public/views/profile/extensions-tab.maturity.js +4 -1
- package/dist/public/views/profile/inbox-tab/components.js +17 -1
- package/dist/public/views/profile/inbox-tab/use-thread-ux.js +29 -2
- package/dist/public/views/profile/libraries-tab.js +10 -2
- package/dist/public/views/profile/wallet-tab.js +64 -0
- package/dist/scripts/check-openapi.d.ts +2 -0
- package/dist/scripts/check-openapi.d.ts.map +1 -0
- package/dist/scripts/check-openapi.js +139 -0
- package/dist/scripts/check-openapi.js.map +1 -0
- package/dist/scripts/embed-viewport.d.ts +23 -0
- package/dist/scripts/embed-viewport.d.ts.map +1 -0
- package/dist/scripts/embed-viewport.js +116 -0
- package/dist/scripts/embed-viewport.js.map +1 -0
- package/dist/src/auth/sse-domain-scopes.d.ts +50 -0
- package/dist/src/auth/sse-domain-scopes.d.ts.map +1 -0
- package/dist/src/auth/sse-domain-scopes.js +111 -0
- package/dist/src/auth/sse-domain-scopes.js.map +1 -0
- package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-tasks.js +4 -1
- package/dist/src/cli/connect/mcp/tools/agent-tasks.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.js +14 -3
- package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/workspaces.d.ts +3 -0
- package/dist/src/cli/connect/mcp/tools/workspaces.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/workspaces.js +4 -2
- package/dist/src/cli/connect/mcp/tools/workspaces.js.map +1 -1
- package/dist/src/cli/connect/tool-call-defs-core.d.ts.map +1 -1
- package/dist/src/cli/connect/tool-call-defs-core.js +13 -1
- package/dist/src/cli/connect/tool-call-defs-core.js.map +1 -1
- package/dist/src/commerce/__tests__/x402-registry.test.d.ts +2 -0
- package/dist/src/commerce/__tests__/x402-registry.test.d.ts.map +1 -0
- package/dist/src/commerce/__tests__/x402-registry.test.js +105 -0
- package/dist/src/commerce/__tests__/x402-registry.test.js.map +1 -0
- package/dist/src/commerce/evm-address.d.ts +21 -0
- package/dist/src/commerce/evm-address.d.ts.map +1 -0
- package/dist/src/commerce/evm-address.js +94 -0
- package/dist/src/commerce/evm-address.js.map +1 -0
- package/dist/src/commerce/money.d.ts +18 -5
- package/dist/src/commerce/money.d.ts.map +1 -1
- package/dist/src/commerce/money.js +27 -6
- package/dist/src/commerce/money.js.map +1 -1
- package/dist/src/commerce/x402-facilitator.d.ts +64 -13
- package/dist/src/commerce/x402-facilitator.d.ts.map +1 -1
- package/dist/src/commerce/x402-facilitator.js +71 -19
- package/dist/src/commerce/x402-facilitator.js.map +1 -1
- package/dist/src/commerce/x402-handler.d.ts +13 -7
- package/dist/src/commerce/x402-handler.d.ts.map +1 -1
- package/dist/src/commerce/x402-handler.js +23 -10
- package/dist/src/commerce/x402-handler.js.map +1 -1
- package/dist/src/commerce/x402.d.ts +11 -6
- package/dist/src/commerce/x402.d.ts.map +1 -1
- package/dist/src/commerce/x402.js +10 -7
- package/dist/src/commerce/x402.js.map +1 -1
- package/dist/src/config-types.d.ts +12 -0
- package/dist/src/config-types.d.ts.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +8 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/data/app-templates/shells.d.ts +11 -3
- package/dist/src/data/app-templates/shells.d.ts.map +1 -1
- package/dist/src/data/app-templates/shells.js +87 -6
- package/dist/src/data/app-templates/shells.js.map +1 -1
- package/dist/src/data/app-templates.js +2 -2
- package/dist/src/data/app-templates.js.map +1 -1
- package/dist/src/data/library-packs/cortex.d.ts.map +1 -1
- package/dist/src/data/library-packs/cortex.js +108 -5
- package/dist/src/data/library-packs/cortex.js.map +1 -1
- package/dist/src/data/library-packs/sdk.d.ts.map +1 -1
- package/dist/src/data/library-packs/sdk.js +32 -8
- package/dist/src/data/library-packs/sdk.js.map +1 -1
- package/dist/src/data/library-packs/vendored.d.ts.map +1 -1
- package/dist/src/data/library-packs/vendored.js +83 -6
- package/dist/src/data/library-packs/vendored.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +478 -19
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/agent-tasks.d.ts +4 -0
- package/dist/src/mcp/agent-tasks.d.ts.map +1 -1
- package/dist/src/mcp/agent-tasks.js +20 -2
- package/dist/src/mcp/agent-tasks.js.map +1 -1
- package/dist/src/mcp/capabilities.d.ts +7 -1
- package/dist/src/mcp/capabilities.d.ts.map +1 -1
- package/dist/src/mcp/capabilities.js +5 -2
- package/dist/src/mcp/capabilities.js.map +1 -1
- package/dist/src/mcp/catalog/definitions/commerce.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions/commerce.js +4 -2
- package/dist/src/mcp/catalog/definitions/commerce.js.map +1 -1
- package/dist/src/mcp/catalog/definitions/discovery-work-boards.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions/discovery-work-boards.js +4 -3
- package/dist/src/mcp/catalog/definitions/discovery-work-boards.js.map +1 -1
- package/dist/src/mcp/catalog/definitions/organisms-workspaces-apps.js +1 -1
- package/dist/src/mcp/catalog/definitions/organisms-workspaces-apps.js.map +1 -1
- package/dist/src/mcp/catalog/definitions/schedules-tasks-memory.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions/schedules-tasks-memory.js +3 -2
- package/dist/src/mcp/catalog/definitions/schedules-tasks-memory.js.map +1 -1
- package/dist/src/mcp/catalog/output-schemas.d.ts +6 -1
- package/dist/src/mcp/catalog/output-schemas.d.ts.map +1 -1
- package/dist/src/mcp/catalog/output-schemas.js +6 -1
- package/dist/src/mcp/catalog/output-schemas.js.map +1 -1
- package/dist/src/mcp/commerce.d.ts.map +1 -1
- package/dist/src/mcp/commerce.js +15 -3
- package/dist/src/mcp/commerce.js.map +1 -1
- package/dist/src/mcp/core-storage.d.ts +23 -0
- package/dist/src/mcp/core-storage.d.ts.map +1 -0
- package/dist/src/mcp/core-storage.js +157 -0
- package/dist/src/mcp/core-storage.js.map +1 -0
- package/dist/src/mcp/core.d.ts +16 -0
- package/dist/src/mcp/core.d.ts.map +1 -1
- package/dist/src/mcp/core.js +75 -119
- package/dist/src/mcp/core.js.map +1 -1
- package/dist/src/mcp/dm-messages.d.ts +4 -0
- package/dist/src/mcp/dm-messages.d.ts.map +1 -1
- package/dist/src/mcp/dm-messages.js +43 -2
- package/dist/src/mcp/dm-messages.js.map +1 -1
- package/dist/src/mcp/exchange.d.ts.map +1 -1
- package/dist/src/mcp/exchange.js +3 -2
- package/dist/src/mcp/exchange.js.map +1 -1
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +6 -2
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/mcp/index.d.ts +5 -0
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +23 -4
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/memory-namespace-hints.d.ts +33 -0
- package/dist/src/mcp/memory-namespace-hints.d.ts.map +1 -0
- package/dist/src/mcp/memory-namespace-hints.js +60 -0
- package/dist/src/mcp/memory-namespace-hints.js.map +1 -0
- package/dist/src/mcp/workspaces.d.ts +5 -0
- package/dist/src/mcp/workspaces.d.ts.map +1 -1
- package/dist/src/mcp/workspaces.js +5 -3
- package/dist/src/mcp/workspaces.js.map +1 -1
- package/dist/src/models/agent-task-schemas.d.ts +25 -3
- package/dist/src/models/agent-task-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-task-schemas.js +16 -0
- package/dist/src/models/agent-task-schemas.js.map +1 -1
- package/dist/src/models/app-tool-schemas.d.ts +518 -1
- package/dist/src/models/app-tool-schemas.d.ts.map +1 -1
- package/dist/src/models/app-tool-schemas.js +53 -1
- package/dist/src/models/app-tool-schemas.js.map +1 -1
- package/dist/src/models/odps-schemas.d.ts +305 -0
- package/dist/src/models/odps-schemas.d.ts.map +1 -0
- package/dist/src/models/odps-schemas.js +182 -0
- package/dist/src/models/odps-schemas.js.map +1 -0
- package/dist/src/models/offer-schemas.d.ts +357 -2
- package/dist/src/models/offer-schemas.d.ts.map +1 -1
- package/dist/src/models/offer-schemas.js +37 -0
- package/dist/src/models/offer-schemas.js.map +1 -1
- package/dist/src/models/schemas.d.ts +6 -3
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +10 -1
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/models/webhook-schemas.d.ts +8 -8
- package/dist/src/routes/agent-tasks/create-read.d.ts +4 -0
- package/dist/src/routes/agent-tasks/create-read.d.ts.map +1 -1
- package/dist/src/routes/agent-tasks/create-read.js +20 -1
- package/dist/src/routes/agent-tasks/create-read.js.map +1 -1
- package/dist/src/routes/agent-tasks/lifecycle.d.ts.map +1 -1
- package/dist/src/routes/agent-tasks/lifecycle.js +10 -0
- package/dist/src/routes/agent-tasks/lifecycle.js.map +1 -1
- package/dist/src/routes/agents/offers.d.ts +2 -0
- package/dist/src/routes/agents/offers.d.ts.map +1 -1
- package/dist/src/routes/agents/offers.js +0 -0
- package/dist/src/routes/agents/offers.js.map +1 -1
- package/dist/src/routes/app-grants.d.ts +6 -0
- package/dist/src/routes/app-grants.d.ts.map +1 -1
- package/dist/src/routes/app-grants.js +55 -25
- package/dist/src/routes/app-grants.js.map +1 -1
- package/dist/src/routes/apps/drafts.d.ts +3 -0
- package/dist/src/routes/apps/drafts.d.ts.map +1 -1
- package/dist/src/routes/apps/drafts.js +63 -1
- package/dist/src/routes/apps/drafts.js.map +1 -1
- package/dist/src/routes/apps/publish.d.ts +2 -0
- package/dist/src/routes/apps/publish.d.ts.map +1 -1
- package/dist/src/routes/apps/publish.js +17 -2
- package/dist/src/routes/apps/publish.js.map +1 -1
- package/dist/src/routes/commerce.d.ts.map +1 -1
- package/dist/src/routes/commerce.js +122 -2
- package/dist/src/routes/commerce.js.map +1 -1
- package/dist/src/routes/exchange-market.d.ts +6 -2
- package/dist/src/routes/exchange-market.d.ts.map +1 -1
- package/dist/src/routes/exchange-market.js +154 -28
- package/dist/src/routes/exchange-market.js.map +1 -1
- package/dist/src/routes/exchange.d.ts.map +1 -1
- package/dist/src/routes/exchange.js +5 -2
- package/dist/src/routes/exchange.js.map +1 -1
- package/dist/src/routes/extensions/actions.d.ts.map +1 -1
- package/dist/src/routes/extensions/actions.js +18 -0
- package/dist/src/routes/extensions/actions.js.map +1 -1
- package/dist/src/routes/extensions/crud.d.ts.map +1 -1
- package/dist/src/routes/extensions/crud.js +36 -3
- package/dist/src/routes/extensions/crud.js.map +1 -1
- package/dist/src/routes/extensions/entitlement-gate.d.ts.map +1 -1
- package/dist/src/routes/extensions/entitlement-gate.js +13 -0
- package/dist/src/routes/extensions/entitlement-gate.js.map +1 -1
- package/dist/src/routes/extensions/manifest.d.ts +6 -0
- package/dist/src/routes/extensions/manifest.d.ts.map +1 -1
- package/dist/src/routes/extensions/manifest.js +68 -1
- package/dist/src/routes/extensions/manifest.js.map +1 -1
- package/dist/src/routes/extensions/pacing.d.ts +31 -0
- package/dist/src/routes/extensions/pacing.d.ts.map +1 -0
- package/dist/src/routes/extensions/pacing.js +73 -0
- package/dist/src/routes/extensions/pacing.js.map +1 -0
- package/dist/src/routes/extensions/paywall.d.ts.map +1 -1
- package/dist/src/routes/extensions/paywall.js +17 -17
- package/dist/src/routes/extensions/paywall.js.map +1 -1
- package/dist/src/routes/memory/crud.d.ts.map +1 -1
- package/dist/src/routes/memory/crud.js +10 -0
- package/dist/src/routes/memory/crud.js.map +1 -1
- package/dist/src/routes/organisms/crud.d.ts +3 -0
- package/dist/src/routes/organisms/crud.d.ts.map +1 -1
- package/dist/src/routes/organisms/crud.js +5 -2
- package/dist/src/routes/organisms/crud.js.map +1 -1
- package/dist/src/routes/organisms/workspace-read.d.ts.map +1 -1
- package/dist/src/routes/organisms/workspace-read.js +7 -2
- package/dist/src/routes/organisms/workspace-read.js.map +1 -1
- package/dist/src/routes/public-stats.d.ts +7 -0
- package/dist/src/routes/public-stats.d.ts.map +1 -1
- package/dist/src/routes/public-stats.js +28 -7
- package/dist/src/routes/public-stats.js.map +1 -1
- package/dist/src/routes/sse.d.ts +7 -0
- package/dist/src/routes/sse.d.ts.map +1 -1
- package/dist/src/routes/sse.js +36 -6
- package/dist/src/routes/sse.js.map +1 -1
- package/dist/src/routes/storage-files.d.ts +11 -0
- package/dist/src/routes/storage-files.d.ts.map +1 -1
- package/dist/src/routes/storage-files.js +58 -51
- package/dist/src/routes/storage-files.js.map +1 -1
- package/dist/src/routes/subdomains.d.ts +4 -0
- package/dist/src/routes/subdomains.d.ts.map +1 -1
- package/dist/src/routes/subdomains.js +42 -5
- package/dist/src/routes/subdomains.js.map +1 -1
- package/dist/src/routes/upload.d.ts +11 -0
- package/dist/src/routes/upload.d.ts.map +1 -1
- package/dist/src/routes/upload.js +108 -28
- package/dist/src/routes/upload.js.map +1 -1
- package/dist/src/routes/webmcp.d.ts.map +1 -1
- package/dist/src/routes/webmcp.js +4 -0
- package/dist/src/routes/webmcp.js.map +1 -1
- package/dist/src/schemas/knowledge-package.d.ts +1 -1
- package/dist/src/server-bootstrap/static-files.d.ts +4 -0
- package/dist/src/server-bootstrap/static-files.d.ts.map +1 -1
- package/dist/src/server-bootstrap/static-files.js +11 -0
- package/dist/src/server-bootstrap/static-files.js.map +1 -1
- package/dist/src/services/build-app-prompt.d.ts +19 -0
- package/dist/src/services/build-app-prompt.d.ts.map +1 -1
- package/dist/src/services/build-app-prompt.js +97 -11
- package/dist/src/services/build-app-prompt.js.map +1 -1
- package/dist/src/services/call-timing.d.ts +51 -0
- package/dist/src/services/call-timing.d.ts.map +1 -0
- package/dist/src/services/call-timing.js +91 -0
- package/dist/src/services/call-timing.js.map +1 -0
- package/dist/src/services/capability-invoke.d.ts +2 -0
- package/dist/src/services/capability-invoke.d.ts.map +1 -1
- package/dist/src/services/capability-invoke.js +10 -0
- package/dist/src/services/capability-invoke.js.map +1 -1
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +6 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/draft-token.d.ts +22 -2
- package/dist/src/services/draft-token.d.ts.map +1 -1
- package/dist/src/services/draft-token.js +52 -3
- package/dist/src/services/draft-token.js.map +1 -1
- package/dist/src/services/exchange-market.d.ts +41 -8
- package/dist/src/services/exchange-market.d.ts.map +1 -1
- package/dist/src/services/exchange-market.js +24 -2
- package/dist/src/services/exchange-market.js.map +1 -1
- package/dist/src/services/exchange-odps.d.ts +58 -0
- package/dist/src/services/exchange-odps.d.ts.map +1 -0
- package/dist/src/services/exchange-odps.js +400 -0
- package/dist/src/services/exchange-odps.js.map +1 -0
- package/dist/src/services/exchange-projection.d.ts +61 -0
- package/dist/src/services/exchange-projection.d.ts.map +1 -0
- package/dist/src/services/exchange-projection.js +504 -0
- package/dist/src/services/exchange-projection.js.map +1 -0
- package/dist/src/services/exchange-proposals.d.ts.map +1 -1
- package/dist/src/services/exchange-proposals.js +2 -0
- package/dist/src/services/exchange-proposals.js.map +1 -1
- package/dist/src/services/extension-files.d.ts +64 -0
- package/dist/src/services/extension-files.d.ts.map +1 -0
- package/dist/src/services/extension-files.js +88 -0
- package/dist/src/services/extension-files.js.map +1 -0
- package/dist/src/services/extension-runtime.d.ts +33 -0
- package/dist/src/services/extension-runtime.d.ts.map +1 -1
- package/dist/src/services/extension-runtime.js +52 -0
- package/dist/src/services/extension-runtime.js.map +1 -1
- package/dist/src/services/file-refs.d.ts +105 -0
- package/dist/src/services/file-refs.d.ts.map +1 -0
- package/dist/src/services/file-refs.js +106 -0
- package/dist/src/services/file-refs.js.map +1 -0
- package/dist/src/services/metered-entitlements.d.ts +7 -0
- package/dist/src/services/metered-entitlements.d.ts.map +1 -1
- package/dist/src/services/metered-entitlements.js +4 -0
- package/dist/src/services/metered-entitlements.js.map +1 -1
- package/dist/src/services/owner-memory.d.ts +14 -1
- package/dist/src/services/owner-memory.d.ts.map +1 -1
- package/dist/src/services/owner-memory.js +8 -14
- package/dist/src/services/owner-memory.js.map +1 -1
- package/dist/src/services/task-files.d.ts +51 -0
- package/dist/src/services/task-files.d.ts.map +1 -0
- package/dist/src/services/task-files.js +58 -0
- package/dist/src/services/task-files.js.map +1 -0
- package/dist/src/services/upload-token.d.ts +29 -0
- package/dist/src/services/upload-token.d.ts.map +1 -1
- package/dist/src/services/upload-token.js +37 -0
- package/dist/src/services/upload-token.js.map +1 -1
- package/dist/src/services/upload-zip.d.ts +2 -0
- package/dist/src/services/upload-zip.d.ts.map +1 -1
- package/dist/src/services/upload-zip.js +5 -1
- package/dist/src/services/upload-zip.js.map +1 -1
- package/dist/src/services/workspace-meta.d.ts +15 -0
- package/dist/src/services/workspace-meta.d.ts.map +1 -1
- package/dist/src/services/workspace-meta.js +17 -0
- package/dist/src/services/workspace-meta.js.map +1 -1
- package/dist/src/services/workspace-provision.d.ts +3 -0
- package/dist/src/services/workspace-provision.d.ts.map +1 -1
- package/dist/src/services/workspace-provision.js +5 -3
- package/dist/src/services/workspace-provision.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/app-grants.d.ts +1 -0
- package/dist/src/storage/providers/postgres-kysely/methods/app-grants.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/app-grants.js +10 -0
- package/dist/src/storage/providers/postgres-kysely/methods/app-grants.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/memory.d.ts +2 -0
- package/dist/src/storage/providers/postgres-kysely/methods/memory.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/memory.js +12 -1
- package/dist/src/storage/providers/postgres-kysely/methods/memory.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/organisms.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/organisms.js +5 -1
- package/dist/src/storage/providers/postgres-kysely/methods/organisms.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/migrations/0012_unique_live_app_grant.sql +29 -0
- package/dist/src/storage/providers/sqlite/methods/apps.d.ts +3 -0
- package/dist/src/storage/providers/sqlite/methods/apps.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/apps.js +7 -0
- package/dist/src/storage/providers/sqlite/methods/apps.js.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/governance.d.ts +1 -0
- package/dist/src/storage/providers/sqlite/methods/governance.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/governance.js +4 -1
- package/dist/src/storage/providers/sqlite/methods/governance.js.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/owner.d.ts +5 -0
- package/dist/src/storage/providers/sqlite/methods/owner.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/owner.js +8 -0
- package/dist/src/storage/providers/sqlite/methods/owner.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts +3 -0
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +20 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/app-grant.repository.d.ts +9 -1
- package/dist/src/storage/repositories/app-grant.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/memory.repository.d.ts +14 -0
- package/dist/src/storage/repositories/memory.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories-impl/memory-repository.d.ts +8 -1
- package/dist/src/storage/repositories-impl/memory-repository.d.ts.map +1 -1
- package/dist/src/storage/repositories-impl/memory-repository.js +15 -4
- package/dist/src/storage/repositories-impl/memory-repository.js.map +1 -1
- package/dist/src/storage/types/agents-messaging.d.ts +19 -0
- package/dist/src/storage/types/agents-messaging.d.ts.map +1 -1
- package/dist/src/storage/types/agents-messaging.js.map +1 -1
- package/dist/src/storage/types/organisms-federation.d.ts +29 -0
- package/dist/src/storage/types/organisms-federation.d.ts.map +1 -1
- package/dist/src/utils/app-badge.d.ts.map +1 -1
- package/dist/src/utils/app-badge.js +7 -3
- package/dist/src/utils/app-badge.js.map +1 -1
- package/dist/static/app-catalog/_template.html +6 -5
- package/dist/static/app-catalog/js/cortex.js +3 -1
- package/dist/static/app-catalog/js/detail.js +411 -36
- package/dist/static/app-catalog/js/i18n-data.js +279 -3
- package/dist/static/app-catalog/js/main.js +28 -37
- package/dist/static/app-catalog/js/monetize.js +204 -16
- package/dist/static/app-catalog/js/odps.js +489 -0
- package/dist/static/app-catalog/js/render.js +3 -0
- package/dist/static/app-catalog/js/workcopy.js +198 -0
- package/dist/static/app-catalog/styles/app-catalog.css +60 -0
- package/dist/static/app-catalog.html +1450 -137
- package/dist/static/sdk-libs/agents/index.js +45 -7
- package/dist/static/sdk-libs/auth/cluster.js +97 -0
- package/dist/static/sdk-libs/auth/index.js +25 -1
- package/dist/static/sdk-libs/auth/locale.js +151 -0
- package/dist/static/sdk-libs/auth/modal.js +11 -2
- package/dist/static/sdk-libs/auth/palette.js +172 -0
- package/dist/static/sdk-libs/auth/pill.js +37 -6
- package/dist/static/sdk-libs/auth/theme.js +61 -20
- package/dist/static/sdk-libs/data/index.js +70 -8
- package/dist/static/sdk-libs/dist/aimeat-agents.js +41 -8
- package/dist/static/sdk-libs/dist/aimeat-auth.js +422 -21
- package/dist/static/sdk-libs/dist/aimeat-data.js +50 -8
- package/dist/static/sdk-libs/dist/aimeat-live.js +54 -2
- package/dist/static/sdk-libs/dist/aimeat-markdown.js +32 -1
- package/dist/static/sdk-libs/live/index.js +71 -4
- package/dist/static/sdk-libs/markdown/index.js +49 -1
- package/package.json +6 -2
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
* const { task, deliverable } = await AIMEAT.agents.run('my-agent', { description: '…' });
|
|
13
13
|
* @version-history
|
|
14
14
|
* v1.0.0 — 2026-07-19 — Migrated from src/routes/lib-agents.ts (SDK-libs migration Phase 1).
|
|
15
|
+
* v1.1.0 — 2026-07-25 — createTask() forwards scope/verification/rules/resources (previously it
|
|
16
|
+
* sent only title+description+status, so an app could not tag its own runs through this library
|
|
17
|
+
* and had to hand-roll the POST). deliverable() falls back to the `task:<id>` memory tag when the
|
|
18
|
+
* agent never set task.deliverableKey. Additive.
|
|
15
19
|
*/
|
|
16
20
|
import { makeSession } from '../_core/session.js';
|
|
17
21
|
const { authFetch } = makeSession('aimeat-agents.js');
|
|
@@ -53,7 +57,14 @@ var agents = {
|
|
|
53
57
|
},
|
|
54
58
|
|
|
55
59
|
/** Commission a task for an agent. Returns the created task ({ id, status, ... }).
|
|
56
|
-
* Created 'queued' by default; task-runner agents auto-activate it.
|
|
60
|
+
* Created 'queued' by default; task-runner agents auto-activate it.
|
|
61
|
+
*
|
|
62
|
+
* task.scope: [{ name, value, type }] — app-defined tags stored ON the task. This is how
|
|
63
|
+
* an app finds its own runs again later: filter `tasks({status:'done'})` on a tag you set,
|
|
64
|
+
* instead of trying to parse the agent's memory-key slug. Pass the same array in a
|
|
65
|
+
* schedule's `task_template.scope` so scheduled runs carry it too.
|
|
66
|
+
* task.verification: { user_expects, technical_checks } — what a good result looks like.
|
|
67
|
+
* Both were silently dropped before v1.1.0. */
|
|
57
68
|
async createTask(name, task) {
|
|
58
69
|
if (!task || !task.description) throw new Error('createTask requires { description }');
|
|
59
70
|
var body = {
|
|
@@ -61,6 +72,22 @@ var agents = {
|
|
|
61
72
|
description: task.description,
|
|
62
73
|
status: task.status || 'queued',
|
|
63
74
|
};
|
|
75
|
+
if (Array.isArray(task.scope) && task.scope.length) {
|
|
76
|
+
body.scope = task.scope.map(function (s) {
|
|
77
|
+
return { name: s.name, value: String(s.value), type: s.type || 'text',
|
|
78
|
+
...(s.description ? { description: s.description } : {}) };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (task.verification) {
|
|
82
|
+
// Accept either casing; the route reads snake_case.
|
|
83
|
+
var v = task.verification;
|
|
84
|
+
body.verification = {
|
|
85
|
+
user_expects: v.user_expects != null ? v.user_expects : (v.userExpects || ''),
|
|
86
|
+
technical_checks: v.technical_checks || v.technicalChecks || [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (task.rules) body.rules = task.rules;
|
|
90
|
+
if (task.resources) body.resources = task.resources;
|
|
64
91
|
var data = unwrap(await authFetch('/v1/agents/' + enc(name) + '/tasks', {
|
|
65
92
|
method: 'POST', body: JSON.stringify(body),
|
|
66
93
|
}), 'create task');
|
|
@@ -118,12 +145,23 @@ var agents = {
|
|
|
118
145
|
* agent's memory. Returns { key, value } | { key, gone:true } | null. */
|
|
119
146
|
async deliverable(name, id) {
|
|
120
147
|
var task = await agents.getTask(name, id);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
if (!task) return null;
|
|
149
|
+
var key = task.deliverableKey;
|
|
150
|
+
if (key) {
|
|
151
|
+
var data = unwrap(await authFetch('/v1/memory?agent=' + enc(task.agentGaii) + '&prefix=' + enc(key) + '&per_page=20'), 'read deliverable');
|
|
152
|
+
var found = (data.items || []).find(function (i) { return i.key === key; });
|
|
153
|
+
return found ? { key: key, value: found.value } : { key: key, gone: true };
|
|
154
|
+
}
|
|
155
|
+
// deliverableKey is OPTIONAL and plenty of task-runner agents never set it — they just
|
|
156
|
+
// publish their output and tag the record `task:<taskId>`. Requiring the field made those
|
|
157
|
+
// results look like "no deliverable", so fall back to the tag.
|
|
158
|
+
var byTag = unwrap(await authFetch('/v1/memory?agent=' + enc(task.agentGaii) +
|
|
159
|
+
'&tags=' + enc('task:' + id) + '&per_page=20'), 'read deliverable by tag');
|
|
160
|
+
var items = (byTag.items || []).slice().sort(function (a, b) {
|
|
161
|
+
return String(b.updated_at || '').localeCompare(String(a.updated_at || ''));
|
|
162
|
+
});
|
|
163
|
+
if (items.length) return { key: items[0].key, value: items[0].value, viaTag: true };
|
|
164
|
+
return null;
|
|
127
165
|
},
|
|
128
166
|
|
|
129
167
|
/** Read a specific memory entry under an agent's namespace (or null). */
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file auth/cluster.js
|
|
3
|
+
* @description The shared stylesheet for the platform control cluster — language, light/dark mode
|
|
4
|
+
* and palette — that travels inside the login pill. One injected sheet, class-based (no inline
|
|
5
|
+
* layout/colour styles on the controls), themed through currentColor + the aimeat-theme.css
|
|
6
|
+
* tokens with safe fallbacks, so the same markup reads correctly on the golden pill, on a bare
|
|
7
|
+
* signed-out header, and in the compact mobile popover. The controls are SEGMENTED: every option
|
|
8
|
+
* visible, the active one marked, one click to any — never a blind cycle. Pickers with more
|
|
9
|
+
* options than fit a segment (palettes, 4+ languages) use a small anchored popover whose width
|
|
10
|
+
* is capped at the viewport, which is the deliberate 390px form.
|
|
11
|
+
* @structure ensureClusterStyles() — idempotent <style> injector.
|
|
12
|
+
* @usage import { ensureClusterStyles } from './cluster.js'; (pill.js calls it once per render)
|
|
13
|
+
* @version-history
|
|
14
|
+
* v1.0.0 — 2026-07-25 — Born with the control cluster (theme system v2): replaces the per-button
|
|
15
|
+
* inline styles of the old cycling language button + lone theme toggle.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export function ensureClusterStyles() {
|
|
19
|
+
if (document.getElementById('aimeat-cluster-css')) return;
|
|
20
|
+
var st = document.createElement('style');
|
|
21
|
+
st.id = 'aimeat-cluster-css';
|
|
22
|
+
st.textContent = [
|
|
23
|
+
/* The cluster row. Inherits text colour from its host (gold pill or page header). */
|
|
24
|
+
'.aimeat-ctl{display:inline-flex;align-items:center;gap:6px}',
|
|
25
|
+
|
|
26
|
+
/* Segmented group: one bordered pill, every option a button. */
|
|
27
|
+
'.aimeat-seg{display:inline-flex;align-items:stretch;height:30px;flex:0 0 auto;',
|
|
28
|
+
'border:1px solid color-mix(in oklab,currentColor 38%,transparent);border-radius:999px;',
|
|
29
|
+
'overflow:hidden;background:color-mix(in oklab,currentColor 5%,transparent)}',
|
|
30
|
+
'.aimeat-seg button{appearance:none;border:0;background:transparent;color:currentColor;',
|
|
31
|
+
'opacity:.6;font:700 11px/1 "Inter","Segoe UI",system-ui,sans-serif;letter-spacing:.4px;',
|
|
32
|
+
'padding:0 10px;margin:0;cursor:pointer;display:inline-flex;align-items:center;gap:4px;',
|
|
33
|
+
'transition:opacity var(--motion-fast,120ms) ease,background var(--motion-fast,120ms) ease}',
|
|
34
|
+
'.aimeat-seg button:hover{opacity:.9}',
|
|
35
|
+
'.aimeat-seg button:focus-visible{outline:2px solid currentColor;outline-offset:-2px;opacity:1}',
|
|
36
|
+
'.aimeat-seg button[aria-pressed="true"]{opacity:1;',
|
|
37
|
+
'background:color-mix(in oklab,currentColor 16%,transparent)}',
|
|
38
|
+
'.aimeat-seg button+button{border-left:1px solid color-mix(in oklab,currentColor 22%,transparent)}',
|
|
39
|
+
'.aimeat-seg .seg-ico{font-size:13px;line-height:1}',
|
|
40
|
+
|
|
41
|
+
/* Popover trigger (palette picker; language picker when 4+ languages). */
|
|
42
|
+
'.aimeat-pop-wrap{position:relative;display:inline-flex;flex:0 0 auto}',
|
|
43
|
+
'.aimeat-pop-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;',
|
|
44
|
+
'gap:5px;height:30px;min-width:30px;padding:0 7px;background:color-mix(in oklab,currentColor 5%,transparent);',
|
|
45
|
+
'border:1px solid color-mix(in oklab,currentColor 38%,transparent);border-radius:999px;',
|
|
46
|
+
'cursor:pointer;color:currentColor;font:700 11px/1 "Inter","Segoe UI",system-ui,sans-serif;letter-spacing:.4px;',
|
|
47
|
+
'transition:background var(--motion-fast,120ms) ease}',
|
|
48
|
+
'.aimeat-pop-btn:hover{background:color-mix(in oklab,currentColor 12%,transparent)}',
|
|
49
|
+
'.aimeat-pop-btn:focus-visible{outline:2px solid currentColor;outline-offset:-2px}',
|
|
50
|
+
|
|
51
|
+
/* The popover panel: a real themed surface (not the host pill), so swatches read true. */
|
|
52
|
+
'.aimeat-pop{position:absolute;top:calc(100% + 8px);right:0;z-index:1200;display:none;',
|
|
53
|
+
'background:var(--color-base-200,#ffffff);color:var(--color-base-content,#1a1a2e);',
|
|
54
|
+
'border:1px solid var(--color-base-300,#d9dbe1);border-radius:var(--radius-box,14px);',
|
|
55
|
+
'box-shadow:var(--elev-pop,0 4px 10px rgb(15 18 25 / .1),0 18px 44px rgb(15 18 25 / .16));',
|
|
56
|
+
'padding:8px;width:max-content;max-width:calc(100vw - 24px)}',
|
|
57
|
+
'.aimeat-pop-wrap.aimeat-open .aimeat-pop{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}',
|
|
58
|
+
'.aimeat-pop.aimeat-pop-list{grid-template-columns:minmax(0,1fr)}',
|
|
59
|
+
'.aimeat-pop button{appearance:none;display:flex;align-items:center;gap:8px;padding:7px 9px;margin:0;',
|
|
60
|
+
'background:transparent;border:1px solid transparent;border-radius:calc(var(--radius-box,14px) - 6px);',
|
|
61
|
+
'cursor:pointer;color:inherit;font:600 12px/1.1 "Inter","Segoe UI",system-ui,sans-serif;text-align:left;',
|
|
62
|
+
'transition:background var(--motion-fast,120ms) ease}',
|
|
63
|
+
'.aimeat-pop button:hover{background:color-mix(in oklab,currentColor 8%,transparent)}',
|
|
64
|
+
'.aimeat-pop button:focus-visible{outline:2px solid var(--color-primary,#e8564a);outline-offset:-2px}',
|
|
65
|
+
'.aimeat-pop button[aria-pressed="true"]{border-color:var(--color-primary,#e8564a)}',
|
|
66
|
+
|
|
67
|
+
/* Palette swatch chips: page/card/accent of the palette IN THE CURRENT MODE. The three
|
|
68
|
+
colours are data (they vary per palette), so they arrive as inline background values on
|
|
69
|
+
these spans — layout and everything else stays here. */
|
|
70
|
+
'.aimeat-pal-chip{position:relative;flex:0 0 auto;width:26px;height:20px;border-radius:5px;',
|
|
71
|
+
'border:1px solid color-mix(in oklab,currentColor 25%,transparent);overflow:hidden}',
|
|
72
|
+
'.aimeat-pal-chip .pc-card{position:absolute;inset:5px 5px 3px 5px;border-radius:3px}',
|
|
73
|
+
'.aimeat-pal-chip .pc-acc{position:absolute;right:3px;bottom:3px;width:7px;height:7px;border-radius:50%}',
|
|
74
|
+
/* The trigger's miniature: the active palette's accent as a dot. */
|
|
75
|
+
'.aimeat-pal-dot{width:12px;height:12px;border-radius:50%;flex:0 0 auto;',
|
|
76
|
+
'border:1px solid color-mix(in oklab,currentColor 30%,transparent)}',
|
|
77
|
+
|
|
78
|
+
].join('');
|
|
79
|
+
(document.head || document.documentElement).appendChild(st);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Keep a just-opened popover inside the viewport: anchored right by default, nudged with a
|
|
84
|
+
* transform when the left edge would leave the screen (the 390px case — a swatch grid opened
|
|
85
|
+
* from a cluster that sits mid-header). Measurement-based, so it works inside the compact
|
|
86
|
+
* pill popover too, where CSS alone cannot know where the anchor landed.
|
|
87
|
+
* @param {HTMLElement} pop
|
|
88
|
+
*/
|
|
89
|
+
export function clampPopover(pop) {
|
|
90
|
+
pop.style.transform = '';
|
|
91
|
+
var r = pop.getBoundingClientRect();
|
|
92
|
+
var pad = 12;
|
|
93
|
+
var shift = 0;
|
|
94
|
+
if (r.left < pad) shift = pad - r.left;
|
|
95
|
+
else if (r.right > window.innerWidth - pad) shift = (window.innerWidth - pad) - r.right;
|
|
96
|
+
if (shift) pop.style.transform = 'translateX(' + Math.round(shift) + 'px)';
|
|
97
|
+
}
|
|
@@ -10,11 +10,19 @@
|
|
|
10
10
|
* listeners; attach('auth', auth) + version.
|
|
11
11
|
* @usage <script src="/v1/libs/aimeat-auth.js"></script> const s = await AIMEAT.auth.login();
|
|
12
12
|
* @version-history
|
|
13
|
+
* v1.2.0 — 2026-07-26 — An embedded app inherits the embedder's look: aimeatRestoreMode() joins
|
|
14
|
+
* aimeatRestorePalette() at parse time, so ?mode= / ?palette= in the frame URL win over an
|
|
15
|
+
* origin's own storage without overwriting it.
|
|
16
|
+
* v1.1.0 — 2026-07-25 — Palette surface: AIMEAT.auth.getPalette/setPalette/getPalettes + the
|
|
17
|
+
* stored palette applied at parse time (theme system v2).
|
|
13
18
|
* v1.0.0 — 2026-07-19 — Componentized rewrite of src/routes/libs/auth-lib*.ts (SDK-libs migration Phase 3).
|
|
14
19
|
*/
|
|
15
20
|
import { auth, refreshOnFocus } from './session.js';
|
|
16
21
|
import { maybeShowGoogleSignup } from './signup.js';
|
|
17
22
|
import { attach } from '../_core/namespace.js';
|
|
23
|
+
import { readLocales, aimeatReadLang, aimeatApplyLang } from './locale.js';
|
|
24
|
+
import { PALETTES, aimeatReadPalette, aimeatApplyPalette, aimeatRestorePalette } from './palette.js';
|
|
25
|
+
import { aimeatRestoreMode } from './theme.js';
|
|
18
26
|
|
|
19
27
|
// ── Boot: first-time OIDC signup prompt (after the callback bounced back with ?aimeat_signup=1) ──
|
|
20
28
|
if (typeof document !== 'undefined' && document.addEventListener) {
|
|
@@ -33,5 +41,21 @@ if (typeof window !== 'undefined' && window.addEventListener) {
|
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
// ── Expose globally ──
|
|
44
|
+
// Locale, resolved the ONE way this platform resolves it ('aimeat-lang': ?lang= -> localStorage
|
|
45
|
+
// -> cookie -> navigator). The pill renders the control; these let an app read and set the same
|
|
46
|
+
// value without re-implementing the lookup and drifting from it. They hang off `auth` (NOT the
|
|
47
|
+
// return of attach(), which is the whole window.AIMEAT namespace) so they land on AIMEAT.auth.
|
|
48
|
+
auth.getLang = function (locales) { return aimeatReadLang(readLocales({ locales: locales })); };
|
|
49
|
+
auth.setLang = function (lang) { aimeatApplyLang(String(lang).toLowerCase()); };
|
|
50
|
+
// Palette (the designed look, orthogonal to light/dark — 'aimeat-palette' + <html data-palette>).
|
|
51
|
+
// The pill renders the picker; these let an app read/set/enumerate the same value.
|
|
52
|
+
auth.getPalette = function () { return aimeatReadPalette(); };
|
|
53
|
+
auth.setPalette = function (id) { aimeatApplyPalette(String(id).toLowerCase()); };
|
|
54
|
+
auth.getPalettes = function () { return PALETTES.map(function (p) { return { id: p.id, label: p.label, swatch: p.swatch }; }); };
|
|
55
|
+
|
|
56
|
+
// Apply the stored palette at parse time (before any UI mounts), so a published app follows the
|
|
57
|
+
// user's chosen look with zero app code — the same free ride the mode snippet gives light/dark.
|
|
58
|
+
if (typeof document !== 'undefined') { aimeatRestorePalette(); aimeatRestoreMode(); }
|
|
59
|
+
|
|
36
60
|
const ns = attach('auth', auth);
|
|
37
|
-
ns.version = '2026-07-
|
|
61
|
+
ns.version = '2026-07-25-002';
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file auth/locale.js
|
|
3
|
+
* @description The LANGUAGE switch that travels inside the login pill, exactly as the light/dark
|
|
4
|
+
* toggle does — so every embedding app inherits one identical control instead of hand-rolling its
|
|
5
|
+
* own. Before this, each app invented its own switcher (a text button, a select, a pair of links),
|
|
6
|
+
* and three different storage keys were in play: the SPA's 'aimeat-lang', the aimeat-i18n cortex's
|
|
7
|
+
* 'aimeat-i18n-locale', and a URL parameter read by nobody in particular.
|
|
8
|
+
*
|
|
9
|
+
* ONE key: 'aimeat-lang' — the same one the SPA and the sign-in modal already use. Reading order is
|
|
10
|
+
* ?lang= → localStorage → cookie → navigator, matching public/js/utils.js detectLocale() so an app
|
|
11
|
+
* and the SPA agree on the same origin. Writing sets localStorage AND the cookie and fires an
|
|
12
|
+
* 'aimeat-lang-change' window event; an app re-renders on that event and never manages the control.
|
|
13
|
+
*
|
|
14
|
+
* The app declares which languages it HAS — the pill cannot guess. Either
|
|
15
|
+
* `<meta name="aimeat-locales" content="en fi">` (preferred: declarative, like aimeat-scopes) or
|
|
16
|
+
* `mountLoginButton('#login', { locales: ['en','fi'] })`. Declare nothing and no switch renders,
|
|
17
|
+
* so a single-language app is unaffected.
|
|
18
|
+
* @structure AIMEAT_LANG_KEY · readLocales · aimeatReadLang/aimeatApplyLang · langName ·
|
|
19
|
+
* langSwitchHtml/wireLangSwitch
|
|
20
|
+
* @usage import { langSwitchHtml, wireLangSwitch } from './locale.js';
|
|
21
|
+
* In the app: window.addEventListener('aimeat-lang-change', e => render(e.detail.lang));
|
|
22
|
+
* @version-history
|
|
23
|
+
* v2.0.0 — 2026-07-25 — Segmented control (every option visible, one click to any) up to three
|
|
24
|
+
* languages; a popover list beyond that. Replaces the cycling button, which with three or more
|
|
25
|
+
* languages was a blind carousel.
|
|
26
|
+
* v1.0.0 — 2026-07-25 — Initial. Born from "the language switcher is different in EVERY app".
|
|
27
|
+
*/
|
|
28
|
+
import { escHtml } from './theme.js';
|
|
29
|
+
import { clampPopover } from './cluster.js';
|
|
30
|
+
|
|
31
|
+
/** The one locale key on this platform. Same as public/js/utils.js and auth/i18n.js. */
|
|
32
|
+
export var AIMEAT_LANG_KEY = 'aimeat-lang';
|
|
33
|
+
|
|
34
|
+
/** Two-letter codes the APP says it supports. Order matters: it is the cycle order. */
|
|
35
|
+
export function readLocales(opts) {
|
|
36
|
+
var list = (opts && Array.isArray(opts.locales)) ? opts.locales : null;
|
|
37
|
+
if (!list) {
|
|
38
|
+
try {
|
|
39
|
+
var m = /** @type {HTMLMetaElement|null} */ (document.querySelector('meta[name="aimeat-locales"]'));
|
|
40
|
+
if (m && m.content) list = m.content.split(/[\s,]+/);
|
|
41
|
+
} catch { /* no document */ }
|
|
42
|
+
}
|
|
43
|
+
if (!list) return [];
|
|
44
|
+
var seen = {}, out = [];
|
|
45
|
+
for (var i = 0; i < list.length; i++) {
|
|
46
|
+
var c = String(list[i] || '').trim().toLowerCase();
|
|
47
|
+
if (/^[a-z]{2}$/.test(c) && !seen[c]) { seen[c] = 1; out.push(c); }
|
|
48
|
+
}
|
|
49
|
+
return out.length > 1 ? out : []; // one language needs no switch
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Current locale, resolved the same way the SPA resolves it. */
|
|
53
|
+
export function aimeatReadLang(locales) {
|
|
54
|
+
var ok = function (v) { return v && locales.indexOf(v) >= 0 ? v : null; };
|
|
55
|
+
try {
|
|
56
|
+
var u = ok(new URLSearchParams(location.search).get('lang'));
|
|
57
|
+
if (u) return u;
|
|
58
|
+
var s = ok(localStorage.getItem(AIMEAT_LANG_KEY));
|
|
59
|
+
if (s) return s;
|
|
60
|
+
var c = document.cookie.match(/(?:^|;\s*)aimeat-lang=([a-z]{2})(?:;|$)/);
|
|
61
|
+
if (c && ok(c[1])) return c[1];
|
|
62
|
+
} catch { /* storage blocked */ }
|
|
63
|
+
var nav = ok((navigator.language || '').slice(0, 2).toLowerCase());
|
|
64
|
+
return nav || locales[0];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function aimeatApplyLang(lang) {
|
|
68
|
+
try {
|
|
69
|
+
localStorage.setItem(AIMEAT_LANG_KEY, lang);
|
|
70
|
+
document.cookie = 'aimeat-lang=' + lang + ';path=/;max-age=31536000;SameSite=Lax';
|
|
71
|
+
} catch { /* storage blocked */ }
|
|
72
|
+
try { document.documentElement.setAttribute('lang', lang); } catch { /* no document */ }
|
|
73
|
+
try { window.dispatchEvent(new CustomEvent('aimeat-lang-change', { detail: { lang: lang } })); } catch { /* no window */ }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The language's own name for the picker list ("suomi", not "Finnish"), code as the fallback. */
|
|
77
|
+
function langName(code) {
|
|
78
|
+
try {
|
|
79
|
+
var dn = new Intl.DisplayNames([code], { type: 'language' });
|
|
80
|
+
var n = dn.of(code);
|
|
81
|
+
if (n && n !== code) return n.charAt(0).toUpperCase() + n.slice(1);
|
|
82
|
+
} catch { /* Intl.DisplayNames unavailable */ }
|
|
83
|
+
return code.toUpperCase();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** How many languages still fit as a segmented control before the picker becomes a popover list. */
|
|
87
|
+
var SEGMENT_MAX = 3;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The control: every declared language VISIBLE, the active one marked, one click to any — never a
|
|
91
|
+
* blind cycle. Up to three languages render as a segmented group (FI | EN); four or more become a
|
|
92
|
+
* popover list (current code as the trigger) so eight languages cost the header no more width than
|
|
93
|
+
* two. Cluster classes come from cluster.js (ensureClusterStyles).
|
|
94
|
+
*/
|
|
95
|
+
export function langSwitchHtml(i, locales) {
|
|
96
|
+
if (!locales.length) return '';
|
|
97
|
+
var cur = aimeatReadLang(locales);
|
|
98
|
+
var group = (i && i.switchLanguage ? i.switchLanguage : 'Language');
|
|
99
|
+
if (locales.length <= SEGMENT_MAX) {
|
|
100
|
+
return '<span id="aimeat-lang-switch" class="aimeat-seg" role="group" aria-label="' + escHtml(group) + '">'
|
|
101
|
+
+ locales.map(function (c) {
|
|
102
|
+
return '<button type="button" data-lang="' + escHtml(c) + '" aria-pressed="' + (c === cur) + '" '
|
|
103
|
+
+ 'title="' + escHtml(langName(c)) + '" aria-label="' + escHtml(langName(c)) + '">'
|
|
104
|
+
+ escHtml(c.toUpperCase()) + '</button>';
|
|
105
|
+
}).join('')
|
|
106
|
+
+ '</span>';
|
|
107
|
+
}
|
|
108
|
+
return '<span id="aimeat-lang-switch" class="aimeat-pop-wrap">'
|
|
109
|
+
+ '<button type="button" class="aimeat-pop-btn" aria-haspopup="listbox" aria-expanded="false" '
|
|
110
|
+
+ 'title="' + escHtml(group) + '" aria-label="' + escHtml(group) + '">'
|
|
111
|
+
+ escHtml(cur.toUpperCase()) + ' <span aria-hidden="true">▾</span></button>'
|
|
112
|
+
+ '<span class="aimeat-pop aimeat-pop-list" role="listbox">'
|
|
113
|
+
+ locales.map(function (c) {
|
|
114
|
+
return '<button type="button" role="option" data-lang="' + escHtml(c) + '" aria-pressed="' + (c === cur) + '">'
|
|
115
|
+
+ escHtml(c.toUpperCase()) + ' · ' + escHtml(langName(c)) + '</button>';
|
|
116
|
+
}).join('')
|
|
117
|
+
+ '</span></span>';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function wireLangSwitch(container, i, locales) {
|
|
121
|
+
var root = container.querySelector('#aimeat-lang-switch');
|
|
122
|
+
if (!root || !locales.length) return;
|
|
123
|
+
var trigger = root.querySelector('.aimeat-pop-btn');
|
|
124
|
+
function sync(cur) {
|
|
125
|
+
root.querySelectorAll('button[data-lang]').forEach(function (b) {
|
|
126
|
+
b.setAttribute('aria-pressed', String(b.getAttribute('data-lang') === cur));
|
|
127
|
+
});
|
|
128
|
+
if (trigger) trigger.childNodes[0].textContent = cur.toUpperCase() + ' ';
|
|
129
|
+
}
|
|
130
|
+
root.querySelectorAll('button[data-lang]').forEach(function (b) {
|
|
131
|
+
b.addEventListener('click', function () {
|
|
132
|
+
aimeatApplyLang(b.getAttribute('data-lang'));
|
|
133
|
+
sync(b.getAttribute('data-lang'));
|
|
134
|
+
root.classList.remove('aimeat-open');
|
|
135
|
+
if (trigger) trigger.setAttribute('aria-expanded', 'false');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
if (trigger) trigger.addEventListener('click', function (ev) {
|
|
139
|
+
ev.stopPropagation();
|
|
140
|
+
var open = root.classList.toggle('aimeat-open');
|
|
141
|
+
trigger.setAttribute('aria-expanded', String(open));
|
|
142
|
+
if (open) clampPopover(root.querySelector('.aimeat-pop'));
|
|
143
|
+
});
|
|
144
|
+
// Stay truthful when the APP sets the language itself (AIMEAT.auth.setLang, a deep link, the
|
|
145
|
+
// i18n cortex): a control showing the wrong language is worse than no control.
|
|
146
|
+
window.addEventListener('aimeat-lang-change', function (ev) {
|
|
147
|
+
var e = /** @type {CustomEvent} */ (ev);
|
|
148
|
+
var lang = e && e.detail && e.detail.lang;
|
|
149
|
+
if (lang && locales.indexOf(lang) >= 0) sync(lang);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* @usage import { showLoginModal } from './modal.js';
|
|
12
12
|
* @version-history
|
|
13
13
|
* v1.0.0 — 2026-07-19 — Extracted from src/routes/libs/auth-lib-part2/3.ts (SDK-libs migration Phase 3).
|
|
14
|
+
* v1.0.1 — 2026-07-25 — Fix: adopt the node's full locale dict when any key differs, so newer modal
|
|
15
|
+
* keys (email-step strings) missing from a host's opts.i18n no longer fall back to English.
|
|
14
16
|
*/
|
|
15
17
|
import { auth, api } from './session.js';
|
|
16
18
|
import { escHtml } from './theme.js';
|
|
@@ -61,10 +63,17 @@ export function showLoginModal(opts, renderBtn) {
|
|
|
61
63
|
document.body.appendChild(modal);
|
|
62
64
|
render(true);
|
|
63
65
|
|
|
64
|
-
//
|
|
66
|
+
// Adopt the node's full, correct-language dictionary. `fresh` is authoritative: it corrects a
|
|
67
|
+
// wrong-language opts.i18n AND — the common case — fills newer keys the host's opts.i18n predates
|
|
68
|
+
// (e.g. the email-step strings), which a signInBtn/descNew-only guard would wrongly skip, leaving
|
|
69
|
+
// those keys undefined and rendering their English fallbacks.
|
|
65
70
|
loadModalI18n(lang).then(function (fresh) {
|
|
66
71
|
if (!fresh || !Object.keys(fresh).length) return;
|
|
67
|
-
|
|
72
|
+
var differs = false;
|
|
73
|
+
for (var k in fresh) {
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(fresh, k) && fresh[k] !== i[k]) { differs = true; break; }
|
|
75
|
+
}
|
|
76
|
+
if (!differs) return;
|
|
68
77
|
var vals = captureInputs();
|
|
69
78
|
i = fresh;
|
|
70
79
|
render(false);
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file auth/palette.js
|
|
3
|
+
* @description The PALETTE axis of the AIMEAT theme system — which designed look the user wants,
|
|
4
|
+
* orthogonal to the light/dark MODE (theme.js). Mirrors /lib/aimeat-theme.css: every palette
|
|
5
|
+
* there appears in PALETTES here with its true swatch colours, and `pnpm check:theme` fails the
|
|
6
|
+
* build when the two drift, so this registry can never advertise a palette the CSS does not ship.
|
|
7
|
+
*
|
|
8
|
+
* ONE key: 'aimeat-palette' in localStorage; `data-palette` on <html> (absent = the default
|
|
9
|
+
* 'aimeat' coral palette, so a page with no stored choice is byte-identical to the pre-palette
|
|
10
|
+
* era). Changing it fires an 'aimeat-palette-change' window event — a pure CSS-variable swap,
|
|
11
|
+
* so an app never needs to re-render, exactly like TDR.setPalette() in the MACHINE ROOM kit.
|
|
12
|
+
* @structure AIMEAT_PALETTE_KEY · PALETTES (the registry, synced against aimeat-theme.css) ·
|
|
13
|
+
* aimeatReadPalette/aimeatApplyPalette/aimeatRestorePalette · paletteControlHtml/
|
|
14
|
+
* wirePaletteControl (the swatch picker) · esc
|
|
15
|
+
* @usage import { PALETTES, aimeatApplyPalette, aimeatRestorePalette } from './palette.js';
|
|
16
|
+
* In the app: nothing — the login pill renders the picker and the CSS follows.
|
|
17
|
+
* @version-history
|
|
18
|
+
* v1.1.0 — 2026-07-26 — ?palette= is read first (the ?lang= door): an app EMBEDDED by another
|
|
19
|
+
* page cannot see the look chosen on the embedder's origin, so the embedder says it in the URL
|
|
20
|
+
* and the app inherits it. Never persisted — an embed decides how it looks while it is
|
|
21
|
+
* embedded, not what the app remembers.
|
|
22
|
+
* v1.0.0 — 2026-07-25 — Initial palette registry + apply/restore + swatch picker, born with
|
|
23
|
+
* aimeat-theme.css v2.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The one palette key on this platform (the mode key is 'aimeat-theme' in theme.js). */
|
|
27
|
+
export var AIMEAT_PALETTE_KEY = 'aimeat-palette';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The designed palettes, in display order. `swatch` carries the REAL theme colours (page/card/
|
|
31
|
+
* accent per mode) so a picker can show what it is offering instead of a name; check:theme
|
|
32
|
+
* verifies each hex against the corresponding --color-* in /lib/aimeat-theme.css.
|
|
33
|
+
* @type {{ id: string, label: string, swatch: { light: { bg: string, card: string, accent: string }, dark: { bg: string, card: string, accent: string } } }[]}
|
|
34
|
+
*/
|
|
35
|
+
export var PALETTES = [
|
|
36
|
+
{ id: 'aimeat', label: 'AIMEAT', swatch: {
|
|
37
|
+
light: { bg: '#efeee8', card: '#ffffff', accent: '#e8564a' },
|
|
38
|
+
dark: { bg: '#14151a', card: '#21232e', accent: '#ff6f62' } } },
|
|
39
|
+
{ id: 'paper', label: 'Paper', swatch: {
|
|
40
|
+
light: { bg: '#eae2cf', card: '#fdfaf1', accent: '#a03040' },
|
|
41
|
+
dark: { bg: '#151110', card: '#282017', accent: '#e08590' } } },
|
|
42
|
+
{ id: 'circuit', label: 'Circuit', swatch: {
|
|
43
|
+
light: { bg: '#e9edf1', card: '#ffffff', accent: '#0e7290' },
|
|
44
|
+
dark: { bg: '#0a0f14', card: '#18202b', accent: '#4fd2f2' } } },
|
|
45
|
+
{ id: 'contrast', label: 'Contrast', swatch: {
|
|
46
|
+
light: { bg: '#e9e9e9', card: '#ffffff', accent: '#1d4ed8' },
|
|
47
|
+
dark: { bg: '#000000', card: '#17171c', accent: '#99c2ff' } } },
|
|
48
|
+
{ id: 'mist', label: 'Mist', swatch: {
|
|
49
|
+
light: { bg: '#e6eae4', card: '#fbfcfa', accent: '#47695a' },
|
|
50
|
+
dark: { bg: '#141715', card: '#252b27', accent: '#9cc0ae' } } },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/** The palette in effect: stored choice if valid, else the default (first in PALETTES). */
|
|
54
|
+
export function aimeatReadPalette() {
|
|
55
|
+
var ids = PALETTES.map(function (p) { return p.id; });
|
|
56
|
+
try {
|
|
57
|
+
// ?palette= first, exactly where ?lang= sits in the locale lookup, and for the same reason:
|
|
58
|
+
// localStorage is per ORIGIN, so an app embedded by another page cannot see the look the
|
|
59
|
+
// reader chose there. The embedder says it in the URL and the app inherits it instead of
|
|
60
|
+
// rendering in a palette nobody picked. Not persisted — an embed decides how it looks while
|
|
61
|
+
// it is embedded, and must not overwrite the choice the app's own origin remembers.
|
|
62
|
+
var u = new URLSearchParams(location.search).get('palette');
|
|
63
|
+
if (u && ids.indexOf(u) >= 0) return u;
|
|
64
|
+
} catch { /* no location */ }
|
|
65
|
+
try {
|
|
66
|
+
var s = localStorage.getItem(AIMEAT_PALETTE_KEY);
|
|
67
|
+
if (s && ids.indexOf(s) >= 0) return s;
|
|
68
|
+
} catch { /* storage blocked */ }
|
|
69
|
+
var attr = document.documentElement.getAttribute('data-palette');
|
|
70
|
+
return attr && ids.indexOf(attr) >= 0 ? attr : PALETTES[0].id;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Apply + persist + announce. The default palette REMOVES the attribute (canonical no-attr form). */
|
|
74
|
+
export function aimeatApplyPalette(id) {
|
|
75
|
+
if (id === PALETTES[0].id) document.documentElement.removeAttribute('data-palette');
|
|
76
|
+
else document.documentElement.setAttribute('data-palette', id);
|
|
77
|
+
try { localStorage.setItem(AIMEAT_PALETTE_KEY, id); } catch { /* storage blocked */ }
|
|
78
|
+
try { window.dispatchEvent(new CustomEvent('aimeat-palette-change', { detail: { palette: id } })); } catch { /* no window */ }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Restore the stored choice onto <html> (idempotent; no event — nothing changed from the user's
|
|
83
|
+
* point of view). Runs at lib parse time via auth/index so every app follows the choice even
|
|
84
|
+
* before any UI mounts, and follows other-tab changes through the storage event.
|
|
85
|
+
*/
|
|
86
|
+
export function aimeatRestorePalette() {
|
|
87
|
+
var cur = aimeatReadPalette();
|
|
88
|
+
if (cur !== PALETTES[0].id) document.documentElement.setAttribute('data-palette', cur);
|
|
89
|
+
else document.documentElement.removeAttribute('data-palette'); // an embed may ask for the default
|
|
90
|
+
try {
|
|
91
|
+
window.addEventListener('storage', function (e) {
|
|
92
|
+
if (e.key === AIMEAT_PALETTE_KEY && e.newValue) aimeatApplyPalette(e.newValue);
|
|
93
|
+
});
|
|
94
|
+
} catch { /* no window */ }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The PALETTE picker: a swatch trigger (the active palette's accent as a dot) opening a popover
|
|
99
|
+
* grid where every palette is a true-colour chip (page/card/accent in the CURRENT mode) plus its
|
|
100
|
+
* name — you see what you are choosing, not a dropdown of words. The chip colours are data, so
|
|
101
|
+
* they ride as inline background values; all layout lives in cluster.js.
|
|
102
|
+
* @param {{ chooseLook?: string }} [i]
|
|
103
|
+
*/
|
|
104
|
+
export function paletteControlHtml(i) {
|
|
105
|
+
var cur = aimeatReadPalette();
|
|
106
|
+
var mode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
|
|
107
|
+
var curAcc = (PALETTES.find(function (p) { return p.id === cur; }) || PALETTES[0]).swatch[mode].accent;
|
|
108
|
+
var label = (i && i.chooseLook) || 'Choose look';
|
|
109
|
+
return '<span id="aimeat-palette-switch" class="aimeat-pop-wrap">'
|
|
110
|
+
+ '<button type="button" class="aimeat-pop-btn" aria-haspopup="listbox" aria-expanded="false" '
|
|
111
|
+
+ 'title="' + esc(label) + '" aria-label="' + esc(label) + '">'
|
|
112
|
+
+ '<span class="aimeat-pal-dot" style="background:' + esc(curAcc) + '"></span></button>'
|
|
113
|
+
+ '<span class="aimeat-pop" role="listbox">'
|
|
114
|
+
+ PALETTES.map(function (p) {
|
|
115
|
+
var s = p.swatch[mode];
|
|
116
|
+
return '<button type="button" role="option" data-palette="' + esc(p.id) + '" aria-pressed="' + (p.id === cur) + '">'
|
|
117
|
+
+ '<span class="aimeat-pal-chip" style="background:' + esc(s.bg) + '">'
|
|
118
|
+
+ '<span class="pc-card" style="background:' + esc(s.card) + '"></span>'
|
|
119
|
+
+ '<span class="pc-acc" style="background:' + esc(s.accent) + '"></span></span>'
|
|
120
|
+
+ esc(p.label) + '</button>';
|
|
121
|
+
}).join('')
|
|
122
|
+
+ '</span></span>';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {Element} container
|
|
127
|
+
* @param {(pop: HTMLElement) => void} clampPopover
|
|
128
|
+
*/
|
|
129
|
+
export function wirePaletteControl(container, clampPopover) {
|
|
130
|
+
var root = container.querySelector('#aimeat-palette-switch');
|
|
131
|
+
if (!root) return;
|
|
132
|
+
var trigger = /** @type {HTMLElement} */ (root.querySelector('.aimeat-pop-btn'));
|
|
133
|
+
function syncDot() {
|
|
134
|
+
var cur = aimeatReadPalette();
|
|
135
|
+
var mode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
|
|
136
|
+
var p = PALETTES.find(function (x) { return x.id === cur; }) || PALETTES[0];
|
|
137
|
+
var dot = /** @type {HTMLElement|null} */ (root.querySelector('.aimeat-pal-dot'));
|
|
138
|
+
if (dot) dot.style.background = p.swatch[mode].accent;
|
|
139
|
+
root.querySelectorAll('button[data-palette]').forEach(function (b) {
|
|
140
|
+
b.setAttribute('aria-pressed', String(b.getAttribute('data-palette') === cur));
|
|
141
|
+
// Re-tint the chips for the mode in effect, so the picker always previews truthfully.
|
|
142
|
+
var pp = PALETTES.find(function (x) { return x.id === b.getAttribute('data-palette'); });
|
|
143
|
+
if (!pp) return;
|
|
144
|
+
var s = pp.swatch[mode];
|
|
145
|
+
var chip = /** @type {HTMLElement|null} */ (b.querySelector('.aimeat-pal-chip'));
|
|
146
|
+
var card = /** @type {HTMLElement|null} */ (b.querySelector('.pc-card'));
|
|
147
|
+
var acc = /** @type {HTMLElement|null} */ (b.querySelector('.pc-acc'));
|
|
148
|
+
if (chip) chip.style.background = s.bg;
|
|
149
|
+
if (card) card.style.background = s.card;
|
|
150
|
+
if (acc) acc.style.background = s.accent;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
root.querySelectorAll('button[data-palette]').forEach(function (b) {
|
|
154
|
+
b.addEventListener('click', function () {
|
|
155
|
+
aimeatApplyPalette(b.getAttribute('data-palette') || PALETTES[0].id);
|
|
156
|
+
syncDot();
|
|
157
|
+
root.classList.remove('aimeat-open');
|
|
158
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
trigger.addEventListener('click', function (ev) {
|
|
162
|
+
ev.stopPropagation();
|
|
163
|
+
var open = root.classList.toggle('aimeat-open');
|
|
164
|
+
trigger.setAttribute('aria-expanded', String(open));
|
|
165
|
+
if (open) clampPopover(/** @type {HTMLElement} */ (root.querySelector('.aimeat-pop')));
|
|
166
|
+
});
|
|
167
|
+
window.addEventListener('aimeat-palette-change', syncDot);
|
|
168
|
+
window.addEventListener('aimeat-theme-change', syncDot);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Local escaper (palette.js must not import from theme.js — theme.js may import from here). */
|
|
172
|
+
function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|