aimeat 2.0.0 → 2.1.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/locales/en.json +35 -1
- package/dist/locales/fi.json +35 -1
- package/dist/public/components/LinkPreview.js +101 -0
- package/dist/public/components/NotificationBell.js +18 -1
- package/dist/public/css/theme.css +57 -7
- package/dist/public/css/views/admin.css +8 -1
- package/dist/public/css/views/app-grant.css +18 -0
- package/dist/public/css/views/inbox.css +46 -0
- package/dist/public/css/views/portfolio.css +46 -0
- package/dist/public/css/views/profile.css +29 -4
- package/dist/public/js/services/apps.js +19 -3
- package/dist/public/js/services/messages-ai-prompts.js +38 -0
- package/dist/public/js/services/messages.js +26 -4
- package/dist/public/js/services/notebook.js +76 -0
- package/dist/public/js/services/unfurl.js +43 -0
- package/dist/public/lib/samples/LICENSE.md +11 -0
- package/dist/public/lib/samples/bass/A1.mp3 +0 -0
- package/dist/public/lib/samples/bass/B2.mp3 +0 -0
- package/dist/public/lib/samples/bass/D2.mp3 +0 -0
- package/dist/public/lib/samples/bass/E1.mp3 +0 -0
- package/dist/public/lib/samples/bass/E3.mp3 +0 -0
- package/dist/public/lib/samples/bass/G2.mp3 +0 -0
- package/dist/public/lib/samples/epiano/C2.mp3 +0 -0
- package/dist/public/lib/samples/epiano/C3.mp3 +0 -0
- package/dist/public/lib/samples/epiano/C4.mp3 +0 -0
- package/dist/public/lib/samples/epiano/C5.mp3 +0 -0
- package/dist/public/lib/samples/epiano/C6.mp3 +0 -0
- package/dist/public/lib/samples/epiano/G2.mp3 +0 -0
- package/dist/public/lib/samples/epiano/G3.mp3 +0 -0
- package/dist/public/lib/samples/epiano/G4.mp3 +0 -0
- package/dist/public/lib/samples/epiano/G5.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/A2.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/A4.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/B3.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/D3.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/E2.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/E4.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/E5.mp3 +0 -0
- package/dist/public/lib/samples/guitar-steel/G3.mp3 +0 -0
- package/dist/public/lib/samples/organ/C2.mp3 +0 -0
- package/dist/public/lib/samples/organ/C3.mp3 +0 -0
- package/dist/public/lib/samples/organ/C4.mp3 +0 -0
- package/dist/public/lib/samples/organ/C5.mp3 +0 -0
- package/dist/public/lib/samples/organ/C6.mp3 +0 -0
- package/dist/public/lib/samples/organ/G2.mp3 +0 -0
- package/dist/public/lib/samples/organ/G3.mp3 +0 -0
- package/dist/public/lib/samples/organ/G4.mp3 +0 -0
- package/dist/public/lib/samples/organ/G5.mp3 +0 -0
- package/dist/public/lib/samples/strings/C2.mp3 +0 -0
- package/dist/public/lib/samples/strings/C3.mp3 +0 -0
- package/dist/public/lib/samples/strings/C4.mp3 +0 -0
- package/dist/public/lib/samples/strings/C5.mp3 +0 -0
- package/dist/public/lib/samples/strings/C6.mp3 +0 -0
- package/dist/public/lib/samples/strings/G2.mp3 +0 -0
- package/dist/public/lib/samples/strings/G3.mp3 +0 -0
- package/dist/public/lib/samples/strings/G4.mp3 +0 -0
- package/dist/public/lib/samples/strings/G5.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/A3.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/A4.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/A5.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/C4.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/C5.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/E3.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/E4.mp3 +0 -0
- package/dist/public/lib/samples/trumpet/E5.mp3 +0 -0
- package/dist/public/spa.html +2 -0
- package/dist/public/views/app-grant.js +24 -4
- package/dist/public/views/portfolio.js +61 -6
- package/dist/public/views/profile/apps-tab.js +18 -1
- package/dist/public/views/profile/inbox-tab/ai-actions.js +86 -0
- package/dist/public/views/profile/inbox-tab/components.js +124 -9
- package/dist/public/views/profile/inbox-tab/helpers.js +26 -0
- package/dist/public/views/profile/inbox-tab/panels.js +18 -3
- package/dist/public/views/profile/inbox-tab/use-thread-ux.js +18 -1
- package/dist/public/views/profile/inbox-tab.js +46 -49
- package/dist/public/views/profile/landing-page.cards.js +7 -0
- package/dist/public/views/profile/landing-page.js +12 -1
- package/dist/public/views/profile/skills-tab.js +1 -1
- package/dist/public/views/public-workspace-viewer.js +1 -1
- package/dist/public/views/start.js +2 -1
- package/dist/scripts/build-sdk-libs.d.ts +16 -0
- package/dist/scripts/build-sdk-libs.d.ts.map +1 -0
- package/dist/scripts/build-sdk-libs.js +112 -0
- package/dist/scripts/build-sdk-libs.js.map +1 -0
- package/dist/src/cli/connect/auth.d.ts +15 -0
- package/dist/src/cli/connect/auth.d.ts.map +1 -1
- package/dist/src/cli/connect/auth.js +25 -6
- package/dist/src/cli/connect/auth.js.map +1 -1
- package/dist/src/cli/connect/keychain.d.ts +9 -0
- package/dist/src/cli/connect/keychain.d.ts.map +1 -1
- package/dist/src/cli/connect/keychain.js +21 -8
- package/dist/src/cli/connect/keychain.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/exchange.d.ts +18 -0
- package/dist/src/cli/connect/mcp/tools/exchange.d.ts.map +1 -0
- package/dist/src/cli/connect/mcp/tools/exchange.js +170 -0
- package/dist/src/cli/connect/mcp/tools/exchange.js.map +1 -0
- package/dist/src/cli/connect/mcp/tools/index.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/index.js +2 -0
- package/dist/src/cli/connect/mcp/tools/index.js.map +1 -1
- package/dist/src/cli/connect/tool-call-defs-exchange.d.ts +3 -0
- package/dist/src/cli/connect/tool-call-defs-exchange.d.ts.map +1 -0
- package/dist/src/cli/connect/tool-call-defs-exchange.js +146 -0
- package/dist/src/cli/connect/tool-call-defs-exchange.js.map +1 -0
- package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
- package/dist/src/cli/connect/tool-call.js +2 -0
- package/dist/src/cli/connect/tool-call.js.map +1 -1
- package/dist/src/commerce/session-service.d.ts +14 -0
- package/dist/src/commerce/session-service.d.ts.map +1 -1
- package/dist/src/commerce/session-service.js +1 -1
- package/dist/src/commerce/session-service.js.map +1 -1
- package/dist/src/data/app-templates/shells.d.ts +7 -3
- package/dist/src/data/app-templates/shells.d.ts.map +1 -1
- package/dist/src/data/app-templates/shells.js +10 -6
- package/dist/src/data/app-templates/shells.js.map +1 -1
- package/dist/src/data/appdev-pitfalls.d.ts +2 -0
- package/dist/src/data/appdev-pitfalls.d.ts.map +1 -1
- package/dist/src/data/appdev-pitfalls.js +24 -1
- package/dist/src/data/appdev-pitfalls.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +1565 -9
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/annotations.d.ts.map +1 -1
- package/dist/src/mcp/annotations.js +20 -0
- package/dist/src/mcp/annotations.js.map +1 -1
- package/dist/src/mcp/apps.d.ts.map +1 -1
- package/dist/src/mcp/apps.js +8 -4
- package/dist/src/mcp/apps.js.map +1 -1
- package/dist/src/mcp/catalog/definitions/exchange.d.ts +18 -0
- package/dist/src/mcp/catalog/definitions/exchange.d.ts.map +1 -0
- package/dist/src/mcp/catalog/definitions/exchange.js +182 -0
- package/dist/src/mcp/catalog/definitions/exchange.js.map +1 -0
- package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.js +2 -0
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
- package/dist/src/mcp/catalog/scopes.js +22 -0
- package/dist/src/mcp/catalog/scopes.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +14 -0
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/mcp/exchange-run.d.ts +5 -0
- package/dist/src/mcp/exchange-run.d.ts.map +1 -0
- package/dist/src/mcp/exchange-run.js +226 -0
- package/dist/src/mcp/exchange-run.js.map +1 -0
- package/dist/src/mcp/exchange.d.ts +5 -0
- package/dist/src/mcp/exchange.d.ts.map +1 -0
- package/dist/src/mcp/exchange.js +351 -0
- package/dist/src/mcp/exchange.js.map +1 -0
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +11 -0
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +6 -2
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/models/app-tool-schemas.d.ts +55 -1
- package/dist/src/models/app-tool-schemas.d.ts.map +1 -1
- package/dist/src/models/app-tool-schemas.js +20 -1
- package/dist/src/models/app-tool-schemas.js.map +1 -1
- package/dist/src/models/offer-schemas.d.ts +2 -2
- package/dist/src/models/schemas.d.ts +1 -0
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +3 -0
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/routes/agents/device-auth.d.ts +4 -0
- package/dist/src/routes/agents/device-auth.d.ts.map +1 -1
- package/dist/src/routes/agents/device-auth.js +19 -4
- package/dist/src/routes/agents/device-auth.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 +35 -2
- package/dist/src/routes/app-grants.js.map +1 -1
- package/dist/src/routes/apps/fork-manage.d.ts.map +1 -1
- package/dist/src/routes/apps/fork-manage.js +24 -1
- package/dist/src/routes/apps/fork-manage.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 +27 -1
- package/dist/src/routes/apps/publish.js.map +1 -1
- package/dist/src/routes/apps-cost.d.ts +25 -0
- package/dist/src/routes/apps-cost.d.ts.map +1 -0
- package/dist/src/routes/apps-cost.js +107 -0
- package/dist/src/routes/apps-cost.js.map +1 -0
- package/dist/src/routes/exchange-market.d.ts +28 -0
- package/dist/src/routes/exchange-market.d.ts.map +1 -0
- package/dist/src/routes/exchange-market.js +454 -0
- package/dist/src/routes/exchange-market.js.map +1 -0
- package/dist/src/routes/exchange.d.ts +33 -0
- package/dist/src/routes/exchange.d.ts.map +1 -0
- package/dist/src/routes/exchange.js +395 -0
- package/dist/src/routes/exchange.js.map +1 -0
- package/dist/src/routes/extensions/entitlement-gate.d.ts +33 -0
- package/dist/src/routes/extensions/entitlement-gate.d.ts.map +1 -0
- package/dist/src/routes/extensions/entitlement-gate.js +143 -0
- package/dist/src/routes/extensions/entitlement-gate.js.map +1 -0
- package/dist/src/routes/extensions/manifest.d.ts +9 -0
- package/dist/src/routes/extensions/manifest.d.ts.map +1 -1
- package/dist/src/routes/extensions/manifest.js +33 -1
- package/dist/src/routes/extensions/manifest.js.map +1 -1
- package/dist/src/routes/extensions/paywall.d.ts.map +1 -1
- package/dist/src/routes/extensions/paywall.js +9 -0
- package/dist/src/routes/extensions/paywall.js.map +1 -1
- package/dist/src/routes/federation-sync/messaging.d.ts.map +1 -1
- package/dist/src/routes/federation-sync/messaging.js +3 -1
- package/dist/src/routes/federation-sync/messaging.js.map +1 -1
- package/dist/src/routes/ghii/recovery.d.ts +2 -0
- package/dist/src/routes/ghii/recovery.d.ts.map +1 -1
- package/dist/src/routes/ghii/recovery.js +7 -0
- package/dist/src/routes/ghii/recovery.js.map +1 -1
- package/dist/src/routes/ghii/register-login.d.ts +4 -0
- package/dist/src/routes/ghii/register-login.d.ts.map +1 -1
- package/dist/src/routes/ghii/register-login.js +45 -22
- package/dist/src/routes/ghii/register-login.js.map +1 -1
- package/dist/src/routes/ghii/web-verify.d.ts +5 -0
- package/dist/src/routes/ghii/web-verify.d.ts.map +1 -1
- package/dist/src/routes/ghii/web-verify.js +24 -3
- package/dist/src/routes/ghii/web-verify.js.map +1 -1
- package/dist/src/routes/libs/sdk-serve.d.ts +11 -0
- package/dist/src/routes/libs/sdk-serve.d.ts.map +1 -0
- package/dist/src/routes/libs/sdk-serve.js +53 -0
- package/dist/src/routes/libs/sdk-serve.js.map +1 -0
- package/dist/src/routes/libs.d.ts +7 -1
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +65 -146
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/messages.d.ts +3 -0
- package/dist/src/routes/messages.d.ts.map +1 -1
- package/dist/src/routes/messages.js +9 -0
- package/dist/src/routes/messages.js.map +1 -1
- package/dist/src/routes/notifications.d.ts +6 -3
- package/dist/src/routes/notifications.d.ts.map +1 -1
- package/dist/src/routes/notifications.js +32 -3
- package/dist/src/routes/notifications.js.map +1 -1
- package/dist/src/routes/organisms/workspace-access.d.ts.map +1 -1
- package/dist/src/routes/organisms/workspace-access.js +38 -20
- package/dist/src/routes/organisms/workspace-access.js.map +1 -1
- package/dist/src/routes/unfurl.d.ts +27 -0
- package/dist/src/routes/unfurl.d.ts.map +1 -0
- package/dist/src/routes/unfurl.js +180 -0
- package/dist/src/routes/unfurl.js.map +1 -0
- package/dist/src/routes/upload.d.ts +2 -0
- package/dist/src/routes/upload.d.ts.map +1 -1
- package/dist/src/routes/upload.js +7 -0
- package/dist/src/routes/upload.js.map +1 -1
- package/dist/src/routes/webmcp.d.ts +3 -0
- package/dist/src/routes/webmcp.d.ts.map +1 -1
- package/dist/src/routes/webmcp.js +55 -0
- package/dist/src/routes/webmcp.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts +1 -0
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +8 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/agent-face.d.ts +10 -2
- package/dist/src/services/agent-face.d.ts.map +1 -1
- package/dist/src/services/agent-face.js +10 -4
- package/dist/src/services/agent-face.js.map +1 -1
- package/dist/src/services/app-tool-interfaces.d.ts +37 -0
- package/dist/src/services/app-tool-interfaces.d.ts.map +1 -0
- package/dist/src/services/app-tool-interfaces.js +101 -0
- package/dist/src/services/app-tool-interfaces.js.map +1 -0
- package/dist/src/services/build-app-prompt.d.ts +3 -0
- package/dist/src/services/build-app-prompt.d.ts.map +1 -1
- package/dist/src/services/build-app-prompt.js +12 -1
- package/dist/src/services/build-app-prompt.js.map +1 -1
- package/dist/src/services/contacts.d.ts +25 -1
- package/dist/src/services/contacts.d.ts.map +1 -1
- package/dist/src/services/contacts.js +30 -0
- package/dist/src/services/contacts.js.map +1 -1
- package/dist/src/services/entitlement-money.d.ts +55 -0
- package/dist/src/services/entitlement-money.d.ts.map +1 -0
- package/dist/src/services/entitlement-money.js +68 -0
- package/dist/src/services/entitlement-money.js.map +1 -0
- package/dist/src/services/exchange-market.d.ts +247 -0
- package/dist/src/services/exchange-market.d.ts.map +1 -0
- package/dist/src/services/exchange-market.js +254 -0
- package/dist/src/services/exchange-market.js.map +1 -0
- package/dist/src/services/exchange-proposals.d.ts +47 -0
- package/dist/src/services/exchange-proposals.d.ts.map +1 -0
- package/dist/src/services/exchange-proposals.js +71 -0
- package/dist/src/services/exchange-proposals.js.map +1 -0
- package/dist/src/services/exchange-work.d.ts +30 -0
- package/dist/src/services/exchange-work.d.ts.map +1 -0
- package/dist/src/services/exchange-work.js +43 -0
- package/dist/src/services/exchange-work.js.map +1 -0
- package/dist/src/services/message-send.d.ts.map +1 -1
- package/dist/src/services/message-send.js +4 -1
- package/dist/src/services/message-send.js.map +1 -1
- package/dist/src/services/metered-entitlements.d.ts +203 -0
- package/dist/src/services/metered-entitlements.d.ts.map +1 -0
- package/dist/src/services/metered-entitlements.js +264 -0
- package/dist/src/services/metered-entitlements.js.map +1 -0
- package/dist/src/services/notify.d.ts +7 -0
- package/dist/src/services/notify.d.ts.map +1 -1
- package/dist/src/services/notify.js +35 -0
- package/dist/src/services/notify.js.map +1 -1
- package/dist/src/services/owner-memory.d.ts +16 -0
- package/dist/src/services/owner-memory.d.ts.map +1 -1
- package/dist/src/services/owner-memory.js +23 -0
- package/dist/src/services/owner-memory.js.map +1 -1
- package/dist/src/services/owner-provisioning.d.ts +5 -0
- package/dist/src/services/owner-provisioning.d.ts.map +1 -1
- package/dist/src/services/owner-provisioning.js +20 -2
- package/dist/src/services/owner-provisioning.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/apps.d.ts +1 -0
- package/dist/src/storage/providers/postgres-kysely/methods/apps.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/apps.js +2 -0
- package/dist/src/storage/providers/postgres-kysely/methods/apps.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/identity.d.ts +1 -0
- package/dist/src/storage/providers/postgres-kysely/methods/identity.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/identity.js +4 -0
- package/dist/src/storage/providers/postgres-kysely/methods/identity.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/migrations/0011_unique_verified_email.sql +25 -0
- package/dist/src/storage/providers/sqlite/methods/apps.d.ts +1 -0
- package/dist/src/storage/providers/sqlite/methods/apps.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/apps.js +2 -0
- package/dist/src/storage/providers/sqlite/methods/apps.js.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/identity-nodes.d.ts +1 -0
- package/dist/src/storage/providers/sqlite/methods/identity-nodes.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/identity-nodes.js +4 -0
- package/dist/src/storage/providers/sqlite/methods/identity-nodes.js.map +1 -1
- 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.repository.d.ts +0 -0
- package/dist/src/storage/repositories/app.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/identity.repository.d.ts +6 -0
- package/dist/src/storage/repositories/identity.repository.d.ts.map +1 -1
- package/dist/src/storage/types/apps.d.ts +1 -0
- package/dist/src/storage/types/apps.d.ts.map +1 -1
- package/dist/src/storage/types/organisms-federation.d.ts +21 -0
- package/dist/src/storage/types/organisms-federation.d.ts.map +1 -1
- package/dist/src/utils/app-mobile-lint.d.ts +17 -0
- package/dist/src/utils/app-mobile-lint.d.ts.map +1 -0
- package/dist/src/utils/app-mobile-lint.js +40 -0
- package/dist/src/utils/app-mobile-lint.js.map +1 -0
- package/dist/static/app-catalog/_template.html +12 -77
- package/dist/static/app-catalog/js/apps-io.js +51 -165
- package/dist/static/app-catalog/js/cost.js +100 -0
- package/dist/static/app-catalog/js/db.js +23 -93
- package/dist/static/app-catalog/js/detail.js +158 -24
- package/dist/static/app-catalog/js/favorites.js +71 -0
- package/dist/static/app-catalog/js/i18n-data.js +90 -16
- package/dist/static/app-catalog/js/main.js +23 -103
- package/dist/static/app-catalog/js/migrate.js +88 -0
- package/dist/static/app-catalog/js/monetize.js +1 -1
- package/dist/static/app-catalog/js/promote.js +70 -0
- package/dist/static/app-catalog/js/render.js +26 -139
- package/dist/static/app-catalog/js/server-io.js +126 -223
- package/dist/static/app-catalog/js/settings.js +11 -204
- package/dist/static/app-catalog/styles/app-catalog.css +40 -0
- package/dist/static/app-catalog.html +790 -912
- package/dist/static/sdk-libs/_core/config.js +52 -0
- package/dist/static/sdk-libs/_core/http.js +52 -0
- package/dist/static/sdk-libs/_core/namespace.js +33 -0
- package/dist/static/sdk-libs/_core/sdk-globals.d.ts +50 -0
- package/dist/static/sdk-libs/_core/session.js +66 -0
- package/dist/static/sdk-libs/agentface/index.js +102 -0
- package/dist/static/sdk-libs/agents/index.js +267 -0
- package/dist/static/sdk-libs/ai/index.js +153 -0
- package/dist/static/sdk-libs/audio/core.js +111 -0
- package/dist/static/sdk-libs/audio/index.js +380 -0
- package/dist/static/sdk-libs/audio/instruments.js +369 -0
- package/dist/static/sdk-libs/auth/config.js +46 -0
- package/dist/static/sdk-libs/auth/crypto.js +166 -0
- package/dist/static/sdk-libs/auth/events.js +27 -0
- package/dist/static/sdk-libs/auth/i18n.js +64 -0
- package/dist/static/sdk-libs/auth/index.js +37 -0
- package/dist/static/sdk-libs/auth/modal.js +545 -0
- package/dist/static/sdk-libs/auth/pill.js +167 -0
- package/dist/static/sdk-libs/auth/session.js +792 -0
- package/dist/static/sdk-libs/auth/signup.js +232 -0
- package/dist/static/sdk-libs/auth/theme.js +96 -0
- package/dist/static/sdk-libs/capabilities/index.js +179 -0
- package/dist/static/sdk-libs/commerce/index.js +243 -0
- package/dist/static/sdk-libs/data/index.js +188 -0
- package/dist/static/sdk-libs/dist/aimeat-agentface.js +91 -0
- package/dist/static/sdk-libs/dist/aimeat-agents.js +315 -0
- package/dist/static/sdk-libs/dist/aimeat-ai.js +168 -0
- package/dist/static/sdk-libs/dist/aimeat-audio.js +979 -0
- package/dist/static/sdk-libs/dist/aimeat-auth.js +1831 -0
- package/dist/static/sdk-libs/dist/aimeat-capabilities.js +177 -0
- package/dist/static/sdk-libs/dist/aimeat-commerce.js +265 -0
- package/dist/static/sdk-libs/dist/aimeat-data.js +205 -0
- package/dist/static/sdk-libs/dist/aimeat-editor.js +299 -0
- package/dist/static/sdk-libs/dist/aimeat-header.js +270 -0
- package/dist/static/sdk-libs/dist/aimeat-intake.js +94 -0
- package/dist/static/sdk-libs/dist/aimeat-live.js +265 -0
- package/dist/static/sdk-libs/dist/aimeat-markdown.js +602 -0
- package/dist/static/sdk-libs/dist/aimeat-organism.js +394 -0
- package/dist/static/sdk-libs/dist/aimeat-portfolio-standalone.js +90 -0
- package/dist/static/sdk-libs/dist/aimeat-social.js +157 -0
- package/dist/static/sdk-libs/dist/aimeat-speech.js +300 -0
- package/dist/static/sdk-libs/dist/aimeat-storage.js +210 -0
- package/dist/static/sdk-libs/dist/aimeat-tunnel.js +383 -0
- package/dist/static/sdk-libs/dist/aimeat-wallet.js +118 -0
- package/dist/static/sdk-libs/dist/aimeat-webmcp.js +187 -0
- package/dist/static/sdk-libs/dist/aimeat-work.js +213 -0
- package/dist/static/sdk-libs/dist/aimeat-workflows.js +147 -0
- package/dist/static/sdk-libs/editor/index.js +229 -0
- package/dist/static/sdk-libs/header/index.js +249 -0
- package/dist/static/sdk-libs/intake/index.js +75 -0
- package/dist/static/sdk-libs/live/index.js +156 -0
- package/dist/static/sdk-libs/markdown/index.js +522 -0
- package/dist/static/sdk-libs/organism/index.js +381 -0
- package/dist/static/sdk-libs/portfolio-standalone/index.js +78 -0
- package/dist/static/sdk-libs/social/index.js +140 -0
- package/dist/static/sdk-libs/speech/index.js +321 -0
- package/dist/static/sdk-libs/storage/index.js +189 -0
- package/dist/static/sdk-libs/tunnel/index.js +428 -0
- package/dist/static/sdk-libs/wallet/index.js +115 -0
- package/dist/static/sdk-libs/webmcp/index.js +185 -0
- package/dist/static/sdk-libs/work/index.js +191 -0
- package/dist/static/sdk-libs/workflows/index.js +113 -0
- package/package.json +5 -2
package/dist/locales/en.json
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
"inbox.quoteYou": "You",
|
|
15
15
|
"inbox.replyingTo": "Replying to",
|
|
16
16
|
"inbox.quoteCancel": "Cancel the quoted reply",
|
|
17
|
+
"inbox.linkPreview.label": "Previews",
|
|
18
|
+
"inbox.linkPreview.hide": "Hide this preview",
|
|
19
|
+
"inbox.linkPreview.hideAll": "Hide link previews",
|
|
20
|
+
"inbox.linkPreview.showAll": "Show link previews",
|
|
21
|
+
"inbox.thread.showAll": "Show all messages",
|
|
22
|
+
"inbox.thread.showRecent": "Show last 50",
|
|
17
23
|
"inbox.attachmentRemove": "Remove attachment",
|
|
18
24
|
"inbox.contactAgentSuffix": "(your agent)",
|
|
19
25
|
"inbox.messageThis": "Send a message",
|
|
@@ -49,6 +55,23 @@
|
|
|
49
55
|
"inbox.schedError": "Could not schedule.",
|
|
50
56
|
"inbox.parkToNotebook": "Add to notebook for later",
|
|
51
57
|
"inbox.parkedToNotebook": "Added to your notebook.",
|
|
58
|
+
"inbox.notebook.toNotebook": "Summarize to Notebook",
|
|
59
|
+
"inbox.notebook.toNotebookShort": "Notebook",
|
|
60
|
+
"inbox.notebook.modeAi": "AI summary (my key)",
|
|
61
|
+
"inbox.notebook.modeCopy": "Copy prompt",
|
|
62
|
+
"inbox.notebook.modeRaw": "Raw",
|
|
63
|
+
"inbox.notebook.hintAi": "Summarize this whole conversation with your own OpenRouter key, review it, then send it to the Notebook to file into a workspace.",
|
|
64
|
+
"inbox.notebook.hintCopy": "Copy this prompt into your own AI chat, then paste the summary back below and send it to the Notebook.",
|
|
65
|
+
"inbox.notebook.hintRaw": "Send the whole chain (text + images) to the Notebook as-is, and refine it there with classify / distribute.",
|
|
66
|
+
"inbox.notebook.genSummary": "Summarize with AI",
|
|
67
|
+
"inbox.notebook.summarizing": "Summarizing…",
|
|
68
|
+
"inbox.notebook.regen": "Regenerate",
|
|
69
|
+
"inbox.notebook.park": "Send to Notebook",
|
|
70
|
+
"inbox.notebook.parkRaw": "Send to Notebook",
|
|
71
|
+
"inbox.notebook.parked": "Sent to the Notebook.",
|
|
72
|
+
"inbox.notebook.pasteHint": "Paste the AI's summary here:",
|
|
73
|
+
"inbox.notebook.pastePh": "Paste the summary the AI gave you…",
|
|
74
|
+
"inbox.notebook.summaryEmpty": "The model returned nothing — try again.",
|
|
52
75
|
"inbox.viaAgent": "via",
|
|
53
76
|
"inbox.sentByAgent": "Sent by your agent",
|
|
54
77
|
"inbox.viaAgentReadonly": "Sent by your agent {agent} — view only.",
|
|
@@ -148,6 +171,8 @@
|
|
|
148
171
|
"admin.messages.detail": "Detail",
|
|
149
172
|
"admin.messages.none": "No deliveries yet.",
|
|
150
173
|
"inbox.attach": "Attach a file",
|
|
174
|
+
"inbox.expand": "Enlarge the editor",
|
|
175
|
+
"inbox.collapse": "Shrink the editor",
|
|
151
176
|
"inbox.today": "Today",
|
|
152
177
|
"inbox.yesterday": "Yesterday",
|
|
153
178
|
"inbox.youPrefix": "You:",
|
|
@@ -579,7 +604,10 @@
|
|
|
579
604
|
"loginCta": "Log in",
|
|
580
605
|
"errorTitle": "Cannot grant access",
|
|
581
606
|
"missing": "No authorization request.",
|
|
582
|
-
"expired": "This request has expired."
|
|
607
|
+
"expired": "This request has expired.",
|
|
608
|
+
"ownBadge": "Your app",
|
|
609
|
+
"ownIntro": "This is your own app. It still uses a scoped, revocable key — never your login session.",
|
|
610
|
+
"newScope": "new"
|
|
583
611
|
},
|
|
584
612
|
"common": {
|
|
585
613
|
"cancel": "Cancel",
|
|
@@ -607,6 +635,7 @@
|
|
|
607
635
|
"notif": {
|
|
608
636
|
"title": "Notifications",
|
|
609
637
|
"empty": "No notifications yet",
|
|
638
|
+
"clearAll": "Clear all",
|
|
610
639
|
"action": {
|
|
611
640
|
"reply": "Reply",
|
|
612
641
|
"approve": "Approve",
|
|
@@ -2888,6 +2917,8 @@
|
|
|
2888
2917
|
"operatorHiddenHint": "An operator has hidden this app from public view. Only you can still see it. Contact the operator to appeal.",
|
|
2889
2918
|
"agentBadge": "Ships an agent",
|
|
2890
2919
|
"agentBadgeHint": "This app ships its own agent - deploy it onto your fleet below",
|
|
2920
|
+
"promotedBadge": "Promoted",
|
|
2921
|
+
"promotedBadgeHint": "Promoted on your public profile — manage the pitch in the app catalog",
|
|
2891
2922
|
"agentsLabel": "Bundled agents",
|
|
2892
2923
|
"agentDeploy": "Deploy to my fleet",
|
|
2893
2924
|
"agentUndeploy": "Undeploy",
|
|
@@ -4502,6 +4533,8 @@
|
|
|
4502
4533
|
"completeAccountTitle": "One last step",
|
|
4503
4534
|
"completeAccountDesc": "Add an email to finish setting up your account. We’ll send a verification code to confirm it.",
|
|
4504
4535
|
"completeAccountDescResend": "Confirm your email to finish signing in. We’ll send a verification code — edit the address if it’s wrong.",
|
|
4536
|
+
"registerEmailTitle": "Add your email",
|
|
4537
|
+
"registerEmailDesc": "Enter your email to create your account. We’ll send a verification code to confirm it.",
|
|
4505
4538
|
"sendVerificationCode": "Send Verification Code",
|
|
4506
4539
|
"enterCodeTitle": "Enter Verification Code",
|
|
4507
4540
|
"enterCodeDesc": "We sent a 6-digit code to your email. Enter it below to finish and sign in.",
|
|
@@ -6447,6 +6480,7 @@
|
|
|
6447
6480
|
"notFound": "Portfolio not found",
|
|
6448
6481
|
"notFoundDesc": "This user hasn't published a portfolio yet.",
|
|
6449
6482
|
"loginToSeeMore": "Log in to see more content",
|
|
6483
|
+
"promoted": "Promoted apps",
|
|
6450
6484
|
"backToPortal": "Home",
|
|
6451
6485
|
"fullscreen": "Fullscreen",
|
|
6452
6486
|
"standalone": "Own address"
|
package/dist/locales/fi.json
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
"inbox.quoteYou": "Sinä",
|
|
15
15
|
"inbox.replyingTo": "Vastataan:",
|
|
16
16
|
"inbox.quoteCancel": "Peru lainattu vastaus",
|
|
17
|
+
"inbox.linkPreview.label": "Esikatselut",
|
|
18
|
+
"inbox.linkPreview.hide": "Piilota tämä esikatselu",
|
|
19
|
+
"inbox.linkPreview.hideAll": "Piilota linkkien esikatselut",
|
|
20
|
+
"inbox.linkPreview.showAll": "Näytä linkkien esikatselut",
|
|
21
|
+
"inbox.thread.showAll": "Näytä kaikki viestit",
|
|
22
|
+
"inbox.thread.showRecent": "Näytä viimeiset 50",
|
|
17
23
|
"inbox.attachmentRemove": "Poista liite",
|
|
18
24
|
"inbox.contactAgentSuffix": "(agenttisi)",
|
|
19
25
|
"inbox.messageThis": "Lähetä viesti",
|
|
@@ -49,6 +55,23 @@
|
|
|
49
55
|
"inbox.schedError": "Ajastus epäonnistui.",
|
|
50
56
|
"inbox.parkToNotebook": "Lisää muistikirjaan myöhempää varten",
|
|
51
57
|
"inbox.parkedToNotebook": "Lisätty muistikirjaasi.",
|
|
58
|
+
"inbox.notebook.toNotebook": "Yhteenveto Notebookiin",
|
|
59
|
+
"inbox.notebook.toNotebookShort": "Notebook",
|
|
60
|
+
"inbox.notebook.modeAi": "AI-yhteenveto (oma avain)",
|
|
61
|
+
"inbox.notebook.modeCopy": "Kopioi prompti",
|
|
62
|
+
"inbox.notebook.modeRaw": "Raakana",
|
|
63
|
+
"inbox.notebook.hintAi": "Tiivistä koko tämä keskustelu omalla OpenRouter-avaimellasi, tarkista se, ja vie se Notebookiin arkistoitavaksi workspaceen.",
|
|
64
|
+
"inbox.notebook.hintCopy": "Kopioi tämä prompti omaan AI-chattiisi, liitä yhteenveto alle ja vie se Notebookiin.",
|
|
65
|
+
"inbox.notebook.hintRaw": "Vie koko ketju (teksti + kuvat) Notebookiin sellaisenaan, ja jalosta se siellä classify / distribute -toiminnoilla.",
|
|
66
|
+
"inbox.notebook.genSummary": "Tee yhteenveto AI:lla",
|
|
67
|
+
"inbox.notebook.summarizing": "Tiivistetään…",
|
|
68
|
+
"inbox.notebook.regen": "Uudelleen",
|
|
69
|
+
"inbox.notebook.park": "Vie Notebookiin",
|
|
70
|
+
"inbox.notebook.parkRaw": "Vie Notebookiin",
|
|
71
|
+
"inbox.notebook.parked": "Viety Notebookiin.",
|
|
72
|
+
"inbox.notebook.pasteHint": "Liitä AI:n yhteenveto tähän:",
|
|
73
|
+
"inbox.notebook.pastePh": "Liitä AI:n antama yhteenveto…",
|
|
74
|
+
"inbox.notebook.summaryEmpty": "Malli ei palauttanut mitään — yritä uudelleen.",
|
|
52
75
|
"inbox.viaAgent": "via",
|
|
53
76
|
"inbox.sentByAgent": "Lähettänyt agenttisi",
|
|
54
77
|
"inbox.viaAgentReadonly": "Lähettänyt agenttisi {agent} — vain katselu.",
|
|
@@ -148,6 +171,8 @@
|
|
|
148
171
|
"admin.messages.detail": "Lisätieto",
|
|
149
172
|
"admin.messages.none": "Ei toimituksia vielä.",
|
|
150
173
|
"inbox.attach": "Liitä tiedosto",
|
|
174
|
+
"inbox.expand": "Suurenna editori",
|
|
175
|
+
"inbox.collapse": "Pienennä editori",
|
|
151
176
|
"inbox.today": "Tänään",
|
|
152
177
|
"inbox.yesterday": "Eilen",
|
|
153
178
|
"inbox.youPrefix": "Sinä:",
|
|
@@ -579,7 +604,10 @@
|
|
|
579
604
|
"loginCta": "Kirjaudu",
|
|
580
605
|
"errorTitle": "Pääsyä ei voida myöntää",
|
|
581
606
|
"missing": "Ei valtuutuspyyntöä.",
|
|
582
|
-
"expired": "Tämä pyyntö on vanhentunut."
|
|
607
|
+
"expired": "Tämä pyyntö on vanhentunut.",
|
|
608
|
+
"ownBadge": "Oma appisi",
|
|
609
|
+
"ownIntro": "Tämä on oma appisi. Sekin käyttää rajattua, peruttavaa avainta — ei koskaan kirjautumisistuntoasi.",
|
|
610
|
+
"newScope": "uusi"
|
|
583
611
|
},
|
|
584
612
|
"common": {
|
|
585
613
|
"cancel": "Peruuta",
|
|
@@ -607,6 +635,7 @@
|
|
|
607
635
|
"notif": {
|
|
608
636
|
"title": "Ilmoitukset",
|
|
609
637
|
"empty": "Ei ilmoituksia vielä",
|
|
638
|
+
"clearAll": "Tyhjennä kaikki",
|
|
610
639
|
"action": {
|
|
611
640
|
"reply": "Vastaa",
|
|
612
641
|
"approve": "Hyväksy",
|
|
@@ -2888,6 +2917,8 @@
|
|
|
2888
2917
|
"operatorHiddenHint": "Operaattori on piilottanut tämän sovelluksen julkisesta näkymästä. Vain sinä näet sen edelleen. Ota yhteyttä operaattoriin valittaaksesi.",
|
|
2889
2918
|
"agentBadge": "Sisältää agentin",
|
|
2890
2919
|
"agentBadgeHint": "Tämä sovellus tuo mukanaan oman agentin - ota se käyttöön omaan laivueeseesi alta",
|
|
2920
|
+
"promotedBadge": "Nostettu",
|
|
2921
|
+
"promotedBadgeHint": "Nostettu julkiseen profiiliisi — hallitse myyntipuhetta sovelluskatalogissa",
|
|
2891
2922
|
"agentsLabel": "Mukana tulevat agentit",
|
|
2892
2923
|
"agentDeploy": "Ota käyttöön laivueeseeni",
|
|
2893
2924
|
"agentUndeploy": "Poista käytöstä",
|
|
@@ -4502,6 +4533,8 @@
|
|
|
4502
4533
|
"completeAccountTitle": "Vielä yksi vaihe",
|
|
4503
4534
|
"completeAccountDesc": "Lisää sähköpostiosoite viimeistelläksesi tilisi. Lähetämme vahvistuskoodin sen vahvistamiseksi.",
|
|
4504
4535
|
"completeAccountDescResend": "Vahvista sähköpostiosoitteesi kirjautuaksesi sisään. Lähetämme vahvistuskoodin — muokkaa osoitetta, jos se on väärin.",
|
|
4536
|
+
"registerEmailTitle": "Lisää sähköpostiosoitteesi",
|
|
4537
|
+
"registerEmailDesc": "Anna sähköpostiosoitteesi luodaksesi tilisi. Lähetämme vahvistuskoodin sen vahvistamiseksi.",
|
|
4505
4538
|
"sendVerificationCode": "Lähetä vahvistuskoodi",
|
|
4506
4539
|
"enterCodeTitle": "Syötä vahvistuskoodi",
|
|
4507
4540
|
"enterCodeDesc": "Lähetimme 6-numeroisen koodin sähköpostiisi. Syötä se alle viimeistelläksesi ja kirjautuaksesi sisään.",
|
|
@@ -6447,6 +6480,7 @@
|
|
|
6447
6480
|
"notFound": "Portfoliota ei löytynyt",
|
|
6448
6481
|
"notFoundDesc": "Tämä käyttäjä ei ole julkaissut portfoliota.",
|
|
6449
6482
|
"loginToSeeMore": "Kirjaudu sisään nähdäksesi lisää",
|
|
6483
|
+
"promoted": "Nostetut sovellukset",
|
|
6450
6484
|
"backToPortal": "Koti",
|
|
6451
6485
|
"fullscreen": "Koko näyttö",
|
|
6452
6486
|
"standalone": "Oma osoite"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file public/components/LinkPreview.js
|
|
3
|
+
* @description Rich link-preview (unfurl) cards for a pasted URL. `LinkPreview` fetches one URL's
|
|
4
|
+
* OpenGraph metadata from the node (`/js/services/unfurl.js`) and renders a card (site · title ·
|
|
5
|
+
* description · thumbnail); the thumbnail is pulled through the node's image proxy into a `blob:`
|
|
6
|
+
* URL so the SPA's remote-image CSP is satisfied. `MessageLinkPreviews` extracts the URLs from a
|
|
7
|
+
* message body and renders a card per link, each with a ✕ that hides it (persisted in localStorage,
|
|
8
|
+
* so a dismissed card stays hidden). `extractUrls` is exported for reuse/testing.
|
|
9
|
+
* @structure extractUrls(text,max) · LinkPreview({url,onDismiss}) · MessageLinkPreviews({msg})
|
|
10
|
+
* @usage html`<${MessageLinkPreviews} msg=${msg} />`
|
|
11
|
+
* @version-history
|
|
12
|
+
* v1.0.0 — 2026-07-21 — Initial link-preview cards (inbox message threads).
|
|
13
|
+
*/
|
|
14
|
+
import { h } from 'preact';
|
|
15
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
16
|
+
import htm from 'htm';
|
|
17
|
+
const html = htm.bind(h);
|
|
18
|
+
import { t } from '/js/i18n.js';
|
|
19
|
+
import { escHtml } from '/js/utils.js';
|
|
20
|
+
import * as unfurl from '/js/services/unfurl.js';
|
|
21
|
+
|
|
22
|
+
/** Pull up to `max` distinct bare http(s) URLs out of a message body, trimming trailing sentence
|
|
23
|
+
* punctuation (mirrors the Markdown autolink trimming so the card links the same target the text does). */
|
|
24
|
+
export function extractUrls(text, max = 3) {
|
|
25
|
+
const urls = [];
|
|
26
|
+
if (typeof text !== 'string') return urls;
|
|
27
|
+
const re = /(?:^|[\s(<[])(https?:\/\/[^\s<>()[\]]+)/gi;
|
|
28
|
+
let m;
|
|
29
|
+
while ((m = re.exec(text)) !== null && urls.length < max) {
|
|
30
|
+
let u = m[1].replace(/[.,;:!?'"]+$/, '');
|
|
31
|
+
if (u && !urls.includes(u)) urls.push(u);
|
|
32
|
+
}
|
|
33
|
+
return urls;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Dismissed cards persist across reloads: a Set of `${messageId}::${url}` keys in localStorage (capped).
|
|
37
|
+
const DISMISS_KEY = 'aimeat.inbox.linkcard.dismissed';
|
|
38
|
+
function loadDismissed() {
|
|
39
|
+
try { return new Set(JSON.parse(localStorage.getItem(DISMISS_KEY) || '[]')); } catch { return new Set(); }
|
|
40
|
+
}
|
|
41
|
+
function saveDismissed(set) {
|
|
42
|
+
try { localStorage.setItem(DISMISS_KEY, JSON.stringify([...set].slice(-500))); } catch { /* quota */ }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** One preview card. Renders nothing while loading, or if the link has no usable metadata. */
|
|
46
|
+
export function LinkPreview({ url, onDismiss }) {
|
|
47
|
+
const [meta, setMeta] = useState(undefined); // undefined = loading, null = nothing to show
|
|
48
|
+
const [imgUrl, setImgUrl] = useState(null);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
let alive = true;
|
|
52
|
+
let objectUrl = null;
|
|
53
|
+
setMeta(undefined); setImgUrl(null);
|
|
54
|
+
(async () => {
|
|
55
|
+
const m = await unfurl.fetchPreview(url);
|
|
56
|
+
if (!alive) return;
|
|
57
|
+
// A card with no title/description/image isn't worth showing (avoid an empty box on a bare domain).
|
|
58
|
+
if (!m || (!m.title && !m.description && !m.image)) { setMeta(null); return; }
|
|
59
|
+
setMeta(m);
|
|
60
|
+
if (m.image) {
|
|
61
|
+
objectUrl = await unfurl.fetchPreviewImageUrl(m.image);
|
|
62
|
+
if (!alive) { if (objectUrl) URL.revokeObjectURL(objectUrl); return; }
|
|
63
|
+
if (objectUrl) setImgUrl(objectUrl);
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
return () => { alive = false; if (objectUrl) URL.revokeObjectURL(objectUrl); };
|
|
67
|
+
}, [url]);
|
|
68
|
+
|
|
69
|
+
if (meta === undefined || meta === null) return null;
|
|
70
|
+
return html`
|
|
71
|
+
<div class="inbox-linkcard">
|
|
72
|
+
<a class="inbox-linkcard-main" href=${meta.resolvedUrl || url} target="_blank" rel="noopener noreferrer nofollow">
|
|
73
|
+
${imgUrl ? html`<span class="inbox-linkcard-thumb"><img src=${imgUrl} alt="" loading="lazy" /></span>` : null}
|
|
74
|
+
<span class="inbox-linkcard-text">
|
|
75
|
+
${meta.siteName ? html`<span class="inbox-linkcard-site">${escHtml(meta.siteName)}</span>` : null}
|
|
76
|
+
${meta.title ? html`<span class="inbox-linkcard-title">${escHtml(meta.title)}</span>` : null}
|
|
77
|
+
${meta.description ? html`<span class="inbox-linkcard-desc">${escHtml(meta.description)}</span>` : null}
|
|
78
|
+
</span>
|
|
79
|
+
</a>
|
|
80
|
+
<button class="inbox-linkcard-x" title=${t('inbox.linkPreview.hide')}
|
|
81
|
+
onClick=${() => onDismiss?.(url)}>✕</button>
|
|
82
|
+
</div>`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** All preview cards for one message: a card per non-dismissed URL in its body. */
|
|
86
|
+
export function MessageLinkPreviews({ msg }) {
|
|
87
|
+
const urls = extractUrls(msg?.body || '');
|
|
88
|
+
const [dismissed, setDismissed] = useState(loadDismissed);
|
|
89
|
+
if (urls.length === 0) return null;
|
|
90
|
+
const visible = urls.filter(u => !dismissed.has(`${msg.id}::${u}`));
|
|
91
|
+
if (visible.length === 0) return null;
|
|
92
|
+
const dismiss = (u) => {
|
|
93
|
+
const next = new Set(dismissed);
|
|
94
|
+
next.add(`${msg.id}::${u}`);
|
|
95
|
+
setDismissed(next);
|
|
96
|
+
saveDismissed(next);
|
|
97
|
+
};
|
|
98
|
+
return html`<div class="inbox-linkcards">
|
|
99
|
+
${visible.map(u => html`<${LinkPreview} key=${u} url=${u} onDismiss=${dismiss} />`)}
|
|
100
|
+
</div>`;
|
|
101
|
+
}
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* same deep-link translation as bell clicks (supports both '#hash' and '?tab=' forms).
|
|
18
18
|
* v1.2.0 — 2026-07-18 — Inline notification actions: reply box (POST /v1/messages) + api buttons
|
|
19
19
|
* (approve/deny/accept/decline/reject) that call the action endpoint with the owner's session.
|
|
20
|
+
* v1.3.0 — 2026-07-19 — "Clear all" in the dropdown head (DELETE /v1/notifications) — deletes every
|
|
21
|
+
* notification for the owner (not just mark-read), optimistic empty + reconcile.
|
|
20
22
|
*/
|
|
21
23
|
import { h } from 'preact';
|
|
22
24
|
import { useState, useEffect, useRef, useCallback } from 'preact/hooks';
|
|
@@ -99,11 +101,23 @@ export function NotificationBell({ t, onNavigate }) {
|
|
|
99
101
|
const [results, setResults] = useState({}); // notifId → { ok, msg }
|
|
100
102
|
const ref = useRef(null);
|
|
101
103
|
|
|
104
|
+
const [clearing, setClearing] = useState(false);
|
|
105
|
+
|
|
102
106
|
const load = useCallback(async () => {
|
|
103
107
|
const r = await api('/v1/notifications');
|
|
104
108
|
if (r && r.data) { setItems(r.data.notifications || []); setUnread(r.data.unread || 0); }
|
|
105
109
|
}, []);
|
|
106
110
|
|
|
111
|
+
// "Clear all" — delete every notification for the owner (not just mark read). Optimistic: empty the
|
|
112
|
+
// list + zero the badge immediately, then reconcile from the server response.
|
|
113
|
+
const clearAll = useCallback(async () => {
|
|
114
|
+
setClearing(true);
|
|
115
|
+
setItems([]); setUnread(0); setReplyFor(null); setResults({});
|
|
116
|
+
await api('/v1/notifications', { method: 'DELETE', body: JSON.stringify({}) });
|
|
117
|
+
setClearing(false);
|
|
118
|
+
load();
|
|
119
|
+
}, [load]);
|
|
120
|
+
|
|
107
121
|
useEffect(() => {
|
|
108
122
|
load();
|
|
109
123
|
return onLiveUpdate(['agent-messages', 'agent-tasks', 'notifications', 'workspace-access'], () => load());
|
|
@@ -215,7 +229,10 @@ export function NotificationBell({ t, onNavigate }) {
|
|
|
215
229
|
</button>
|
|
216
230
|
${open ? html`
|
|
217
231
|
<div class="notif-dropdown">
|
|
218
|
-
<div class="notif-dropdown-head"
|
|
232
|
+
<div class="notif-dropdown-head">
|
|
233
|
+
<span>${tr('notif.title', 'Notifications')}</span>
|
|
234
|
+
${items.length > 0 ? html`<button class="notif-clear-all" disabled=${clearing} onClick=${(e) => { e.stopPropagation(); clearAll(); }}>${clearing ? '…' : tr('notif.clearAll', 'Clear all')}</button>` : null}
|
|
235
|
+
</div>
|
|
219
236
|
${items.length === 0
|
|
220
237
|
? html`<div class="notif-empty">${tr('notif.empty', 'No notifications yet')}</div>`
|
|
221
238
|
: items.map(n => renderItem(n))}
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
@usage Loaded first in spa.html <head>; the theme is toggled by /js/theme.js
|
|
11
11
|
which flips the data-theme attribute on <html>.
|
|
12
12
|
@version-history
|
|
13
|
+
v1.24.0 — 2026-07-21 — App-shell scroll model: html/body no longer scroll (height:100%;
|
|
14
|
+
overflow:hidden); #app is a full-viewport (100dvh) flex column with the topnav as a fixed-height
|
|
15
|
+
row and .page-content as the sole overflow-y:auto scroll region. Removes the ~nav-height phantom
|
|
16
|
+
overscroll and stops view content sliding under the (formerly sticky, now in-frame) header. Coupled
|
|
17
|
+
offset fixes: .pf-sidebar/.adm-sidebar stick to top:0 of the region and cap at 100dvh−nav-height.
|
|
18
|
+
v1.23.1 — 2026-07-20 — Mobile: pin .notif-dropdown to fixed viewport insets under 560px (the
|
|
19
|
+
320px, right:0-anchored panel ran off the LEFT edge — the bell sits mid-right of the bar — and
|
|
20
|
+
got clipped by body{overflow-x:hidden}); long notif titles/bodies wrap (overflow-wrap:anywhere).
|
|
13
21
|
v1.23.0 — 2026-07-18 — Notification bell inline actions: split .notif-item into a clickable
|
|
14
22
|
.notif-item-main + an actions row; add .notif-actions/.notif-action-btn/.notif-reply*/.notif-result.
|
|
15
23
|
v1.22.0 — 2026-07-17 — UI-unification foundation: extend the spacing scale (--sp-5=20/--sp-6=24/
|
|
@@ -307,15 +315,22 @@
|
|
|
307
315
|
}
|
|
308
316
|
|
|
309
317
|
/* ── Base ── */
|
|
310
|
-
html { scroll-behavior: smooth; }
|
|
318
|
+
html { scroll-behavior: smooth; height: 100%; }
|
|
311
319
|
|
|
312
320
|
body {
|
|
313
321
|
font-family: var(--font);
|
|
314
322
|
background: var(--bg);
|
|
315
323
|
color: var(--text);
|
|
316
324
|
line-height: 1.6;
|
|
317
|
-
|
|
318
|
-
|
|
325
|
+
height: 100%;
|
|
326
|
+
margin: 0; /* zero the UA default 8px — with the fixed app-shell frame it would push #app past the viewport */
|
|
327
|
+
/* App-shell: the page (html/body) never scrolls. The topnav is a fixed-height
|
|
328
|
+
flex row and .page-content owns the ONLY scroll region (see #app +
|
|
329
|
+
.page-content). This is what stops view content sliding under the header and
|
|
330
|
+
removes the ~nav-height phantom overscroll that body-scroll + an in-flow
|
|
331
|
+
(sticky) header produced — the content fills the space below the header and
|
|
332
|
+
scrolls within it. overflow:hidden also subsumes the old overflow-x clip. */
|
|
333
|
+
overflow: hidden;
|
|
319
334
|
-webkit-font-smoothing: antialiased;
|
|
320
335
|
}
|
|
321
336
|
|
|
@@ -402,14 +417,28 @@ a { color: inherit; text-decoration: none; }
|
|
|
402
417
|
#app {
|
|
403
418
|
position: relative;
|
|
404
419
|
z-index: 1;
|
|
420
|
+
/* App-shell frame: a full-viewport flex column — topnav (fixed height) on top,
|
|
421
|
+
.page-content (the scroll region) filling the rest. dvh keeps the frame flush
|
|
422
|
+
with the visual viewport as mobile browser chrome shows/hides. */
|
|
423
|
+
height: 100vh;
|
|
424
|
+
height: 100dvh;
|
|
425
|
+
display: flex;
|
|
426
|
+
flex-direction: column;
|
|
405
427
|
}
|
|
406
428
|
|
|
407
429
|
/* ── Top Navigation ── */
|
|
408
430
|
.topnav {
|
|
409
|
-
|
|
410
|
-
|
|
431
|
+
/* App-shell: a fixed-height flex row inside #app (no longer sticky — #app
|
|
432
|
+
itself doesn't scroll, so the header stays put for free). z-index keeps the
|
|
433
|
+
header's own dropdowns (notifications, mobile menu) above .page-content. */
|
|
434
|
+
position: relative;
|
|
411
435
|
z-index: 100;
|
|
436
|
+
flex: 0 0 auto;
|
|
412
437
|
height: var(--nav-height);
|
|
438
|
+
/* border-box so the 1px border-bottom is INCLUDED in --nav-height — otherwise the
|
|
439
|
+
header renders 1px taller than the token and every `100dvh - var(--nav-height)`
|
|
440
|
+
scroll region (sidebars) is 1px too tall and can't stay pinned. */
|
|
441
|
+
box-sizing: border-box;
|
|
413
442
|
display: flex;
|
|
414
443
|
align-items: center;
|
|
415
444
|
justify-content: space-between;
|
|
@@ -481,7 +510,10 @@ a { color: inherit; text-decoration: none; }
|
|
|
481
510
|
.notif-bell-btn:hover { background: var(--bg-dim); }
|
|
482
511
|
.notif-badge { position: absolute; top: -1px; right: -1px; background: var(--accent); color: #fff; border-radius: 10px; font-size: 0.6rem; font-weight: 700; min-width: 15px; height: 15px; padding: 0 3px; display: flex; align-items: center; justify-content: center; }
|
|
483
512
|
.notif-dropdown { position: absolute; right: 0; top: calc(100% + 6px); width: 320px; max-height: 60vh; overflow-y: auto; background: var(--card); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.18); z-index: 1200; }
|
|
484
|
-
.notif-dropdown-head { font-weight: 700; font-size: 0.9rem; color: var(--text); padding: 0.6rem 0.85rem; border-bottom: 1px solid var(--border-subtle); position: sticky; top: 0; background: var(--card); }
|
|
513
|
+
.notif-dropdown-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; font-weight: 700; font-size: 0.9rem; color: var(--text); padding: 0.6rem 0.85rem; border-bottom: 1px solid var(--border-subtle); position: sticky; top: 0; background: var(--card); z-index: 1; }
|
|
514
|
+
.notif-clear-all { font-weight: 600; font-size: 0.78rem; color: var(--muted); background: none; border: none; padding: 0.15rem 0.35rem; border-radius: 6px; cursor: pointer; }
|
|
515
|
+
.notif-clear-all:hover:not(:disabled) { color: var(--brand); background: var(--bg-dim); }
|
|
516
|
+
.notif-clear-all:disabled { opacity: 0.6; cursor: default; }
|
|
485
517
|
.notif-empty { padding: 1rem 0.85rem; color: var(--muted); font-size: 0.85rem; }
|
|
486
518
|
.notif-item { display: block; border-bottom: 1px solid var(--border-subtle); }
|
|
487
519
|
.notif-item.unread { background: var(--accent-subtle); }
|
|
@@ -499,6 +531,21 @@ a { color: inherit; text-decoration: none; }
|
|
|
499
531
|
.notif-result { font-size: 0.76rem; padding: 0.3rem 0.85rem 0.55rem; }
|
|
500
532
|
.notif-result.ok { color: var(--success, #4caf50); }
|
|
501
533
|
.notif-result.err { color: var(--danger, #e0564a); }
|
|
534
|
+
/* Long sender GHIIs / subjects wrap instead of forcing the panel wider than the screen. */
|
|
535
|
+
.notif-item-title, .notif-item-body { overflow-wrap: anywhere; }
|
|
536
|
+
/* Mobile: the 320px-wide, right:0-anchored dropdown runs off the LEFT edge (the bell sits
|
|
537
|
+
mid-right of the bar) and gets clipped by body{overflow-x:hidden}. Pin it to fixed viewport
|
|
538
|
+
insets so it spans the screen width minus small margins, regardless of the bell's position. */
|
|
539
|
+
@media (max-width: 560px) {
|
|
540
|
+
.notif-dropdown {
|
|
541
|
+
position: fixed;
|
|
542
|
+
top: calc(var(--nav-height, 56px) + 4px);
|
|
543
|
+
right: 8px;
|
|
544
|
+
left: 8px;
|
|
545
|
+
width: auto;
|
|
546
|
+
max-height: 72vh;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
502
549
|
|
|
503
550
|
.topnav-right a {
|
|
504
551
|
font-size: 0.84rem;
|
|
@@ -1277,7 +1324,10 @@ select.input-field { cursor: pointer; }
|
|
|
1277
1324
|
}
|
|
1278
1325
|
|
|
1279
1326
|
/* ── Page entrance ── */
|
|
1280
|
-
|
|
1327
|
+
/* App-shell scroll region: fills the space under the topnav and owns the only
|
|
1328
|
+
scroll. min-height:0 lets the flex child shrink so overflow-y actually scrolls;
|
|
1329
|
+
overflow-x:hidden replaces the clip that used to live on body. */
|
|
1330
|
+
.page-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; animation: pageIn 0.8s ease; }
|
|
1281
1331
|
|
|
1282
1332
|
/* ── Responsive ── */
|
|
1283
1333
|
@media (max-width: 480px) {
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* its distinct indigo accent and a permanently-dark sidebar, but all content
|
|
5
5
|
* surfaces map to global theme.css tokens so they flip with dark/light mode.
|
|
6
6
|
* @version-history
|
|
7
|
+
* v1.10.0 — 2026-07-21 — App-shell scroll model (theme.css v1.24.0): .adm-sidebar height 100vh →
|
|
8
|
+
* calc(100dvh − nav-height) so the sticky sidebar fills the .page-content scroll region instead of
|
|
9
|
+
* overflowing the frame by the header's height.
|
|
7
10
|
* v1.9.0 — 2026-07-16 — Drop the dead .adm-nav-item .icon rule (nav is label-only now).
|
|
8
11
|
* v1.8.0 — 2026-06-25 — Applications tab: .adm-apps-rowactions (Hide/Restore + Delete row).
|
|
9
12
|
* v1.7.0 — 2026-06-24 — Applications moderation tab: .adm-apps-* (filters row,
|
|
@@ -53,9 +56,13 @@
|
|
|
53
56
|
border-right: none;
|
|
54
57
|
padding: 16px 0;
|
|
55
58
|
flex-shrink: 0;
|
|
59
|
+
/* App-shell: sticks to the top of the .page-content scroll region; height caps
|
|
60
|
+
at that region (viewport minus the fixed header) so the sidebar's own nav
|
|
61
|
+
scrolls instead of overflowing past the frame by the header's height. */
|
|
56
62
|
position: sticky;
|
|
57
63
|
top: 0;
|
|
58
|
-
height:
|
|
64
|
+
height: calc(100dvh - var(--nav-height, 56px));
|
|
65
|
+
box-sizing: border-box; /* fold the 16px vertical padding into the height so it fits the scroll region */
|
|
59
66
|
overflow-y: auto;
|
|
60
67
|
display: flex;
|
|
61
68
|
flex-direction: column;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* screen where an owner reviews a published app's requested scopes (H-2 Phase 3).
|
|
5
5
|
* @version-history
|
|
6
6
|
* v1.0.0 — 2026-06-20 — Initial (H-2 app-origin isolation, Phase 3).
|
|
7
|
+
* v1.1.0 — 2026-07-19 — Own-app badge variant + "new" chip for newly requested scopes.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
.agr-wrap { max-width: 540px; margin: 3rem auto; padding: 0 1.25rem; }
|
|
@@ -64,6 +65,23 @@
|
|
|
64
65
|
padding: 0.18rem 0.6rem;
|
|
65
66
|
margin-bottom: 0.6rem;
|
|
66
67
|
}
|
|
68
|
+
/* The signed-in owner's OWN app — reassuring, not warning */
|
|
69
|
+
.agr-badge-own { color: var(--success, #3dbf6e); }
|
|
70
|
+
|
|
71
|
+
/* Chip on a scope the app requests but the existing grant doesn't hold yet */
|
|
72
|
+
.agr-scope-newbadge {
|
|
73
|
+
display: inline-block;
|
|
74
|
+
font-size: 0.68rem;
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
letter-spacing: 0.04em;
|
|
77
|
+
text-transform: uppercase;
|
|
78
|
+
color: var(--accent);
|
|
79
|
+
border: 1px solid var(--accent);
|
|
80
|
+
border-radius: 999px;
|
|
81
|
+
padding: 0 0.4rem;
|
|
82
|
+
margin-left: 0.45rem;
|
|
83
|
+
vertical-align: 0.08em;
|
|
84
|
+
}
|
|
67
85
|
|
|
68
86
|
/* Advanced toggle (link-style button) */
|
|
69
87
|
.agr-advanced-toggle {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* ticks, and a sticky composer. All colors/spacing use theme.css variables (dark-mode safe).
|
|
6
6
|
* Classes are `inbox-` prefixed. NOTE: this file must be linked in spa.html (useViewCSS is a no-op).
|
|
7
7
|
* @version-history
|
|
8
|
+
* v1.7.0 -- 2026-07-21 -- Link-preview (unfurl) cards under a pasted link (.inbox-linkcard*) + the
|
|
9
|
+
* thread-head show/hide toggle (.inbox-linkprev-toggle).
|
|
8
10
|
* v1.6.0 -- 2026-07-18 -- TRUE full-screen mobile chat: ≤760px open panel becomes `position:fixed;inset:0`
|
|
9
11
|
* over the whole viewport (covers the app shell nav/menu/breadcrumb); body height var(--inbox-avail,
|
|
10
12
|
* 100dvh) + safe-area padding under the composer. ← Back drops `.inbox--panel` → shell returns. Pairs
|
|
@@ -485,7 +487,14 @@
|
|
|
485
487
|
font-size: 14px;
|
|
486
488
|
}
|
|
487
489
|
|
|
490
|
+
/* Expanded composer (the ⤢ toggle): grow the fallback/simple textarea + preview to ~60vh so a long or
|
|
491
|
+
formatted draft is fully visible. The rich Toast UI editor is resized in JS via setHeight instead. */
|
|
492
|
+
.inbox-composer--tall .inbox-textarea { min-height: 60vh; }
|
|
493
|
+
.inbox-composer--tall .inbox-textarea--chat { max-height: 60vh; }
|
|
494
|
+
.inbox-composer--tall .inbox-md-preview { max-height: 60vh; }
|
|
495
|
+
|
|
488
496
|
.inbox-composer-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
497
|
+
.inbox-bar-left { display: flex; align-items: center; gap: 8px; }
|
|
489
498
|
.inbox-attach-btn {
|
|
490
499
|
display: inline-flex;
|
|
491
500
|
align-items: center;
|
|
@@ -743,3 +752,40 @@
|
|
|
743
752
|
.inbox-sched-new { display: flex; flex-direction: column; gap: 6px; }
|
|
744
753
|
.inbox-sched-new .btn-primary { align-self: flex-start; }
|
|
745
754
|
.inbox-sched-desc { min-height: 38px; resize: vertical; }
|
|
755
|
+
|
|
756
|
+
/* ── Link previews (unfurl cards) — a rich card under a pasted link in a message bubble. The card is a
|
|
757
|
+
* clickable row (thumbnail · site · title · description) plus a ✕ to hide it. Theme tokens only. ── */
|
|
758
|
+
.inbox-linkcards { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
|
|
759
|
+
.inbox-linkcard {
|
|
760
|
+
position: relative; display: flex; align-items: stretch;
|
|
761
|
+
border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
|
|
762
|
+
background: var(--card-bg-alt); overflow: hidden;
|
|
763
|
+
}
|
|
764
|
+
.inbox-linkcard-main {
|
|
765
|
+
display: flex; align-items: stretch; gap: 10px; min-width: 0; flex: 1;
|
|
766
|
+
padding: 8px 10px; text-decoration: none; color: inherit;
|
|
767
|
+
}
|
|
768
|
+
.inbox-linkcard-main:hover { background: var(--surface-hover); }
|
|
769
|
+
.inbox-linkcard-thumb { flex: none; width: 72px; display: flex; align-items: center; justify-content: center; }
|
|
770
|
+
.inbox-linkcard-thumb img {
|
|
771
|
+
width: 72px; height: 72px; object-fit: cover; border-radius: var(--radius-xs); display: block; margin: 0;
|
|
772
|
+
}
|
|
773
|
+
.inbox-linkcard-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; justify-content: center; }
|
|
774
|
+
.inbox-linkcard-site { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .02em; }
|
|
775
|
+
.inbox-linkcard-title {
|
|
776
|
+
font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.3;
|
|
777
|
+
overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
778
|
+
}
|
|
779
|
+
.inbox-linkcard-desc {
|
|
780
|
+
font-size: 12px; color: var(--text-dim); line-height: 1.35;
|
|
781
|
+
overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
782
|
+
}
|
|
783
|
+
.inbox-linkcard-x {
|
|
784
|
+
flex: none; align-self: flex-start; border: none; background: transparent; cursor: pointer;
|
|
785
|
+
color: var(--text-dim); font-size: 12px; line-height: 1; padding: 6px 8px; border-radius: var(--radius-xs);
|
|
786
|
+
}
|
|
787
|
+
.inbox-linkcard-x:hover { color: var(--text); background: var(--surface-hover); }
|
|
788
|
+
|
|
789
|
+
/* Thread-head toggle: show/hide all link previews (persisted). --on marks the active state. */
|
|
790
|
+
.inbox-linkprev-toggle { flex: none; white-space: nowrap; }
|
|
791
|
+
.inbox-linkprev-toggle--on { background: var(--accent-subtle); border-color: var(--accent-border); color: var(--accent); }
|
|
@@ -317,6 +317,52 @@
|
|
|
317
317
|
margin-bottom: 0.5rem;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
/* ── Promoted apps (public profile) ── */
|
|
321
|
+
.portfolio-profile-head {
|
|
322
|
+
text-align: center;
|
|
323
|
+
padding: 3rem 2rem 1rem;
|
|
324
|
+
color: var(--text-dim, #6B7280);
|
|
325
|
+
}
|
|
326
|
+
.portfolio-profile-head h2 { color: var(--text, #1A1A2E); margin-bottom: 0.5rem; }
|
|
327
|
+
.portfolio-promoted {
|
|
328
|
+
max-width: 900px;
|
|
329
|
+
margin: 0 auto;
|
|
330
|
+
padding: 0 1.5rem 1rem;
|
|
331
|
+
}
|
|
332
|
+
.portfolio-promoted-title {
|
|
333
|
+
font-size: 1rem;
|
|
334
|
+
color: var(--text, #1A1A2E);
|
|
335
|
+
margin: 0 0 0.75rem;
|
|
336
|
+
}
|
|
337
|
+
.portfolio-promoted-grid {
|
|
338
|
+
display: grid;
|
|
339
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
340
|
+
gap: 0.75rem;
|
|
341
|
+
}
|
|
342
|
+
.portfolio-promoted-card {
|
|
343
|
+
display: block;
|
|
344
|
+
text-decoration: none;
|
|
345
|
+
border: 1px solid var(--border, #E5E7EB);
|
|
346
|
+
border-radius: 12px;
|
|
347
|
+
padding: 0.85rem 1rem;
|
|
348
|
+
background: var(--card, #fff);
|
|
349
|
+
transition: border-color 0.15s, transform 0.15s;
|
|
350
|
+
}
|
|
351
|
+
.portfolio-promoted-card:hover {
|
|
352
|
+
border-color: var(--accent, #E8564A);
|
|
353
|
+
transform: translateY(-1px);
|
|
354
|
+
}
|
|
355
|
+
.portfolio-promoted-name {
|
|
356
|
+
font-weight: 700;
|
|
357
|
+
color: var(--text, #1A1A2E);
|
|
358
|
+
margin-bottom: 0.3rem;
|
|
359
|
+
}
|
|
360
|
+
.portfolio-promoted-pitch {
|
|
361
|
+
font-size: 0.85rem;
|
|
362
|
+
color: var(--text-dim, #6B7280);
|
|
363
|
+
line-height: 1.4;
|
|
364
|
+
}
|
|
365
|
+
|
|
320
366
|
/* ── Upload Section ── */
|
|
321
367
|
.portfolio-upload-zone {
|
|
322
368
|
border: 2px dashed var(--border, #E5E7EB);
|