@k2b/cloud 0.6.0-rc.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/README.md +98 -0
- package/package.json +128 -0
- package/public/logo.svg +1 -0
- package/scripts/app-favicon.test.ts +49 -0
- package/scripts/app-favicon.ts +51 -0
- package/scripts/build.ts +205 -0
- package/scripts/preload.ts +164 -0
- package/scripts/sync-recovery-smoke.ts +200 -0
- package/src/_internal/app-favicon.ts +2 -0
- package/src/_internal/app-presentation.ts +61 -0
- package/src/_internal/app-workspace-state.ts +24 -0
- package/src/_internal/bounded-json.ts +47 -0
- package/src/_internal/build-metadata.ts +9 -0
- package/src/_internal/capabilities.ts +1118 -0
- package/src/_internal/define-app.ts +801 -0
- package/src/_internal/heartbeat.ts +167 -0
- package/src/_internal/help-catalog.ts +241 -0
- package/src/_internal/help.ts +95 -0
- package/src/_internal/nats-connection.ts +34 -0
- package/src/_internal/page-responses.ts +40 -0
- package/src/_internal/process-sync.ts +117 -0
- package/src/_internal/readiness.ts +3 -0
- package/src/_internal/registry-validation.ts +136 -0
- package/src/_internal/registry-watch.ts +52 -0
- package/src/_internal/registry.ts +338 -0
- package/src/_internal/runtime-compatibility.ts +64 -0
- package/src/_internal/runtime-context.ts +84 -0
- package/src/_internal/runtime-watcher.ts +78 -0
- package/src/_internal/static-assets.ts +86 -0
- package/src/_internal/status-preserving-ssr.ts +49 -0
- package/src/access/PermissionEditor.tsx +486 -0
- package/src/access/ResourceApiKeys.tsx +271 -0
- package/src/access/messages.ts +121 -0
- package/src/access/ui.ts +16 -0
- package/src/account/Avatar.tsx +28 -0
- package/src/account/EntitySearch.tsx +363 -0
- package/src/account/avatar-upload-dialog.tsx +197 -0
- package/src/account/avatar-upload.ts +149 -0
- package/src/account/ui.ts +3 -0
- package/src/ai/admin.ts +47 -0
- package/src/ai/approval-routes.ts +130 -0
- package/src/ai/approvals.ts +139 -0
- package/src/ai/assistant-models.ts +61 -0
- package/src/ai/attachments.ts +40 -0
- package/src/ai/browser.ts +92 -0
- package/src/ai/capabilities.ts +714 -0
- package/src/ai/capability-execution.ts +151 -0
- package/src/ai/chat/blocks.render.test.tsx +1108 -0
- package/src/ai/chat/blocks.tsx +730 -0
- package/src/ai/chat/builtin-tools.tsx +428 -0
- package/src/ai/chat/capability-table.tsx +87 -0
- package/src/ai/chat/composer-adapter.ts +169 -0
- package/src/ai/chat/file-tools.tsx +81 -0
- package/src/ai/chat/message-actions.tsx +370 -0
- package/src/ai/chat/message-utils.ts +479 -0
- package/src/ai/chat/messages.ts +43 -0
- package/src/ai/chat/presentation.tsx +395 -0
- package/src/ai/chat/primitives.tsx +3 -0
- package/src/ai/chat/tool-disclosure.tsx +41 -0
- package/src/ai/chat/turn-navigator-utils.ts +28 -0
- package/src/ai/chat/turn-navigator.tsx +164 -0
- package/src/ai/chat/user-message.tsx +149 -0
- package/src/ai/chat/visual-tools.tsx +435 -0
- package/src/ai/chat/web-tools.tsx +202 -0
- package/src/ai/chat-task-contracts.ts +113 -0
- package/src/ai/chat-tasks.ts +952 -0
- package/src/ai/client/controller.ts +1211 -0
- package/src/ai/client/file-source.ts +95 -0
- package/src/ai/client/live-connection.ts +140 -0
- package/src/ai/client/projection.ts +178 -0
- package/src/ai/client/transport.ts +111 -0
- package/src/ai/compaction.ts +182 -0
- package/src/ai/credentials.ts +124 -0
- package/src/ai/default-tool-contracts.ts +108 -0
- package/src/ai/default-tools.ts +127 -0
- package/src/ai/enrich.ts +280 -0
- package/src/ai/executor.ts +1624 -0
- package/src/ai/fetch-file-tool.ts +313 -0
- package/src/ai/file-context.ts +92 -0
- package/src/ai/file-media-type.ts +28 -0
- package/src/ai/file-mount.ts +16 -0
- package/src/ai/file-tools.ts +566 -0
- package/src/ai/files-store.ts +390 -0
- package/src/ai/firecrawl-tools.ts +305 -0
- package/src/ai/grids-skill.ts +126 -0
- package/src/ai/http.ts +265 -0
- package/src/ai/index.ts +337 -0
- package/src/ai/kit-skill.ts +22 -0
- package/src/ai/limits.ts +5 -0
- package/src/ai/live-events.ts +289 -0
- package/src/ai/live-messages.ts +45 -0
- package/src/ai/live-outbox.ts +102 -0
- package/src/ai/live-routes.ts +412 -0
- package/src/ai/live.ts +2 -0
- package/src/ai/maintenance.ts +302 -0
- package/src/ai/markdown-pdf-tool.ts +95 -0
- package/src/ai/memories.ts +605 -0
- package/src/ai/memory-learning-runs.ts +222 -0
- package/src/ai/memory-learning.ts +619 -0
- package/src/ai/memory-tool.ts +120 -0
- package/src/ai/memory-workflow-evidence.ts +158 -0
- package/src/ai/migrate.ts +2082 -0
- package/src/ai/model-access-migrate.ts +30 -0
- package/src/ai/model-access.ts +191 -0
- package/src/ai/personal-agent.ts +6 -0
- package/src/ai/prefs.ts +72 -0
- package/src/ai/project-tool.ts +94 -0
- package/src/ai/projects-routes.ts +277 -0
- package/src/ai/projects.ts +902 -0
- package/src/ai/protocol.ts +279 -0
- package/src/ai/provider.ts +73 -0
- package/src/ai/public-projection.ts +25 -0
- package/src/ai/resource-markers.ts +31 -0
- package/src/ai/resource-refs.ts +70 -0
- package/src/ai/routes.ts +1131 -0
- package/src/ai/runtime-tools.ts +39 -0
- package/src/ai/runtime.ts +544 -0
- package/src/ai/settings.ts +529 -0
- package/src/ai/short-id.ts +45 -0
- package/src/ai/skill-catalog.ts +73 -0
- package/src/ai/skill-format.ts +145 -0
- package/src/ai/skill-seeds.ts +430 -0
- package/src/ai/skill-tool.ts +81 -0
- package/src/ai/skills-routes.ts +143 -0
- package/src/ai/skills.ts +934 -0
- package/src/ai/solid.ts +27 -0
- package/src/ai/store.ts +3113 -0
- package/src/ai/stream.ts +308 -0
- package/src/ai/structured-runs.ts +66 -0
- package/src/ai/structured.ts +186 -0
- package/src/ai/system-prompt.ts +170 -0
- package/src/ai/task-prompt.ts +22 -0
- package/src/ai/timeline.ts +148 -0
- package/src/ai/tool-audit.ts +177 -0
- package/src/ai/tool-result-budget.ts +16 -0
- package/src/ai/tools.ts +155 -0
- package/src/ai/types.ts +967 -0
- package/src/ai/ui.tsx +39 -0
- package/src/ai/usage-migrate.ts +81 -0
- package/src/ai/usage.ts +315 -0
- package/src/ai/validate.ts +54 -0
- package/src/ai/vision-tool.ts +79 -0
- package/src/api/accounts-entities.ts +174 -0
- package/src/api/admin-ai-projects.ts +74 -0
- package/src/api/admin-ai-skills.ts +76 -0
- package/src/api/admin-ai-usage.ts +42 -0
- package/src/api/admin-core-settings.ts +508 -0
- package/src/api/admin-identity.ts +148 -0
- package/src/api/admin-lifecycle.ts +206 -0
- package/src/api/admin-linux-identities.ts +66 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/app-approval.ts +129 -0
- package/src/api/apps.ts +51 -0
- package/src/api/auth/schemas.ts +54 -0
- package/src/api/auth.ts +370 -0
- package/src/api/capabilities.ts +658 -0
- package/src/api/help-messages.ts +19 -0
- package/src/api/help.ts +78 -0
- package/src/api/index.ts +108 -0
- package/src/api/mcp.ts +744 -0
- package/src/api/me-rail.ts +45 -0
- package/src/api/me.ts +753 -0
- package/src/api/search/schemas.ts +79 -0
- package/src/api/search.bench.ts +185 -0
- package/src/api/search.ts +407 -0
- package/src/api/sync-ops.ts +79 -0
- package/src/api/widgets.ts +143 -0
- package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
- package/src/browser/CloudResourceSearch.tsx +791 -0
- package/src/browser/app-approval-vault.ts +305 -0
- package/src/browser/app-approval.ts +234 -0
- package/src/browser/live-websocket.ts +268 -0
- package/src/browser/locale-preference.ts +25 -0
- package/src/browser/notification-target.ts +27 -0
- package/src/browser/notification-timeout.ts +14 -0
- package/src/browser/notifications.ts +181 -0
- package/src/browser/resource-clipboard.ts +70 -0
- package/src/browser/resource-picker.tsx +39 -0
- package/src/browser/resource-search-messages.ts +55 -0
- package/src/browser/resource-search.ts +27 -0
- package/src/capabilities/client.ts +149 -0
- package/src/capabilities/response.ts +39 -0
- package/src/capabilities/server.ts +215 -0
- package/src/capabilities/signals.ts +4 -0
- package/src/capabilities/testing.ts +12 -0
- package/src/capabilities/types.ts +32 -0
- package/src/cli/access.ts +499 -0
- package/src/cli/account.ts +382 -0
- package/src/cli/admin/account-administration.ts +45 -0
- package/src/cli/admin/account-categories.ts +49 -0
- package/src/cli/admin/ai-usage.ts +118 -0
- package/src/cli/admin/app-sign-in.ts +49 -0
- package/src/cli/admin/data.ts +343 -0
- package/src/cli/admin/documentation.ts +34 -0
- package/src/cli/admin/gateway.ts +155 -0
- package/src/cli/admin/index.ts +61 -0
- package/src/cli/admin/instance.ts +250 -0
- package/src/cli/admin/jobs.ts +223 -0
- package/src/cli/admin/legal.ts +122 -0
- package/src/cli/admin/linux.ts +53 -0
- package/src/cli/admin/logs.ts +324 -0
- package/src/cli/admin/metrics.ts +142 -0
- package/src/cli/admin/nats.ts +167 -0
- package/src/cli/admin/notifications.ts +516 -0
- package/src/cli/admin/shared.ts +113 -0
- package/src/cli/admin/sync.ts +188 -0
- package/src/cli/admin/telemetry.ts +314 -0
- package/src/cli/admin/webhooks.ts +222 -0
- package/src/cli/admin/workflows.ts +367 -0
- package/src/cli/apps.ts +40 -0
- package/src/cli/capabilities.ts +239 -0
- package/src/cli/commands.ts +604 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/locale.ts +31 -0
- package/src/cli/output.ts +58 -0
- package/src/clients/core.ts +20 -0
- package/src/config/env.ts +54 -0
- package/src/config/index.ts +6 -0
- package/src/contracts/account-categories.ts +62 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app-approval.ts +197 -0
- package/src/contracts/app.ts +162 -0
- package/src/contracts/capabilities.ts +484 -0
- package/src/contracts/index.ts +14 -0
- package/src/contracts/notification-live.ts +66 -0
- package/src/contracts/notification-types.ts +235 -0
- package/src/contracts/posix.ts +69 -0
- package/src/contracts/profile.ts +85 -0
- package/src/contracts/rail-preferences.ts +53 -0
- package/src/contracts/registry.ts +110 -0
- package/src/contracts/resource-clipboard.ts +33 -0
- package/src/contracts/settings-types.ts +98 -0
- package/src/contracts/shared.ts +408 -0
- package/src/contracts/user-notifications.ts +101 -0
- package/src/contracts/widgets.ts +183 -0
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/index.ts +58 -0
- package/src/server/actor.ts +41 -0
- package/src/server/api/index.ts +2 -0
- package/src/server/api/respond.ts +96 -0
- package/src/server/api-client.ts +54 -0
- package/src/server/app-context.ts +39 -0
- package/src/server/help.ts +187 -0
- package/src/server/index.ts +89 -0
- package/src/server/locale.ts +55 -0
- package/src/server/middleware/auth.ts +386 -0
- package/src/server/middleware/index.ts +28 -0
- package/src/server/middleware/invocation.ts +55 -0
- package/src/server/middleware/middleware.ts +65 -0
- package/src/server/middleware/openapi.ts +126 -0
- package/src/server/middleware/rate-limit.ts +125 -0
- package/src/server/middleware/request-logger.ts +41 -0
- package/src/server/middleware/route-template.ts +46 -0
- package/src/server/middleware/runtime.ts +22 -0
- package/src/server/middleware/settings.ts +55 -0
- package/src/server/middleware/validator.ts +49 -0
- package/src/server/middleware/workload.ts +22 -0
- package/src/server/ratelimit.ts +151 -0
- package/src/server/request-cookies.ts +18 -0
- package/src/server/services/access.ts +644 -0
- package/src/server/services/freeipa/client.ts +137 -0
- package/src/server/services/freeipa/index.ts +9 -0
- package/src/server/services/freeipa/session.ts +123 -0
- package/src/server/services/freeipa/test-certificates.ts +44 -0
- package/src/server/services/freeipa/tls.ts +44 -0
- package/src/server/services/freeipa/transport.ts +116 -0
- package/src/server/services/freeipa/util.ts +59 -0
- package/src/server/services/geo.ts +198 -0
- package/src/server/services/index.ts +48 -0
- package/src/server/services/pagination.ts +22 -0
- package/src/server/services/services.ts +13 -0
- package/src/server/time.ts +33 -0
- package/src/services/account-category-policy.ts +51 -0
- package/src/services/account-lifecycle/audit.ts +41 -0
- package/src/services/account-lifecycle/index.ts +940 -0
- package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
- package/src/services/account-lifecycle/notification-sender.ts +13 -0
- package/src/services/account-lifecycle/scheduler.ts +506 -0
- package/src/services/account-model.ts +22 -0
- package/src/services/accounts/app.ts +1671 -0
- package/src/services/accounts/authz.ts +44 -0
- package/src/services/accounts/avatar.ts +120 -0
- package/src/services/accounts/base-group.ts +11 -0
- package/src/services/accounts/base-user.ts +46 -0
- package/src/services/accounts/duplicate-emails.ts +82 -0
- package/src/services/accounts/entities.ts +622 -0
- package/src/services/accounts/group-sql.ts +106 -0
- package/src/services/accounts/groups.ts +272 -0
- package/src/services/accounts/index.ts +14 -0
- package/src/services/accounts/ipa-data.ts +66 -0
- package/src/services/accounts/lifecycle.ts +2 -0
- package/src/services/accounts/local-groups.ts +499 -0
- package/src/services/accounts/model.ts +129 -0
- package/src/services/accounts/notification-sender.ts +31 -0
- package/src/services/accounts/posix.ts +386 -0
- package/src/services/accounts/request-policy.ts +11 -0
- package/src/services/accounts/switching.ts +126 -0
- package/src/services/accounts/users.ts +745 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/app-approval.ts +498 -0
- package/src/services/audit/index.ts +438 -0
- package/src/services/auth-flows/index.ts +15 -0
- package/src/services/auth-flows/ipa.ts +197 -0
- package/src/services/auth-flows/magic-link.ts +185 -0
- package/src/services/auth-flows/notification-sender.ts +10 -0
- package/src/services/auth-flows/password-reset.ts +268 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/document-extraction.ts +148 -0
- package/src/services/freeipa-config.ts +179 -0
- package/src/services/gateway.ts +186 -0
- package/src/services/identity/constants.ts +18 -0
- package/src/services/identity/index.ts +52 -0
- package/src/services/identity/invocation-actor.ts +120 -0
- package/src/services/identity/invocation-authority.ts +53 -0
- package/src/services/identity/invocation-operations.ts +15 -0
- package/src/services/identity/invocation-token.ts +276 -0
- package/src/services/identity/key-config.ts +41 -0
- package/src/services/identity/key-ring.ts +530 -0
- package/src/services/identity/metrics.ts +29 -0
- package/src/services/identity/routes.ts +18 -0
- package/src/services/identity/runtime-config.ts +88 -0
- package/src/services/identity/session-token.ts +152 -0
- package/src/services/identity/signer-refresh.ts +44 -0
- package/src/services/identity/workload-auth.ts +64 -0
- package/src/services/index.ts +187 -0
- package/src/services/ipa/auth.ts +216 -0
- package/src/services/ipa/connection.ts +57 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/groups.ts +722 -0
- package/src/services/ipa/guard.ts +17 -0
- package/src/services/ipa/index.ts +17 -0
- package/src/services/ipa/posix.ts +19 -0
- package/src/services/ipa/profile.ts +132 -0
- package/src/services/ipa/runtime.ts +28 -0
- package/src/services/ipa/search.ts +154 -0
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.ts +100 -0
- package/src/services/ipa/sync.ts +862 -0
- package/src/services/ipa/users.ts +806 -0
- package/src/services/legal-consent.ts +76 -0
- package/src/services/logging/index.ts +477 -0
- package/src/services/logging/redaction.ts +20 -0
- package/src/services/logging/trace.ts +1231 -0
- package/src/services/mandates/index.ts +855 -0
- package/src/services/mandates/policy.ts +85 -0
- package/src/services/network-security.ts +31 -0
- package/src/services/notifications/batches.ts +722 -0
- package/src/services/notifications/browser.ts +252 -0
- package/src/services/notifications/catalog.ts +113 -0
- package/src/services/notifications/channels.ts +104 -0
- package/src/services/notifications/dispatcher.ts +139 -0
- package/src/services/notifications/email.ts +118 -0
- package/src/services/notifications/index.ts +633 -0
- package/src/services/notifications/live.ts +50 -0
- package/src/services/notifications/observability.ts +407 -0
- package/src/services/notifications/platform.ts +421 -0
- package/src/services/notifications/runtime.ts +120 -0
- package/src/services/notifications/user.ts +248 -0
- package/src/services/notifications/web-push-transport.ts +135 -0
- package/src/services/oauth-tokens.ts +209 -0
- package/src/services/outbox.ts +187 -0
- package/src/services/pdf/gotenberg.ts +290 -0
- package/src/services/pdf/index.ts +43 -0
- package/src/services/pdf/markdown.ts +209 -0
- package/src/services/pdf/template-preview.ts +122 -0
- package/src/services/postgres.ts +76 -0
- package/src/services/providers/index.ts +27 -0
- package/src/services/providers/local/auth.ts +51 -0
- package/src/services/providers/local/index.ts +4 -0
- package/src/services/providers/local/users.ts +263 -0
- package/src/services/rail-preferences.ts +27 -0
- package/src/services/runtime-lifecycle.ts +191 -0
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.ts +891 -0
- package/src/services/service-accounts.ts +184 -0
- package/src/services/session/index.ts +236 -0
- package/src/services/session/test-fixture.ts +18 -0
- package/src/services/session/user.ts +144 -0
- package/src/services/settings/api.ts +61 -0
- package/src/services/settings/app.ts +86 -0
- package/src/services/settings/core-settings.ts +796 -0
- package/src/services/settings/crypto.ts +69 -0
- package/src/services/settings/defaults.ts +374 -0
- package/src/services/settings/index.ts +215 -0
- package/src/services/settings/namespace.ts +9 -0
- package/src/services/settings/snapshot.ts +49 -0
- package/src/services/settings/store.ts +241 -0
- package/src/services/settings/templates.ts +4 -0
- package/src/services/sync-ops.ts +403 -0
- package/src/services/topic-cursor.ts +22 -0
- package/src/services/weather/forecast.ts +323 -0
- package/src/services/weather/geo.ts +119 -0
- package/src/services/weather/index.ts +67 -0
- package/src/services/weather/location.ts +24 -0
- package/src/services/weather/locations.ts +133 -0
- package/src/services/weather/migrate.ts +45 -0
- package/src/services/weather/settings.ts +66 -0
- package/src/services/weather/short-id.ts +57 -0
- package/src/services/weather/types.ts +61 -0
- package/src/services/weather/ui.ts +50 -0
- package/src/services/webauthn.ts +385 -0
- package/src/shared/account-action-notice.ts +70 -0
- package/src/shared/account-display.ts +15 -0
- package/src/shared/account-session.ts +15 -0
- package/src/shared/ai-platform-prompt.ts +134 -0
- package/src/shared/ai-usage.ts +49 -0
- package/src/shared/app-presentation.ts +48 -0
- package/src/shared/app-url.ts +12 -0
- package/src/shared/branding.ts +14 -0
- package/src/shared/capability-messages.ts +70 -0
- package/src/shared/email-html.ts +54 -0
- package/src/shared/format.ts +91 -0
- package/src/shared/help.ts +68 -0
- package/src/shared/icons.ts +400 -0
- package/src/shared/index.ts +54 -0
- package/src/shared/locale.ts +48 -0
- package/src/shared/login-method.ts +44 -0
- package/src/shared/markdown/client.ts +130 -0
- package/src/shared/markdown/extensions/code.ts +105 -0
- package/src/shared/markdown/extensions/guided-help.ts +100 -0
- package/src/shared/markdown/extensions/images.ts +79 -0
- package/src/shared/markdown/extensions/info-blocks.ts +97 -0
- package/src/shared/markdown/extensions/katex.ts +120 -0
- package/src/shared/markdown/extensions/links.ts +43 -0
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +109 -0
- package/src/shared/markdown/extensions/task-list.ts +53 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +193 -0
- package/src/shared/markdown/shared.ts +36 -0
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/network-address.ts +106 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/template-rendering.ts +140 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +53 -0
- package/src/ssr/AdminSidebar.tsx +50 -0
- package/src/ssr/AppLaunchpad.island.tsx +236 -0
- package/src/ssr/AppWorkspaceController.island.tsx +21 -0
- package/src/ssr/BrowserNotifications.island.tsx +135 -0
- package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
- package/src/ssr/GlobalSearchDialog.tsx +35 -0
- package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
- package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
- package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
- package/src/ssr/Layout.render.test.tsx +148 -0
- package/src/ssr/Layout.tsx +292 -0
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutFooter.tsx +22 -0
- package/src/ssr/LayoutHeader.tsx +81 -0
- package/src/ssr/LayoutHelp.tsx +869 -0
- package/src/ssr/LayoutPreferences.island.tsx +36 -0
- package/src/ssr/LayoutRail.tsx +58 -0
- package/src/ssr/MinimalLayout.tsx +46 -0
- package/src/ssr/PageError.tsx +29 -0
- package/src/ssr/ProfilePreferences.island.tsx +92 -0
- package/src/ssr/RailApps.island.tsx +61 -0
- package/src/ssr/RailEditor.tsx +293 -0
- package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/admin-active-link.ts +47 -0
- package/src/ssr/admin-navigation.ts +78 -0
- package/src/ssr/app-appearance.ts +23 -0
- package/src/ssr/app-navigation.ts +41 -0
- package/src/ssr/help-messages.ts +104 -0
- package/src/ssr/index.ts +14 -0
- package/src/ssr/islands/SearchBar.island.tsx +80 -0
- package/src/ssr/islands/index.ts +16 -0
- package/src/ssr/layout-context.ts +17 -0
- package/src/ssr/layout-help-markdown.ts +9 -0
- package/src/ssr/layout-help-navigation.ts +24 -0
- package/src/ssr/layout-help-url.ts +5 -0
- package/src/ssr/layout-runtime.ts +22 -0
- package/src/ssr/page-error-messages.ts +25 -0
- package/src/ssr/platform-messages.ts +135 -0
- package/src/ssr/preference-controller.ts +44 -0
- package/src/ssr/profile-preferences-messages.ts +29 -0
- package/src/ssr/rail-context.ts +21 -0
- package/src/ssr/rail-messages.ts +66 -0
- package/src/ssr/rail-navigation.ts +60 -0
- package/src/ssr/runtime.ts +31 -0
- package/src/ssr/url-filter.ts +127 -0
- package/src/styles/base-popover.css +36 -0
- package/src/styles/effects.css +851 -0
- package/src/styles/global.css +268 -0
- package/src/styles/input.css +58 -0
- package/src/styles/tokens.css +230 -0
- package/src/styles/utilities-buttons.css +405 -0
- package/src/styles/utilities-data.css +33 -0
- package/src/styles/utilities-feedback.css +232 -0
- package/src/styles/utilities-layout.css +603 -0
- package/src/styles/utilities-markdown-editor.css +525 -0
- package/src/styles/utilities-markdown-table.css +234 -0
- package/src/styles/utilities-navigation.css +1054 -0
- package/src/types/ambient.d.ts +24 -0
- package/src/workflows/ai/index.ts +17 -0
- package/src/workflows/ai/runtime.ts +286 -0
- package/src/workflows/ai/store.ts +314 -0
- package/src/workflows/ai/types.ts +111 -0
- package/src/workflows/ai-actions.ts +269 -0
- package/src/workflows/authoring.ts +88 -0
- package/src/workflows/builtins.ts +193 -0
- package/src/workflows/contracts.ts +280 -0
- package/src/workflows/definition.ts +334 -0
- package/src/workflows/editor.ts +113 -0
- package/src/workflows/index.ts +71 -0
- package/src/workflows/language/binder.ts +45 -0
- package/src/workflows/language/canonical.ts +29 -0
- package/src/workflows/language/compiler.ts +396 -0
- package/src/workflows/language/expressions.ts +34 -0
- package/src/workflows/language/index.ts +20 -0
- package/src/workflows/language/references.ts +55 -0
- package/src/workflows/language/schema.ts +134 -0
- package/src/workflows/language/strict-yaml.ts +131 -0
- package/src/workflows/manifest.ts +24 -0
- package/src/workflows/module.ts +12 -0
- package/src/workflows/runtime/coordinator.ts +115 -0
- package/src/workflows/runtime/executor.ts +1003 -0
- package/src/workflows/runtime/index.ts +60 -0
- package/src/workflows/runtime/instant.ts +8 -0
- package/src/workflows/runtime/ports.ts +213 -0
- package/src/workflows/runtime/schedule.ts +203 -0
- package/src/workflows/runtime/trigger.ts +37 -0
- package/src/workflows/runtime/wake.ts +13 -0
- package/src/workflows/store/actions.ts +438 -0
- package/src/workflows/store/budget.ts +145 -0
- package/src/workflows/store/definitions.ts +345 -0
- package/src/workflows/store/events.ts +383 -0
- package/src/workflows/store/index.ts +107 -0
- package/src/workflows/store/observability.ts +594 -0
- package/src/workflows/store/runs.ts +975 -0
- package/src/workflows/store/transaction.ts +23 -0
- package/src/workflows/store/worker.ts +439 -0
- package/src/workflows/testing/conformance.ts +72 -0
- package/src/workflows/testing/index.ts +11 -0
- package/src/workflows/testing/process-fixtures.ts +275 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize Mermaid diagrams within a container element.
|
|
3
|
+
* Call this in onMount() of your client-side component.
|
|
4
|
+
*
|
|
5
|
+
* @param container - The container element containing rendered markdown
|
|
6
|
+
*/
|
|
7
|
+
export async function initMermaid(container: HTMLElement): Promise<void> {
|
|
8
|
+
const mermaid = (await import("mermaid")).default;
|
|
9
|
+
// Initialize mermaid with theme detection
|
|
10
|
+
const isDark = document.documentElement.classList.contains("dark");
|
|
11
|
+
mermaid.initialize({
|
|
12
|
+
startOnLoad: false,
|
|
13
|
+
theme: "base",
|
|
14
|
+
themeVariables: isDark
|
|
15
|
+
? {
|
|
16
|
+
darkMode: true,
|
|
17
|
+
background: "#09090b",
|
|
18
|
+
textColor: "#e5e7eb",
|
|
19
|
+
lineColor: "#6b7280",
|
|
20
|
+
primaryTextColor: "#e5e7eb",
|
|
21
|
+
secondaryTextColor: "#e5e7eb",
|
|
22
|
+
tertiaryTextColor: "#e5e7eb",
|
|
23
|
+
noteTextColor: "#e5e7eb",
|
|
24
|
+
mainBkg: "#111827",
|
|
25
|
+
secondBkg: "#1f2937",
|
|
26
|
+
tertiaryColor: "#374151",
|
|
27
|
+
}
|
|
28
|
+
: {
|
|
29
|
+
darkMode: false,
|
|
30
|
+
background: "#ffffff",
|
|
31
|
+
textColor: "#111827",
|
|
32
|
+
lineColor: "#6b7280",
|
|
33
|
+
primaryTextColor: "#111827",
|
|
34
|
+
secondaryTextColor: "#111827",
|
|
35
|
+
tertiaryTextColor: "#111827",
|
|
36
|
+
noteTextColor: "#111827",
|
|
37
|
+
mainBkg: "#ffffff",
|
|
38
|
+
secondBkg: "#f9fafb",
|
|
39
|
+
tertiaryColor: "#f3f4f6",
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Find mermaid blocks (rendered with fixed-height container from server)
|
|
44
|
+
const mermaidBlocks = container.querySelectorAll(".md-mermaid-block");
|
|
45
|
+
if (mermaidBlocks.length === 0) return;
|
|
46
|
+
|
|
47
|
+
const renderPromises = Array.from(mermaidBlocks).map(async (block, index) => {
|
|
48
|
+
const codeElement = block.querySelector("code.language-mermaid");
|
|
49
|
+
const innerContainer = block.querySelector(".h-full.w-full.flex") as HTMLElement;
|
|
50
|
+
if (!codeElement || !innerContainer) return;
|
|
51
|
+
|
|
52
|
+
const code = codeElement.textContent || "";
|
|
53
|
+
const mermaidId = `mermaid-${index}-${Date.now()}`;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Render mermaid to SVG
|
|
57
|
+
const { svg } = await mermaid.render(mermaidId, code);
|
|
58
|
+
|
|
59
|
+
// Remove loading indicator and hidden pre
|
|
60
|
+
const loading = innerContainer.querySelector(".md-mermaid-loading");
|
|
61
|
+
const pre = innerContainer.querySelector("pre");
|
|
62
|
+
loading?.remove();
|
|
63
|
+
pre?.remove();
|
|
64
|
+
|
|
65
|
+
// Create container for SVG with scaling
|
|
66
|
+
const svgContainer = document.createElement("div");
|
|
67
|
+
svgContainer.className = "flex items-center justify-center w-full h-full";
|
|
68
|
+
svgContainer.innerHTML = svg;
|
|
69
|
+
|
|
70
|
+
// Scale SVG to fit container
|
|
71
|
+
const svgElement = svgContainer.querySelector("svg");
|
|
72
|
+
if (svgElement) {
|
|
73
|
+
svgElement.style.maxWidth = "100%";
|
|
74
|
+
svgElement.style.maxHeight = "100%";
|
|
75
|
+
svgElement.style.width = "auto";
|
|
76
|
+
svgElement.style.height = "auto";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
innerContainer.appendChild(svgContainer);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Show error
|
|
82
|
+
const loading = innerContainer.querySelector(".md-mermaid-loading");
|
|
83
|
+
if (loading) {
|
|
84
|
+
loading.innerHTML = `
|
|
85
|
+
<div class="flex flex-col items-center gap-2 text-red-500">
|
|
86
|
+
<i class="ti ti-alert-circle text-xl"></i>
|
|
87
|
+
<span class="text-sm">Invalid mermaid syntax</span>
|
|
88
|
+
</div>
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await Promise.all(renderPromises);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set external links to open in new tab.
|
|
99
|
+
* Call this in onMount() of your client-side component.
|
|
100
|
+
*
|
|
101
|
+
* @param container - The container element containing rendered markdown
|
|
102
|
+
*/
|
|
103
|
+
export function initExternalLinks(container: HTMLElement): void {
|
|
104
|
+
const links = container.querySelectorAll("a");
|
|
105
|
+
links.forEach((link) => {
|
|
106
|
+
if (link.href && !link.href.startsWith(window.location.origin)) {
|
|
107
|
+
link.target = "_blank";
|
|
108
|
+
link.rel = "noopener noreferrer";
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Initialize all markdown enhancements (Mermaid + external links).
|
|
115
|
+
* KaTeX is rendered server-side and doesn't need client initialization.
|
|
116
|
+
* Call this in onMount() of your client-side component.
|
|
117
|
+
*
|
|
118
|
+
* @param container - The container element containing rendered markdown
|
|
119
|
+
*/
|
|
120
|
+
export async function initMarkdownEnhancements(container: HTMLElement): Promise<void> {
|
|
121
|
+
await initMermaid(container);
|
|
122
|
+
initExternalLinks(container);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const markdownClient = {
|
|
126
|
+
initMermaid,
|
|
127
|
+
initExternalLinks,
|
|
128
|
+
initEnhancements: initMarkdownEnhancements,
|
|
129
|
+
initMarkdownEnhancements,
|
|
130
|
+
} as const;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code extension for marked
|
|
3
|
+
*
|
|
4
|
+
* Renders code blocks and inline code with consistent styling.
|
|
5
|
+
* Uses the dependency-free stdlib highlighter for common languages. Custom
|
|
6
|
+
* documentation languages stay shallow on purpose: highlighting must never
|
|
7
|
+
* change source text or require a browser-side parser.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { type Highlighter, highlight } from "@k2b/stdlib";
|
|
11
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
12
|
+
import { escapeHtml } from "../shared";
|
|
13
|
+
|
|
14
|
+
const gqlHighlighter = highlight.compile([
|
|
15
|
+
{ kind: "comment", match: /#[^\n]*/ },
|
|
16
|
+
{ kind: "string", match: /"""[\s\S]*?"""|"(?:\\[\s\S]|[^"\\])*"/ },
|
|
17
|
+
{ kind: "variable", match: /\$[a-zA-Z_][a-zA-Z0-9_]*/ },
|
|
18
|
+
{
|
|
19
|
+
kind: "keyword",
|
|
20
|
+
match: /\b(?:query|mutation|subscription|fragment|on|type|input|enum|interface|union|scalar|schema|extend|directive)\b/,
|
|
21
|
+
},
|
|
22
|
+
{ kind: "number", match: /\b-?\d+(?:\.\d+)?\b/ },
|
|
23
|
+
{ kind: "operator", match: /[!$():=@[\]{|}]+/ },
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const yamlHighlighter = highlight.compile([
|
|
27
|
+
{ kind: "comment", match: /#[^\n]*/ },
|
|
28
|
+
{ kind: "string", match: /"(?:\\[\s\S]|[^"\\])*"|'(?:''|[^'])*'/ },
|
|
29
|
+
{ kind: "variable", match: /[&*!][a-zA-Z_][a-zA-Z0-9_-]*/ },
|
|
30
|
+
{ kind: "keyword", match: /\b(?:true|false|null|yes|no|on|off)\b/i },
|
|
31
|
+
{ kind: "number", match: /\b-?\d+(?:\.\d+)?\b/ },
|
|
32
|
+
{ kind: "operator", match: /---|\.\.\.|[|>{}[\],:&*!?-]+/ },
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const languageHighlighters: Readonly<Record<string, Highlighter>> = {
|
|
36
|
+
bash: highlight.presets.shell,
|
|
37
|
+
gql: gqlHighlighter,
|
|
38
|
+
graphql: gqlHighlighter,
|
|
39
|
+
javascript: highlight.presets.code,
|
|
40
|
+
js: highlight.presets.code,
|
|
41
|
+
jsx: highlight.presets.code,
|
|
42
|
+
script: highlight.presets.code,
|
|
43
|
+
sh: highlight.presets.shell,
|
|
44
|
+
shell: highlight.presets.shell,
|
|
45
|
+
ts: highlight.presets.code,
|
|
46
|
+
tsx: highlight.presets.code,
|
|
47
|
+
typescript: highlight.presets.code,
|
|
48
|
+
yaml: yamlHighlighter,
|
|
49
|
+
yml: yamlHighlighter,
|
|
50
|
+
zsh: highlight.presets.shell,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const renderCode = (source: string, language?: string): string => {
|
|
54
|
+
const highlighter = language ? languageHighlighters[language.toLowerCase()] : undefined;
|
|
55
|
+
return highlighter ? highlighter(source) : escapeHtml(source);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export function codeExtension(): MarkedExtension {
|
|
59
|
+
return {
|
|
60
|
+
renderer: {
|
|
61
|
+
code(token: Tokens.Code): string {
|
|
62
|
+
const { text, lang } = token;
|
|
63
|
+
const langLower = lang?.toLowerCase();
|
|
64
|
+
const renderedCode = renderCode(text, langLower);
|
|
65
|
+
const isMermaid = langLower === "mermaid";
|
|
66
|
+
|
|
67
|
+
// Language class for syntax highlighting / mermaid detection
|
|
68
|
+
const langClass = lang ? ` language-${escapeHtml(lang)}` : "";
|
|
69
|
+
|
|
70
|
+
// Special rendering for mermaid blocks with fixed height container
|
|
71
|
+
if (isMermaid) {
|
|
72
|
+
return (
|
|
73
|
+
`<div class="md-mermaid-block my-3 rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800" style="height: 400px;">` +
|
|
74
|
+
`<div class="h-full w-full flex items-center justify-center p-4">` +
|
|
75
|
+
`<pre class="hidden"><code class="language-mermaid">${renderedCode}</code></pre>` +
|
|
76
|
+
`<div class="md-mermaid-loading text-dimmed text-sm flex items-center gap-2">` +
|
|
77
|
+
`<i class="ti ti-loader-2 animate-spin"></i> Loading diagram...` +
|
|
78
|
+
`</div>` +
|
|
79
|
+
`</div>` +
|
|
80
|
+
`</div>`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Language badge if specified
|
|
85
|
+
const langBadge = lang
|
|
86
|
+
? `<span class="absolute top-2 right-2 text-xs text-gray-400 dark:text-gray-500 font-mono select-none">${escapeHtml(lang)}</span>`
|
|
87
|
+
: "";
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
`<div class="md-code-block relative my-3">` +
|
|
91
|
+
langBadge +
|
|
92
|
+
`<pre class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md p-4 overflow-x-auto">` +
|
|
93
|
+
`<code class="text-sm font-mono text-gray-800 dark:text-gray-200 whitespace-pre${langClass}">${renderedCode}</code>` +
|
|
94
|
+
`</pre>` +
|
|
95
|
+
`</div>`
|
|
96
|
+
);
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
codespan(token: Tokens.Codespan): string {
|
|
100
|
+
const escapedCode = escapeHtml(token.text);
|
|
101
|
+
return `<code class="bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 px-1.5 py-0.5 rounded text-sm font-mono">${escapedCode}</code>`;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
2
|
+
|
|
3
|
+
type GuidedBlock = "compare" | "reference" | "steps";
|
|
4
|
+
|
|
5
|
+
const guidedBlocks = new Set<GuidedBlock>(["compare", "reference", "steps"]);
|
|
6
|
+
const headingMetaPattern = /\s+\{icon="([a-z0-9-]+)"\}\s*$/i;
|
|
7
|
+
|
|
8
|
+
const slug = (value: string): string =>
|
|
9
|
+
value
|
|
10
|
+
.toLocaleLowerCase()
|
|
11
|
+
.normalize("NFKD")
|
|
12
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
13
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
14
|
+
.replace(/^-+|-+$/g, "") || "section";
|
|
15
|
+
|
|
16
|
+
const headingTitleTokens = (token: Tokens.Heading, hasMetadata: boolean): Tokens.Heading["tokens"] => {
|
|
17
|
+
if (!hasMetadata) return token.tokens;
|
|
18
|
+
|
|
19
|
+
const tokens = [...token.tokens];
|
|
20
|
+
const trailingToken = tokens.at(-1);
|
|
21
|
+
if (trailingToken?.type !== "text") return tokens;
|
|
22
|
+
|
|
23
|
+
const raw = trailingToken.raw.replace(headingMetaPattern, "");
|
|
24
|
+
const text = trailingToken.text.replace(headingMetaPattern, "");
|
|
25
|
+
if (!raw && !text) {
|
|
26
|
+
tokens.pop();
|
|
27
|
+
} else {
|
|
28
|
+
tokens[tokens.length - 1] = { ...trailingToken, raw, text };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return tokens;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Help-only Markdown affordances. The source stays useful as plain Markdown:
|
|
36
|
+
* the directives merely add reading semantics to otherwise ordinary lists.
|
|
37
|
+
*/
|
|
38
|
+
export function guidedHelpExtension(): MarkedExtension {
|
|
39
|
+
const headingIds = new Map<string, number>();
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
hooks: {
|
|
43
|
+
preprocess(source) {
|
|
44
|
+
headingIds.clear();
|
|
45
|
+
return source;
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
renderer: {
|
|
49
|
+
paragraph(token: Tokens.Paragraph) {
|
|
50
|
+
if (token.tokens.length !== 1 || token.tokens[0]?.type !== "strong") return false;
|
|
51
|
+
return `<p class="help-eyebrow">${this.parser.parseInline(token.tokens)}</p>`;
|
|
52
|
+
},
|
|
53
|
+
heading(token: Tokens.Heading) {
|
|
54
|
+
const metadata = token.text.match(headingMetaPattern);
|
|
55
|
+
const title = metadata ? token.text.slice(0, metadata.index).trim() : token.text;
|
|
56
|
+
const baseId = slug(title);
|
|
57
|
+
const occurrence = headingIds.get(baseId) ?? 0;
|
|
58
|
+
headingIds.set(baseId, occurrence + 1);
|
|
59
|
+
const id = occurrence === 0 ? baseId : `${baseId}-${occurrence + 1}`;
|
|
60
|
+
|
|
61
|
+
if (token.depth === 2) {
|
|
62
|
+
const icon = metadata?.[1] ? `ti ti-${metadata[1]}` : "ti ti-point";
|
|
63
|
+
return `<h2 id="${id}" class="help-section-title" data-help-icon="${icon}">
|
|
64
|
+
<span class="help-section-icon" aria-hidden="true"><i class="${icon}"></i></span>
|
|
65
|
+
<span>${this.parser.parseInline(headingTitleTokens(token, Boolean(metadata)))}</span>
|
|
66
|
+
</h2>`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return `<h${token.depth} id="${id}">${this.parser.parseInline(token.tokens)}</h${token.depth}>`;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
extensions: [
|
|
73
|
+
{
|
|
74
|
+
name: "guidedHelpBlock",
|
|
75
|
+
level: "block",
|
|
76
|
+
start(source) {
|
|
77
|
+
return source.match(/^:::(?:compare|reference|steps)\b/)?.index;
|
|
78
|
+
},
|
|
79
|
+
tokenizer(source) {
|
|
80
|
+
const match = source.match(/^:::(compare|reference|steps)[ \t]*\n([\s\S]*?)\n:::(?:\n|$)/);
|
|
81
|
+
if (!match) return undefined;
|
|
82
|
+
const kind = match[1] as GuidedBlock;
|
|
83
|
+
if (!guidedBlocks.has(kind)) return undefined;
|
|
84
|
+
const content = match[2]?.trim() ?? "";
|
|
85
|
+
return {
|
|
86
|
+
type: "guidedHelpBlock",
|
|
87
|
+
raw: match[0],
|
|
88
|
+
kind,
|
|
89
|
+
tokens: this.lexer.blockTokens(content),
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
renderer(token: Tokens.Generic) {
|
|
93
|
+
const kind = token.kind as GuidedBlock;
|
|
94
|
+
return `<div class="help-${kind}">${this.parser.parse(token.tokens as Tokens.Generic[])}</div>`;
|
|
95
|
+
},
|
|
96
|
+
childTokens: ["tokens"],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Images extension for marked
|
|
3
|
+
*
|
|
4
|
+
* Renders images with the same visual style as the CodeMirror editor:
|
|
5
|
+
* - Centered figure with max-height constraint
|
|
6
|
+
* - Rounded border
|
|
7
|
+
* - Optional caption from alt text
|
|
8
|
+
*
|
|
9
|
+
* Supports an optional Pandoc/HFM-style size suffix on the URL:
|
|
10
|
+
*
|
|
11
|
+
*  // both
|
|
12
|
+
*  // width only
|
|
13
|
+
*  // height only
|
|
14
|
+
*
|
|
15
|
+
* Marked's default tokenizer treats the trailing ` =WxH` as part of the
|
|
16
|
+
* URL — we parse and strip it here before emitting the `<img>` tag.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
20
|
+
import { escapeHtml, IMAGE_STYLES } from "../shared";
|
|
21
|
+
|
|
22
|
+
const SIZE_SUFFIX_REGEX = /\s+=(\d+)?x(\d+)?$/;
|
|
23
|
+
|
|
24
|
+
type ParsedImage = {
|
|
25
|
+
href: string;
|
|
26
|
+
width: string | null;
|
|
27
|
+
height: string | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const parseImageUrl = (href: string): ParsedImage => {
|
|
31
|
+
const match = SIZE_SUFFIX_REGEX.exec(href);
|
|
32
|
+
if (!match) return { href, width: null, height: null };
|
|
33
|
+
return {
|
|
34
|
+
href: href.slice(0, match.index),
|
|
35
|
+
width: match[1] ?? null,
|
|
36
|
+
height: match[2] ?? null,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function imagesExtension(): MarkedExtension {
|
|
41
|
+
return {
|
|
42
|
+
renderer: {
|
|
43
|
+
image(token: Tokens.Image): string {
|
|
44
|
+
const { href: rawHref, title, text: alt } = token;
|
|
45
|
+
const { href, width, height } = parseImageUrl(rawHref);
|
|
46
|
+
|
|
47
|
+
// Build the image element
|
|
48
|
+
const imgAttrs = [`src="${escapeHtml(href)}"`, `alt="${escapeHtml(alt || "")}"`, `loading="lazy"`, `class="${IMAGE_STYLES.img}"`];
|
|
49
|
+
|
|
50
|
+
if (title) imgAttrs.push(`title="${escapeHtml(title)}"`);
|
|
51
|
+
if (width) imgAttrs.push(`width="${width}"`);
|
|
52
|
+
if (height) imgAttrs.push(`height="${height}"`);
|
|
53
|
+
|
|
54
|
+
// Inline style overrides the global `max-height: 400px` from
|
|
55
|
+
// IMAGE_STYLES.img only when the user opted into a custom size.
|
|
56
|
+
if (width || height) {
|
|
57
|
+
const styles: string[] = ["max-height: none"];
|
|
58
|
+
if (width) styles.push(`max-width: ${width}px`);
|
|
59
|
+
imgAttrs.push(`style="${styles.join("; ")}"`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const imgHtml = `<img ${imgAttrs.join(" ")} />`;
|
|
63
|
+
|
|
64
|
+
// Build caption if alt text provided
|
|
65
|
+
const captionHtml = alt ? `<figcaption class="${IMAGE_STYLES.caption}">${escapeHtml(alt)}</figcaption>` : "";
|
|
66
|
+
|
|
67
|
+
// Wrap in figure for centering
|
|
68
|
+
return (
|
|
69
|
+
`<div class="${IMAGE_STYLES.wrapper}">` +
|
|
70
|
+
`<figure class="${IMAGE_STYLES.figure}">` +
|
|
71
|
+
imgHtml +
|
|
72
|
+
captionHtml +
|
|
73
|
+
`</figure>` +
|
|
74
|
+
`</div>`
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Info Blocks Extension for Marked
|
|
3
|
+
*
|
|
4
|
+
* Renders custom info blocks with syntax:
|
|
5
|
+
* :::note
|
|
6
|
+
* Content here
|
|
7
|
+
* :::
|
|
8
|
+
*
|
|
9
|
+
* The type follows the colons directly — `::: note` with a space does not
|
|
10
|
+
* match. An optional argument after the type overrides the label:
|
|
11
|
+
* `:::warning Before deleting`.
|
|
12
|
+
*
|
|
13
|
+
* Supported types: note, info, success, warning, danger
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
17
|
+
import { NOTICE_CARD_CLASSES, NOTICE_CARD_ICONS, type NoticeTone } from "@k2b/ui";
|
|
18
|
+
import { escapeHtml } from "../shared";
|
|
19
|
+
|
|
20
|
+
type BlockType = "note" | "info" | "success" | "warning" | "danger";
|
|
21
|
+
|
|
22
|
+
const blockConfig: Record<BlockType, { label: string; tone: NoticeTone }> = {
|
|
23
|
+
note: {
|
|
24
|
+
label: "Note",
|
|
25
|
+
tone: "neutral",
|
|
26
|
+
},
|
|
27
|
+
info: {
|
|
28
|
+
label: "Info",
|
|
29
|
+
tone: "info",
|
|
30
|
+
},
|
|
31
|
+
success: {
|
|
32
|
+
label: "Success",
|
|
33
|
+
tone: "success",
|
|
34
|
+
},
|
|
35
|
+
warning: {
|
|
36
|
+
label: "Warning",
|
|
37
|
+
tone: "warning",
|
|
38
|
+
},
|
|
39
|
+
danger: {
|
|
40
|
+
label: "Danger",
|
|
41
|
+
tone: "danger",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const renderInlineContent = (content: string): string => {
|
|
46
|
+
return content
|
|
47
|
+
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
48
|
+
.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<em>$1</em>")
|
|
49
|
+
.replace(/`([^`]+)`/g, '<code class="bg-black/10 dark:bg-white/10 px-1 py-0.5 rounded text-sm">$1</code>')
|
|
50
|
+
.replace(/\n/g, "<br>");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export function infoBlocksExtension(): MarkedExtension {
|
|
54
|
+
return {
|
|
55
|
+
extensions: [
|
|
56
|
+
{
|
|
57
|
+
name: "infoBlock",
|
|
58
|
+
level: "block",
|
|
59
|
+
start(src: string) {
|
|
60
|
+
return src.match(/^:::/)?.index;
|
|
61
|
+
},
|
|
62
|
+
tokenizer(src: string) {
|
|
63
|
+
const match = src.match(/^:::(\w+)(?:[ \t]+([^\n]+))?\s*\n([\s\S]*?)\n:::/);
|
|
64
|
+
if (!match) return undefined;
|
|
65
|
+
|
|
66
|
+
const typeStr = match[1]?.toLowerCase() as BlockType;
|
|
67
|
+
if (!blockConfig[typeStr]) return undefined;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
type: "infoBlock",
|
|
71
|
+
raw: match[0],
|
|
72
|
+
blockType: typeStr,
|
|
73
|
+
title: match[2]?.trim(),
|
|
74
|
+
content: match[3]?.trim() ?? "",
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
renderer(token: Tokens.Generic) {
|
|
78
|
+
const blockType = token.blockType as BlockType;
|
|
79
|
+
const config = blockConfig[blockType];
|
|
80
|
+
const content = escapeHtml(token.content as string);
|
|
81
|
+
const title = escapeHtml((token.title as string | undefined) ?? config.label);
|
|
82
|
+
const renderedContent = renderInlineContent(content);
|
|
83
|
+
|
|
84
|
+
return `<aside class="${NOTICE_CARD_CLASSES.root}" data-tone="${config.tone}">
|
|
85
|
+
<div class="${NOTICE_CARD_CLASSES.inner}">
|
|
86
|
+
<i class="${NOTICE_CARD_ICONS[config.tone]} ${NOTICE_CARD_CLASSES.icon}" aria-hidden="true"></i>
|
|
87
|
+
<div class="${NOTICE_CARD_CLASSES.content}">
|
|
88
|
+
<p class="${NOTICE_CARD_CLASSES.title}">${title}</p>
|
|
89
|
+
<div class="${NOTICE_CARD_CLASSES.body}">${renderedContent}</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</aside>`;
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KaTeX extension for marked
|
|
3
|
+
*
|
|
4
|
+
* Renders LaTeX math expressions server-side:
|
|
5
|
+
* - Inline: $..$ or \(..\)
|
|
6
|
+
* - Block: $$...$$ or \[..\] or ```math
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import katex from "katex";
|
|
10
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Token shape produced by the math tokenizers below. The marked extension API
|
|
14
|
+
* types renderer params as `Tokens.Generic` (open `[index: string]: any`), so
|
|
15
|
+
* we narrow at the call boundary by reading `latex` off the typed sub-shape.
|
|
16
|
+
*/
|
|
17
|
+
type MathToken = Tokens.Generic & { latex: string };
|
|
18
|
+
|
|
19
|
+
function renderBlockMath(latex: string): string {
|
|
20
|
+
try {
|
|
21
|
+
const html = katex.renderToString(latex, {
|
|
22
|
+
throwOnError: false,
|
|
23
|
+
displayMode: true,
|
|
24
|
+
});
|
|
25
|
+
return `<div class="md-katex-block my-3 flex items-center justify-center">${html}</div>`;
|
|
26
|
+
} catch {
|
|
27
|
+
return `<div class="md-katex-block my-3 flex items-center justify-center text-red-500 text-sm"><i class="ti ti-alert-circle mr-1"></i> Invalid LaTeX</div>`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function renderInlineMath(latex: string): string {
|
|
32
|
+
try {
|
|
33
|
+
const html = katex.renderToString(latex, {
|
|
34
|
+
throwOnError: false,
|
|
35
|
+
displayMode: false,
|
|
36
|
+
});
|
|
37
|
+
return `<span class="md-katex-inline">${html}</span>`;
|
|
38
|
+
} catch {
|
|
39
|
+
return `<span class="md-katex-inline text-red-500 font-mono text-sm">$${latex}$</span>`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function katexExtension(): MarkedExtension {
|
|
44
|
+
return {
|
|
45
|
+
extensions: [
|
|
46
|
+
// ```math code blocks
|
|
47
|
+
{
|
|
48
|
+
name: "mathCodeBlock",
|
|
49
|
+
level: "block",
|
|
50
|
+
start(src: string) {
|
|
51
|
+
const match = src.match(/^```math/m);
|
|
52
|
+
return match ? match.index : undefined;
|
|
53
|
+
},
|
|
54
|
+
tokenizer(src: string) {
|
|
55
|
+
const match = /^```math\n([\s\S]*?)\n```/.exec(src);
|
|
56
|
+
if (match) {
|
|
57
|
+
return {
|
|
58
|
+
type: "mathCodeBlock",
|
|
59
|
+
raw: match[0],
|
|
60
|
+
latex: match[1]!.trim(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
},
|
|
65
|
+
renderer(token: Tokens.Generic) {
|
|
66
|
+
const latex = (token as MathToken).latex;
|
|
67
|
+
return renderBlockMath(latex);
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// Block math with $$ ... $$
|
|
71
|
+
{
|
|
72
|
+
name: "blockMath",
|
|
73
|
+
level: "block",
|
|
74
|
+
start(src: string) {
|
|
75
|
+
const match = src.match(/\$\$/);
|
|
76
|
+
return match ? match.index : undefined;
|
|
77
|
+
},
|
|
78
|
+
tokenizer(src: string) {
|
|
79
|
+
const match = /^\$\$([\s\S]+?)\$\$/.exec(src);
|
|
80
|
+
if (match) {
|
|
81
|
+
return {
|
|
82
|
+
type: "blockMath",
|
|
83
|
+
raw: match[0],
|
|
84
|
+
latex: match[1]!.trim(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
},
|
|
89
|
+
renderer(token: Tokens.Generic) {
|
|
90
|
+
const latex = (token as MathToken).latex;
|
|
91
|
+
return renderBlockMath(latex);
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
// Inline math with $ ... $
|
|
95
|
+
{
|
|
96
|
+
name: "inlineMath",
|
|
97
|
+
level: "inline",
|
|
98
|
+
start(src: string) {
|
|
99
|
+
const match = src.match(/(?<!\$)\$(?!\$)/);
|
|
100
|
+
return match ? match.index : undefined;
|
|
101
|
+
},
|
|
102
|
+
tokenizer(src: string) {
|
|
103
|
+
const match = /^(?<!\$)\$(?!\$)([^$\n]+)\$(?!\$)/.exec(src);
|
|
104
|
+
if (match) {
|
|
105
|
+
return {
|
|
106
|
+
type: "inlineMath",
|
|
107
|
+
raw: match[0],
|
|
108
|
+
latex: match[1]!.trim(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
},
|
|
113
|
+
renderer(token: Tokens.Generic) {
|
|
114
|
+
const latex = (token as MathToken).latex;
|
|
115
|
+
return renderInlineMath(latex);
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links extension for marked
|
|
3
|
+
*
|
|
4
|
+
* Renders links with the same visual style as the CodeMirror editor:
|
|
5
|
+
* - Shows [label] in bold followed by an arrow icon
|
|
6
|
+
* - Opens in new tab with noopener,noreferrer
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
10
|
+
import { escapeHtml, LINK_STYLES } from "../shared";
|
|
11
|
+
|
|
12
|
+
export type LinksExtensionOptions = {
|
|
13
|
+
/** Open external links in a new tab by default. */
|
|
14
|
+
externalTarget?: "_blank" | "_self";
|
|
15
|
+
/** Existing content keeps `_blank`; help opts into in-app navigation. */
|
|
16
|
+
internalTarget?: "_blank" | "_self";
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const isExternalHref = (href: string) => /^(?:https?:)?\/\//i.test(href);
|
|
20
|
+
|
|
21
|
+
export function linksExtension(options: LinksExtensionOptions = {}): MarkedExtension {
|
|
22
|
+
return {
|
|
23
|
+
renderer: {
|
|
24
|
+
link(token: Tokens.Link): string {
|
|
25
|
+
const { href, title, text } = token;
|
|
26
|
+
|
|
27
|
+
// Build title attribute if provided
|
|
28
|
+
const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
|
|
29
|
+
|
|
30
|
+
// Match CodeMirror style: [label] with arrow icon
|
|
31
|
+
const target = isExternalHref(href) ? (options.externalTarget ?? "_blank") : (options.internalTarget ?? "_blank");
|
|
32
|
+
const targetAttrs = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : "";
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
`<a href="${escapeHtml(href)}"${titleAttr}${targetAttrs} class="${LINK_STYLES.link}">` +
|
|
36
|
+
`<span class="${LINK_STYLES.label}">[${escapeHtml(text)}]</span>` +
|
|
37
|
+
`<i class="${LINK_STYLES.icon} ti ti-arrow-up-right text-xs" aria-hidden="true"></i>` +
|
|
38
|
+
`</a>`
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|