@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,556 @@
1
+ import { useActionQuery } from "@agent-native/core/client/hooks";
2
+ import { useT } from "@agent-native/core/client/i18n";
3
+ import {
4
+ CommandMenu,
5
+ useCommandMenuShortcut,
6
+ } from "@agent-native/core/client/navigation";
7
+ import {
8
+ IconAlertTriangle,
9
+ IconBuilding,
10
+ IconChartBar,
11
+ IconChecklist,
12
+ IconKeyboard,
13
+ IconLayoutBoard,
14
+ IconLayoutDashboard,
15
+ IconList,
16
+ IconMessageCircle,
17
+ IconMoon,
18
+ IconPencilCheck,
19
+ IconPlus,
20
+ IconRoute,
21
+ IconSettings,
22
+ IconSun,
23
+ IconTable,
24
+ IconUsers,
25
+ } from "@tabler/icons-react";
26
+ import { useTheme } from "next-themes";
27
+ import { useCallback, useMemo, useState } from "react";
28
+ import { useNavigate, type NavigateFunction } from "react-router";
29
+
30
+ import {
31
+ Dialog,
32
+ DialogContent,
33
+ DialogDescription,
34
+ DialogHeader,
35
+ DialogTitle,
36
+ } from "@/components/ui/dialog";
37
+ import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
38
+
39
+ import changelog from "../../../CHANGELOG.md?raw";
40
+ import {
41
+ commandPaletteKeywords,
42
+ rankCommandPaletteEntries,
43
+ } from "./command-palette-search";
44
+ import {
45
+ CRM_EDIT_RECORD_EVENT,
46
+ CRM_NEW_RECORD_EVENT,
47
+ CRM_NEW_TASK_EVENT,
48
+ emitCrmUiIntent,
49
+ focusCrmSearch,
50
+ } from "./crm-ui-intents";
51
+ import {
52
+ normalizeCrmLists,
53
+ normalizeCrmSavedViews,
54
+ savedViewHref,
55
+ } from "./sidebar-lists";
56
+
57
+ type TablerIcon = typeof IconList;
58
+
59
+ interface PaletteEntry {
60
+ id: string;
61
+ /** Stable ranking value; never shown. */
62
+ value: string;
63
+ label: string;
64
+ keywords: string[];
65
+ icon: TablerIcon;
66
+ run: () => void;
67
+ }
68
+
69
+ interface PaletteGroup {
70
+ id: string;
71
+ heading: string;
72
+ entries: PaletteEntry[];
73
+ }
74
+
75
+ const GO_TO_ROUTES = [
76
+ {
77
+ id: "overview",
78
+ to: "/",
79
+ labelKey: "navigation.overview",
80
+ icon: IconLayoutDashboard,
81
+ },
82
+ {
83
+ id: "accounts",
84
+ to: "/accounts",
85
+ labelKey: "navigation.accounts",
86
+ icon: IconBuilding,
87
+ },
88
+ {
89
+ id: "people",
90
+ to: "/people",
91
+ labelKey: "navigation.people",
92
+ icon: IconUsers,
93
+ },
94
+ {
95
+ id: "opportunities",
96
+ to: "/opportunities",
97
+ labelKey: "navigation.opportunities",
98
+ icon: IconRoute,
99
+ },
100
+ { id: "lists", to: "/lists", labelKey: "navigation.lists", icon: IconList },
101
+ {
102
+ id: "tasks",
103
+ to: "/tasks",
104
+ labelKey: "navigation.tasks",
105
+ icon: IconChecklist,
106
+ },
107
+ {
108
+ id: "proposals",
109
+ to: "/proposals",
110
+ labelKey: "navigation.proposals",
111
+ icon: IconPencilCheck,
112
+ },
113
+ {
114
+ id: "dashboard",
115
+ to: "/dashboard",
116
+ labelKey: "navigation.dashboard",
117
+ icon: IconChartBar,
118
+ },
119
+ {
120
+ id: "ask",
121
+ to: "/ask",
122
+ labelKey: "navigation.askCrm",
123
+ icon: IconMessageCircle,
124
+ },
125
+ ] as const;
126
+
127
+ const RECORD_KIND_ICONS: Record<string, TablerIcon> = {
128
+ account: IconBuilding,
129
+ person: IconUsers,
130
+ opportunity: IconRoute,
131
+ };
132
+
133
+ function rankGroups(groups: PaletteGroup[], search: string): PaletteGroup[] {
134
+ return groups
135
+ .map((group, index) => {
136
+ const ranked = rankCommandPaletteEntries(
137
+ group.entries,
138
+ search,
139
+ (entry) => ({
140
+ value: entry.value,
141
+ keywords: entry.keywords,
142
+ }),
143
+ );
144
+ return {
145
+ index,
146
+ topScore: ranked[0]?.score ?? 0,
147
+ group: { ...group, entries: ranked.map(({ entry }) => entry) },
148
+ };
149
+ })
150
+ .filter(({ group }) => group.entries.length > 0)
151
+ .sort((a, b) => b.topScore - a.topScore || a.index - b.index)
152
+ .map(({ group }) => group);
153
+ }
154
+
155
+ function CrmCommandResults({
156
+ search,
157
+ navigate,
158
+ onShowShortcuts,
159
+ }: {
160
+ search: string;
161
+ navigate: NavigateFunction;
162
+ onShowShortcuts: () => void;
163
+ }) {
164
+ const t = useT();
165
+ const { resolvedTheme, setTheme } = useTheme();
166
+ const isDark = resolvedTheme === "dark";
167
+ const trimmedSearch = search.trim();
168
+
169
+ const listsQuery = useActionQuery<unknown>(
170
+ "list-crm-lists" as never,
171
+ { limit: 50 } as never,
172
+ { staleTime: 30_000, retry: false } as never,
173
+ );
174
+ const viewsQuery = useActionQuery<unknown>(
175
+ "list-crm-saved-views" as never,
176
+ { limit: 50 } as never,
177
+ { staleTime: 30_000, retry: false } as never,
178
+ );
179
+ const recordsQuery = useActionQuery<unknown>(
180
+ "list-crm-records" as never,
181
+ { query: trimmedSearch, limit: 8 } as never,
182
+ {
183
+ enabled: trimmedSearch.length > 0,
184
+ staleTime: 15_000,
185
+ retry: false,
186
+ } as never,
187
+ );
188
+
189
+ const records = useMemo(() => {
190
+ const payload = recordsQuery.data;
191
+ const rows =
192
+ payload && typeof payload === "object" && "records" in payload
193
+ ? (payload as { records: unknown }).records
194
+ : payload;
195
+ if (!Array.isArray(rows)) return [];
196
+ return rows.flatMap((row) => {
197
+ if (!row || typeof row !== "object") return [];
198
+ const record = row as Record<string, unknown>;
199
+ const id = typeof record.id === "string" ? record.id : null;
200
+ const displayName =
201
+ typeof record.displayName === "string" ? record.displayName : null;
202
+ if (!id || !displayName) return [];
203
+ return [
204
+ {
205
+ id,
206
+ displayName,
207
+ kind: typeof record.kind === "string" ? record.kind : "",
208
+ subtitle: typeof record.subtitle === "string" ? record.subtitle : "",
209
+ },
210
+ ];
211
+ });
212
+ }, [recordsQuery.data]);
213
+
214
+ const lists = useMemo(
215
+ () => normalizeCrmLists(listsQuery.data),
216
+ [listsQuery.data],
217
+ );
218
+ const views = useMemo(
219
+ () => normalizeCrmSavedViews(viewsQuery.data),
220
+ [viewsQuery.data],
221
+ );
222
+
223
+ const groups: PaletteGroup[] = [
224
+ {
225
+ id: "records",
226
+ heading: t("commandMenu.groupRecords"),
227
+ entries: records.map((record) => ({
228
+ id: `record-${record.id}`,
229
+ value: `record:${record.id}:${record.displayName}`,
230
+ label: record.displayName,
231
+ keywords: commandPaletteKeywords(
232
+ record.displayName,
233
+ record.subtitle,
234
+ record.kind,
235
+ ),
236
+ icon: RECORD_KIND_ICONS[record.kind] ?? IconTable,
237
+ run: () => navigate(`/records/${encodeURIComponent(record.id)}`),
238
+ })),
239
+ },
240
+ {
241
+ id: "lists",
242
+ heading: t("commandMenu.groupLists"),
243
+ entries: lists.map((list) => ({
244
+ id: `list-${list.id}`,
245
+ value: `list:${list.id}:${list.name}`,
246
+ label: list.name,
247
+ keywords: commandPaletteKeywords(
248
+ list.name,
249
+ list.parentObjectType,
250
+ "list",
251
+ ),
252
+ icon: IconList,
253
+ run: () => navigate(`/lists/${encodeURIComponent(list.id)}`),
254
+ })),
255
+ },
256
+ {
257
+ id: "views",
258
+ heading: t("commandMenu.groupViews"),
259
+ entries: views.map((view) => ({
260
+ id: `view-${view.id}`,
261
+ value: `view:${view.id}:${view.name}`,
262
+ label: view.name,
263
+ keywords: commandPaletteKeywords(
264
+ view.name,
265
+ view.viewKind,
266
+ "saved view",
267
+ ),
268
+ icon: view.viewKind === "board" ? IconLayoutBoard : IconTable,
269
+ run: () => navigate(savedViewHref(view)),
270
+ })),
271
+ },
272
+ {
273
+ id: "go-to",
274
+ heading: t("commandMenu.groupGoTo"),
275
+ entries: GO_TO_ROUTES.map((route) => ({
276
+ id: `go-${route.id}`,
277
+ value: `go:${route.id}:${t(route.labelKey)}`,
278
+ label: t(route.labelKey),
279
+ keywords: commandPaletteKeywords(t(route.labelKey), route.id, "go to"),
280
+ icon: route.icon,
281
+ run: () => navigate(route.to),
282
+ })),
283
+ },
284
+ {
285
+ id: "commands",
286
+ heading: t("commandMenu.groupCommands"),
287
+ entries: [
288
+ {
289
+ id: "create-record",
290
+ value: `command:create-record:${t("commandMenu.createRecord")}`,
291
+ label: t("commandMenu.createRecord"),
292
+ keywords: commandPaletteKeywords(
293
+ t("commandMenu.createRecord"),
294
+ "new record",
295
+ "add",
296
+ ),
297
+ icon: IconPlus,
298
+ run: () => emitCrmUiIntent(CRM_NEW_RECORD_EVENT),
299
+ },
300
+ {
301
+ id: "create-list",
302
+ value: `command:create-list:${t("commandMenu.createList")}`,
303
+ label: t("commandMenu.createList"),
304
+ keywords: commandPaletteKeywords(
305
+ t("commandMenu.createList"),
306
+ "new list",
307
+ ),
308
+ icon: IconList,
309
+ run: () => navigate("/lists?new=1"),
310
+ },
311
+ {
312
+ id: "new-task",
313
+ value: `command:new-task:${t("commandMenu.newTask")}`,
314
+ label: t("commandMenu.newTask"),
315
+ keywords: commandPaletteKeywords(t("commandMenu.newTask"), "todo"),
316
+ icon: IconChecklist,
317
+ run: () => emitCrmUiIntent(CRM_NEW_TASK_EVENT),
318
+ },
319
+ {
320
+ id: "open-settings",
321
+ value: `command:settings:${t("commandMenu.openSettings")}`,
322
+ label: t("commandMenu.openSettings"),
323
+ keywords: commandPaletteKeywords(
324
+ t("commandMenu.openSettings"),
325
+ t("navigation.connections"),
326
+ "preferences",
327
+ ),
328
+ icon: IconSettings,
329
+ run: () => navigate("/settings/connections"),
330
+ },
331
+ {
332
+ id: "shortcuts",
333
+ value: `command:shortcuts:${t("commandMenu.keyboardShortcuts")}`,
334
+ label: t("commandMenu.keyboardShortcuts"),
335
+ keywords: commandPaletteKeywords(
336
+ t("commandMenu.keyboardShortcuts"),
337
+ "keys",
338
+ "hotkeys",
339
+ ),
340
+ icon: IconKeyboard,
341
+ run: onShowShortcuts,
342
+ },
343
+ ],
344
+ },
345
+ {
346
+ id: "appearance",
347
+ heading: t("commandMenu.groupAppearance"),
348
+ entries: [
349
+ {
350
+ id: "toggle-theme",
351
+ value: `appearance:theme:${t("commandMenu.toggleTheme")}`,
352
+ label: t("commandMenu.toggleTheme"),
353
+ keywords: commandPaletteKeywords(
354
+ t("commandMenu.toggleTheme"),
355
+ "dark",
356
+ "light",
357
+ "mode",
358
+ ),
359
+ icon: isDark ? IconSun : IconMoon,
360
+ run: () => setTheme(isDark ? "light" : "dark"),
361
+ },
362
+ ],
363
+ },
364
+ ];
365
+
366
+ const ranked = rankGroups(groups, search);
367
+
368
+ return (
369
+ <>
370
+ {recordsQuery.isError && (
371
+ <CommandMenu.Group heading={t("commandMenu.groupRecords")}>
372
+ <CommandMenu.Item
373
+ onSelect={() => void recordsQuery.refetch()}
374
+ deferSelect={false}
375
+ >
376
+ <IconAlertTriangle className="size-4 text-destructive" />
377
+ <span className="min-w-0 flex-1 truncate">
378
+ {t("commandMenu.recordSearchFailed")}
379
+ </span>
380
+ <span className="text-xs text-muted-foreground">
381
+ {t("navigation.retry")}
382
+ </span>
383
+ </CommandMenu.Item>
384
+ </CommandMenu.Group>
385
+ )}
386
+ {ranked.map((group) => (
387
+ <CommandMenu.Group key={group.id} heading={group.heading}>
388
+ {group.entries.map((entry) => {
389
+ const Icon = entry.icon;
390
+ return (
391
+ <CommandMenu.Item key={entry.id} onSelect={entry.run}>
392
+ <Icon className="size-4 text-muted-foreground" />
393
+ <span className="min-w-0 flex-1 truncate">{entry.label}</span>
394
+ </CommandMenu.Item>
395
+ );
396
+ })}
397
+ </CommandMenu.Group>
398
+ ))}
399
+ </>
400
+ );
401
+ }
402
+
403
+ const SHORTCUT_HELP = [
404
+ {
405
+ groupKey: "shortcuts.groupNavigation",
406
+ rows: [
407
+ { keys: ["g", "a"], labelKey: "shortcuts.goAccounts" },
408
+ { keys: ["g", "p"], labelKey: "shortcuts.goPeople" },
409
+ { keys: ["g", "o"], labelKey: "shortcuts.goOpportunities" },
410
+ { keys: ["g", "l"], labelKey: "shortcuts.goLists" },
411
+ { keys: ["g", "t"], labelKey: "shortcuts.goTasks" },
412
+ ],
413
+ },
414
+ {
415
+ groupKey: "shortcuts.groupActions",
416
+ rows: [
417
+ { keys: ["n"], labelKey: "shortcuts.newRecord" },
418
+ { keys: ["t"], labelKey: "shortcuts.newTask" },
419
+ { keys: ["e"], labelKey: "shortcuts.edit" },
420
+ ],
421
+ },
422
+ {
423
+ groupKey: "shortcuts.groupGeneral",
424
+ rows: [
425
+ { keys: ["⌘", "K"], labelKey: "shortcuts.commandMenu" },
426
+ { keys: ["/"], labelKey: "shortcuts.focusSearch" },
427
+ { keys: ["?"], labelKey: "shortcuts.showHelp" },
428
+ ],
429
+ },
430
+ ] as const;
431
+
432
+ function ShortcutsDialog({
433
+ open,
434
+ onOpenChange,
435
+ }: {
436
+ open: boolean;
437
+ onOpenChange: (open: boolean) => void;
438
+ }) {
439
+ const t = useT();
440
+ return (
441
+ <Dialog open={open} onOpenChange={onOpenChange}>
442
+ <DialogContent className="max-w-md">
443
+ <DialogHeader>
444
+ <DialogTitle>{t("shortcuts.title")}</DialogTitle>
445
+ <DialogDescription>{t("shortcuts.description")}</DialogDescription>
446
+ </DialogHeader>
447
+ <div className="grid gap-5">
448
+ {SHORTCUT_HELP.map((group) => (
449
+ <div key={group.groupKey} className="grid gap-1.5">
450
+ <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
451
+ {t(group.groupKey)}
452
+ </p>
453
+ {group.rows.map((row) => (
454
+ <div
455
+ key={row.labelKey}
456
+ className="flex items-center justify-between gap-4 text-sm"
457
+ >
458
+ <span className="min-w-0 truncate">{t(row.labelKey)}</span>
459
+ <span className="flex shrink-0 items-center gap-1">
460
+ {row.keys.map((key) => (
461
+ <kbd
462
+ key={key}
463
+ className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground"
464
+ >
465
+ {key}
466
+ </kbd>
467
+ ))}
468
+ </span>
469
+ </div>
470
+ ))}
471
+ </div>
472
+ ))}
473
+ </div>
474
+ </DialogContent>
475
+ </Dialog>
476
+ );
477
+ }
478
+
479
+ export function CrmCommandMenu() {
480
+ const t = useT();
481
+ const navigate = useNavigate();
482
+ const [open, setOpen] = useState(false);
483
+ const [shortcutsOpen, setShortcutsOpen] = useState(false);
484
+
485
+ useCommandMenuShortcut(useCallback(() => setOpen(true), []));
486
+
487
+ const showShortcuts = useCallback(() => setShortcutsOpen(true), []);
488
+ const renderResults = useCallback(
489
+ (search: string) => (
490
+ <CrmCommandResults
491
+ search={search}
492
+ navigate={navigate}
493
+ onShowShortcuts={showShortcuts}
494
+ />
495
+ ),
496
+ [navigate, showShortcuts],
497
+ );
498
+
499
+ useCrmKeyboardShortcuts({
500
+ navigate,
501
+ enabled: !open && !shortcutsOpen,
502
+ onShowShortcuts: showShortcuts,
503
+ });
504
+
505
+ return (
506
+ <>
507
+ <CommandMenu
508
+ open={open}
509
+ onOpenChange={setOpen}
510
+ placeholder={t("commandMenu.placeholder")}
511
+ renderResults={renderResults}
512
+ changelog={changelog}
513
+ changelogKey="crm"
514
+ >
515
+ {null}
516
+ </CommandMenu>
517
+ <ShortcutsDialog open={shortcutsOpen} onOpenChange={setShortcutsOpen} />
518
+ </>
519
+ );
520
+ }
521
+
522
+ function useCrmKeyboardShortcuts({
523
+ navigate,
524
+ enabled,
525
+ onShowShortcuts,
526
+ }: {
527
+ navigate: NavigateFunction;
528
+ enabled: boolean;
529
+ onShowShortcuts: () => void;
530
+ }) {
531
+ const shortcuts = useMemo(
532
+ () => [
533
+ { key: "/", handler: focusCrmSearch },
534
+ { key: "n", handler: () => emitCrmUiIntent(CRM_NEW_RECORD_EVENT) },
535
+ { key: "t", handler: () => emitCrmUiIntent(CRM_NEW_TASK_EVENT) },
536
+ { key: "e", handler: () => emitCrmUiIntent(CRM_EDIT_RECORD_EVENT) },
537
+ // `?` needs Shift on US layouts and none on several others.
538
+ { key: "?", handler: onShowShortcuts },
539
+ { key: "?", shift: true, handler: onShowShortcuts },
540
+ ],
541
+ [onShowShortcuts],
542
+ );
543
+
544
+ const sequences = useMemo(
545
+ () => [
546
+ { keys: ["g", "a"], handler: () => navigate("/accounts") },
547
+ { keys: ["g", "p"], handler: () => navigate("/people") },
548
+ { keys: ["g", "o"], handler: () => navigate("/opportunities") },
549
+ { keys: ["g", "l"], handler: () => navigate("/lists") },
550
+ { keys: ["g", "t"], handler: () => navigate("/tasks") },
551
+ ],
552
+ [navigate],
553
+ );
554
+
555
+ useKeyboardShortcuts({ shortcuts, sequences, enabled });
556
+ }
@@ -0,0 +1,130 @@
1
+ import {
2
+ AgentSidebar,
3
+ AgentToggleButton,
4
+ focusAgentChat,
5
+ isAgentChatHomeHandoffActive,
6
+ navigateWithAgentChatViewTransition,
7
+ useAgentChatHomeHandoff,
8
+ useAgentChatHomeHandoffLinks,
9
+ } from "@agent-native/core/client/agent-chat";
10
+ import { useT } from "@agent-native/core/client/i18n";
11
+ import { openCommandMenu } from "@agent-native/core/client/navigation";
12
+ import { IconMenu2, IconSearch } from "@tabler/icons-react";
13
+ import { useEffect, useState } from "react";
14
+ import { useLocation, useNavigate } from "react-router";
15
+
16
+ import { CrmSidebar } from "@/components/layout/CrmSidebar";
17
+ import { Button } from "@/components/ui/button";
18
+ import {
19
+ Sheet,
20
+ SheetContent,
21
+ SheetDescription,
22
+ SheetTitle,
23
+ } from "@/components/ui/sheet";
24
+ import { TAB_ID } from "@/lib/tab-id";
25
+
26
+ export function CrmLayout({ children }: { children: React.ReactNode }) {
27
+ const t = useT();
28
+ const location = useLocation();
29
+ const navigate = useNavigate();
30
+ const [mobileOpen, setMobileOpen] = useState(false);
31
+ const isAskRoute = location.pathname === "/ask";
32
+ const handoffActive = useAgentChatHomeHandoff({
33
+ storageKey: "crm",
34
+ activePath: location.pathname,
35
+ enabled: !isAskRoute,
36
+ });
37
+ const handoffPending = isAgentChatHomeHandoffActive("crm");
38
+ useAgentChatHomeHandoffLinks({
39
+ storageKey: "crm",
40
+ chatPath: "/ask",
41
+ requireActiveHandoff: false,
42
+ });
43
+
44
+ useEffect(() => setMobileOpen(false), [location.pathname]);
45
+
46
+ const shell = (
47
+ <div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden">
48
+ <header className="flex h-12 shrink-0 items-center gap-2 border-b border-border/70 px-3 md:hidden">
49
+ <Button
50
+ type="button"
51
+ variant="ghost"
52
+ size="icon"
53
+ onClick={() => setMobileOpen(true)}
54
+ aria-label={t("navigation.openNavigation")}
55
+ >
56
+ <IconMenu2 className="size-4" />
57
+ </Button>
58
+ <p className="text-sm font-semibold">CRM</p>
59
+ <Button
60
+ type="button"
61
+ variant="ghost"
62
+ size="icon"
63
+ className="ms-auto"
64
+ onClick={openCommandMenu}
65
+ aria-label={t("navigation.search")}
66
+ >
67
+ <IconSearch className="size-4" />
68
+ </Button>
69
+ {!isAskRoute ? <AgentToggleButton /> : null}
70
+ </header>
71
+ <main className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
72
+ {children}
73
+ </main>
74
+ </div>
75
+ );
76
+
77
+ const navigation = (
78
+ <>
79
+ <div className="hidden md:block">
80
+ <CrmSidebar />
81
+ </div>
82
+ <Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
83
+ <SheetContent side="left" className="w-60 p-0">
84
+ <SheetTitle className="sr-only">
85
+ {t("navigation.navigation")}
86
+ </SheetTitle>
87
+ <SheetDescription className="sr-only">
88
+ {t("navigation.navigationDescription")}
89
+ </SheetDescription>
90
+ <CrmSidebar onNavigate={() => setMobileOpen(false)} />
91
+ </SheetContent>
92
+ </Sheet>
93
+ </>
94
+ );
95
+
96
+ if (isAskRoute)
97
+ return (
98
+ <div className="flex h-screen overflow-hidden bg-background text-foreground">
99
+ {navigation}
100
+ {shell}
101
+ </div>
102
+ );
103
+
104
+ return (
105
+ <div className="flex h-screen overflow-hidden bg-background text-foreground">
106
+ {navigation}
107
+ <AgentSidebar
108
+ position="right"
109
+ storageKey="crm"
110
+ browserTabId={TAB_ID}
111
+ chatViewTransition
112
+ chatViewTransitionHandoff={handoffPending}
113
+ openOnChatRunning={handoffActive}
114
+ onFullscreenRequest={() => {
115
+ focusAgentChat();
116
+ navigateWithAgentChatViewTransition(navigate, "/ask");
117
+ }}
118
+ emptyStateText="Ask CRM about your connected records"
119
+ suggestions={[
120
+ "What needs follow-up?",
121
+ "Summarize this account",
122
+ "Which opportunities need attention?",
123
+ ]}
124
+ agentPageHref="/agent"
125
+ >
126
+ {shell}
127
+ </AgentSidebar>
128
+ </div>
129
+ );
130
+ }