@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.
Files changed (334) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +24 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/templates-meta.ts +12 -0
  5. package/corpus/core/src/deploy/build.ts +8 -0
  6. package/corpus/core/src/server/builder-browser.ts +76 -0
  7. package/corpus/core/src/server/core-routes-plugin.ts +15 -5
  8. package/corpus/templates/assets/app/global.css +0 -4
  9. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +195 -167
  10. package/corpus/templates/assets/changelog/2026-07-23-drafts-preview-panel-now-fills-the-full-width-with-a-large-p.md +6 -0
  11. package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
  12. package/corpus/templates/content/actions/_builder-cms-write-client.ts +55 -9
  13. package/corpus/templates/content/actions/_database-source-utils.ts +756 -182
  14. package/corpus/templates/content/actions/change-content-database-source-role.ts +21 -20
  15. package/corpus/templates/content/actions/execute-builder-source-execution.ts +49 -8
  16. package/corpus/templates/content/actions/materialize-builder-required-fields.ts +42 -9
  17. package/corpus/templates/content/actions/prepare-builder-source-review.ts +88 -90
  18. package/corpus/templates/content/actions/preview-builder-source-review.ts +4 -3
  19. package/corpus/templates/content/actions/refresh-content-database-source.ts +36 -9
  20. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +28 -24
  21. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +62 -12
  22. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +91 -16
  23. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +15 -1
  24. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +234 -44
  25. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +194 -16
  26. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +95 -42
  27. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +4 -2
  28. package/corpus/templates/content/app/components/editor/extensions/CodeBlockNode.tsx +10 -166
  29. package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +32 -8
  30. package/corpus/templates/content/app/components/editor/extensions/VideoNode.ts +14 -0
  31. package/corpus/templates/content/app/components/editor/registrySlashItems.ts +5 -1
  32. package/corpus/templates/content/app/hooks/use-content-database.ts +59 -2
  33. package/corpus/templates/content/app/hooks/use-documents.ts +18 -26
  34. package/corpus/templates/content/app/root.tsx +1 -6
  35. package/corpus/templates/content/changelog/2026-07-24-builder-can-now-publish-approved-updates-to-entries-that-are.md +6 -0
  36. package/corpus/templates/content/changelog/2026-07-24-builder-collection-loading-no-longer-restarts-from-the-begin.md +6 -0
  37. package/corpus/templates/content/changelog/2026-07-24-builder-connections-now-complete-securely-from-netlify-deplo.md +6 -0
  38. package/corpus/templates/content/changelog/2026-07-24-builder-draft-creation-now-scopes-execution-to-the-selected-.md +6 -0
  39. package/corpus/templates/content/changelog/2026-07-24-builder-review-now-distinguishes-preparing-a-safe-dry-run-from.md +6 -0
  40. package/corpus/templates/content/changelog/2026-07-24-builder-source-imports-now-recover-from-a-transient-paginati.md +6 -0
  41. package/corpus/templates/content/changelog/2026-07-24-builder-source-refresh-now-preserves-the-provider-bound-fiel.md +6 -0
  42. package/corpus/templates/content/changelog/2026-07-24-builder-update-reviews-now-open-reliably-on-large-collection.md +6 -0
  43. package/corpus/templates/content/changelog/2026-07-24-builder-write-policy-controls-now-reflect-successful-changes.md +6 -0
  44. package/corpus/templates/content/changelog/2026-07-24-date-properties-now-save-reliably-when-selected-with-the-nat.md +6 -0
  45. package/corpus/templates/content/changelog/2026-07-24-editor-slash-search-now-keeps-multi-word-block-names-open.md +6 -0
  46. package/corpus/templates/content/changelog/2026-07-24-exact-editor-slash-commands-now-execute-on-enter-even-if-the.md +6 -0
  47. package/corpus/templates/content/changelog/2026-07-24-new-collaborative-pages-now-save-their-first-edits.md +6 -0
  48. package/corpus/templates/content/changelog/2026-07-24-new-local-builder-rows-no-longer-remain-locked-behind-a-none.md +6 -0
  49. package/corpus/templates/content/changelog/2026-07-24-opening-or-closing-a-collaborative-document-no-longer-lets-u.md +6 -0
  50. package/corpus/templates/content/changelog/2026-07-24-selected-text-now-opens-the-link-editor-with-command-k-or-co.md +6 -0
  51. package/corpus/templates/content/changelog/2026-07-24-slash-menu-block-choices-no-longer-stall-pointer-interaction.md +6 -0
  52. package/corpus/templates/content/changelog/2026-07-24-uploaded-and-embedded-media-now-stay-intact-while-collaborative-document-drafts-save.md +6 -0
  53. package/corpus/templates/content/changelog/2026-07-24-video-link-insertion-now-keeps-its-source-panel-open-while-t.md +6 -0
  54. package/corpus/templates/content/changelog/2026-07-26-builder-publishing-now-shows-safe-validation-details-instead.md +6 -0
  55. package/corpus/templates/content/changelog/2026-07-26-builder-reviews-stay-responsive-on-large-collections-and-pre.md +6 -0
  56. package/corpus/templates/content/changelog/2026-07-26-collaborative-documents-no-longer-flash-duplicated-recent-bl.md +6 -0
  57. package/corpus/templates/content/changelog/2026-07-26-corrected-builder-updates-are-now-reviewed-separately-from-f.md +6 -0
  58. package/corpus/templates/content/changelog/2026-07-26-documents-now-remain-read-only-until-their-live-collaborativ.md +6 -0
  59. package/corpus/templates/content/changelog/2026-07-26-slash-command-menus-now-stay-reachable-when-editing-near-the.md +6 -0
  60. package/corpus/templates/content/changelog/2026-07-26-the-slash-menu-now-shows-one-clear-callout-choice-instead-of.md +6 -0
  61. package/corpus/templates/content/shared/api.ts +1 -0
  62. package/corpus/templates/content/shared/builder-mdx.ts +73 -16
  63. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
  64. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
  65. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +205 -0
  66. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  67. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
  68. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +228 -0
  69. package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +178 -0
  70. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
  71. package/corpus/templates/crm/.env.example +5 -0
  72. package/corpus/templates/crm/AGENTS.md +82 -0
  73. package/corpus/templates/crm/CHANGELOG.md +25 -0
  74. package/corpus/templates/crm/DEVELOPING.md +66 -0
  75. package/corpus/templates/crm/README.md +122 -0
  76. package/corpus/templates/crm/_gitignore +12 -0
  77. package/corpus/templates/crm/actions/_crm-action-utils.ts +98 -0
  78. package/corpus/templates/crm/actions/_crm-attribute-utils.ts +219 -0
  79. package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
  80. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  81. package/corpus/templates/crm/actions/_crm-list-utils.ts +775 -0
  82. package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
  83. package/corpus/templates/crm/actions/add-crm-record-to-list.ts +164 -0
  84. package/corpus/templates/crm/actions/apply-crm-proposals.ts +291 -0
  85. package/corpus/templates/crm/actions/archive-crm-attribute.ts +69 -0
  86. package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
  87. package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
  88. package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
  89. package/corpus/templates/crm/actions/create-crm-attribute.ts +350 -0
  90. package/corpus/templates/crm/actions/create-crm-list.ts +194 -0
  91. package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
  92. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
  93. package/corpus/templates/crm/actions/delete-crm-saved-view.ts +68 -0
  94. package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
  95. package/corpus/templates/crm/actions/estimate-crm-enrichment.ts +229 -0
  96. package/corpus/templates/crm/actions/find-crm-duplicates.ts +87 -0
  97. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
  98. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +35 -0
  99. package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
  100. package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
  101. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
  102. package/corpus/templates/crm/actions/get-crm-record-page.ts +287 -0
  103. package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
  104. package/corpus/templates/crm/actions/get-crm-workspace.ts +364 -0
  105. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
  106. package/corpus/templates/crm/actions/list-crm-attributes.ts +71 -0
  107. package/corpus/templates/crm/actions/list-crm-connections.ts +68 -0
  108. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
  109. package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
  110. package/corpus/templates/crm/actions/list-crm-enrichment-slots.ts +28 -0
  111. package/corpus/templates/crm/actions/list-crm-list-entries.ts +192 -0
  112. package/corpus/templates/crm/actions/list-crm-lists.ts +80 -0
  113. package/corpus/templates/crm/actions/list-crm-proposals.ts +55 -0
  114. package/corpus/templates/crm/actions/list-crm-record-field-history.ts +201 -0
  115. package/corpus/templates/crm/actions/list-crm-record-values.ts +200 -0
  116. package/corpus/templates/crm/actions/list-crm-records.ts +103 -0
  117. package/corpus/templates/crm/actions/list-crm-saved-views.ts +66 -0
  118. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
  119. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
  120. package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
  121. package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
  122. package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
  123. package/corpus/templates/crm/actions/manage-crm-attribute-option.ts +272 -0
  124. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
  125. package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
  126. package/corpus/templates/crm/actions/merge-crm-records.ts +691 -0
  127. package/corpus/templates/crm/actions/navigate.ts +54 -0
  128. package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
  129. package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
  130. package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
  131. package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
  132. package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
  133. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
  134. package/corpus/templates/crm/actions/remove-crm-list-entry.ts +79 -0
  135. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
  136. package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
  137. package/corpus/templates/crm/actions/run-crm-attribute-fill.ts +443 -0
  138. package/corpus/templates/crm/actions/run-crm-enrichment.ts +647 -0
  139. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
  140. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +265 -0
  141. package/corpus/templates/crm/actions/run.ts +7 -0
  142. package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
  143. package/corpus/templates/crm/actions/save-crm-saved-view.ts +296 -0
  144. package/corpus/templates/crm/actions/sync-crm.ts +146 -0
  145. package/corpus/templates/crm/actions/update-crm-attribute.ts +111 -0
  146. package/corpus/templates/crm/actions/update-crm-list-entry.ts +102 -0
  147. package/corpus/templates/crm/actions/update-crm-list.ts +97 -0
  148. package/corpus/templates/crm/actions/update-crm-record.ts +543 -0
  149. package/corpus/templates/crm/actions/view-screen.ts +324 -0
  150. package/corpus/templates/crm/agent-native.app-skill.json +69 -0
  151. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
  152. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +78 -0
  153. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +220 -0
  154. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +414 -0
  155. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +648 -0
  156. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +488 -0
  157. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +232 -0
  158. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +93 -0
  159. package/corpus/templates/crm/app/components/crm/Surface.tsx +97 -0
  160. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
  161. package/corpus/templates/crm/app/components/crm/board/CrmBoard.tsx +988 -0
  162. package/corpus/templates/crm/app/components/crm/board/board-model.ts +399 -0
  163. package/corpus/templates/crm/app/components/crm/board/entry-filter.ts +150 -0
  164. package/corpus/templates/crm/app/components/crm/board/view-draft.ts +227 -0
  165. package/corpus/templates/crm/app/components/crm/grid/CrmGrid.tsx +1033 -0
  166. package/corpus/templates/crm/app/components/crm/grid/GridCell.tsx +620 -0
  167. package/corpus/templates/crm/app/components/crm/grid/clipboard.ts +84 -0
  168. package/corpus/templates/crm/app/components/crm/grid/model.ts +304 -0
  169. package/corpus/templates/crm/app/components/crm/grid/navigation.ts +220 -0
  170. package/corpus/templates/crm/app/components/crm/grid/query.ts +214 -0
  171. package/corpus/templates/crm/app/components/crm/record/AttributePanel.tsx +192 -0
  172. package/corpus/templates/crm/app/components/crm/record/FieldHistory.tsx +179 -0
  173. package/corpus/templates/crm/app/components/crm/record/RecordHeader.tsx +103 -0
  174. package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +235 -0
  175. package/corpus/templates/crm/app/components/crm/record/RecordTabs.tsx +206 -0
  176. package/corpus/templates/crm/app/components/crm/record/field-editors.tsx +245 -0
  177. package/corpus/templates/crm/app/components/crm/record/record-data.ts +603 -0
  178. package/corpus/templates/crm/app/components/crm/settings/AdvancedSettings.tsx +49 -0
  179. package/corpus/templates/crm/app/components/crm/settings/AttributeOptionsEditor.tsx +441 -0
  180. package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +169 -0
  181. package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +935 -0
  182. package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +339 -0
  183. package/corpus/templates/crm/app/components/crm/settings/settings-admin.ts +377 -0
  184. package/corpus/templates/crm/app/components/crm/shared/AttributeValueParts.tsx +69 -0
  185. package/corpus/templates/crm/app/components/crm/shared/attribute-value.ts +591 -0
  186. package/corpus/templates/crm/app/components/layout/CrmCommandMenu.tsx +556 -0
  187. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +130 -0
  188. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +407 -0
  189. package/corpus/templates/crm/app/components/layout/command-palette-search.ts +126 -0
  190. package/corpus/templates/crm/app/components/layout/crm-ui-intents.ts +41 -0
  191. package/corpus/templates/crm/app/components/layout/sidebar-collapse.ts +40 -0
  192. package/corpus/templates/crm/app/components/layout/sidebar-lists.ts +109 -0
  193. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
  194. package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
  195. package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
  196. package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
  197. package/corpus/templates/crm/app/components/ui/checkbox.tsx +1 -0
  198. package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
  199. package/corpus/templates/crm/app/components/ui/dropdown-menu.tsx +1 -0
  200. package/corpus/templates/crm/app/components/ui/hover-card.tsx +1 -0
  201. package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
  202. package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
  203. package/corpus/templates/crm/app/components/ui/popover.tsx +1 -0
  204. package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
  205. package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
  206. package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
  207. package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
  208. package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
  209. package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
  210. package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
  211. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
  212. package/corpus/templates/crm/app/components/ui/tooltip.tsx +1 -0
  213. package/corpus/templates/crm/app/design-system.ts +3 -0
  214. package/corpus/templates/crm/app/entry.client.tsx +19 -0
  215. package/corpus/templates/crm/app/entry.server.tsx +10 -0
  216. package/corpus/templates/crm/app/global.css +167 -0
  217. package/corpus/templates/crm/app/hooks/use-keyboard-shortcuts.ts +206 -0
  218. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +48 -0
  219. package/corpus/templates/crm/app/i18n/en-US.ts +684 -0
  220. package/corpus/templates/crm/app/i18n/index.ts +34 -0
  221. package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
  222. package/corpus/templates/crm/app/lib/navigation.ts +17 -0
  223. package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
  224. package/corpus/templates/crm/app/lib/types.ts +172 -0
  225. package/corpus/templates/crm/app/lib/utils.ts +1 -0
  226. package/corpus/templates/crm/app/root.tsx +93 -0
  227. package/corpus/templates/crm/app/routes/_index.tsx +26 -0
  228. package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
  229. package/corpus/templates/crm/app/routes/agent.tsx +5 -0
  230. package/corpus/templates/crm/app/routes/ask.tsx +53 -0
  231. package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
  232. package/corpus/templates/crm/app/routes/lists.tsx +228 -0
  233. package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
  234. package/corpus/templates/crm/app/routes/people.tsx +41 -0
  235. package/corpus/templates/crm/app/routes/proposals.tsx +388 -0
  236. package/corpus/templates/crm/app/routes/records.$recordId.tsx +53 -0
  237. package/corpus/templates/crm/app/routes/records.tsx +64 -0
  238. package/corpus/templates/crm/app/routes/settings.tsx +118 -0
  239. package/corpus/templates/crm/app/routes/setup.tsx +308 -0
  240. package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
  241. package/corpus/templates/crm/app/routes/views.tsx +683 -0
  242. package/corpus/templates/crm/app/routes.ts +19 -0
  243. package/corpus/templates/crm/app/vite-env.d.ts +6 -0
  244. 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
  245. package/corpus/templates/crm/changelog/2026-07-26-crm-now-finds-likely-duplicate-records-and-tells-you-exactly.md +6 -0
  246. package/corpus/templates/crm/changelog/2026-07-26-enrichment-now-shows-what-a-run-will-cost-before-it-starts-g.md +6 -0
  247. package/corpus/templates/crm/changelog/2026-07-26-lists-turn-any-set-of-records-into-a-pipeline-with-its-own-s.md +6 -0
  248. package/corpus/templates/crm/changelog/2026-07-26-moving-a-record-or-list-entry-into-a-stage-that-was-retired-.md +6 -0
  249. package/corpus/templates/crm/changelog/2026-07-26-opening-a-record-now-shows-one-page-with-its-fields-the-list.md +6 -0
  250. 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
  251. package/corpus/templates/crm/changelog/2026-07-26-provider-record-edits-now-show-an-exact-before-after-diff-an.md +6 -0
  252. package/corpus/templates/crm/changelog/2026-07-26-record-page-attribute-labels-now-read-in-full-instead-of-bei.md +6 -0
  253. package/corpus/templates/crm/changelog/2026-07-26-records-now-use-typed-fields-currency-dates-ratings-status-s.md +6 -0
  254. package/corpus/templates/crm/changelog/2026-07-26-retyping-a-value-on-the-record-page-now-replaces-it-instead-.md +6 -0
  255. package/corpus/templates/crm/changelog/2026-07-26-settings-now-has-fields-and-lists-tabs-for-adding-fields-edi.md +6 -0
  256. package/corpus/templates/crm/changelog/2026-07-26-the-proposal-review-now-shows-the-field-that-actually-change.md +6 -0
  257. package/corpus/templates/crm/changelog/2026-07-26-the-record-grid-is-now-a-real-spreadsheet-filter-sort-and-pa.md +6 -0
  258. package/corpus/templates/crm/components.json +20 -0
  259. package/corpus/templates/crm/docs/architecture/crm-contract.md +214 -0
  260. package/corpus/templates/crm/docs/features/README.md +55 -0
  261. package/corpus/templates/crm/docs/features/f1-records-and-fields.mdx +52 -0
  262. package/corpus/templates/crm/docs/features/f2-lists-pipelines-and-views.mdx +48 -0
  263. package/corpus/templates/crm/docs/features/f3-work-and-record-quality.mdx +40 -0
  264. package/corpus/templates/crm/docs/features/f4-evidence-and-chat.mdx +47 -0
  265. package/corpus/templates/crm/learnings.defaults.md +5 -0
  266. package/corpus/templates/crm/netlify.toml +11 -0
  267. package/corpus/templates/crm/package.json +61 -0
  268. package/corpus/templates/crm/public/favicon.svg +1 -0
  269. package/corpus/templates/crm/public/icon-180.svg +1 -0
  270. package/corpus/templates/crm/public/manifest.json +11 -0
  271. package/corpus/templates/crm/react-router.config.ts +7 -0
  272. package/corpus/templates/crm/server/crm/adapter.ts +32 -0
  273. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
  274. package/corpus/templates/crm/server/crm/crm-mirror.ts +721 -0
  275. package/corpus/templates/crm/server/crm/field-policy-attributes.ts +48 -0
  276. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1146 -0
  277. package/corpus/templates/crm/server/crm/native-adapter.ts +1810 -0
  278. package/corpus/templates/crm/server/crm/provider-record-link.ts +264 -0
  279. package/corpus/templates/crm/server/crm/read-through.ts +207 -0
  280. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
  281. package/corpus/templates/crm/server/db/crm-store.ts +1112 -0
  282. package/corpus/templates/crm/server/db/index.ts +191 -0
  283. package/corpus/templates/crm/server/db/schema.ts +647 -0
  284. package/corpus/templates/crm/server/lib/crm-query.ts +1558 -0
  285. package/corpus/templates/crm/server/lib/dedupe.ts +714 -0
  286. package/corpus/templates/crm/server/lib/enrichment-cost.ts +381 -0
  287. package/corpus/templates/crm/server/lib/enrichment-slots.ts +519 -0
  288. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
  289. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
  290. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +94 -0
  291. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +39 -0
  292. package/corpus/templates/crm/server/lib/intelligence/summary.ts +19 -0
  293. package/corpus/templates/crm/server/lib/lifecycle.ts +634 -0
  294. package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
  295. package/corpus/templates/crm/server/middleware/auth.ts +4 -0
  296. package/corpus/templates/crm/server/plugins/agent-chat.ts +65 -0
  297. package/corpus/templates/crm/server/plugins/auth.ts +14 -0
  298. package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
  299. package/corpus/templates/crm/server/plugins/db.ts +738 -0
  300. package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
  301. package/corpus/templates/crm/shared/crm-attributes.ts +469 -0
  302. package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
  303. package/corpus/templates/crm/shared/crm-contract.ts +417 -0
  304. package/corpus/templates/crm/shared/crm-navigation.ts +190 -0
  305. package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
  306. package/corpus/templates/crm/ssr-entry.ts +11 -0
  307. package/corpus/templates/crm/tsconfig.json +24 -0
  308. package/corpus/templates/crm/vite.config.ts +15 -0
  309. package/corpus/templates/crm/vitest.config.ts +5 -0
  310. package/corpus/templates/design/app/i18n-data.ts +12 -12
  311. package/corpus/templates/design/changelog/2026-07-26-apply-design-updates-from-a-centered-top-toolbar-without-cov.md +6 -0
  312. package/corpus/toolkit/CHANGELOG.md +6 -0
  313. package/corpus/toolkit/package.json +1 -1
  314. package/corpus/toolkit/src/editor/useCollabReconcile.ts +14 -1
  315. package/dist/cli/templates-meta.d.ts.map +1 -1
  316. package/dist/cli/templates-meta.js +12 -0
  317. package/dist/cli/templates-meta.js.map +1 -1
  318. package/dist/collab/routes.d.ts +2 -2
  319. package/dist/deploy/build.js +6 -0
  320. package/dist/deploy/build.js.map +1 -1
  321. package/dist/observability/routes.d.ts +3 -3
  322. package/dist/server/builder-browser.d.ts +13 -0
  323. package/dist/server/builder-browser.d.ts.map +1 -1
  324. package/dist/server/builder-browser.js +59 -0
  325. package/dist/server/builder-browser.js.map +1 -1
  326. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  327. package/dist/server/core-routes-plugin.js +10 -5
  328. package/dist/server/core-routes-plugin.js.map +1 -1
  329. package/dist/server/transcribe-voice.d.ts +1 -1
  330. package/package.json +2 -2
  331. package/src/cli/templates-meta.ts +12 -0
  332. package/src/deploy/build.ts +8 -0
  333. package/src/server/builder-browser.ts +76 -0
  334. package/src/server/core-routes-plugin.ts +15 -5
