@agent-native/core 0.37.3 → 0.38.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 +19 -6
- package/dist/action.d.ts +60 -2
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +6 -2
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts +12 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -11
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +2 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/catalog.json +2 -2
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +15 -0
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/index.js +10 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plan-publish-store.d.ts +52 -0
- package/dist/cli/plan-publish-store.d.ts.map +1 -0
- package/dist/cli/plan-publish-store.js +103 -0
- package/dist/cli/plan-publish-store.js.map +1 -0
- package/dist/cli/skills.d.ts +29 -4
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +851 -275
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/templates-meta.js +12 -12
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +3 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +65 -15
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +20 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +12 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-engine-key.d.ts +24 -0
- package/dist/client/agent-engine-key.d.ts.map +1 -0
- package/dist/client/agent-engine-key.js +49 -0
- package/dist/client/agent-engine-key.js.map +1 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +34 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/BlockView.d.ts +26 -0
- package/dist/client/blocks/BlockView.d.ts.map +1 -0
- package/dist/client/blocks/BlockView.js +24 -0
- package/dist/client/blocks/BlockView.js.map +1 -0
- package/dist/client/blocks/SchemaBlockEditor.d.ts +25 -0
- package/dist/client/blocks/SchemaBlockEditor.d.ts.map +1 -0
- package/dist/client/blocks/SchemaBlockEditor.js +72 -0
- package/dist/client/blocks/SchemaBlockEditor.js.map +1 -0
- package/dist/client/blocks/agent.d.ts +30 -0
- package/dist/client/blocks/agent.d.ts.map +1 -0
- package/dist/client/blocks/agent.js +61 -0
- package/dist/client/blocks/agent.js.map +1 -0
- package/dist/client/blocks/index.d.ts +34 -0
- package/dist/client/blocks/index.d.ts.map +1 -0
- package/dist/client/blocks/index.js +42 -0
- package/dist/client/blocks/index.js.map +1 -0
- package/dist/client/blocks/library/checklist.config.d.ts +36 -0
- package/dist/client/blocks/library/checklist.config.d.ts.map +1 -0
- package/dist/client/blocks/library/checklist.config.js +25 -0
- package/dist/client/blocks/library/checklist.config.js.map +1 -0
- package/dist/client/blocks/library/checklist.d.ts +26 -0
- package/dist/client/blocks/library/checklist.d.ts.map +1 -0
- package/dist/client/blocks/library/checklist.js +76 -0
- package/dist/client/blocks/library/checklist.js.map +1 -0
- package/dist/client/blocks/library/code-tabs.config.d.ts +36 -0
- package/dist/client/blocks/library/code-tabs.config.d.ts.map +1 -0
- package/dist/client/blocks/library/code-tabs.config.js +30 -0
- package/dist/client/blocks/library/code-tabs.config.js.map +1 -0
- package/dist/client/blocks/library/code-tabs.d.ts +3 -0
- package/dist/client/blocks/library/code-tabs.d.ts.map +1 -0
- package/dist/client/blocks/library/code-tabs.js +165 -0
- package/dist/client/blocks/library/code-tabs.js.map +1 -0
- package/dist/client/blocks/library/html.config.d.ts +37 -0
- package/dist/client/blocks/library/html.config.d.ts.map +1 -0
- package/dist/client/blocks/library/html.config.js +46 -0
- package/dist/client/blocks/library/html.config.js.map +1 -0
- package/dist/client/blocks/library/html.d.ts +21 -0
- package/dist/client/blocks/library/html.d.ts.map +1 -0
- package/dist/client/blocks/library/html.js +69 -0
- package/dist/client/blocks/library/html.js.map +1 -0
- package/dist/client/blocks/library/table.config.d.ts +30 -0
- package/dist/client/blocks/library/table.config.d.ts.map +1 -0
- package/dist/client/blocks/library/table.config.js +22 -0
- package/dist/client/blocks/library/table.config.js.map +1 -0
- package/dist/client/blocks/library/table.d.ts +8 -0
- package/dist/client/blocks/library/table.d.ts.map +1 -0
- package/dist/client/blocks/library/table.js +107 -0
- package/dist/client/blocks/library/table.js.map +1 -0
- package/dist/client/blocks/library/tabs.config.d.ts +56 -0
- package/dist/client/blocks/library/tabs.config.d.ts.map +1 -0
- package/dist/client/blocks/library/tabs.config.js +36 -0
- package/dist/client/blocks/library/tabs.config.js.map +1 -0
- package/dist/client/blocks/library/tabs.d.ts +20 -0
- package/dist/client/blocks/library/tabs.d.ts.map +1 -0
- package/dist/client/blocks/library/tabs.js +123 -0
- package/dist/client/blocks/library/tabs.js.map +1 -0
- package/dist/client/blocks/mdx.d.ts +74 -0
- package/dist/client/blocks/mdx.d.ts.map +1 -0
- package/dist/client/blocks/mdx.js +205 -0
- package/dist/client/blocks/mdx.js.map +1 -0
- package/dist/client/blocks/provider.d.ts +25 -0
- package/dist/client/blocks/provider.d.ts.map +1 -0
- package/dist/client/blocks/provider.js +19 -0
- package/dist/client/blocks/provider.js.map +1 -0
- package/dist/client/blocks/registry.d.ts +24 -0
- package/dist/client/blocks/registry.d.ts.map +1 -0
- package/dist/client/blocks/registry.js +50 -0
- package/dist/client/blocks/registry.js.map +1 -0
- package/dist/client/blocks/schema-form/introspect.d.ts +31 -0
- package/dist/client/blocks/schema-form/introspect.d.ts.map +1 -0
- package/dist/client/blocks/schema-form/introspect.js +164 -0
- package/dist/client/blocks/schema-form/introspect.js.map +1 -0
- package/dist/client/blocks/server.d.ts +22 -0
- package/dist/client/blocks/server.d.ts.map +1 -0
- package/dist/client/blocks/server.js +25 -0
- package/dist/client/blocks/server.js.map +1 -0
- package/dist/client/blocks/types.d.ts +212 -0
- package/dist/client/blocks/types.d.ts.map +1 -0
- package/dist/client/blocks/types.js +5 -0
- package/dist/client/blocks/types.js.map +1 -0
- package/dist/client/composer/ComposerPlusMenu.js +10 -1
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/guided-questions.d.ts +68 -0
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +158 -3
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/index.d.ts +5 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +15 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts +37 -0
- package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/BubbleToolbar.js +161 -0
- package/dist/client/rich-markdown-editor/BubbleToolbar.js.map +1 -0
- package/dist/client/rich-markdown-editor/ImageExtension.d.ts +63 -0
- package/dist/client/rich-markdown-editor/ImageExtension.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/ImageExtension.js +242 -0
- package/dist/client/rich-markdown-editor/ImageExtension.js.map +1 -0
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts +51 -0
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.js +37 -0
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.js.map +1 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +61 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +121 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -0
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +36 -0
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/SlashCommandMenu.js +193 -0
- package/dist/client/rich-markdown-editor/SlashCommandMenu.js.map +1 -0
- package/dist/client/rich-markdown-editor/extensions.d.ts +166 -0
- package/dist/client/rich-markdown-editor/extensions.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/extensions.js +222 -0
- package/dist/client/rich-markdown-editor/extensions.js.map +1 -0
- package/dist/client/rich-markdown-editor/index.d.ts +9 -0
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/index.js +9 -0
- package/dist/client/rich-markdown-editor/index.js.map +1 -0
- package/dist/client/rich-markdown-editor/uploadEditorImage.d.ts +18 -0
- package/dist/client/rich-markdown-editor/uploadEditorImage.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/uploadEditorImage.js +57 -0
- package/dist/client/rich-markdown-editor/uploadEditorImage.js.map +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +91 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +342 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -0
- package/dist/client/track.d.ts +25 -0
- package/dist/client/track.d.ts.map +1 -0
- package/dist/client/track.js +53 -0
- package/dist/client/track.js.map +1 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +6 -0
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-session.d.ts +3 -2
- package/dist/client/use-session.d.ts.map +1 -1
- package/dist/client/use-session.js +3 -2
- package/dist/client/use-session.js.map +1 -1
- package/dist/deploy/build.d.ts +5 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +67 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/schema.d.ts +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +9 -2
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +35 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/provider-api/index.d.ts +1 -1
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/scripts/docs/search.d.ts.map +1 -1
- package/dist/scripts/docs/search.js +5 -2
- package/dist/scripts/docs/search.js.map +1 -1
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +16 -3
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +2 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +30 -4
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +65 -19
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +8 -1
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/agents-bundle.d.ts +27 -1
- package/dist/server/agents-bundle.d.ts.map +1 -1
- package/dist/server/agents-bundle.js +41 -3
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +76 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +60 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +160 -22
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +6 -0
- package/dist/server/sentry.js.map +1 -1
- package/dist/server/social-og-image.d.ts +2 -1
- package/dist/server/social-og-image.d.ts.map +1 -1
- package/dist/server/social-og-image.js +24 -4
- package/dist/server/social-og-image.js.map +1 -1
- package/dist/sharing/schema.d.ts +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/rich-markdown-editor.css +439 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +4 -1
- package/dist/templates/default/.agents/skills/security/SKILL.md +13 -4
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +15 -3
- package/dist/templates/default/AGENTS.md +1 -0
- package/dist/templates/default/DEVELOPING.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +10 -3
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +98 -10
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +45 -3
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +37 -4
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +9 -4
- package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +106 -0
- package/dist/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +53 -0
- package/dist/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +62 -61
- package/dist/templates/workspace-core/.agents/skills/context-xray/SKILL.md +47 -0
- package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +52 -1
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +95 -433
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +285 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/examples.md +259 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +398 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +157 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +17 -0
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +13 -2
- package/dist/templates/workspace-core/.agents/skills/mvp-followup/SKILL.md +51 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +14 -4
- package/dist/templates/workspace-core/.agents/skills/onboarding/SKILL.md +13 -1
- package/dist/templates/workspace-core/.agents/skills/portability/SKILL.md +27 -5
- package/dist/templates/workspace-core/.agents/skills/qa/SKILL.md +24 -8
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +53 -7
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +43 -10
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +43 -14
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +50 -1
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +4 -2
- package/dist/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +11 -1
- package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +15 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +5 -1
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +48 -19
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +7 -3
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +13 -6
- package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +236 -0
- package/dist/templates/workspace-core/AGENTS.md +5 -1
- package/dist/templates/workspace-root/AGENTS.md +5 -2
- package/dist/tracking/route.d.ts +43 -0
- package/dist/tracking/route.d.ts.map +1 -0
- package/dist/tracking/route.js +85 -0
- package/dist/tracking/route.js.map +1 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +15 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/a2a-protocol.md +18 -4
- package/docs/content/actions.md +87 -0
- package/docs/content/agent-mentions.md +2 -1
- package/docs/content/authentication.md +2 -1
- package/docs/content/client.md +64 -13
- package/docs/content/cloneable-saas.md +1 -1
- package/docs/content/code-agents-ui.md +17 -11
- package/docs/content/context-awareness.md +23 -28
- package/docs/content/creating-templates.md +1 -1
- package/docs/content/drop-in-agent.md +2 -0
- package/docs/content/getting-started.md +2 -2
- package/docs/content/key-concepts.md +2 -2
- package/docs/content/messaging.md +57 -15
- package/docs/content/migration-workbench.md +1 -1
- package/docs/content/multi-app-workspace.md +1 -1
- package/docs/content/multi-tenancy.md +17 -15
- package/docs/content/real-time-collaboration.md +1 -1
- package/docs/content/recurring-jobs.md +1 -1
- package/docs/content/security.md +2 -2
- package/docs/content/server.md +4 -4
- package/docs/content/skills-guide.md +30 -0
- package/docs/content/template-analytics.md +2 -2
- package/docs/content/template-assets.md +17 -1
- package/docs/content/template-brain.md +2 -2
- package/docs/content/template-calendar.md +1 -1
- package/docs/content/template-clips.md +3 -3
- package/docs/content/template-content.md +2 -2
- package/docs/content/template-design.md +2 -2
- package/docs/content/template-dispatch.md +3 -3
- package/docs/content/template-forms.md +14 -2
- package/docs/content/template-mail.md +1 -3
- package/docs/content/template-plan.md +118 -0
- package/docs/content/template-slides.md +5 -4
- package/docs/content/template-starter.md +4 -4
- package/docs/content/template-videos.md +6 -11
- package/docs/content/tracking.md +21 -1
- package/docs/content/visual-plans.md +72 -0
- package/docs/content/workspace.md +9 -9
- package/package.json +26 -11
- package/src/templates/default/.agents/skills/actions/SKILL.md +4 -1
- package/src/templates/default/.agents/skills/security/SKILL.md +13 -4
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +15 -3
- package/src/templates/default/AGENTS.md +1 -0
- package/src/templates/default/DEVELOPING.md +2 -0
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +10 -3
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +98 -10
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +45 -3
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +37 -4
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +9 -4
- package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +106 -0
- package/src/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +53 -0
- package/src/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +62 -61
- package/src/templates/workspace-core/.agents/skills/context-xray/SKILL.md +47 -0
- package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +52 -1
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +95 -433
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +285 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/examples.md +259 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +398 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +157 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +17 -0
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +13 -2
- package/src/templates/workspace-core/.agents/skills/mvp-followup/SKILL.md +51 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +14 -4
- package/src/templates/workspace-core/.agents/skills/onboarding/SKILL.md +13 -1
- package/src/templates/workspace-core/.agents/skills/portability/SKILL.md +27 -5
- package/src/templates/workspace-core/.agents/skills/qa/SKILL.md +24 -8
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +53 -7
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +43 -10
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +43 -14
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +50 -1
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +4 -2
- package/src/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +11 -1
- package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +15 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +5 -1
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +48 -19
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +7 -3
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +13 -6
- package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +236 -0
- package/src/templates/workspace-core/AGENTS.md +5 -1
- package/src/templates/workspace-root/AGENTS.md +5 -2
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import Image from "@tiptap/extension-image";
|
|
2
|
+
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
3
|
+
const sharedImageUploadPluginKey = new PluginKey("an-shared-image-upload");
|
|
4
|
+
/** A monotonically increasing id so concurrent uploads patch the right node. */
|
|
5
|
+
let uploadCounter = 0;
|
|
6
|
+
function nextUploadId() {
|
|
7
|
+
uploadCounter += 1;
|
|
8
|
+
return `an-img-${Date.now()}-${uploadCounter}`;
|
|
9
|
+
}
|
|
10
|
+
/** Image files only — never ingest non-image clipboard/drop payloads. */
|
|
11
|
+
function imageFilesFrom(data) {
|
|
12
|
+
if (!data)
|
|
13
|
+
return [];
|
|
14
|
+
return Array.from(data.files ?? []).filter((file) => file.type.startsWith("image/"));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Insert a placeholder `image` node (empty `src`, transient `uploadId`) at
|
|
18
|
+
* `pos` for every file, then resolve each upload and patch the matching node's
|
|
19
|
+
* `src`. Mirrors Content's optimistic upload flow but is self-contained in the
|
|
20
|
+
* shared extension so it works for ANY editor wrapper (the plan's
|
|
21
|
+
* `SharedRichEditor` and Content's hand-rolled `useEditor` alike) without a new
|
|
22
|
+
* `editorProps` seam.
|
|
23
|
+
*/
|
|
24
|
+
function uploadAndInsertImages(view, files, pos, upload) {
|
|
25
|
+
if (files.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
const pending = [];
|
|
28
|
+
// Insert all placeholders first, top-down from `pos`, so multi-file
|
|
29
|
+
// paste/drop keeps source order.
|
|
30
|
+
let insertAt = pos;
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
const uploadId = nextUploadId();
|
|
33
|
+
const node = view.state.schema.nodes.image?.create({
|
|
34
|
+
src: "",
|
|
35
|
+
alt: "",
|
|
36
|
+
uploadId,
|
|
37
|
+
});
|
|
38
|
+
if (!node)
|
|
39
|
+
continue;
|
|
40
|
+
const tr = view.state.tr.insert(insertAt, node);
|
|
41
|
+
view.dispatch(tr);
|
|
42
|
+
pending.push({ uploadId, file });
|
|
43
|
+
// Advance past the inserted atom for the next placeholder.
|
|
44
|
+
insertAt += node.nodeSize;
|
|
45
|
+
}
|
|
46
|
+
for (const item of pending) {
|
|
47
|
+
void (async () => {
|
|
48
|
+
try {
|
|
49
|
+
const { src, alt } = await upload(item.file);
|
|
50
|
+
if (!view.dom.isConnected || view.isDestroyed)
|
|
51
|
+
return;
|
|
52
|
+
patchUploadNode(view, item.uploadId, { src, alt: alt ?? "" });
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error("Image upload failed:", error);
|
|
56
|
+
// Drop the placeholder so a failed upload does not leave an empty box.
|
|
57
|
+
if (view.dom.isConnected && !view.isDestroyed) {
|
|
58
|
+
removeUploadNode(view, item.uploadId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Find a placeholder node by its transient `uploadId` and return its position. */
|
|
65
|
+
function findUploadNode(view, uploadId) {
|
|
66
|
+
let found = null;
|
|
67
|
+
view.state.doc.descendants((node, pos) => {
|
|
68
|
+
if (found)
|
|
69
|
+
return false;
|
|
70
|
+
if (node.type.name === "image" && node.attrs.uploadId === uploadId) {
|
|
71
|
+
found = { pos, nodeSize: node.nodeSize };
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
return found;
|
|
77
|
+
}
|
|
78
|
+
/** Patch the resolved `src`/`alt` onto the placeholder and clear `uploadId`. */
|
|
79
|
+
function patchUploadNode(view, uploadId, attrs) {
|
|
80
|
+
const target = findUploadNode(view, uploadId);
|
|
81
|
+
if (!target)
|
|
82
|
+
return;
|
|
83
|
+
const node = view.state.doc.nodeAt(target.pos);
|
|
84
|
+
if (!node)
|
|
85
|
+
return;
|
|
86
|
+
const tr = view.state.tr.setNodeMarkup(target.pos, undefined, {
|
|
87
|
+
...node.attrs,
|
|
88
|
+
src: attrs.src,
|
|
89
|
+
alt: attrs.alt,
|
|
90
|
+
uploadId: null,
|
|
91
|
+
});
|
|
92
|
+
view.dispatch(tr);
|
|
93
|
+
}
|
|
94
|
+
/** Remove a placeholder whose upload failed. */
|
|
95
|
+
function removeUploadNode(view, uploadId) {
|
|
96
|
+
const target = findUploadNode(view, uploadId);
|
|
97
|
+
if (!target)
|
|
98
|
+
return;
|
|
99
|
+
const tr = view.state.tr.delete(target.pos, target.pos + target.nodeSize);
|
|
100
|
+
view.dispatch(tr);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The SHARED block-level image node. Built on `@tiptap/extension-image` (a
|
|
104
|
+
* node named `image`), so:
|
|
105
|
+
*
|
|
106
|
+
* - **GFM serialization** emits pure `` markdown via a block-aware
|
|
107
|
+
* `addStorage().markdown.serialize` (it calls `closeBlock` so a block image
|
|
108
|
+
* keeps its blank-line separator, byte-stable). It emits NO `<img width>`
|
|
109
|
+
* HTML, so plans stay source-syncable under the GFM `html:false` contract.
|
|
110
|
+
* - **Paste / drop** of local image files is handled by a self-contained
|
|
111
|
+
* ProseMirror plugin that calls the injected {@link ImageUploadFn}. Pasting
|
|
112
|
+
* an image URL or `` markdown works through the base node's input
|
|
113
|
+
* rules even when no uploader is supplied.
|
|
114
|
+
*
|
|
115
|
+
* The node adds one transient `uploadId` attribute (never parsed/rendered to
|
|
116
|
+
* HTML, never serialized to markdown) used to track an in-flight upload.
|
|
117
|
+
*
|
|
118
|
+
* Content keeps its own richer `ImageNode` (Assets picker, AI alt-text, resize,
|
|
119
|
+
* NFM `{color}` serialization) and does NOT use this shared node — both nodes
|
|
120
|
+
* are named `image` but never coexist in the same editor because Content leaves
|
|
121
|
+
* `features.image` off and injects its own via `extraExtensions`.
|
|
122
|
+
*/
|
|
123
|
+
export const SharedImage = Image.extend({
|
|
124
|
+
// Block-level atom (Content's image is also a block atom). The base extension
|
|
125
|
+
// is block when `inline:false`.
|
|
126
|
+
inline: false,
|
|
127
|
+
group: "block",
|
|
128
|
+
addOptions() {
|
|
129
|
+
return {
|
|
130
|
+
...this.parent?.(),
|
|
131
|
+
onImageUpload: null,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
addAttributes() {
|
|
135
|
+
return {
|
|
136
|
+
...this.parent?.(),
|
|
137
|
+
// Transient: marks an in-flight upload so the plugin can patch `src` on
|
|
138
|
+
// resolve. Never written to HTML or markdown.
|
|
139
|
+
uploadId: {
|
|
140
|
+
default: null,
|
|
141
|
+
parseHTML: () => null,
|
|
142
|
+
renderHTML: () => ({}),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
addStorage() {
|
|
147
|
+
return {
|
|
148
|
+
...this.parent?.(),
|
|
149
|
+
// A BLOCK-aware markdown serializer. tiptap-markdown's built-in fallback
|
|
150
|
+
// for the `image` node is prosemirror-markdown's INLINE image serializer,
|
|
151
|
+
// which omits the trailing block separator — so an image immediately
|
|
152
|
+
// followed by a paragraph loses its blank line on round-trip. This
|
|
153
|
+
// node-level spec (merged OVER the fallback by tiptap-markdown's
|
|
154
|
+
// `getMarkdownSpec`) emits the same pure `` markdown but calls
|
|
155
|
+
// `closeBlock`, so a block image stays byte-stable and source-syncable.
|
|
156
|
+
// No `<img width>` / HTML is emitted, so the GFM `html:false` contract and
|
|
157
|
+
// the plan round-trip corpus are preserved.
|
|
158
|
+
markdown: {
|
|
159
|
+
serialize(state, node) {
|
|
160
|
+
const src = node.attrs.src ?? "";
|
|
161
|
+
const alt = node.attrs.alt ?? "";
|
|
162
|
+
const title = node.attrs.title ?? "";
|
|
163
|
+
const titleSuffix = title ? ` "${title.replace(/"/g, '\\"')}"` : "";
|
|
164
|
+
state.write(`}${titleSuffix})`);
|
|
165
|
+
state.closeBlock(node);
|
|
166
|
+
},
|
|
167
|
+
parse: {
|
|
168
|
+
// Parsing `` is handled by markdown-it + the base node's
|
|
169
|
+
// markdown input rule.
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
addProseMirrorPlugins() {
|
|
175
|
+
const upload = this.options.onImageUpload;
|
|
176
|
+
const parentPlugins = this.parent?.() ?? [];
|
|
177
|
+
if (!upload)
|
|
178
|
+
return parentPlugins;
|
|
179
|
+
return [
|
|
180
|
+
...parentPlugins,
|
|
181
|
+
new Plugin({
|
|
182
|
+
key: sharedImageUploadPluginKey,
|
|
183
|
+
props: {
|
|
184
|
+
handlePaste(view, event) {
|
|
185
|
+
const files = imageFilesFrom(event.clipboardData);
|
|
186
|
+
if (files.length === 0)
|
|
187
|
+
return false;
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
uploadAndInsertImages(view, files, view.state.selection.from, upload);
|
|
190
|
+
return true;
|
|
191
|
+
},
|
|
192
|
+
handleDrop(view, event) {
|
|
193
|
+
const files = imageFilesFrom(event.dataTransfer);
|
|
194
|
+
if (files.length === 0)
|
|
195
|
+
return false;
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
const coords = view.posAtCoords({
|
|
198
|
+
left: event.clientX,
|
|
199
|
+
top: event.clientY,
|
|
200
|
+
});
|
|
201
|
+
const pos = coords?.pos ?? view.state.selection.from;
|
|
202
|
+
uploadAndInsertImages(view, files, pos, upload);
|
|
203
|
+
return true;
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
];
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
/**
|
|
211
|
+
* Build the shared image extension, optionally wired with an app uploader.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* createImageExtension({ onImageUpload: uploadEditorImage })
|
|
215
|
+
*/
|
|
216
|
+
export function createImageExtension(options = {}) {
|
|
217
|
+
return SharedImage.configure({
|
|
218
|
+
onImageUpload: options.onImageUpload ?? null,
|
|
219
|
+
HTMLAttributes: { class: "an-rich-md-image" },
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Open a native file picker, then upload + insert the chosen image(s) through
|
|
224
|
+
* the same flow as paste/drop. Used by the `/image` slash command.
|
|
225
|
+
*/
|
|
226
|
+
export function pickAndInsertImage(view, upload) {
|
|
227
|
+
const input = document.createElement("input");
|
|
228
|
+
input.type = "file";
|
|
229
|
+
input.accept = "image/*";
|
|
230
|
+
input.multiple = true;
|
|
231
|
+
input.style.display = "none";
|
|
232
|
+
input.addEventListener("change", () => {
|
|
233
|
+
const files = Array.from(input.files ?? []).filter((file) => file.type.startsWith("image/"));
|
|
234
|
+
input.remove();
|
|
235
|
+
if (files.length === 0)
|
|
236
|
+
return;
|
|
237
|
+
uploadAndInsertImages(view, files, view.state.selection.from, upload);
|
|
238
|
+
});
|
|
239
|
+
document.body.appendChild(input);
|
|
240
|
+
input.click();
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=ImageExtension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageExtension.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/ImageExtension.ts"],"names":[],"mappings":"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","sourcesContent":["import Image, { type ImageOptions } from \"@tiptap/extension-image\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\n\n/**\n * The injectable async upload contract for the shared image block.\n *\n * An app provides this to turn a picked / pasted / dropped {@link File} into a\n * hosted image. It returns the final `src` (a hosted URL) plus an optional\n * `alt`. Plans wire it to the framework `upload-image` action; Content keeps its\n * own richer image block and does not use this contract.\n *\n * When NO upload function is supplied, the shared block still renders images and\n * round-trips `` markdown — it just cannot ingest local files (paste\n * of an image URL / markdown still works via the base node's input rules).\n */\nexport type ImageUploadFn = (\n file: File,\n) => Promise<{ src: string; alt?: string }>;\n\nexport interface SharedImageOptions extends ImageOptions {\n /**\n * App-injected uploader. When present, the shared block accepts pasted /\n * dropped image files and the `/image` slash command, uploading each file\n * through this function and patching the node's `src` on resolve.\n */\n onImageUpload?: ImageUploadFn | null;\n}\n\nconst sharedImageUploadPluginKey = new PluginKey(\"an-shared-image-upload\");\n\n/** A monotonically increasing id so concurrent uploads patch the right node. */\nlet uploadCounter = 0;\nfunction nextUploadId(): string {\n uploadCounter += 1;\n return `an-img-${Date.now()}-${uploadCounter}`;\n}\n\n/** Image files only — never ingest non-image clipboard/drop payloads. */\nfunction imageFilesFrom(data: DataTransfer | null | undefined): File[] {\n if (!data) return [];\n return Array.from(data.files ?? []).filter((file) =>\n file.type.startsWith(\"image/\"),\n );\n}\n\n/**\n * Insert a placeholder `image` node (empty `src`, transient `uploadId`) at\n * `pos` for every file, then resolve each upload and patch the matching node's\n * `src`. Mirrors Content's optimistic upload flow but is self-contained in the\n * shared extension so it works for ANY editor wrapper (the plan's\n * `SharedRichEditor` and Content's hand-rolled `useEditor` alike) without a new\n * `editorProps` seam.\n */\nfunction uploadAndInsertImages(\n view: EditorView,\n files: File[],\n pos: number,\n upload: ImageUploadFn,\n): void {\n if (files.length === 0) return;\n\n const pending: Array<{ uploadId: string; file: File }> = [];\n\n // Insert all placeholders first, top-down from `pos`, so multi-file\n // paste/drop keeps source order.\n let insertAt = pos;\n for (const file of files) {\n const uploadId = nextUploadId();\n const node = view.state.schema.nodes.image?.create({\n src: \"\",\n alt: \"\",\n uploadId,\n });\n if (!node) continue;\n const tr = view.state.tr.insert(insertAt, node);\n view.dispatch(tr);\n pending.push({ uploadId, file });\n // Advance past the inserted atom for the next placeholder.\n insertAt += node.nodeSize;\n }\n\n for (const item of pending) {\n void (async () => {\n try {\n const { src, alt } = await upload(item.file);\n if (!view.dom.isConnected || view.isDestroyed) return;\n patchUploadNode(view, item.uploadId, { src, alt: alt ?? \"\" });\n } catch (error) {\n console.error(\"Image upload failed:\", error);\n // Drop the placeholder so a failed upload does not leave an empty box.\n if (view.dom.isConnected && !view.isDestroyed) {\n removeUploadNode(view, item.uploadId);\n }\n }\n })();\n }\n}\n\n/** Find a placeholder node by its transient `uploadId` and return its position. */\nfunction findUploadNode(\n view: EditorView,\n uploadId: string,\n): { pos: number; nodeSize: number } | null {\n let found: { pos: number; nodeSize: number } | null = null;\n view.state.doc.descendants((node, pos) => {\n if (found) return false;\n if (node.type.name === \"image\" && node.attrs.uploadId === uploadId) {\n found = { pos, nodeSize: node.nodeSize };\n return false;\n }\n return true;\n });\n return found;\n}\n\n/** Patch the resolved `src`/`alt` onto the placeholder and clear `uploadId`. */\nfunction patchUploadNode(\n view: EditorView,\n uploadId: string,\n attrs: { src: string; alt: string },\n): void {\n const target = findUploadNode(view, uploadId);\n if (!target) return;\n const node = view.state.doc.nodeAt(target.pos);\n if (!node) return;\n const tr = view.state.tr.setNodeMarkup(target.pos, undefined, {\n ...node.attrs,\n src: attrs.src,\n alt: attrs.alt,\n uploadId: null,\n });\n view.dispatch(tr);\n}\n\n/** Remove a placeholder whose upload failed. */\nfunction removeUploadNode(view: EditorView, uploadId: string): void {\n const target = findUploadNode(view, uploadId);\n if (!target) return;\n const tr = view.state.tr.delete(target.pos, target.pos + target.nodeSize);\n view.dispatch(tr);\n}\n\n/**\n * The SHARED block-level image node. Built on `@tiptap/extension-image` (a\n * node named `image`), so:\n *\n * - **GFM serialization** emits pure `` markdown via a block-aware\n * `addStorage().markdown.serialize` (it calls `closeBlock` so a block image\n * keeps its blank-line separator, byte-stable). It emits NO `<img width>`\n * HTML, so plans stay source-syncable under the GFM `html:false` contract.\n * - **Paste / drop** of local image files is handled by a self-contained\n * ProseMirror plugin that calls the injected {@link ImageUploadFn}. Pasting\n * an image URL or `` markdown works through the base node's input\n * rules even when no uploader is supplied.\n *\n * The node adds one transient `uploadId` attribute (never parsed/rendered to\n * HTML, never serialized to markdown) used to track an in-flight upload.\n *\n * Content keeps its own richer `ImageNode` (Assets picker, AI alt-text, resize,\n * NFM `{color}` serialization) and does NOT use this shared node — both nodes\n * are named `image` but never coexist in the same editor because Content leaves\n * `features.image` off and injects its own via `extraExtensions`.\n */\nexport const SharedImage = Image.extend<SharedImageOptions>({\n // Block-level atom (Content's image is also a block atom). The base extension\n // is block when `inline:false`.\n inline: false,\n group: \"block\",\n\n addOptions() {\n return {\n ...this.parent?.(),\n onImageUpload: null,\n };\n },\n\n addAttributes() {\n return {\n ...this.parent?.(),\n // Transient: marks an in-flight upload so the plugin can patch `src` on\n // resolve. Never written to HTML or markdown.\n uploadId: {\n default: null,\n parseHTML: () => null,\n renderHTML: () => ({}),\n },\n };\n },\n\n addStorage() {\n return {\n ...this.parent?.(),\n // A BLOCK-aware markdown serializer. tiptap-markdown's built-in fallback\n // for the `image` node is prosemirror-markdown's INLINE image serializer,\n // which omits the trailing block separator — so an image immediately\n // followed by a paragraph loses its blank line on round-trip. This\n // node-level spec (merged OVER the fallback by tiptap-markdown's\n // `getMarkdownSpec`) emits the same pure `` markdown but calls\n // `closeBlock`, so a block image stays byte-stable and source-syncable.\n // No `<img width>` / HTML is emitted, so the GFM `html:false` contract and\n // the plan round-trip corpus are preserved.\n markdown: {\n serialize(\n state: {\n esc: (s: string) => string;\n write: (s: string) => void;\n closeBlock: (n: unknown) => void;\n },\n node: { attrs: { src?: string; alt?: string; title?: string } },\n ) {\n const src = node.attrs.src ?? \"\";\n const alt = node.attrs.alt ?? \"\";\n const title = node.attrs.title ?? \"\";\n const titleSuffix = title ? ` \"${title.replace(/\"/g, '\\\\\"')}\"` : \"\";\n state.write(`}${titleSuffix})`);\n state.closeBlock(node);\n },\n parse: {\n // Parsing `` is handled by markdown-it + the base node's\n // markdown input rule.\n },\n },\n };\n },\n\n addProseMirrorPlugins() {\n const upload = this.options.onImageUpload;\n const parentPlugins = this.parent?.() ?? [];\n if (!upload) return parentPlugins;\n\n return [\n ...parentPlugins,\n new Plugin({\n key: sharedImageUploadPluginKey,\n props: {\n handlePaste(view, event) {\n const files = imageFilesFrom(event.clipboardData);\n if (files.length === 0) return false;\n event.preventDefault();\n uploadAndInsertImages(\n view,\n files,\n view.state.selection.from,\n upload,\n );\n return true;\n },\n handleDrop(view, event) {\n const files = imageFilesFrom(event.dataTransfer);\n if (files.length === 0) return false;\n event.preventDefault();\n const coords = view.posAtCoords({\n left: event.clientX,\n top: event.clientY,\n });\n const pos = coords?.pos ?? view.state.selection.from;\n uploadAndInsertImages(view, files, pos, upload);\n return true;\n },\n },\n }),\n ];\n },\n});\n\n/**\n * Build the shared image extension, optionally wired with an app uploader.\n *\n * @example\n * createImageExtension({ onImageUpload: uploadEditorImage })\n */\nexport function createImageExtension(\n options: { onImageUpload?: ImageUploadFn | null } = {},\n) {\n return SharedImage.configure({\n onImageUpload: options.onImageUpload ?? null,\n HTMLAttributes: { class: \"an-rich-md-image\" },\n });\n}\n\n/**\n * Open a native file picker, then upload + insert the chosen image(s) through\n * the same flow as paste/drop. Used by the `/image` slash command.\n */\nexport function pickAndInsertImage(\n view: EditorView,\n upload: ImageUploadFn,\n): void {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"image/*\";\n input.multiple = true;\n input.style.display = \"none\";\n input.addEventListener(\"change\", () => {\n const files = Array.from(input.files ?? []).filter((file) =>\n file.type.startsWith(\"image/\"),\n );\n input.remove();\n if (files.length === 0) return;\n uploadAndInsertImages(view, files, view.state.selection.from, upload);\n });\n document.body.appendChild(input);\n input.click();\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-compat surface for the shared rich markdown editor.
|
|
3
|
+
*
|
|
4
|
+
* The editor core now lives in dedicated modules:
|
|
5
|
+
* - {@link createSharedEditorExtensions} — the ONE extension factory.
|
|
6
|
+
* - {@link useCollabReconcile} — seed / reconcile / lead-client logic.
|
|
7
|
+
* - {@link SlashCommandMenu} / {@link BubbleToolbar} — shared menus.
|
|
8
|
+
* - {@link SharedRichEditor} — the editor component.
|
|
9
|
+
*
|
|
10
|
+
* This file keeps the historical `RichMarkdownEditor` component name (aliased to
|
|
11
|
+
* {@link SharedRichEditor}) and the `createRichMarkdownExtensions` factory so
|
|
12
|
+
* existing embedders and the round-trip / collab specs keep working unchanged.
|
|
13
|
+
*/
|
|
14
|
+
import { type RichMarkdownDialect, type RichMarkdownEditorPreset, type RichMarkdownCollabUser } from "./extensions.js";
|
|
15
|
+
import { SharedRichEditor, type SharedRichEditorProps } from "./SharedRichEditor.js";
|
|
16
|
+
import type { Doc as YDoc } from "yjs";
|
|
17
|
+
import type { Awareness } from "y-protocols/awareness";
|
|
18
|
+
export type { RichMarkdownDialect, RichMarkdownEditorPreset, RichMarkdownCollabUser, };
|
|
19
|
+
/** @deprecated Prefer {@link CreateSharedEditorExtensionsOptions}. */
|
|
20
|
+
export interface CreateRichMarkdownExtensionsOptions {
|
|
21
|
+
dialect?: RichMarkdownDialect;
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Yjs document for collaborative editing. When present, the editor binds the
|
|
25
|
+
* shared Collaboration + CollaborationCaret extensions and StarterKit's
|
|
26
|
+
* built-in undo/redo is disabled (Yjs owns history).
|
|
27
|
+
*/
|
|
28
|
+
ydoc?: YDoc | null;
|
|
29
|
+
/** Shared awareness instance for live multi-user cursors. */
|
|
30
|
+
awareness?: Awareness | null;
|
|
31
|
+
/** Current user info for the collaborative cursor label. */
|
|
32
|
+
user?: RichMarkdownCollabUser | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Back-compat factory preserving today's GFM/plan behavior EXACTLY. Implemented
|
|
36
|
+
* in terms of {@link createSharedEditorExtensions}: it maps the flat
|
|
37
|
+
* `{ ydoc, awareness, user }` collab options into the nested `collab` shape the
|
|
38
|
+
* shared factory expects. The round-trip and collab specs build their `Editor`
|
|
39
|
+
* from this, so its output must stay byte-stable.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createRichMarkdownExtensions({ dialect, placeholder, ydoc, awareness, user, }?: CreateRichMarkdownExtensionsOptions): (import("@tiptap/core").Extension<any, any> | import("@tiptap/core").Node<any, any> | import("@tiptap/core").Mark<any, any>)[];
|
|
42
|
+
/** @deprecated Prefer {@link SharedRichEditorProps}. */
|
|
43
|
+
export type RichMarkdownEditorProps = SharedRichEditorProps;
|
|
44
|
+
/**
|
|
45
|
+
* Historical name for {@link SharedRichEditor}. Kept so existing imports
|
|
46
|
+
* (`import { RichMarkdownEditor } from "@agent-native/core/client"`) and the
|
|
47
|
+
* plan editor tests (which assert the source mentions `RichMarkdownEditor`)
|
|
48
|
+
* keep working. New code should import `SharedRichEditor`.
|
|
49
|
+
*/
|
|
50
|
+
export declare const RichMarkdownEditor: typeof SharedRichEditor;
|
|
51
|
+
//# sourceMappingURL=RichMarkdownEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichMarkdownEditor.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/RichMarkdownEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,GACvB,CAAC;AAEF,sEAAsE;AACtE,MAAM,WAAW,mCAAmC;IAClD,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,OAAe,EACf,WAAwC,EACxC,IAAW,EACX,SAAgB,EAChB,IAAW,GACZ,GAAE,mCAAwC,kIAM1C;AAED,wDAAwD;AACxD,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,yBAAmB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-compat surface for the shared rich markdown editor.
|
|
3
|
+
*
|
|
4
|
+
* The editor core now lives in dedicated modules:
|
|
5
|
+
* - {@link createSharedEditorExtensions} — the ONE extension factory.
|
|
6
|
+
* - {@link useCollabReconcile} — seed / reconcile / lead-client logic.
|
|
7
|
+
* - {@link SlashCommandMenu} / {@link BubbleToolbar} — shared menus.
|
|
8
|
+
* - {@link SharedRichEditor} — the editor component.
|
|
9
|
+
*
|
|
10
|
+
* This file keeps the historical `RichMarkdownEditor` component name (aliased to
|
|
11
|
+
* {@link SharedRichEditor}) and the `createRichMarkdownExtensions` factory so
|
|
12
|
+
* existing embedders and the round-trip / collab specs keep working unchanged.
|
|
13
|
+
*/
|
|
14
|
+
import { createSharedEditorExtensions, } from "./extensions.js";
|
|
15
|
+
import { SharedRichEditor, } from "./SharedRichEditor.js";
|
|
16
|
+
/**
|
|
17
|
+
* Back-compat factory preserving today's GFM/plan behavior EXACTLY. Implemented
|
|
18
|
+
* in terms of {@link createSharedEditorExtensions}: it maps the flat
|
|
19
|
+
* `{ ydoc, awareness, user }` collab options into the nested `collab` shape the
|
|
20
|
+
* shared factory expects. The round-trip and collab specs build their `Editor`
|
|
21
|
+
* from this, so its output must stay byte-stable.
|
|
22
|
+
*/
|
|
23
|
+
export function createRichMarkdownExtensions({ dialect = "gfm", placeholder = "Type '/' for commands...", ydoc = null, awareness = null, user = null, } = {}) {
|
|
24
|
+
return createSharedEditorExtensions({
|
|
25
|
+
dialect,
|
|
26
|
+
placeholder,
|
|
27
|
+
collab: ydoc ? { ydoc, awareness, user } : null,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Historical name for {@link SharedRichEditor}. Kept so existing imports
|
|
32
|
+
* (`import { RichMarkdownEditor } from "@agent-native/core/client"`) and the
|
|
33
|
+
* plan editor tests (which assert the source mentions `RichMarkdownEditor`)
|
|
34
|
+
* keep working. New code should import `SharedRichEditor`.
|
|
35
|
+
*/
|
|
36
|
+
export const RichMarkdownEditor = SharedRichEditor;
|
|
37
|
+
//# sourceMappingURL=RichMarkdownEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichMarkdownEditor.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/RichMarkdownEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,4BAA4B,GAI7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AA0B/B;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAAC,EAC3C,OAAO,GAAG,KAAK,EACf,WAAW,GAAG,0BAA0B,EACxC,IAAI,GAAG,IAAI,EACX,SAAS,GAAG,IAAI,EAChB,IAAI,GAAG,IAAI,MAC4B,EAAE;IACzC,OAAO,4BAA4B,CAAC;QAClC,OAAO;QACP,WAAW;QACX,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;KAChD,CAAC,CAAC;AACL,CAAC;AAKD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC","sourcesContent":["/**\n * Back-compat surface for the shared rich markdown editor.\n *\n * The editor core now lives in dedicated modules:\n * - {@link createSharedEditorExtensions} — the ONE extension factory.\n * - {@link useCollabReconcile} — seed / reconcile / lead-client logic.\n * - {@link SlashCommandMenu} / {@link BubbleToolbar} — shared menus.\n * - {@link SharedRichEditor} — the editor component.\n *\n * This file keeps the historical `RichMarkdownEditor` component name (aliased to\n * {@link SharedRichEditor}) and the `createRichMarkdownExtensions` factory so\n * existing embedders and the round-trip / collab specs keep working unchanged.\n */\nimport {\n createSharedEditorExtensions,\n type RichMarkdownDialect,\n type RichMarkdownEditorPreset,\n type RichMarkdownCollabUser,\n} from \"./extensions.js\";\nimport {\n SharedRichEditor,\n type SharedRichEditorProps,\n} from \"./SharedRichEditor.js\";\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\n\nexport type {\n RichMarkdownDialect,\n RichMarkdownEditorPreset,\n RichMarkdownCollabUser,\n};\n\n/** @deprecated Prefer {@link CreateSharedEditorExtensionsOptions}. */\nexport interface CreateRichMarkdownExtensionsOptions {\n dialect?: RichMarkdownDialect;\n placeholder?: string;\n /**\n * Yjs document for collaborative editing. When present, the editor binds the\n * shared Collaboration + CollaborationCaret extensions and StarterKit's\n * built-in undo/redo is disabled (Yjs owns history).\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live multi-user cursors. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n}\n\n/**\n * Back-compat factory preserving today's GFM/plan behavior EXACTLY. Implemented\n * in terms of {@link createSharedEditorExtensions}: it maps the flat\n * `{ ydoc, awareness, user }` collab options into the nested `collab` shape the\n * shared factory expects. The round-trip and collab specs build their `Editor`\n * from this, so its output must stay byte-stable.\n */\nexport function createRichMarkdownExtensions({\n dialect = \"gfm\",\n placeholder = \"Type '/' for commands...\",\n ydoc = null,\n awareness = null,\n user = null,\n}: CreateRichMarkdownExtensionsOptions = {}) {\n return createSharedEditorExtensions({\n dialect,\n placeholder,\n collab: ydoc ? { ydoc, awareness, user } : null,\n });\n}\n\n/** @deprecated Prefer {@link SharedRichEditorProps}. */\nexport type RichMarkdownEditorProps = SharedRichEditorProps;\n\n/**\n * Historical name for {@link SharedRichEditor}. Kept so existing imports\n * (`import { RichMarkdownEditor } from \"@agent-native/core/client\"`) and the\n * plan editor tests (which assert the source mentions `RichMarkdownEditor`)\n * keep working. New code should import `SharedRichEditor`.\n */\nexport const RichMarkdownEditor = SharedRichEditor;\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Extension, Node, Mark } from "@tiptap/core";
|
|
2
|
+
import type { Doc as YDoc } from "yjs";
|
|
3
|
+
import type { Awareness } from "y-protocols/awareness";
|
|
4
|
+
import { type RichMarkdownDialect, type RichMarkdownEditorPreset, type RichMarkdownCollabUser, type SharedEditorFeatures } from "./extensions.js";
|
|
5
|
+
import type { ImageUploadFn } from "./ImageExtension.js";
|
|
6
|
+
import { type SlashCommandItem } from "./SlashCommandMenu.js";
|
|
7
|
+
import { type BubbleToolbarItem } from "./BubbleToolbar.js";
|
|
8
|
+
export interface SharedRichEditorProps {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (markdown: string) => void;
|
|
11
|
+
onBlur?: () => void;
|
|
12
|
+
contentUpdatedAt?: string | null;
|
|
13
|
+
editable?: boolean;
|
|
14
|
+
dialect?: RichMarkdownDialect;
|
|
15
|
+
preset?: RichMarkdownEditorPreset;
|
|
16
|
+
/** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */
|
|
17
|
+
features?: SharedEditorFeatures;
|
|
18
|
+
/**
|
|
19
|
+
* Injectable image uploader for the shared image block. Used only when
|
|
20
|
+
* `features.image` is on. Pass `uploadEditorImage` (the framework
|
|
21
|
+
* `upload-image` action) for a real uploading image block.
|
|
22
|
+
*/
|
|
23
|
+
onImageUpload?: ImageUploadFn | null;
|
|
24
|
+
/**
|
|
25
|
+
* App-specific extensions (Notion nodes, media, drag handles, comment
|
|
26
|
+
* anchors, …) appended after the shared base schema.
|
|
27
|
+
*/
|
|
28
|
+
extraExtensions?: Array<Extension | Node | Mark>;
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
editorClassName?: string;
|
|
32
|
+
interactive?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Yjs document for real-time multi-user editing. When provided, prose is
|
|
35
|
+
* authored against the shared Y.Doc and mirrored back to `value` as markdown
|
|
36
|
+
* (still the source of truth). When omitted, the editor is a plain controlled
|
|
37
|
+
* `value`/`onChange` editor — the existing, non-collaborative behavior.
|
|
38
|
+
*/
|
|
39
|
+
ydoc?: YDoc | null;
|
|
40
|
+
/** Shared awareness instance for live cursors/presence. */
|
|
41
|
+
awareness?: Awareness | null;
|
|
42
|
+
/** Current user info for the collaborative cursor label. */
|
|
43
|
+
user?: RichMarkdownCollabUser | null;
|
|
44
|
+
/** Override the slash-menu block command list. */
|
|
45
|
+
slashItems?: SlashCommandItem[];
|
|
46
|
+
/** Override the bubble-toolbar item builder. */
|
|
47
|
+
buildBubbleItems?: (editor: import("@tiptap/react").Editor, toggleLink: () => void) => BubbleToolbarItem[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The single shared rich markdown editor surface. Combines
|
|
51
|
+
* {@link createSharedEditorExtensions} (schema + dialect-keyed markdown +
|
|
52
|
+
* optional collab + app extras), {@link useCollabReconcile} (seed / reconcile /
|
|
53
|
+
* lead-client logic), and the shared {@link SlashCommandMenu} +
|
|
54
|
+
* {@link BubbleToolbar}.
|
|
55
|
+
*
|
|
56
|
+
* With no `ydoc` it is a controlled `value`/`onChange` single-user editor.
|
|
57
|
+
* With a `ydoc` it binds the framework collaboration stack; markdown stays the
|
|
58
|
+
* canonical saved representation while the Y.Doc is transient live state.
|
|
59
|
+
*/
|
|
60
|
+
export declare function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable, dialect, preset, features, onImageUpload, extraExtensions, placeholder, className, editorClassName, interactive, ydoc, awareness, user, slashItems, buildBubbleItems, }: SharedRichEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
61
|
+
//# sourceMappingURL=SharedRichEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedRichEditor.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACrC,kDAAkD;IAClD,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,CACjB,MAAM,EAAE,OAAO,eAAe,EAAE,MAAM,EACtC,UAAU,EAAE,MAAM,IAAI,KACnB,iBAAiB,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,QAAe,EACf,OAAe,EACf,MAAe,EACf,QAAQ,EACR,aAAoB,EACpB,eAAe,EACf,WAAwC,EACxC,SAAS,EACT,eAAe,EACf,WAAsB,EACtB,IAAW,EACX,SAAgB,EAChB,IAAW,EACX,UAAU,EACV,gBAAgB,GACjB,EAAE,qBAAqB,2CAqIvB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
3
|
+
import { EditorContent, useEditor } from "@tiptap/react";
|
|
4
|
+
import { cn } from "../utils.js";
|
|
5
|
+
import { createSharedEditorExtensions, } from "./extensions.js";
|
|
6
|
+
import { useCollabReconcile, getEditorMarkdown, } from "./useCollabReconcile.js";
|
|
7
|
+
import { SlashCommandMenu } from "./SlashCommandMenu.js";
|
|
8
|
+
import { BubbleToolbar } from "./BubbleToolbar.js";
|
|
9
|
+
/**
|
|
10
|
+
* The single shared rich markdown editor surface. Combines
|
|
11
|
+
* {@link createSharedEditorExtensions} (schema + dialect-keyed markdown +
|
|
12
|
+
* optional collab + app extras), {@link useCollabReconcile} (seed / reconcile /
|
|
13
|
+
* lead-client logic), and the shared {@link SlashCommandMenu} +
|
|
14
|
+
* {@link BubbleToolbar}.
|
|
15
|
+
*
|
|
16
|
+
* With no `ydoc` it is a controlled `value`/`onChange` single-user editor.
|
|
17
|
+
* With a `ydoc` it binds the framework collaboration stack; markdown stays the
|
|
18
|
+
* canonical saved representation while the Y.Doc is transient live state.
|
|
19
|
+
*/
|
|
20
|
+
export function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable = true, dialect = "gfm", preset = "plan", features, onImageUpload = null, extraExtensions, placeholder = "Type '/' for commands...", className, editorClassName, interactive = editable, ydoc = null, awareness = null, user = null, slashItems, buildBubbleItems, }) {
|
|
21
|
+
const onChangeRef = useRef(onChange);
|
|
22
|
+
const onBlurRef = useRef(onBlur);
|
|
23
|
+
onChangeRef.current = onChange;
|
|
24
|
+
onBlurRef.current = onBlur;
|
|
25
|
+
const extensions = useMemo(() => createSharedEditorExtensions({
|
|
26
|
+
dialect,
|
|
27
|
+
preset,
|
|
28
|
+
placeholder,
|
|
29
|
+
features,
|
|
30
|
+
extraExtensions,
|
|
31
|
+
onImageUpload,
|
|
32
|
+
collab: ydoc ? { ydoc, awareness, user } : null,
|
|
33
|
+
}),
|
|
34
|
+
// `preset` is retained in the dependency list so future preset-specific
|
|
35
|
+
// schema branches re-create the editor; it is currently schema-neutral. The
|
|
36
|
+
// collab inputs are identity-stable per block (one Y.Doc per docId), so they
|
|
37
|
+
// only change on a genuine doc switch — exactly when the editor must
|
|
38
|
+
// re-create to rebind Collaboration.
|
|
39
|
+
[
|
|
40
|
+
dialect,
|
|
41
|
+
placeholder,
|
|
42
|
+
preset,
|
|
43
|
+
features,
|
|
44
|
+
extraExtensions,
|
|
45
|
+
onImageUpload,
|
|
46
|
+
ydoc,
|
|
47
|
+
awareness,
|
|
48
|
+
user?.name,
|
|
49
|
+
user?.email,
|
|
50
|
+
user?.color,
|
|
51
|
+
]);
|
|
52
|
+
const collab = !!ydoc;
|
|
53
|
+
// The collab hook needs the editor, but useEditor's `onUpdate` needs the
|
|
54
|
+
// hook's guards. Break the cycle with a ref: `onUpdate` reads the guards
|
|
55
|
+
// through `guardsRef`, which is populated right after the hook runs below.
|
|
56
|
+
// `onUpdate` only ever fires after the editor exists, by which point the ref
|
|
57
|
+
// holds the real guards.
|
|
58
|
+
const guardsRef = useRef(null);
|
|
59
|
+
const editor = useEditor({
|
|
60
|
+
extensions,
|
|
61
|
+
// With Collaboration active the prose is owned by the shared Y.XmlFragment.
|
|
62
|
+
// Seeding `content` here too would make the editor initialize from BOTH the
|
|
63
|
+
// prop and the Y.Doc, firing a spurious initial update that could autosave a
|
|
64
|
+
// stale value over newer SQL. The lead-client seed effect populates an empty
|
|
65
|
+
// doc instead. Non-collab editors keep initializing from `value`.
|
|
66
|
+
content: collab ? undefined : value,
|
|
67
|
+
editable,
|
|
68
|
+
editorProps: {
|
|
69
|
+
attributes: {
|
|
70
|
+
class: cn("an-rich-md-prose", editorClassName),
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
onUpdate: ({ editor, transaction }) => {
|
|
74
|
+
const guards = guardsRef.current;
|
|
75
|
+
if (!guards || guards.shouldIgnoreUpdate(transaction))
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
const markdown = getEditorMarkdown(editor);
|
|
79
|
+
if (!guards.registerEmitted(markdown))
|
|
80
|
+
return;
|
|
81
|
+
queueMicrotask(() => onChangeRef.current(markdown));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error("Markdown serialization error:", error);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
onBlur: () => {
|
|
88
|
+
onBlurRef.current?.();
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const collabState = useCollabReconcile({
|
|
92
|
+
editor,
|
|
93
|
+
ydoc,
|
|
94
|
+
awareness,
|
|
95
|
+
value,
|
|
96
|
+
contentUpdatedAt,
|
|
97
|
+
editable,
|
|
98
|
+
getMarkdown: getEditorMarkdown,
|
|
99
|
+
});
|
|
100
|
+
guardsRef.current = collabState;
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!editor || editor.isDestroyed)
|
|
103
|
+
return;
|
|
104
|
+
editor.setEditable(editable);
|
|
105
|
+
}, [editable, editor]);
|
|
106
|
+
useEffect(() => () => editor?.destroy(), [editor]);
|
|
107
|
+
const handleWrapperClick = (event) => {
|
|
108
|
+
if (!editable || !editor || editor.isDestroyed)
|
|
109
|
+
return;
|
|
110
|
+
const target = event.target;
|
|
111
|
+
if (target.classList.contains("an-rich-md-wrapper") ||
|
|
112
|
+
target.classList.contains("an-rich-md-clickable")) {
|
|
113
|
+
editor.chain().focus("end").run();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
if (!editor) {
|
|
117
|
+
return (_jsx("div", { className: cn("an-rich-md-wrapper an-rich-md-loading", className), "data-plan-interactive": interactive ? true : undefined }));
|
|
118
|
+
}
|
|
119
|
+
return (_jsxs("div", { className: cn("an-rich-md-wrapper an-rich-md-clickable", !editable && "an-rich-md-wrapper--readonly", className), onClick: handleWrapperClick, "data-plan-interactive": interactive ? true : undefined, children: [editable ? (_jsx(BubbleToolbar, { editor: editor, buildItems: buildBubbleItems })) : null, editable ? (_jsx(SlashCommandMenu, { editor: editor, items: slashItems })) : null, _jsx(EditorContent, { editor: editor })] }));
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=SharedRichEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedRichEditor.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { EditorContent, useEditor } from \"@tiptap/react\";\nimport type { Extension, Node, Mark } from \"@tiptap/core\";\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { cn } from \"../utils.js\";\nimport {\n createSharedEditorExtensions,\n type RichMarkdownDialect,\n type RichMarkdownEditorPreset,\n type RichMarkdownCollabUser,\n type SharedEditorFeatures,\n} from \"./extensions.js\";\nimport type { ImageUploadFn } from \"./ImageExtension.js\";\nimport {\n useCollabReconcile,\n getEditorMarkdown,\n type UseCollabReconcileResult,\n} from \"./useCollabReconcile.js\";\nimport { SlashCommandMenu, type SlashCommandItem } from \"./SlashCommandMenu.js\";\nimport { BubbleToolbar, type BubbleToolbarItem } from \"./BubbleToolbar.js\";\n\nexport interface SharedRichEditorProps {\n value: string;\n onChange: (markdown: string) => void;\n onBlur?: () => void;\n contentUpdatedAt?: string | null;\n editable?: boolean;\n dialect?: RichMarkdownDialect;\n preset?: RichMarkdownEditorPreset;\n /** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */\n features?: SharedEditorFeatures;\n /**\n * Injectable image uploader for the shared image block. Used only when\n * `features.image` is on. Pass `uploadEditorImage` (the framework\n * `upload-image` action) for a real uploading image block.\n */\n onImageUpload?: ImageUploadFn | null;\n /**\n * App-specific extensions (Notion nodes, media, drag handles, comment\n * anchors, …) appended after the shared base schema.\n */\n extraExtensions?: Array<Extension | Node | Mark>;\n placeholder?: string;\n className?: string;\n editorClassName?: string;\n interactive?: boolean;\n /**\n * Yjs document for real-time multi-user editing. When provided, prose is\n * authored against the shared Y.Doc and mirrored back to `value` as markdown\n * (still the source of truth). When omitted, the editor is a plain controlled\n * `value`/`onChange` editor — the existing, non-collaborative behavior.\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live cursors/presence. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n /** Override the slash-menu block command list. */\n slashItems?: SlashCommandItem[];\n /** Override the bubble-toolbar item builder. */\n buildBubbleItems?: (\n editor: import(\"@tiptap/react\").Editor,\n toggleLink: () => void,\n ) => BubbleToolbarItem[];\n}\n\n/**\n * The single shared rich markdown editor surface. Combines\n * {@link createSharedEditorExtensions} (schema + dialect-keyed markdown +\n * optional collab + app extras), {@link useCollabReconcile} (seed / reconcile /\n * lead-client logic), and the shared {@link SlashCommandMenu} +\n * {@link BubbleToolbar}.\n *\n * With no `ydoc` it is a controlled `value`/`onChange` single-user editor.\n * With a `ydoc` it binds the framework collaboration stack; markdown stays the\n * canonical saved representation while the Y.Doc is transient live state.\n */\nexport function SharedRichEditor({\n value,\n onChange,\n onBlur,\n contentUpdatedAt,\n editable = true,\n dialect = \"gfm\",\n preset = \"plan\",\n features,\n onImageUpload = null,\n extraExtensions,\n placeholder = \"Type '/' for commands...\",\n className,\n editorClassName,\n interactive = editable,\n ydoc = null,\n awareness = null,\n user = null,\n slashItems,\n buildBubbleItems,\n}: SharedRichEditorProps) {\n const onChangeRef = useRef(onChange);\n const onBlurRef = useRef(onBlur);\n onChangeRef.current = onChange;\n onBlurRef.current = onBlur;\n\n const extensions = useMemo(\n () =>\n createSharedEditorExtensions({\n dialect,\n preset,\n placeholder,\n features,\n extraExtensions,\n onImageUpload,\n collab: ydoc ? { ydoc, awareness, user } : null,\n }),\n // `preset` is retained in the dependency list so future preset-specific\n // schema branches re-create the editor; it is currently schema-neutral. The\n // collab inputs are identity-stable per block (one Y.Doc per docId), so they\n // only change on a genuine doc switch — exactly when the editor must\n // re-create to rebind Collaboration.\n [\n dialect,\n placeholder,\n preset,\n features,\n extraExtensions,\n onImageUpload,\n ydoc,\n awareness,\n user?.name,\n user?.email,\n user?.color,\n ],\n );\n\n const collab = !!ydoc;\n\n // The collab hook needs the editor, but useEditor's `onUpdate` needs the\n // hook's guards. Break the cycle with a ref: `onUpdate` reads the guards\n // through `guardsRef`, which is populated right after the hook runs below.\n // `onUpdate` only ever fires after the editor exists, by which point the ref\n // holds the real guards.\n const guardsRef = useRef<UseCollabReconcileResult | null>(null);\n\n const editor = useEditor({\n extensions,\n // With Collaboration active the prose is owned by the shared Y.XmlFragment.\n // Seeding `content` here too would make the editor initialize from BOTH the\n // prop and the Y.Doc, firing a spurious initial update that could autosave a\n // stale value over newer SQL. The lead-client seed effect populates an empty\n // doc instead. Non-collab editors keep initializing from `value`.\n content: collab ? undefined : value,\n editable,\n editorProps: {\n attributes: {\n class: cn(\"an-rich-md-prose\", editorClassName),\n },\n },\n onUpdate: ({ editor, transaction }) => {\n const guards = guardsRef.current;\n if (!guards || guards.shouldIgnoreUpdate(transaction)) return;\n try {\n const markdown = getEditorMarkdown(editor);\n if (!guards.registerEmitted(markdown)) return;\n queueMicrotask(() => onChangeRef.current(markdown));\n } catch (error) {\n console.error(\"Markdown serialization error:\", error);\n }\n },\n onBlur: () => {\n onBlurRef.current?.();\n },\n });\n\n const collabState = useCollabReconcile({\n editor,\n ydoc,\n awareness,\n value,\n contentUpdatedAt,\n editable,\n getMarkdown: getEditorMarkdown,\n });\n guardsRef.current = collabState;\n\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n editor.setEditable(editable);\n }, [editable, editor]);\n\n useEffect(() => () => editor?.destroy(), [editor]);\n\n const handleWrapperClick = (event: React.MouseEvent<HTMLDivElement>) => {\n if (!editable || !editor || editor.isDestroyed) return;\n const target = event.target as HTMLElement;\n if (\n target.classList.contains(\"an-rich-md-wrapper\") ||\n target.classList.contains(\"an-rich-md-clickable\")\n ) {\n editor.chain().focus(\"end\").run();\n }\n };\n\n if (!editor) {\n return (\n <div\n className={cn(\"an-rich-md-wrapper an-rich-md-loading\", className)}\n data-plan-interactive={interactive ? true : undefined}\n />\n );\n }\n\n return (\n <div\n className={cn(\n \"an-rich-md-wrapper an-rich-md-clickable\",\n !editable && \"an-rich-md-wrapper--readonly\",\n className,\n )}\n onClick={handleWrapperClick}\n data-plan-interactive={interactive ? true : undefined}\n >\n {editable ? (\n <BubbleToolbar editor={editor} buildItems={buildBubbleItems} />\n ) : null}\n {editable ? (\n <SlashCommandMenu editor={editor} items={slashItems} />\n ) : null}\n <EditorContent editor={editor} />\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/react";
|
|
2
|
+
import { type ImageUploadFn } from "./ImageExtension.js";
|
|
3
|
+
/** A single slash-menu block command. Apps can extend the default list. */
|
|
4
|
+
export interface SlashCommandItem {
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
/** Short text glyph shown in the menu (T, H1, tbl, …). */
|
|
8
|
+
icon: string;
|
|
9
|
+
action: (editor: Editor) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The default block commands — Plan's current set. Apps pass their own `items`
|
|
13
|
+
* (typically `[...DEFAULT_SLASH_COMMANDS, ...extra]`) to extend it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_SLASH_COMMANDS: SlashCommandItem[];
|
|
16
|
+
/**
|
|
17
|
+
* Build the `/image` slash command for the shared image block. Requires the
|
|
18
|
+
* editor to mount the shared image extension (`features.image`) and an
|
|
19
|
+
* {@link ImageUploadFn}; the command opens a native file picker and uploads +
|
|
20
|
+
* inserts the chosen image(s). Add it to the list an app passes to
|
|
21
|
+
* {@link SlashCommandMenu} (e.g. `[...DEFAULT_SLASH_COMMANDS, createImageSlashCommand(upload)]`).
|
|
22
|
+
*/
|
|
23
|
+
export declare function createImageSlashCommand(upload: ImageUploadFn): SlashCommandItem;
|
|
24
|
+
export interface SlashCommandMenuProps {
|
|
25
|
+
editor: Editor;
|
|
26
|
+
/** Block command list. Defaults to {@link DEFAULT_SLASH_COMMANDS}. */
|
|
27
|
+
items?: SlashCommandItem[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The shared "/" block-insert menu. Detects a `/query` at the caret, filters the
|
|
31
|
+
* provided command list, and renders a fixed-position picker with keyboard
|
|
32
|
+
* navigation. Extracted from the inline plan menu so apps share one
|
|
33
|
+
* implementation and only swap the command list.
|
|
34
|
+
*/
|
|
35
|
+
export declare function SlashCommandMenu({ editor, items, }: SlashCommandMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
//# sourceMappingURL=SlashCommandMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlashCommandMenu.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SlashCommandMenu.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,gBAAgB,EA2EpD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,GACpB,gBAAgB,CAOlB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAA8B,GAC/B,EAAE,qBAAqB,2CAgJvB"}
|