@agent-native/core 0.123.0 → 0.123.2
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +24 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/templates-meta.ts +12 -0
- package/corpus/core/src/deploy/build.ts +8 -0
- package/corpus/core/src/server/builder-browser.ts +76 -0
- package/corpus/core/src/server/core-routes-plugin.ts +15 -5
- package/corpus/templates/assets/app/global.css +0 -4
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +195 -167
- package/corpus/templates/assets/changelog/2026-07-23-drafts-preview-panel-now-fills-the-full-width-with-a-large-p.md +6 -0
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +55 -9
- package/corpus/templates/content/actions/_database-source-utils.ts +756 -182
- package/corpus/templates/content/actions/change-content-database-source-role.ts +21 -20
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +49 -8
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +42 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +88 -90
- package/corpus/templates/content/actions/preview-builder-source-review.ts +4 -3
- package/corpus/templates/content/actions/refresh-content-database-source.ts +36 -9
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +28 -24
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +62 -12
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +91 -16
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +15 -1
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +234 -44
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +194 -16
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +95 -42
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +4 -2
- package/corpus/templates/content/app/components/editor/extensions/CodeBlockNode.tsx +10 -166
- package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +32 -8
- package/corpus/templates/content/app/components/editor/extensions/VideoNode.ts +14 -0
- package/corpus/templates/content/app/components/editor/registrySlashItems.ts +5 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +59 -2
- package/corpus/templates/content/app/hooks/use-documents.ts +18 -26
- package/corpus/templates/content/app/root.tsx +1 -6
- package/corpus/templates/content/changelog/2026-07-24-builder-can-now-publish-approved-updates-to-entries-that-are.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-collection-loading-no-longer-restarts-from-the-begin.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-connections-now-complete-securely-from-netlify-deplo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-draft-creation-now-scopes-execution-to-the-selected-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-review-now-distinguishes-preparing-a-safe-dry-run-from.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-source-imports-now-recover-from-a-transient-paginati.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-source-refresh-now-preserves-the-provider-bound-fiel.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-update-reviews-now-open-reliably-on-large-collection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-write-policy-controls-now-reflect-successful-changes.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-date-properties-now-save-reliably-when-selected-with-the-nat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-editor-slash-search-now-keeps-multi-word-block-names-open.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-exact-editor-slash-commands-now-execute-on-enter-even-if-the.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-new-collaborative-pages-now-save-their-first-edits.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-new-local-builder-rows-no-longer-remain-locked-behind-a-none.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-opening-or-closing-a-collaborative-document-no-longer-lets-u.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-selected-text-now-opens-the-link-editor-with-command-k-or-co.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-slash-menu-block-choices-no-longer-stall-pointer-interaction.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-uploaded-and-embedded-media-now-stay-intact-while-collaborative-document-drafts-save.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-video-link-insertion-now-keeps-its-source-panel-open-while-t.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-builder-publishing-now-shows-safe-validation-details-instead.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-builder-reviews-stay-responsive-on-large-collections-and-pre.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-collaborative-documents-no-longer-flash-duplicated-recent-bl.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-corrected-builder-updates-are-now-reviewed-separately-from-f.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-documents-now-remain-read-only-until-their-live-collaborativ.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-slash-command-menus-now-stay-reachable-when-editing-near-the.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-the-slash-menu-now-shows-one-clear-callout-choice-instead-of.md +6 -0
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/content/shared/builder-mdx.ts +73 -16
- package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
- package/corpus/templates/crm/.agents/skills/crm/SKILL.md +205 -0
- package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +220 -0
- package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +228 -0
- package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +178 -0
- package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
- package/corpus/templates/crm/.env.example +5 -0
- package/corpus/templates/crm/AGENTS.md +82 -0
- package/corpus/templates/crm/CHANGELOG.md +25 -0
- package/corpus/templates/crm/DEVELOPING.md +66 -0
- package/corpus/templates/crm/README.md +122 -0
- package/corpus/templates/crm/_gitignore +12 -0
- package/corpus/templates/crm/actions/_crm-action-utils.ts +98 -0
- package/corpus/templates/crm/actions/_crm-attribute-utils.ts +219 -0
- package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
- package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
- package/corpus/templates/crm/actions/_crm-list-utils.ts +775 -0
- package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
- package/corpus/templates/crm/actions/add-crm-record-to-list.ts +164 -0
- package/corpus/templates/crm/actions/apply-crm-proposals.ts +291 -0
- package/corpus/templates/crm/actions/archive-crm-attribute.ts +69 -0
- package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
- package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
- package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
- package/corpus/templates/crm/actions/create-crm-attribute.ts +350 -0
- package/corpus/templates/crm/actions/create-crm-list.ts +194 -0
- package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
- package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
- package/corpus/templates/crm/actions/delete-crm-saved-view.ts +68 -0
- package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
- package/corpus/templates/crm/actions/estimate-crm-enrichment.ts +229 -0
- package/corpus/templates/crm/actions/find-crm-duplicates.ts +87 -0
- package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
- package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +35 -0
- package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
- package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
- package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
- package/corpus/templates/crm/actions/get-crm-record-page.ts +287 -0
- package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
- package/corpus/templates/crm/actions/get-crm-workspace.ts +364 -0
- package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
- package/corpus/templates/crm/actions/list-crm-attributes.ts +71 -0
- package/corpus/templates/crm/actions/list-crm-connections.ts +68 -0
- package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
- package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
- package/corpus/templates/crm/actions/list-crm-enrichment-slots.ts +28 -0
- package/corpus/templates/crm/actions/list-crm-list-entries.ts +192 -0
- package/corpus/templates/crm/actions/list-crm-lists.ts +80 -0
- package/corpus/templates/crm/actions/list-crm-proposals.ts +55 -0
- package/corpus/templates/crm/actions/list-crm-record-field-history.ts +201 -0
- package/corpus/templates/crm/actions/list-crm-record-values.ts +200 -0
- package/corpus/templates/crm/actions/list-crm-records.ts +103 -0
- package/corpus/templates/crm/actions/list-crm-saved-views.ts +66 -0
- package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
- package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
- package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
- package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
- package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
- package/corpus/templates/crm/actions/manage-crm-attribute-option.ts +272 -0
- package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
- package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
- package/corpus/templates/crm/actions/merge-crm-records.ts +691 -0
- package/corpus/templates/crm/actions/navigate.ts +54 -0
- package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
- package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
- package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
- package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
- package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
- package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
- package/corpus/templates/crm/actions/remove-crm-list-entry.ts +79 -0
- package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
- package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
- package/corpus/templates/crm/actions/run-crm-attribute-fill.ts +443 -0
- package/corpus/templates/crm/actions/run-crm-enrichment.ts +647 -0
- package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
- package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +265 -0
- package/corpus/templates/crm/actions/run.ts +7 -0
- package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
- package/corpus/templates/crm/actions/save-crm-saved-view.ts +296 -0
- package/corpus/templates/crm/actions/sync-crm.ts +146 -0
- package/corpus/templates/crm/actions/update-crm-attribute.ts +111 -0
- package/corpus/templates/crm/actions/update-crm-list-entry.ts +102 -0
- package/corpus/templates/crm/actions/update-crm-list.ts +97 -0
- package/corpus/templates/crm/actions/update-crm-record.ts +543 -0
- package/corpus/templates/crm/actions/view-screen.ts +324 -0
- package/corpus/templates/crm/agent-native.app-skill.json +69 -0
- package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
- package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +78 -0
- package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +220 -0
- package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +414 -0
- package/corpus/templates/crm/app/components/crm/RecordActions.tsx +648 -0
- package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +488 -0
- package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +232 -0
- package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +93 -0
- package/corpus/templates/crm/app/components/crm/Surface.tsx +97 -0
- package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
- package/corpus/templates/crm/app/components/crm/board/CrmBoard.tsx +988 -0
- package/corpus/templates/crm/app/components/crm/board/board-model.ts +399 -0
- package/corpus/templates/crm/app/components/crm/board/entry-filter.ts +150 -0
- package/corpus/templates/crm/app/components/crm/board/view-draft.ts +227 -0
- package/corpus/templates/crm/app/components/crm/grid/CrmGrid.tsx +1033 -0
- package/corpus/templates/crm/app/components/crm/grid/GridCell.tsx +620 -0
- package/corpus/templates/crm/app/components/crm/grid/clipboard.ts +84 -0
- package/corpus/templates/crm/app/components/crm/grid/model.ts +304 -0
- package/corpus/templates/crm/app/components/crm/grid/navigation.ts +220 -0
- package/corpus/templates/crm/app/components/crm/grid/query.ts +214 -0
- package/corpus/templates/crm/app/components/crm/record/AttributePanel.tsx +192 -0
- package/corpus/templates/crm/app/components/crm/record/FieldHistory.tsx +179 -0
- package/corpus/templates/crm/app/components/crm/record/RecordHeader.tsx +103 -0
- package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +235 -0
- package/corpus/templates/crm/app/components/crm/record/RecordTabs.tsx +206 -0
- package/corpus/templates/crm/app/components/crm/record/field-editors.tsx +245 -0
- package/corpus/templates/crm/app/components/crm/record/record-data.ts +603 -0
- package/corpus/templates/crm/app/components/crm/settings/AdvancedSettings.tsx +49 -0
- package/corpus/templates/crm/app/components/crm/settings/AttributeOptionsEditor.tsx +441 -0
- package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +169 -0
- package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +935 -0
- package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +339 -0
- package/corpus/templates/crm/app/components/crm/settings/settings-admin.ts +377 -0
- package/corpus/templates/crm/app/components/crm/shared/AttributeValueParts.tsx +69 -0
- package/corpus/templates/crm/app/components/crm/shared/attribute-value.ts +591 -0
- package/corpus/templates/crm/app/components/layout/CrmCommandMenu.tsx +556 -0
- package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +130 -0
- package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +407 -0
- package/corpus/templates/crm/app/components/layout/command-palette-search.ts +126 -0
- package/corpus/templates/crm/app/components/layout/crm-ui-intents.ts +41 -0
- package/corpus/templates/crm/app/components/layout/sidebar-collapse.ts +40 -0
- package/corpus/templates/crm/app/components/layout/sidebar-lists.ts +109 -0
- package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/hover-card.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
- package/corpus/templates/crm/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/crm/app/design-system.ts +3 -0
- package/corpus/templates/crm/app/entry.client.tsx +19 -0
- package/corpus/templates/crm/app/entry.server.tsx +10 -0
- package/corpus/templates/crm/app/global.css +167 -0
- package/corpus/templates/crm/app/hooks/use-keyboard-shortcuts.ts +206 -0
- package/corpus/templates/crm/app/hooks/use-navigation-state.ts +48 -0
- package/corpus/templates/crm/app/i18n/en-US.ts +684 -0
- package/corpus/templates/crm/app/i18n/index.ts +34 -0
- package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
- package/corpus/templates/crm/app/lib/navigation.ts +17 -0
- package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
- package/corpus/templates/crm/app/lib/types.ts +172 -0
- package/corpus/templates/crm/app/lib/utils.ts +1 -0
- package/corpus/templates/crm/app/root.tsx +93 -0
- package/corpus/templates/crm/app/routes/_index.tsx +26 -0
- package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
- package/corpus/templates/crm/app/routes/agent.tsx +5 -0
- package/corpus/templates/crm/app/routes/ask.tsx +53 -0
- package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
- package/corpus/templates/crm/app/routes/lists.tsx +228 -0
- package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
- package/corpus/templates/crm/app/routes/people.tsx +41 -0
- package/corpus/templates/crm/app/routes/proposals.tsx +388 -0
- package/corpus/templates/crm/app/routes/records.$recordId.tsx +53 -0
- package/corpus/templates/crm/app/routes/records.tsx +64 -0
- package/corpus/templates/crm/app/routes/settings.tsx +118 -0
- package/corpus/templates/crm/app/routes/setup.tsx +308 -0
- package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
- package/corpus/templates/crm/app/routes/views.tsx +683 -0
- package/corpus/templates/crm/app/routes.ts +19 -0
- package/corpus/templates/crm/app/vite-env.d.ts +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-any-list-or-view-can-be-shown-as-a-board-grouped-by-stage-so.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-crm-now-finds-likely-duplicate-records-and-tells-you-exactly.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-enrichment-now-shows-what-a-run-will-cost-before-it-starts-g.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-lists-turn-any-set-of-records-into-a-pipeline-with-its-own-s.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-moving-a-record-or-list-entry-into-a-stage-that-was-retired-.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-opening-a-record-now-shows-one-page-with-its-fields-the-list.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-press-cmd-k-to-jump-to-any-record-list-view-or-setting-and-u.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-provider-record-edits-now-show-an-exact-before-after-diff-an.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-record-page-attribute-labels-now-read-in-full-instead-of-bei.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-records-now-use-typed-fields-currency-dates-ratings-status-s.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-retyping-a-value-on-the-record-page-now-replaces-it-instead-.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-settings-now-has-fields-and-lists-tabs-for-adding-fields-edi.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-the-proposal-review-now-shows-the-field-that-actually-change.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-the-record-grid-is-now-a-real-spreadsheet-filter-sort-and-pa.md +6 -0
- package/corpus/templates/crm/components.json +20 -0
- package/corpus/templates/crm/docs/architecture/crm-contract.md +214 -0
- package/corpus/templates/crm/docs/features/README.md +55 -0
- package/corpus/templates/crm/docs/features/f1-records-and-fields.mdx +52 -0
- package/corpus/templates/crm/docs/features/f2-lists-pipelines-and-views.mdx +48 -0
- package/corpus/templates/crm/docs/features/f3-work-and-record-quality.mdx +40 -0
- package/corpus/templates/crm/docs/features/f4-evidence-and-chat.mdx +47 -0
- package/corpus/templates/crm/learnings.defaults.md +5 -0
- package/corpus/templates/crm/netlify.toml +11 -0
- package/corpus/templates/crm/package.json +61 -0
- package/corpus/templates/crm/public/favicon.svg +1 -0
- package/corpus/templates/crm/public/icon-180.svg +1 -0
- package/corpus/templates/crm/public/manifest.json +11 -0
- package/corpus/templates/crm/react-router.config.ts +7 -0
- package/corpus/templates/crm/server/crm/adapter.ts +32 -0
- package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
- package/corpus/templates/crm/server/crm/crm-mirror.ts +721 -0
- package/corpus/templates/crm/server/crm/field-policy-attributes.ts +48 -0
- package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1146 -0
- package/corpus/templates/crm/server/crm/native-adapter.ts +1810 -0
- package/corpus/templates/crm/server/crm/provider-record-link.ts +264 -0
- package/corpus/templates/crm/server/crm/read-through.ts +207 -0
- package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
- package/corpus/templates/crm/server/db/crm-store.ts +1112 -0
- package/corpus/templates/crm/server/db/index.ts +191 -0
- package/corpus/templates/crm/server/db/schema.ts +647 -0
- package/corpus/templates/crm/server/lib/crm-query.ts +1558 -0
- package/corpus/templates/crm/server/lib/dedupe.ts +714 -0
- package/corpus/templates/crm/server/lib/enrichment-cost.ts +381 -0
- package/corpus/templates/crm/server/lib/enrichment-slots.ts +519 -0
- package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
- package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
- package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +94 -0
- package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +39 -0
- package/corpus/templates/crm/server/lib/intelligence/summary.ts +19 -0
- package/corpus/templates/crm/server/lib/lifecycle.ts +634 -0
- package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
- package/corpus/templates/crm/server/middleware/auth.ts +4 -0
- package/corpus/templates/crm/server/plugins/agent-chat.ts +65 -0
- package/corpus/templates/crm/server/plugins/auth.ts +14 -0
- package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
- package/corpus/templates/crm/server/plugins/db.ts +738 -0
- package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
- package/corpus/templates/crm/shared/crm-attributes.ts +469 -0
- package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
- package/corpus/templates/crm/shared/crm-contract.ts +417 -0
- package/corpus/templates/crm/shared/crm-navigation.ts +190 -0
- package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
- package/corpus/templates/crm/ssr-entry.ts +11 -0
- package/corpus/templates/crm/tsconfig.json +24 -0
- package/corpus/templates/crm/vite.config.ts +15 -0
- package/corpus/templates/crm/vitest.config.ts +5 -0
- package/corpus/templates/design/app/i18n-data.ts +12 -12
- package/corpus/templates/design/changelog/2026-07-26-apply-design-updates-from-a-centered-top-toolbar-without-cov.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/editor/useCollabReconcile.ts +14 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +12 -0
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/deploy/build.js +6 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/server/builder-browser.d.ts +13 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +59 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -5
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +2 -2
- package/src/cli/templates-meta.ts +12 -0
- package/src/deploy/build.ts +8 -0
- package/src/server/builder-browser.ts +76 -0
- package/src/server/core-routes-plugin.ts +15 -5
|
@@ -29,7 +29,14 @@ import { TableRow } from "@tiptap/extension-table-row";
|
|
|
29
29
|
import TaskItem from "@tiptap/extension-task-item";
|
|
30
30
|
import TaskList from "@tiptap/extension-task-list";
|
|
31
31
|
import { Fragment, type Node as ProseMirrorNode } from "@tiptap/pm/model";
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
Plugin,
|
|
34
|
+
PluginKey,
|
|
35
|
+
AllSelection,
|
|
36
|
+
NodeSelection,
|
|
37
|
+
Selection,
|
|
38
|
+
type Transaction,
|
|
39
|
+
} from "@tiptap/pm/state";
|
|
33
40
|
import { Decoration, DecorationSet, type EditorView } from "@tiptap/pm/view";
|
|
34
41
|
import {
|
|
35
42
|
useEditor,
|
|
@@ -925,6 +932,47 @@ export function shouldPersistLocalFileEditorUpdate({
|
|
|
925
932
|
return Boolean(transactionUiEvent);
|
|
926
933
|
}
|
|
927
934
|
|
|
935
|
+
export function shouldPersistCollaborativeEditorUpdate({
|
|
936
|
+
collab,
|
|
937
|
+
editorFocused,
|
|
938
|
+
userInitiated,
|
|
939
|
+
}: {
|
|
940
|
+
collab: boolean;
|
|
941
|
+
editorFocused: boolean;
|
|
942
|
+
userInitiated: boolean;
|
|
943
|
+
}) {
|
|
944
|
+
// Collaborative mount/reconcile normalization can produce a local-looking
|
|
945
|
+
// transaction after the remote Y.Doc has loaded. If the editor is not
|
|
946
|
+
// focused and no human input event preceded the transaction, it has no
|
|
947
|
+
// authority to overwrite SQL. Focused commands and explicit user-intent
|
|
948
|
+
// transactions remain persistable; structural/media actions additionally
|
|
949
|
+
// use their immediate proof-of-save callbacks.
|
|
950
|
+
return !collab || editorFocused || userInitiated;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export function isUserInitiatedCollaborativeEditorUpdate({
|
|
954
|
+
editorFocused,
|
|
955
|
+
explicitUserEdit,
|
|
956
|
+
recentUserEditIntent,
|
|
957
|
+
transactionUiEvent,
|
|
958
|
+
}: {
|
|
959
|
+
editorFocused: boolean;
|
|
960
|
+
explicitUserEdit: boolean;
|
|
961
|
+
recentUserEditIntent: boolean;
|
|
962
|
+
transactionUiEvent: unknown;
|
|
963
|
+
}) {
|
|
964
|
+
// A recent input event is useful for grouping the follow-up transactions
|
|
965
|
+
// produced while the editor still owns focus. Once focus has left, however,
|
|
966
|
+
// only provenance on this exact transaction may authorize persistence;
|
|
967
|
+
// otherwise mount/Yjs normalization could borrow a stale two-second intent
|
|
968
|
+
// window and overwrite canonical SQL.
|
|
969
|
+
return (
|
|
970
|
+
explicitUserEdit ||
|
|
971
|
+
Boolean(transactionUiEvent) ||
|
|
972
|
+
(editorFocused && recentUserEditIntent)
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
|
|
928
976
|
function isEffectivelyEmptyEditorContent(value: string): boolean {
|
|
929
977
|
const normalized = value.trim();
|
|
930
978
|
return normalized === "" || normalized === "<empty-block/>";
|
|
@@ -975,19 +1023,21 @@ interface VisualEditorExtensionOptions {
|
|
|
975
1023
|
localFilePath?: string | null;
|
|
976
1024
|
referenceDepth?: number;
|
|
977
1025
|
emptyBlockPlaceholder?: string;
|
|
1026
|
+
onMediaSourceCommitted?: (
|
|
1027
|
+
editor: CoreEditor,
|
|
1028
|
+
transaction: Transaction,
|
|
1029
|
+
) => void;
|
|
978
1030
|
}
|
|
979
1031
|
|
|
980
|
-
function hasAncestorType(
|
|
1032
|
+
export function hasAncestorType(
|
|
981
1033
|
editor: CoreEditor,
|
|
982
1034
|
pos: number,
|
|
983
1035
|
typeName: string,
|
|
984
1036
|
): boolean {
|
|
985
1037
|
const doc = editor.state.doc;
|
|
986
|
-
const
|
|
987
|
-
Math.max(0,
|
|
988
|
-
|
|
989
|
-
Math.min(doc.content.size, pos + 1),
|
|
990
|
-
];
|
|
1038
|
+
const clampPosition = (candidate: number) =>
|
|
1039
|
+
Math.min(doc.content.size, Math.max(0, candidate));
|
|
1040
|
+
const positions = [...new Set([pos - 1, pos, pos + 1].map(clampPosition))];
|
|
991
1041
|
|
|
992
1042
|
return positions.some((candidatePos) => {
|
|
993
1043
|
const resolvedPos = doc.resolve(candidatePos);
|
|
@@ -1002,6 +1052,88 @@ function hasAncestorType(
|
|
|
1002
1052
|
|
|
1003
1053
|
type MediaNodeType = "image" | "video" | "audio";
|
|
1004
1054
|
|
|
1055
|
+
const MEDIA_NODE_TYPES = new Set<MediaNodeType>(["image", "video", "audio"]);
|
|
1056
|
+
|
|
1057
|
+
function mediaSourceCounts(doc: ProseMirrorNode) {
|
|
1058
|
+
const counts = new Map<string, number>();
|
|
1059
|
+
doc.descendants((node) => {
|
|
1060
|
+
if (!MEDIA_NODE_TYPES.has(node.type.name as MediaNodeType)) return true;
|
|
1061
|
+
const src = typeof node.attrs.src === "string" ? node.attrs.src : "";
|
|
1062
|
+
if (!src) return false;
|
|
1063
|
+
const key = `${node.type.name}\u0000${src}`;
|
|
1064
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
1065
|
+
return false;
|
|
1066
|
+
});
|
|
1067
|
+
return counts;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
export function didCommitMediaSource(transaction: Transaction): boolean {
|
|
1071
|
+
if (!transaction.docChanged) return false;
|
|
1072
|
+
const before = mediaSourceCounts(transaction.before);
|
|
1073
|
+
const after = mediaSourceCounts(transaction.doc);
|
|
1074
|
+
return [...after].some(([key, count]) => count > (before.get(key) ?? 0));
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const MediaSourceCommit = Extension.create<{
|
|
1078
|
+
onMediaSourceCommitted?: (
|
|
1079
|
+
editor: CoreEditor,
|
|
1080
|
+
transaction: Transaction,
|
|
1081
|
+
) => void;
|
|
1082
|
+
}>({
|
|
1083
|
+
name: "mediaSourceCommit",
|
|
1084
|
+
addOptions() {
|
|
1085
|
+
return { onMediaSourceCommitted: undefined };
|
|
1086
|
+
},
|
|
1087
|
+
onTransaction({ editor, transaction }) {
|
|
1088
|
+
if (didCommitMediaSource(transaction)) {
|
|
1089
|
+
this.options.onMediaSourceCommitted?.(editor, transaction);
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Empty media nodes are transient editor UI, not durable document content.
|
|
1096
|
+
*
|
|
1097
|
+
* Persisting the placeholder before its async upload/link enrichment finishes
|
|
1098
|
+
* lets the SQL echo reconcile the empty `src` back into the live Y.Doc. That
|
|
1099
|
+
* can erase a successfully uploaded image or embedded video. Keep the local
|
|
1100
|
+
* draft out of autosave until it has a source; uploads inserted by drop/paste
|
|
1101
|
+
* are covered by their `uploadId` even when the selection is elsewhere.
|
|
1102
|
+
*/
|
|
1103
|
+
export function shouldSkipMediaDraftPersistence(editor: CoreEditor): boolean {
|
|
1104
|
+
let hasPendingUpload = false;
|
|
1105
|
+
editor.state.doc.descendants((node) => {
|
|
1106
|
+
if (
|
|
1107
|
+
MEDIA_NODE_TYPES.has(node.type.name as MediaNodeType) &&
|
|
1108
|
+
Boolean(node.attrs.uploadId)
|
|
1109
|
+
) {
|
|
1110
|
+
hasPendingUpload = true;
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
return !hasPendingUpload;
|
|
1114
|
+
});
|
|
1115
|
+
if (hasPendingUpload) return true;
|
|
1116
|
+
|
|
1117
|
+
const { selection } = editor.state;
|
|
1118
|
+
if (!(selection instanceof NodeSelection)) return false;
|
|
1119
|
+
return (
|
|
1120
|
+
MEDIA_NODE_TYPES.has(selection.node.type.name as MediaNodeType) &&
|
|
1121
|
+
!selection.node.attrs.src
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Serialize only complete editor drafts. Structural slash commands explicitly
|
|
1127
|
+
* ask to persist after their transaction, so this guard must live in the shared
|
|
1128
|
+
* persistence path rather than only in `onUpdate`.
|
|
1129
|
+
*/
|
|
1130
|
+
export function serializeEditorDraftForPersistence(
|
|
1131
|
+
editor: CoreEditor,
|
|
1132
|
+
): string | null {
|
|
1133
|
+
if (shouldSkipMediaDraftPersistence(editor)) return null;
|
|
1134
|
+
return docToNfm(editor.getJSON() as any);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1005
1137
|
function mediaNodeLabel(typeName: MediaNodeType) {
|
|
1006
1138
|
if (typeName === "image") return "Image";
|
|
1007
1139
|
if (typeName === "video") return "Video";
|
|
@@ -1305,6 +1437,7 @@ export function createVisualEditorExtensions({
|
|
|
1305
1437
|
localFilePath,
|
|
1306
1438
|
referenceDepth = 0,
|
|
1307
1439
|
emptyBlockPlaceholder = DEFAULT_EMPTY_BLOCK_PLACEHOLDER,
|
|
1440
|
+
onMediaSourceCommitted,
|
|
1308
1441
|
}: VisualEditorExtensionOptions = {}): Extensions {
|
|
1309
1442
|
// Build on the SHARED editor core (StarterKit base + the Collaboration /
|
|
1310
1443
|
// CollaborationCaret wiring + collab undo/redo gating + ordering), then inject
|
|
@@ -1372,6 +1505,7 @@ export function createVisualEditorExtensions({
|
|
|
1372
1505
|
documentId,
|
|
1373
1506
|
onAudioComment: onImageComment,
|
|
1374
1507
|
}),
|
|
1508
|
+
MediaSourceCommit.configure({ onMediaSourceCommitted }),
|
|
1375
1509
|
CustomTable.configure({
|
|
1376
1510
|
resizable: false,
|
|
1377
1511
|
HTMLAttributes: { class: "notion-table" },
|
|
@@ -1688,6 +1822,15 @@ export function VisualEditor({
|
|
|
1688
1822
|
onSaveContentRef.current = onSaveContent;
|
|
1689
1823
|
const notionPageLinksRef = useRef(notionPageLinks);
|
|
1690
1824
|
notionPageLinksRef.current = notionPageLinks;
|
|
1825
|
+
const onMediaSourceCommittedRef = useRef<
|
|
1826
|
+
((editor: CoreEditor, transaction: Transaction) => void) | null
|
|
1827
|
+
>(null);
|
|
1828
|
+
const onMediaSourceCommitted = useCallback(
|
|
1829
|
+
(editor: CoreEditor, transaction: Transaction) => {
|
|
1830
|
+
onMediaSourceCommittedRef.current?.(editor, transaction);
|
|
1831
|
+
},
|
|
1832
|
+
[],
|
|
1833
|
+
);
|
|
1691
1834
|
const resolveNotionPageLink = useCallback((notionPageId: string) => {
|
|
1692
1835
|
const normalized = notionPageId.replace(/-/g, "").toLowerCase();
|
|
1693
1836
|
return (
|
|
@@ -1745,6 +1888,7 @@ export function VisualEditor({
|
|
|
1745
1888
|
localFilePath,
|
|
1746
1889
|
referenceDepth,
|
|
1747
1890
|
emptyBlockPlaceholder: t("editor.emptyBlockPlaceholder"),
|
|
1891
|
+
onMediaSourceCommitted,
|
|
1748
1892
|
}),
|
|
1749
1893
|
[
|
|
1750
1894
|
documentId,
|
|
@@ -1760,6 +1904,7 @@ export function VisualEditor({
|
|
|
1760
1904
|
localFilePath,
|
|
1761
1905
|
referenceDepth,
|
|
1762
1906
|
t,
|
|
1907
|
+
onMediaSourceCommitted,
|
|
1763
1908
|
],
|
|
1764
1909
|
);
|
|
1765
1910
|
|
|
@@ -1784,8 +1929,9 @@ export function VisualEditor({
|
|
|
1784
1929
|
const guards = guardsRef.current;
|
|
1785
1930
|
if (!guards) return false;
|
|
1786
1931
|
try {
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1932
|
+
const serialized = serializeEditorDraftForPersistence(editorToPersist);
|
|
1933
|
+
if (serialized === null) return true;
|
|
1934
|
+
const normalized = options?.markdown ?? serialized;
|
|
1789
1935
|
if (localFileMode && normalized === content) return true;
|
|
1790
1936
|
// TipTap/Yjs can emit a local-looking empty-paragraph transaction while
|
|
1791
1937
|
// an editor is mounting or reconciling. Content serializes that filler
|
|
@@ -1820,6 +1966,23 @@ export function VisualEditor({
|
|
|
1820
1966
|
},
|
|
1821
1967
|
[content, localFileMode, t],
|
|
1822
1968
|
);
|
|
1969
|
+
onMediaSourceCommittedRef.current = async (editorToPersist, transaction) => {
|
|
1970
|
+
const guards = guardsRef.current;
|
|
1971
|
+
if (!guards || guards.shouldIgnoreUpdate(transaction)) return;
|
|
1972
|
+
try {
|
|
1973
|
+
const persisted = await persistEditorContent(editorToPersist, {
|
|
1974
|
+
immediate: true,
|
|
1975
|
+
userInitiated: true,
|
|
1976
|
+
});
|
|
1977
|
+
if (!persisted) throw new Error(t("empty.genericError"));
|
|
1978
|
+
} catch (error) {
|
|
1979
|
+
// The ordinary onUpdate path still queues its debounced retry. Keep the
|
|
1980
|
+
// immediate durability attempt from becoming an unhandled rejection,
|
|
1981
|
+
// but fail visibly instead of treating a skipped save as success.
|
|
1982
|
+
toast.error(t("empty.genericError"));
|
|
1983
|
+
console.error("Media source persistence error:", error);
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1823
1986
|
|
|
1824
1987
|
const editor = useEditor({
|
|
1825
1988
|
extensions,
|
|
@@ -1973,12 +2136,27 @@ export function VisualEditor({
|
|
|
1973
2136
|
) {
|
|
1974
2137
|
return;
|
|
1975
2138
|
}
|
|
2139
|
+
const userInitiated = isUserInitiatedCollaborativeEditorUpdate({
|
|
2140
|
+
editorFocused: editor.isFocused,
|
|
2141
|
+
explicitUserEdit:
|
|
2142
|
+
transaction.getMeta(LOCAL_FILE_USER_EDIT_META) === true,
|
|
2143
|
+
recentUserEditIntent:
|
|
2144
|
+
Date.now() - lastUserEditIntentAtRef.current < 2000,
|
|
2145
|
+
transactionUiEvent: transaction.getMeta("uiEvent"),
|
|
2146
|
+
});
|
|
2147
|
+
if (
|
|
2148
|
+
!shouldPersistCollaborativeEditorUpdate({
|
|
2149
|
+
collab: !!ydoc,
|
|
2150
|
+
editorFocused: editor.isFocused,
|
|
2151
|
+
userInitiated,
|
|
2152
|
+
})
|
|
2153
|
+
) {
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
1976
2156
|
if (isActiveSlashCommandDraft(editor)) return;
|
|
2157
|
+
if (shouldSkipMediaDraftPersistence(editor)) return;
|
|
1977
2158
|
persistEditorContent(editor, {
|
|
1978
|
-
userInitiated
|
|
1979
|
-
transaction.getMeta(LOCAL_FILE_USER_EDIT_META) === true ||
|
|
1980
|
-
Date.now() - lastUserEditIntentAtRef.current < 2000 ||
|
|
1981
|
-
Boolean(transaction.getMeta("uiEvent")),
|
|
2159
|
+
userInitiated,
|
|
1982
2160
|
});
|
|
1983
2161
|
},
|
|
1984
2162
|
});
|
|
@@ -2294,9 +2472,9 @@ export function VisualEditor({
|
|
|
2294
2472
|
editor={editor}
|
|
2295
2473
|
documentId={documentId}
|
|
2296
2474
|
notionPageId={notionPageId}
|
|
2297
|
-
onDraftCommitted={() =>
|
|
2298
|
-
|
|
2299
|
-
}
|
|
2475
|
+
onDraftCommitted={() =>
|
|
2476
|
+
persistEditorContent(editor, { userInitiated: true })
|
|
2477
|
+
}
|
|
2300
2478
|
onDraftPersisted={(markdown) =>
|
|
2301
2479
|
persistEditorContent(editor, {
|
|
2302
2480
|
markdown,
|
|
@@ -159,6 +159,8 @@ import {
|
|
|
159
159
|
TooltipTrigger,
|
|
160
160
|
} from "@/components/ui/tooltip";
|
|
161
161
|
import {
|
|
162
|
+
applyOptimisticBuilderWriteMode,
|
|
163
|
+
contentDatabaseQueryFilter,
|
|
162
164
|
isContentDatabaseUnavailable,
|
|
163
165
|
useAddDatabaseItem,
|
|
164
166
|
useAddContentDatabaseSourceFieldProperty,
|
|
@@ -910,6 +912,7 @@ function DatabaseTable({
|
|
|
910
912
|
);
|
|
911
913
|
const autoContinueBuilderSourceRef = useRef<Set<string>>(new Set());
|
|
912
914
|
const builderContinuationWatchdogRef = useRef<Map<string, number>>(new Map());
|
|
915
|
+
const builderContinuationFailureRef = useRef<Map<string, number>>(new Map());
|
|
913
916
|
const refreshSourceInFlightRef = useRef<string | null>(null);
|
|
914
917
|
const hydrationSourceInFlightRef = useRef<string | null>(null);
|
|
915
918
|
const workspaceSelectionQueueRef = useRef(createContentSpaceSelectionQueue());
|
|
@@ -1115,14 +1118,24 @@ function DatabaseTable({
|
|
|
1115
1118
|
),
|
|
1116
1119
|
};
|
|
1117
1120
|
const runSourceRefresh = useCallback(
|
|
1118
|
-
(
|
|
1121
|
+
(
|
|
1122
|
+
sourceId: string,
|
|
1123
|
+
onError?: () => void,
|
|
1124
|
+
expectedBuilderContinuationOffset?: number,
|
|
1125
|
+
onSuccess?: () => void,
|
|
1126
|
+
) => {
|
|
1119
1127
|
if (!acquireDatabaseSourceOperation(refreshSourceInFlightRef, sourceId)) {
|
|
1120
1128
|
return false;
|
|
1121
1129
|
}
|
|
1122
1130
|
refreshSource.mutate(
|
|
1123
|
-
{
|
|
1131
|
+
{
|
|
1132
|
+
documentId: document.id,
|
|
1133
|
+
sourceId,
|
|
1134
|
+
expectedBuilderContinuationOffset,
|
|
1135
|
+
},
|
|
1124
1136
|
{
|
|
1125
1137
|
onError,
|
|
1138
|
+
onSuccess,
|
|
1126
1139
|
onSettled: () => {
|
|
1127
1140
|
releaseDatabaseSourceOperation(refreshSourceInFlightRef, sourceId);
|
|
1128
1141
|
},
|
|
@@ -1132,6 +1145,25 @@ function DatabaseTable({
|
|
|
1132
1145
|
},
|
|
1133
1146
|
[document.id, refreshSource.mutate],
|
|
1134
1147
|
);
|
|
1148
|
+
const handleBuilderContinuationError = useCallback(
|
|
1149
|
+
(continuationKey: string) => {
|
|
1150
|
+
const failures =
|
|
1151
|
+
(builderContinuationFailureRef.current.get(continuationKey) ?? 0) + 1;
|
|
1152
|
+
builderContinuationFailureRef.current.set(continuationKey, failures);
|
|
1153
|
+
if (builderSourceContinuationFailureDecision(failures) === "error") {
|
|
1154
|
+
setBuilderContinuationClientErrorKeys((current) =>
|
|
1155
|
+
addUniqueKey(current, continuationKey),
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
[],
|
|
1160
|
+
);
|
|
1161
|
+
const handleBuilderContinuationSuccess = useCallback(
|
|
1162
|
+
(continuationKey: string) => {
|
|
1163
|
+
builderContinuationFailureRef.current.delete(continuationKey);
|
|
1164
|
+
},
|
|
1165
|
+
[],
|
|
1166
|
+
);
|
|
1135
1167
|
const runBuilderHydration = useCallback(
|
|
1136
1168
|
(
|
|
1137
1169
|
sourceId: string,
|
|
@@ -1197,10 +1229,11 @@ function DatabaseTable({
|
|
|
1197
1229
|
continuationKey,
|
|
1198
1230
|
);
|
|
1199
1231
|
if (
|
|
1200
|
-
!runSourceRefresh(
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1232
|
+
!runSourceRefresh(
|
|
1233
|
+
candidate.id,
|
|
1234
|
+
() => handleBuilderContinuationError(continuationKey),
|
|
1235
|
+
candidate.metadata.lastReadNextOffset,
|
|
1236
|
+
() => handleBuilderContinuationSuccess(continuationKey),
|
|
1204
1237
|
)
|
|
1205
1238
|
) {
|
|
1206
1239
|
autoContinueBuilderSourceRef.current.delete(continuationKey);
|
|
@@ -1210,6 +1243,8 @@ function DatabaseTable({
|
|
|
1210
1243
|
builderSources,
|
|
1211
1244
|
canEdit,
|
|
1212
1245
|
isActive,
|
|
1246
|
+
handleBuilderContinuationError,
|
|
1247
|
+
handleBuilderContinuationSuccess,
|
|
1213
1248
|
refreshSource.isPending,
|
|
1214
1249
|
runSourceRefresh,
|
|
1215
1250
|
]);
|
|
@@ -1273,10 +1308,11 @@ function DatabaseTable({
|
|
|
1273
1308
|
return;
|
|
1274
1309
|
}
|
|
1275
1310
|
if (
|
|
1276
|
-
runSourceRefresh(
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1311
|
+
runSourceRefresh(
|
|
1312
|
+
candidate.id,
|
|
1313
|
+
() => handleBuilderContinuationError(continuationKey),
|
|
1314
|
+
candidate.metadata.lastReadNextOffset,
|
|
1315
|
+
() => handleBuilderContinuationSuccess(continuationKey),
|
|
1280
1316
|
)
|
|
1281
1317
|
) {
|
|
1282
1318
|
builderContinuationWatchdogRef.current.set(
|
|
@@ -1295,6 +1331,8 @@ function DatabaseTable({
|
|
|
1295
1331
|
builderSources,
|
|
1296
1332
|
canEdit,
|
|
1297
1333
|
isActive,
|
|
1334
|
+
handleBuilderContinuationError,
|
|
1335
|
+
handleBuilderContinuationSuccess,
|
|
1298
1336
|
refreshSource.isPending,
|
|
1299
1337
|
runSourceRefresh,
|
|
1300
1338
|
]);
|
|
@@ -2545,10 +2583,12 @@ function DatabaseTable({
|
|
|
2545
2583
|
continuationKey,
|
|
2546
2584
|
);
|
|
2547
2585
|
builderContinuationWatchdogRef.current.set(continuationKey, 0);
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
),
|
|
2586
|
+
builderContinuationFailureRef.current.delete(continuationKey);
|
|
2587
|
+
runSourceRefresh(
|
|
2588
|
+
builderSource.id,
|
|
2589
|
+
() => handleBuilderContinuationError(continuationKey),
|
|
2590
|
+
builderSource.metadata.lastReadNextOffset,
|
|
2591
|
+
() => handleBuilderContinuationSuccess(continuationKey),
|
|
2552
2592
|
);
|
|
2553
2593
|
}}
|
|
2554
2594
|
/>
|
|
@@ -2897,36 +2937,44 @@ function DatabaseTable({
|
|
|
2897
2937
|
onReviewBuilderUpdate={(sourceId) => {
|
|
2898
2938
|
openBuilderReview(sourceId);
|
|
2899
2939
|
}}
|
|
2900
|
-
onSetBuilderLiveWrites={(settings) =>
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2940
|
+
onSetBuilderLiveWrites={(settings) => {
|
|
2941
|
+
const request = {
|
|
2942
|
+
documentId: document.id,
|
|
2943
|
+
sourceId: settings.sourceId,
|
|
2944
|
+
writeMode: settings.writeMode,
|
|
2945
|
+
allowPublicationTransitions:
|
|
2946
|
+
settings.writeMode === "publish_updates" &&
|
|
2947
|
+
settings.allowPublicationTransitions === true,
|
|
2948
|
+
};
|
|
2949
|
+
const previous = queryClient.getQueriesData<ContentDatabaseResponse>(
|
|
2950
|
+
contentDatabaseQueryFilter(document.id),
|
|
2951
|
+
);
|
|
2952
|
+
queryClient.setQueriesData<ContentDatabaseResponse>(
|
|
2953
|
+
contentDatabaseQueryFilter(document.id),
|
|
2954
|
+
(current) => applyOptimisticBuilderWriteMode(current, request),
|
|
2955
|
+
);
|
|
2956
|
+
setSourceWriteMode.mutate(request, {
|
|
2957
|
+
onSuccess: () => {
|
|
2958
|
+
toast.success(dbText("builderWriteModeUpdated"), {
|
|
2959
|
+
description:
|
|
2960
|
+
settings.writeMode === "publish_updates"
|
|
2961
|
+
? "Approved updates can write through to Builder while preserving publication state."
|
|
2962
|
+
: settings.writeMode === "stage_only"
|
|
2963
|
+
? "Approved updates will stage Builder autosave revisions."
|
|
2964
|
+
: "Builder writes are disabled for this source.",
|
|
2965
|
+
});
|
|
2909
2966
|
},
|
|
2910
|
-
{
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
: "Builder writes are disabled for this source.",
|
|
2919
|
-
});
|
|
2920
|
-
},
|
|
2921
|
-
onError: (error) => {
|
|
2922
|
-
toast.error(dbText("builderWriteModeWasNotChanged"), {
|
|
2923
|
-
description:
|
|
2924
|
-
error instanceof Error ? error.message : dbText("tryAgain"),
|
|
2925
|
-
});
|
|
2926
|
-
},
|
|
2967
|
+
onError: (error) => {
|
|
2968
|
+
for (const [queryKey, data] of previous) {
|
|
2969
|
+
queryClient.setQueryData(queryKey, data);
|
|
2970
|
+
}
|
|
2971
|
+
toast.error(dbText("builderWriteModeWasNotChanged"), {
|
|
2972
|
+
description:
|
|
2973
|
+
error instanceof Error ? error.message : dbText("tryAgain"),
|
|
2974
|
+
});
|
|
2927
2975
|
},
|
|
2928
|
-
)
|
|
2929
|
-
}
|
|
2976
|
+
});
|
|
2977
|
+
}}
|
|
2930
2978
|
sourceActionPending={
|
|
2931
2979
|
attachSource.isPending ||
|
|
2932
2980
|
changeSourceRole.isPending ||
|
|
@@ -2967,6 +3015,7 @@ function DatabaseTable({
|
|
|
2967
3015
|
executeBuilderExecution.isPending ||
|
|
2968
3016
|
cancelPreparedBuilderUpdate.isPending
|
|
2969
3017
|
}
|
|
3018
|
+
executionPending={executeBuilderExecution.isPending}
|
|
2970
3019
|
error={
|
|
2971
3020
|
builderReviewError ??
|
|
2972
3021
|
(builderReviewPreviewQuery.error instanceof Error
|
|
@@ -10196,6 +10245,10 @@ export function builderSourceContinuationWatchdogDecision(refires: number) {
|
|
|
10196
10245
|
return "refire";
|
|
10197
10246
|
}
|
|
10198
10247
|
|
|
10248
|
+
export function builderSourceContinuationFailureDecision(failures: number) {
|
|
10249
|
+
return failures <= 1 ? "retry" : "error";
|
|
10250
|
+
}
|
|
10251
|
+
|
|
10199
10252
|
export function builderSourceContinuationWatchdogDelay(refires: number) {
|
|
10200
10253
|
return Math.min(
|
|
10201
10254
|
BUILDER_SOURCE_CONTINUATION_MAX_BACKOFF_MS,
|
|
@@ -291,6 +291,7 @@ export function BuilderSourceReviewDialog({
|
|
|
291
291
|
source,
|
|
292
292
|
canEdit,
|
|
293
293
|
pending,
|
|
294
|
+
executionPending = false,
|
|
294
295
|
batchResult = null,
|
|
295
296
|
error = null,
|
|
296
297
|
checkedAt,
|
|
@@ -307,6 +308,7 @@ export function BuilderSourceReviewDialog({
|
|
|
307
308
|
source: ContentDatabaseSource | null;
|
|
308
309
|
canEdit: boolean;
|
|
309
310
|
pending: boolean;
|
|
311
|
+
executionPending?: boolean;
|
|
310
312
|
// Optional so the inline-database caller (DatabaseView) that doesn't surface
|
|
311
313
|
// batch results can still mount the dialog.
|
|
312
314
|
batchResult?: ExecuteBuilderSourceBatchResponse | null;
|
|
@@ -485,7 +487,7 @@ export function BuilderSourceReviewDialog({
|
|
|
485
487
|
: review?.result.status,
|
|
486
488
|
);
|
|
487
489
|
const footerHint = pending
|
|
488
|
-
?
|
|
490
|
+
? executionPending
|
|
489
491
|
? "Sending to Builder…"
|
|
490
492
|
: "Preparing full review…"
|
|
491
493
|
: hasUnconfirmedUnpublish
|
|
@@ -517,7 +519,7 @@ export function BuilderSourceReviewDialog({
|
|
|
517
519
|
? `Push ${selectedReviewRows.length} updates`
|
|
518
520
|
: "Push update";
|
|
519
521
|
const buttonLabel = pending
|
|
520
|
-
?
|
|
522
|
+
? executionPending
|
|
521
523
|
? "Sending…"
|
|
522
524
|
: "Preparing…"
|
|
523
525
|
: checked && review?.result.status === "running"
|