@@ -0,0 +1,304 @@
1
+ /**
2
+ * What the grid adds on top of the shared attribute-value registry: a default
3
+ * column width per type, the read-only demotion for a cell the caller cannot
4
+ * write, and cell provenance.
5
+ *
6
+ * The value logic itself — display text, clipboard text, parsing, options,
7
+ * currency, status SLA — lives in `../shared/attribute-value` and is shared
8
+ * with the record page. The grid body still never branches on attribute type:
9
+ * it asks `cellSpecFor(attribute)`.
10
+ */
11
+
12
+ import {
13
+ ATTRIBUTE_TYPE_SPECS,
14
+ CRM_ATTRIBUTE_TYPES,
15
+ type CrmAttributeType,
16
+ } from "../../../../shared/crm-attributes";
17
+ import type {
18
+ CrmActorType,
19
+ CrmAttributeAuthority,
20
+ CrmAttributeOption,
21
+ CrmFieldStoragePolicy,
22
+ } from "../../../../shared/crm-contract";
23
+ import {
24
+ assertValueRegistryComplete,
25
+ ATTRIBUTE_VALUE_SPECS,
26
+ copyAttributeValue,
27
+ formatAttributeValue,
28
+ parseAttributeValue,
29
+ type CrmAttributeValue,
30
+ type CrmValueParse,
31
+ type CrmValueParseFailure,
32
+ type CrmValueSpec,
33
+ } from "../shared/attribute-value";
34
+
35
+ export { statusOverrunDays } from "../shared/attribute-value";
36
+
37
+ export type CrmCellValue = CrmAttributeValue;
38
+
39
+ /** The attribute shape the grid needs; a subset of `CrmAttributeDefinition`. */
40
+ export interface CrmGridAttribute {
41
+ id: string;
42
+ apiSlug: string;
43
+ label: string;
44
+ attributeType: CrmAttributeType;
45
+ multi: boolean;
46
+ authority: CrmAttributeAuthority;
47
+ storagePolicy: CrmFieldStoragePolicy;
48
+ updateable: boolean;
49
+ options?: CrmAttributeOption[];
50
+ config?: Record<string, unknown>;
51
+ }
52
+
53
+ /**
54
+ * Where one cell's current value came from.
55
+ *
56
+ * `readable: false` means the stored provenance blob could not be parsed — a
57
+ * different state from "no provenance recorded", which is `readable: true` with
58
+ * nothing but an actor. The grid renders them differently on purpose.
59
+ */
60
+ export interface CrmCellProvenance {
61
+ actorType: CrmActorType;
62
+ actorId?: string | null;
63
+ readable: boolean;
64
+ source?: string;
65
+ sourceUrl?: string;
66
+ confidence?: number;
67
+ reasoning?: string;
68
+ observedAt?: string;
69
+ }
70
+
71
+ export interface CrmGridRow {
72
+ id: string;
73
+ displayName: string;
74
+ /** Required by `update-crm-record` for native and provider writes. */
75
+ remoteRevision?: string;
76
+ values: Record<string, CrmCellValue>;
77
+ /** `activeFrom` of the current value — what a stage SLA is measured from. */
78
+ valuesSince?: Record<string, string>;
79
+ provenance?: Record<string, CrmCellProvenance>;
80
+ }
81
+
82
+ /**
83
+ * How a cell is edited: the shared control, plus `readonly` for the types no
84
+ * surface can edit and for any attribute the caller has no write permission on.
85
+ */
86
+ export type CrmCellEditor =
87
+ | "text"
88
+ | "number"
89
+ | "checkbox"
90
+ | "date"
91
+ | "datetime"
92
+ | "options"
93
+ | "rating"
94
+ | "reference"
95
+ | "readonly";
96
+
97
+ export type CrmCellParseFailure = CrmValueParseFailure;
98
+ export type CrmCellParse = CrmValueParse;
99
+
100
+ export interface CrmCellSpec extends CrmValueSpec {
101
+ editor: CrmCellEditor;
102
+ defaultWidth: number;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // The grid registry — the shared value spec plus grid column presentation
107
+ // ---------------------------------------------------------------------------
108
+
109
+ const COLUMN_WIDTHS: Record<CrmAttributeType, number> = {
110
+ text: 220,
111
+ number: 130,
112
+ checkbox: 90,
113
+ currency: 140,
114
+ date: 140,
115
+ timestamp: 180,
116
+ rating: 120,
117
+ status: 170,
118
+ select: 170,
119
+ "record-reference": 200,
120
+ "actor-reference": 200,
121
+ location: 200,
122
+ domain: 190,
123
+ "email-address": 230,
124
+ "phone-number": 170,
125
+ interaction: 200,
126
+ "personal-name": 200,
127
+ };
128
+
129
+ export const CELL_SPECS: Record<CrmAttributeType, CrmCellSpec> =
130
+ Object.fromEntries(
131
+ CRM_ATTRIBUTE_TYPES.map((type) => {
132
+ const spec = ATTRIBUTE_VALUE_SPECS[type];
133
+ return [
134
+ type,
135
+ {
136
+ ...spec,
137
+ editor: spec.control === "none" ? "readonly" : spec.control,
138
+ defaultWidth: COLUMN_WIDTHS[type],
139
+ } satisfies CrmCellSpec,
140
+ ];
141
+ }),
142
+ ) as Record<CrmAttributeType, CrmCellSpec>;
143
+
144
+ export function cellSpecFor(attribute: CrmGridAttribute): CrmCellSpec {
145
+ const spec = CELL_SPECS[attribute.attributeType];
146
+ // A readable-but-not-updateable attribute is a display cell, not a silently
147
+ // failing editor. A provider-owned but updateable one stays editable: the
148
+ // grid routes that write to `update-crm-record` as a proposal.
149
+ if (!attribute.updateable && spec.editor !== "readonly") {
150
+ return { ...spec, editor: "readonly" };
151
+ }
152
+ return spec;
153
+ }
154
+
155
+ export function isCellEditable(attribute: CrmGridAttribute): boolean {
156
+ return cellSpecFor(attribute).editor !== "readonly";
157
+ }
158
+
159
+ /** Every attribute type has a spec and a width — the grid can never fall through. */
160
+ export function assertCellRegistryComplete(): void {
161
+ assertValueRegistryComplete();
162
+ for (const type of CRM_ATTRIBUTE_TYPES) {
163
+ if (typeof COLUMN_WIDTHS[type] !== "number") {
164
+ throw new Error(
165
+ `CRM grid has no column width for attribute type "${type}".`,
166
+ );
167
+ }
168
+ if (
169
+ ATTRIBUTE_TYPE_SPECS[type].systemOnly &&
170
+ CELL_SPECS[type].editor !== "readonly"
171
+ ) {
172
+ throw new Error(
173
+ `CRM grid must render system-only attribute type "${type}" read-only.`,
174
+ );
175
+ }
176
+ }
177
+ }
178
+
179
+ export function formatCell(
180
+ attribute: CrmGridAttribute,
181
+ value: CrmCellValue,
182
+ locale?: string,
183
+ ): string {
184
+ return formatAttributeValue(attribute, value, locale);
185
+ }
186
+
187
+ export function copyCell(
188
+ attribute: CrmGridAttribute,
189
+ value: CrmCellValue,
190
+ locale?: string,
191
+ ): string {
192
+ return copyAttributeValue(attribute, value, locale);
193
+ }
194
+
195
+ export function parseCell(
196
+ attribute: CrmGridAttribute,
197
+ text: string,
198
+ locale?: string,
199
+ ): CrmCellParse {
200
+ if (!isCellEditable(attribute)) return { ok: false, reason: "read-only" };
201
+ return parseAttributeValue(attribute, text, locale);
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Duplicate attributes
206
+ // ---------------------------------------------------------------------------
207
+
208
+ function sameCellValue(a: CrmCellValue, b: CrmCellValue | undefined): boolean {
209
+ if (a === b) return true;
210
+ if (a === null || b === null || b === undefined) return false;
211
+ if (typeof a === "object" || typeof b === "object") {
212
+ return JSON.stringify(a) === JSON.stringify(b);
213
+ }
214
+ return false;
215
+ }
216
+
217
+ /**
218
+ * `displayName` duplicates `name` when the native adapter minted both from
219
+ * the same write — see `record-data.ts`'s `isSuppressedDuplicateAttribute`
220
+ * for the record-page equivalent of this same rule. Suppress only when
221
+ * `name` has a real value equal to `displayName`, so a row missing `name`
222
+ * still shows its `displayName` cell.
223
+ */
224
+ export function isSuppressedDisplayNameCell(
225
+ apiSlug: string,
226
+ rowValues: Record<string, CrmCellValue>,
227
+ ): boolean {
228
+ if (apiSlug !== "displayName") return false;
229
+ const name = rowValues.name;
230
+ if (!name) return false;
231
+ return sameCellValue(name, rowValues.displayName);
232
+ }
233
+
234
+ // ---------------------------------------------------------------------------
235
+ // Provenance
236
+ // ---------------------------------------------------------------------------
237
+
238
+ const PROVENANCE_ACTORS: readonly CrmActorType[] = [
239
+ "user",
240
+ "agent",
241
+ "automation",
242
+ "provider",
243
+ "system",
244
+ ];
245
+
246
+ /**
247
+ * Read one cell's provenance out of a stored `provenance_json` blob.
248
+ *
249
+ * An unreadable blob returns `readable: false` rather than an empty record:
250
+ * "we do not know where this came from" and "nobody recorded a source" are
251
+ * different claims and the grid shows them differently.
252
+ */
253
+ export function parseCellProvenance(input: {
254
+ actorType: string;
255
+ actorId?: string | null;
256
+ provenanceJson: string | null | undefined;
257
+ fieldName?: string;
258
+ }): CrmCellProvenance {
259
+ const actorType = (PROVENANCE_ACTORS as readonly string[]).includes(
260
+ input.actorType,
261
+ )
262
+ ? (input.actorType as CrmActorType)
263
+ : "system";
264
+ const base: CrmCellProvenance = {
265
+ actorType,
266
+ actorId: input.actorId ?? null,
267
+ readable: true,
268
+ };
269
+ if (input.provenanceJson == null || input.provenanceJson === "") return base;
270
+ let parsed: unknown;
271
+ try {
272
+ parsed = JSON.parse(input.provenanceJson);
273
+ } catch {
274
+ return { ...base, readable: false };
275
+ }
276
+ const entries = Array.isArray(parsed) ? parsed : [parsed];
277
+ const match = entries.find(
278
+ (entry): entry is Record<string, unknown> =>
279
+ Boolean(entry) &&
280
+ typeof entry === "object" &&
281
+ (!input.fieldName ||
282
+ (entry as Record<string, unknown>).fieldName === undefined ||
283
+ (entry as Record<string, unknown>).fieldName === input.fieldName),
284
+ );
285
+ if (!match) return base;
286
+ const text = (key: string) =>
287
+ typeof match[key] === "string" && match[key] !== ""
288
+ ? (match[key] as string)
289
+ : undefined;
290
+ const confidence =
291
+ typeof match.confidence === "number" && Number.isFinite(match.confidence)
292
+ ? match.confidence
293
+ : undefined;
294
+ return {
295
+ ...base,
296
+ ...(text("provider") ? { source: text("provider") } : {}),
297
+ ...((text("sourceUrl") ?? text("evidenceRef"))
298
+ ? { sourceUrl: text("sourceUrl") ?? text("evidenceRef") }
299
+ : {}),
300
+ ...(confidence === undefined ? {} : { confidence }),
301
+ ...(text("reasoning") ? { reasoning: text("reasoning") } : {}),
302
+ ...(text("observedAt") ? { observedAt: text("observedAt") } : {}),
303
+ };
304
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The grid's keyboard model, kept out of React so it can be tested without a
3
+ * DOM. `resolveGridKey` maps one keydown to one intent; the component only
4
+ * decides what to do with the intent.
5
+ */
6
+
7
+ export interface CellRef {
8
+ row: number;
9
+ col: number;
10
+ }
11
+
12
+ export interface GridSelection {
13
+ /** Where the range started — fixed while Shift extends it. */
14
+ anchor: CellRef;
15
+ /** The active cell; arrows move it. */
16
+ focus: CellRef;
17
+ }
18
+
19
+ export interface GridBounds {
20
+ rows: number;
21
+ cols: number;
22
+ }
23
+
24
+ export type GridDirection = "up" | "down" | "left" | "right";
25
+
26
+ export type GridKeyIntent =
27
+ | { type: "move"; direction: GridDirection; extend: boolean }
28
+ | { type: "jump"; edge: "top" | "bottom" | "start" | "end"; extend: boolean }
29
+ | { type: "edit" }
30
+ /** Typing over a cell: start editing seeded with this character. */
31
+ | { type: "type"; text: string }
32
+ | { type: "commit"; direction: GridDirection | null }
33
+ | { type: "cancel" }
34
+ | { type: "copy" }
35
+ | { type: "paste" }
36
+ | { type: "clear" }
37
+ | { type: "selectAll" };
38
+
39
+ export interface GridKeyEvent {
40
+ key: string;
41
+ shiftKey: boolean;
42
+ metaKey: boolean;
43
+ ctrlKey: boolean;
44
+ altKey: boolean;
45
+ }
46
+
47
+ const ARROWS: Record<string, GridDirection> = {
48
+ ArrowUp: "up",
49
+ ArrowDown: "down",
50
+ ArrowLeft: "left",
51
+ ArrowRight: "right",
52
+ };
53
+
54
+ /**
55
+ * One printable character starts an edit. `event.key.length === 1` is the
56
+ * reliable test — checking key codes or ranges misses every non-Latin keyboard.
57
+ */
58
+ function isPrintable(event: GridKeyEvent): boolean {
59
+ return (
60
+ event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey
61
+ );
62
+ }
63
+
64
+ export function resolveGridKey(
65
+ event: GridKeyEvent,
66
+ state: { editing: boolean },
67
+ ): GridKeyIntent | null {
68
+ const mod = event.metaKey || event.ctrlKey;
69
+
70
+ if (state.editing) {
71
+ if (event.key === "Escape") return { type: "cancel" };
72
+ if (event.key === "Enter") {
73
+ return { type: "commit", direction: event.shiftKey ? "up" : "down" };
74
+ }
75
+ if (event.key === "Tab") {
76
+ return { type: "commit", direction: event.shiftKey ? "left" : "right" };
77
+ }
78
+ // Everything else belongs to the editor input, not the grid.
79
+ return null;
80
+ }
81
+
82
+ if (mod && (event.key === "c" || event.key === "C")) return { type: "copy" };
83
+ if (mod && (event.key === "v" || event.key === "V")) return { type: "paste" };
84
+ if (mod && (event.key === "a" || event.key === "A")) {
85
+ return { type: "selectAll" };
86
+ }
87
+ if (mod && ARROWS[event.key]) {
88
+ const direction = ARROWS[event.key]!;
89
+ const edge =
90
+ direction === "up"
91
+ ? "top"
92
+ : direction === "down"
93
+ ? "bottom"
94
+ : direction === "left"
95
+ ? "start"
96
+ : "end";
97
+ return { type: "jump", edge, extend: event.shiftKey };
98
+ }
99
+ if (ARROWS[event.key]) {
100
+ return {
101
+ type: "move",
102
+ direction: ARROWS[event.key]!,
103
+ extend: event.shiftKey,
104
+ };
105
+ }
106
+ if (event.key === "Tab") {
107
+ return {
108
+ type: "move",
109
+ direction: event.shiftKey ? "left" : "right",
110
+ extend: false,
111
+ };
112
+ }
113
+ if (event.key === "Enter") return { type: "edit" };
114
+ if (event.key === "Escape") return { type: "cancel" };
115
+ if (event.key === "Backspace" || event.key === "Delete") {
116
+ return { type: "clear" };
117
+ }
118
+ if (isPrintable(event)) return { type: "type", text: event.key };
119
+ return null;
120
+ }
121
+
122
+ function clamp(value: number, max: number): number {
123
+ return Math.max(0, Math.min(max, value));
124
+ }
125
+
126
+ export function moveCell(
127
+ ref: CellRef,
128
+ direction: GridDirection,
129
+ bounds: GridBounds,
130
+ ): CellRef {
131
+ const lastRow = Math.max(0, bounds.rows - 1);
132
+ const lastCol = Math.max(0, bounds.cols - 1);
133
+ if (direction === "up")
134
+ return { row: clamp(ref.row - 1, lastRow), col: ref.col };
135
+ if (direction === "down") {
136
+ return { row: clamp(ref.row + 1, lastRow), col: ref.col };
137
+ }
138
+ if (direction === "left") {
139
+ return { row: ref.row, col: clamp(ref.col - 1, lastCol) };
140
+ }
141
+ return { row: ref.row, col: clamp(ref.col + 1, lastCol) };
142
+ }
143
+
144
+ export function jumpCell(
145
+ ref: CellRef,
146
+ edge: "top" | "bottom" | "start" | "end",
147
+ bounds: GridBounds,
148
+ ): CellRef {
149
+ if (edge === "top") return { row: 0, col: ref.col };
150
+ if (edge === "bottom")
151
+ return { row: Math.max(0, bounds.rows - 1), col: ref.col };
152
+ if (edge === "start") return { row: ref.row, col: 0 };
153
+ return { row: ref.row, col: Math.max(0, bounds.cols - 1) };
154
+ }
155
+
156
+ export interface GridRange {
157
+ top: number;
158
+ left: number;
159
+ bottom: number;
160
+ right: number;
161
+ }
162
+
163
+ export function selectionRange(selection: GridSelection): GridRange {
164
+ return {
165
+ top: Math.min(selection.anchor.row, selection.focus.row),
166
+ bottom: Math.max(selection.anchor.row, selection.focus.row),
167
+ left: Math.min(selection.anchor.col, selection.focus.col),
168
+ right: Math.max(selection.anchor.col, selection.focus.col),
169
+ };
170
+ }
171
+
172
+ export function isInRange(range: GridRange, ref: CellRef): boolean {
173
+ return (
174
+ ref.row >= range.top &&
175
+ ref.row <= range.bottom &&
176
+ ref.col >= range.left &&
177
+ ref.col <= range.right
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Apply a movement intent to a selection. `extend` keeps the anchor so
183
+ * Shift+arrow grows the range; a plain move collapses it to one cell.
184
+ */
185
+ export function applyMove(
186
+ selection: GridSelection,
187
+ next: CellRef,
188
+ extend: boolean,
189
+ ): GridSelection {
190
+ return extend
191
+ ? { anchor: selection.anchor, focus: next }
192
+ : { anchor: next, focus: next };
193
+ }
194
+
195
+ /**
196
+ * Where a pasted rectangle lands. A single-cell selection anchors the paste at
197
+ * that cell; a range clamps the paste to the range so a stray large clipboard
198
+ * cannot overwrite rows the user never selected.
199
+ */
200
+ export function pasteTargets(input: {
201
+ selection: GridSelection;
202
+ bounds: GridBounds;
203
+ rows: number;
204
+ cols: number;
205
+ }): GridRange {
206
+ const range = selectionRange(input.selection);
207
+ const singleCell = range.top === range.bottom && range.left === range.right;
208
+ const bottom = singleCell
209
+ ? range.top + input.rows - 1
210
+ : Math.min(range.bottom, range.top + input.rows - 1);
211
+ const right = singleCell
212
+ ? range.left + input.cols - 1
213
+ : Math.min(range.right, range.left + input.cols - 1);
214
+ return {
215
+ top: range.top,
216
+ left: range.left,
217
+ bottom: Math.min(bottom, input.bounds.rows - 1),
218
+ right: Math.min(right, input.bounds.cols - 1),
219
+ };
220
+ }