@agent-native/core 0.123.0 → 0.123.1

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 (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/templates-meta.ts +12 -0
  5. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
  6. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
  7. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +205 -0
  8. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  9. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
  10. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +228 -0
  11. package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +178 -0
  12. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
  13. package/corpus/templates/crm/.env.example +5 -0
  14. package/corpus/templates/crm/AGENTS.md +82 -0
  15. package/corpus/templates/crm/CHANGELOG.md +25 -0
  16. package/corpus/templates/crm/DEVELOPING.md +66 -0
  17. package/corpus/templates/crm/README.md +122 -0
  18. package/corpus/templates/crm/_gitignore +12 -0
  19. package/corpus/templates/crm/actions/_crm-action-utils.ts +98 -0
  20. package/corpus/templates/crm/actions/_crm-attribute-utils.ts +219 -0
  21. package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
  22. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  23. package/corpus/templates/crm/actions/_crm-list-utils.ts +775 -0
  24. package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
  25. package/corpus/templates/crm/actions/add-crm-record-to-list.ts +164 -0
  26. package/corpus/templates/crm/actions/apply-crm-proposals.ts +291 -0
  27. package/corpus/templates/crm/actions/archive-crm-attribute.ts +69 -0
  28. package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
  29. package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
  30. package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
  31. package/corpus/templates/crm/actions/create-crm-attribute.ts +350 -0
  32. package/corpus/templates/crm/actions/create-crm-list.ts +194 -0
  33. package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
  34. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
  35. package/corpus/templates/crm/actions/delete-crm-saved-view.ts +68 -0
  36. package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
  37. package/corpus/templates/crm/actions/estimate-crm-enrichment.ts +229 -0
  38. package/corpus/templates/crm/actions/find-crm-duplicates.ts +87 -0
  39. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
  40. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +35 -0
  41. package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
  42. package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
  43. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
  44. package/corpus/templates/crm/actions/get-crm-record-page.ts +287 -0
  45. package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
  46. package/corpus/templates/crm/actions/get-crm-workspace.ts +364 -0
  47. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
  48. package/corpus/templates/crm/actions/list-crm-attributes.ts +71 -0
  49. package/corpus/templates/crm/actions/list-crm-connections.ts +68 -0
  50. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
  51. package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
  52. package/corpus/templates/crm/actions/list-crm-enrichment-slots.ts +28 -0
  53. package/corpus/templates/crm/actions/list-crm-list-entries.ts +192 -0
  54. package/corpus/templates/crm/actions/list-crm-lists.ts +80 -0
  55. package/corpus/templates/crm/actions/list-crm-proposals.ts +55 -0
  56. package/corpus/templates/crm/actions/list-crm-record-field-history.ts +201 -0
  57. package/corpus/templates/crm/actions/list-crm-record-values.ts +200 -0
  58. package/corpus/templates/crm/actions/list-crm-records.ts +103 -0
  59. package/corpus/templates/crm/actions/list-crm-saved-views.ts +66 -0
  60. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
  61. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
  62. package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
  63. package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
  64. package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
  65. package/corpus/templates/crm/actions/manage-crm-attribute-option.ts +272 -0
  66. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
  67. package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
  68. package/corpus/templates/crm/actions/merge-crm-records.ts +691 -0
  69. package/corpus/templates/crm/actions/navigate.ts +54 -0
  70. package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
  71. package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
  72. package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
  73. package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
  74. package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
  75. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
  76. package/corpus/templates/crm/actions/remove-crm-list-entry.ts +79 -0
  77. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
  78. package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
  79. package/corpus/templates/crm/actions/run-crm-attribute-fill.ts +443 -0
  80. package/corpus/templates/crm/actions/run-crm-enrichment.ts +647 -0
  81. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
  82. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +265 -0
  83. package/corpus/templates/crm/actions/run.ts +7 -0
  84. package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
  85. package/corpus/templates/crm/actions/save-crm-saved-view.ts +296 -0
  86. package/corpus/templates/crm/actions/sync-crm.ts +146 -0
  87. package/corpus/templates/crm/actions/update-crm-attribute.ts +111 -0
  88. package/corpus/templates/crm/actions/update-crm-list-entry.ts +102 -0
  89. package/corpus/templates/crm/actions/update-crm-list.ts +97 -0
  90. package/corpus/templates/crm/actions/update-crm-record.ts +543 -0
  91. package/corpus/templates/crm/actions/view-screen.ts +324 -0
  92. package/corpus/templates/crm/agent-native.app-skill.json +69 -0
  93. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
  94. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +78 -0
  95. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +220 -0
  96. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +414 -0
  97. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +648 -0
  98. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +488 -0
  99. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +232 -0
  100. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +93 -0
  101. package/corpus/templates/crm/app/components/crm/Surface.tsx +97 -0
  102. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
  103. package/corpus/templates/crm/app/components/crm/board/CrmBoard.tsx +988 -0
  104. package/corpus/templates/crm/app/components/crm/board/board-model.ts +399 -0
  105. package/corpus/templates/crm/app/components/crm/board/entry-filter.ts +150 -0
  106. package/corpus/templates/crm/app/components/crm/board/view-draft.ts +227 -0
  107. package/corpus/templates/crm/app/components/crm/grid/CrmGrid.tsx +1033 -0
  108. package/corpus/templates/crm/app/components/crm/grid/GridCell.tsx +620 -0
  109. package/corpus/templates/crm/app/components/crm/grid/clipboard.ts +84 -0
  110. package/corpus/templates/crm/app/components/crm/grid/model.ts +304 -0
  111. package/corpus/templates/crm/app/components/crm/grid/navigation.ts +220 -0
  112. package/corpus/templates/crm/app/components/crm/grid/query.ts +214 -0
  113. package/corpus/templates/crm/app/components/crm/record/AttributePanel.tsx +192 -0
  114. package/corpus/templates/crm/app/components/crm/record/FieldHistory.tsx +179 -0
  115. package/corpus/templates/crm/app/components/crm/record/RecordHeader.tsx +103 -0
  116. package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +235 -0
  117. package/corpus/templates/crm/app/components/crm/record/RecordTabs.tsx +206 -0
  118. package/corpus/templates/crm/app/components/crm/record/field-editors.tsx +245 -0
  119. package/corpus/templates/crm/app/components/crm/record/record-data.ts +603 -0
  120. package/corpus/templates/crm/app/components/crm/settings/AdvancedSettings.tsx +49 -0
  121. package/corpus/templates/crm/app/components/crm/settings/AttributeOptionsEditor.tsx +441 -0
  122. package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +169 -0
  123. package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +935 -0
  124. package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +339 -0
  125. package/corpus/templates/crm/app/components/crm/settings/settings-admin.ts +377 -0
  126. package/corpus/templates/crm/app/components/crm/shared/AttributeValueParts.tsx +69 -0
  127. package/corpus/templates/crm/app/components/crm/shared/attribute-value.ts +591 -0
  128. package/corpus/templates/crm/app/components/layout/CrmCommandMenu.tsx +556 -0
  129. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +130 -0
  130. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +407 -0
  131. package/corpus/templates/crm/app/components/layout/command-palette-search.ts +126 -0
  132. package/corpus/templates/crm/app/components/layout/crm-ui-intents.ts +41 -0
  133. package/corpus/templates/crm/app/components/layout/sidebar-collapse.ts +40 -0
  134. package/corpus/templates/crm/app/components/layout/sidebar-lists.ts +109 -0
  135. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
  136. package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
  137. package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
  138. package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
  139. package/corpus/templates/crm/app/components/ui/checkbox.tsx +1 -0
  140. package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
  141. package/corpus/templates/crm/app/components/ui/dropdown-menu.tsx +1 -0
  142. package/corpus/templates/crm/app/components/ui/hover-card.tsx +1 -0
  143. package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
  144. package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
  145. package/corpus/templates/crm/app/components/ui/popover.tsx +1 -0
  146. package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
  147. package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
  148. package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
  149. package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
  150. package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
  151. package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
  152. package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
  153. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
  154. package/corpus/templates/crm/app/components/ui/tooltip.tsx +1 -0
  155. package/corpus/templates/crm/app/design-system.ts +3 -0
  156. package/corpus/templates/crm/app/entry.client.tsx +19 -0
  157. package/corpus/templates/crm/app/entry.server.tsx +10 -0
  158. package/corpus/templates/crm/app/global.css +167 -0
  159. package/corpus/templates/crm/app/hooks/use-keyboard-shortcuts.ts +206 -0
  160. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +48 -0
  161. package/corpus/templates/crm/app/i18n/en-US.ts +684 -0
  162. package/corpus/templates/crm/app/i18n/index.ts +34 -0
  163. package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
  164. package/corpus/templates/crm/app/lib/navigation.ts +17 -0
  165. package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
  166. package/corpus/templates/crm/app/lib/types.ts +172 -0
  167. package/corpus/templates/crm/app/lib/utils.ts +1 -0
  168. package/corpus/templates/crm/app/root.tsx +93 -0
  169. package/corpus/templates/crm/app/routes/_index.tsx +26 -0
  170. package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
  171. package/corpus/templates/crm/app/routes/agent.tsx +5 -0
  172. package/corpus/templates/crm/app/routes/ask.tsx +53 -0
  173. package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
  174. package/corpus/templates/crm/app/routes/lists.tsx +228 -0
  175. package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
  176. package/corpus/templates/crm/app/routes/people.tsx +41 -0
  177. package/corpus/templates/crm/app/routes/proposals.tsx +388 -0
  178. package/corpus/templates/crm/app/routes/records.$recordId.tsx +53 -0
  179. package/corpus/templates/crm/app/routes/records.tsx +64 -0
  180. package/corpus/templates/crm/app/routes/settings.tsx +118 -0
  181. package/corpus/templates/crm/app/routes/setup.tsx +308 -0
  182. package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
  183. package/corpus/templates/crm/app/routes/views.tsx +683 -0
  184. package/corpus/templates/crm/app/routes.ts +19 -0
  185. package/corpus/templates/crm/app/vite-env.d.ts +6 -0
  186. 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
  187. package/corpus/templates/crm/changelog/2026-07-26-crm-now-finds-likely-duplicate-records-and-tells-you-exactly.md +6 -0
  188. package/corpus/templates/crm/changelog/2026-07-26-enrichment-now-shows-what-a-run-will-cost-before-it-starts-g.md +6 -0
  189. package/corpus/templates/crm/changelog/2026-07-26-lists-turn-any-set-of-records-into-a-pipeline-with-its-own-s.md +6 -0
  190. package/corpus/templates/crm/changelog/2026-07-26-moving-a-record-or-list-entry-into-a-stage-that-was-retired-.md +6 -0
  191. package/corpus/templates/crm/changelog/2026-07-26-opening-a-record-now-shows-one-page-with-its-fields-the-list.md +6 -0
  192. 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
  193. package/corpus/templates/crm/changelog/2026-07-26-provider-record-edits-now-show-an-exact-before-after-diff-an.md +6 -0
  194. package/corpus/templates/crm/changelog/2026-07-26-record-page-attribute-labels-now-read-in-full-instead-of-bei.md +6 -0
  195. package/corpus/templates/crm/changelog/2026-07-26-records-now-use-typed-fields-currency-dates-ratings-status-s.md +6 -0
  196. package/corpus/templates/crm/changelog/2026-07-26-retyping-a-value-on-the-record-page-now-replaces-it-instead-.md +6 -0
  197. package/corpus/templates/crm/changelog/2026-07-26-settings-now-has-fields-and-lists-tabs-for-adding-fields-edi.md +6 -0
  198. package/corpus/templates/crm/changelog/2026-07-26-the-proposal-review-now-shows-the-field-that-actually-change.md +6 -0
  199. package/corpus/templates/crm/changelog/2026-07-26-the-record-grid-is-now-a-real-spreadsheet-filter-sort-and-pa.md +6 -0
  200. package/corpus/templates/crm/components.json +20 -0
  201. package/corpus/templates/crm/docs/architecture/crm-contract.md +214 -0
  202. package/corpus/templates/crm/docs/features/README.md +55 -0
  203. package/corpus/templates/crm/docs/features/f1-records-and-fields.mdx +52 -0
  204. package/corpus/templates/crm/docs/features/f2-lists-pipelines-and-views.mdx +48 -0
  205. package/corpus/templates/crm/docs/features/f3-work-and-record-quality.mdx +40 -0
  206. package/corpus/templates/crm/docs/features/f4-evidence-and-chat.mdx +47 -0
  207. package/corpus/templates/crm/learnings.defaults.md +5 -0
  208. package/corpus/templates/crm/netlify.toml +11 -0
  209. package/corpus/templates/crm/package.json +61 -0
  210. package/corpus/templates/crm/public/favicon.svg +1 -0
  211. package/corpus/templates/crm/public/icon-180.svg +1 -0
  212. package/corpus/templates/crm/public/manifest.json +11 -0
  213. package/corpus/templates/crm/react-router.config.ts +7 -0
  214. package/corpus/templates/crm/server/crm/adapter.ts +32 -0
  215. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
  216. package/corpus/templates/crm/server/crm/crm-mirror.ts +721 -0
  217. package/corpus/templates/crm/server/crm/field-policy-attributes.ts +48 -0
  218. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1146 -0
  219. package/corpus/templates/crm/server/crm/native-adapter.ts +1810 -0
  220. package/corpus/templates/crm/server/crm/provider-record-link.ts +264 -0
  221. package/corpus/templates/crm/server/crm/read-through.ts +207 -0
  222. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
  223. package/corpus/templates/crm/server/db/crm-store.ts +1112 -0
  224. package/corpus/templates/crm/server/db/index.ts +191 -0
  225. package/corpus/templates/crm/server/db/schema.ts +647 -0
  226. package/corpus/templates/crm/server/lib/crm-query.ts +1558 -0
  227. package/corpus/templates/crm/server/lib/dedupe.ts +714 -0
  228. package/corpus/templates/crm/server/lib/enrichment-cost.ts +381 -0
  229. package/corpus/templates/crm/server/lib/enrichment-slots.ts +519 -0
  230. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
  231. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
  232. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +94 -0
  233. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +39 -0
  234. package/corpus/templates/crm/server/lib/intelligence/summary.ts +19 -0
  235. package/corpus/templates/crm/server/lib/lifecycle.ts +634 -0
  236. package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
  237. package/corpus/templates/crm/server/middleware/auth.ts +4 -0
  238. package/corpus/templates/crm/server/plugins/agent-chat.ts +65 -0
  239. package/corpus/templates/crm/server/plugins/auth.ts +14 -0
  240. package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
  241. package/corpus/templates/crm/server/plugins/db.ts +738 -0
  242. package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
  243. package/corpus/templates/crm/shared/crm-attributes.ts +469 -0
  244. package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
  245. package/corpus/templates/crm/shared/crm-contract.ts +417 -0
  246. package/corpus/templates/crm/shared/crm-navigation.ts +190 -0
  247. package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
  248. package/corpus/templates/crm/ssr-entry.ts +11 -0
  249. package/corpus/templates/crm/tsconfig.json +24 -0
  250. package/corpus/templates/crm/vite.config.ts +15 -0
  251. package/corpus/templates/crm/vitest.config.ts +5 -0
  252. package/corpus/templates/design/app/i18n-data.ts +12 -12
  253. package/corpus/templates/design/changelog/2026-07-26-apply-design-updates-from-a-centered-top-toolbar-without-cov.md +6 -0
  254. package/dist/cli/templates-meta.d.ts.map +1 -1
  255. package/dist/cli/templates-meta.js +12 -0
  256. package/dist/cli/templates-meta.js.map +1 -1
  257. package/dist/collab/routes.d.ts +1 -1
  258. package/dist/observability/routes.d.ts +3 -3
  259. package/dist/resources/handlers.d.ts +1 -1
  260. package/dist/server/realtime-token.d.ts +1 -1
  261. package/dist/server/transcribe-voice.d.ts +1 -1
  262. package/package.json +1 -1
  263. package/src/cli/templates-meta.ts +12 -0
@@ -0,0 +1,407 @@
1
+ import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
2
+ import { useActionQuery } from "@agent-native/core/client/hooks";
3
+ import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
4
+ import { openCommandMenu } from "@agent-native/core/client/navigation";
5
+ import { OrgSwitcher } from "@agent-native/core/client/org";
6
+ import { FeedbackButton } from "@agent-native/core/client/ui";
7
+ import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
8
+ import {
9
+ IconAlertTriangle,
10
+ IconBuilding,
11
+ IconChartBar,
12
+ IconChecklist,
13
+ IconLayoutBoard,
14
+ IconLayoutDashboard,
15
+ IconLayoutSidebarLeftCollapse,
16
+ IconLayoutSidebarLeftExpand,
17
+ IconList,
18
+ IconMessageCircle,
19
+ IconPencilCheck,
20
+ IconRoute,
21
+ IconSearch,
22
+ IconSettings,
23
+ IconStar,
24
+ IconTable,
25
+ IconUsers,
26
+ } from "@tabler/icons-react";
27
+ import { useEffect, useState } from "react";
28
+ import { Link, NavLink, useLocation } from "react-router";
29
+
30
+ import { Button } from "@/components/ui/button";
31
+ import {
32
+ Tooltip,
33
+ TooltipContent,
34
+ TooltipProvider,
35
+ TooltipTrigger,
36
+ } from "@/components/ui/tooltip";
37
+ import { cn } from "@/lib/utils";
38
+
39
+ import {
40
+ getCollapseStorage,
41
+ readSidebarCollapsed,
42
+ writeSidebarCollapsed,
43
+ } from "./sidebar-collapse";
44
+ import {
45
+ normalizeCrmLists,
46
+ normalizeCrmSavedViews,
47
+ savedViewHref,
48
+ type CrmSidebarList,
49
+ type CrmSidebarSavedView,
50
+ } from "./sidebar-lists";
51
+
52
+ const primaryNav = [
53
+ {
54
+ to: "/",
55
+ labelKey: "navigation.overview",
56
+ icon: IconLayoutDashboard,
57
+ end: true,
58
+ },
59
+ { to: "/accounts", labelKey: "navigation.accounts", icon: IconBuilding },
60
+ { to: "/people", labelKey: "navigation.people", icon: IconUsers },
61
+ {
62
+ to: "/opportunities",
63
+ labelKey: "navigation.opportunities",
64
+ icon: IconRoute,
65
+ },
66
+ { to: "/lists", labelKey: "navigation.lists", icon: IconList },
67
+ { to: "/tasks", labelKey: "navigation.tasks", icon: IconChecklist },
68
+ { to: "/proposals", labelKey: "navigation.proposals", icon: IconPencilCheck },
69
+ { to: "/dashboard", labelKey: "navigation.dashboard", icon: IconChartBar },
70
+ ];
71
+
72
+ const footerNav = [
73
+ { to: "/ask", labelKey: "navigation.askCrm", icon: IconMessageCircle },
74
+ {
75
+ to: "/settings/connections",
76
+ labelKey: "navigation.connections",
77
+ icon: IconSettings,
78
+ },
79
+ ];
80
+
81
+ function itemClasses(isActive: boolean, collapsed: boolean) {
82
+ return cn(
83
+ "flex items-center rounded-md text-sm",
84
+ collapsed ? "h-9 w-9 justify-center" : "h-8 gap-2 px-2",
85
+ isActive
86
+ ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
87
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
88
+ );
89
+ }
90
+
91
+ function CollapsibleTooltip({
92
+ collapsed,
93
+ label,
94
+ children,
95
+ }: {
96
+ collapsed: boolean;
97
+ label: string;
98
+ children: React.ReactNode;
99
+ }) {
100
+ if (!collapsed) return <>{children}</>;
101
+ return (
102
+ <Tooltip delayDuration={0}>
103
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
104
+ <TooltipContent side="right">{label}</TooltipContent>
105
+ </Tooltip>
106
+ );
107
+ }
108
+
109
+ function SidebarSection({
110
+ heading,
111
+ children,
112
+ }: {
113
+ heading: string;
114
+ children: React.ReactNode;
115
+ }) {
116
+ return (
117
+ <div className="mt-4 grid gap-0.5">
118
+ <p className="px-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-sidebar-foreground/50">
119
+ {heading}
120
+ </p>
121
+ {children}
122
+ </div>
123
+ );
124
+ }
125
+
126
+ function ListRow({
127
+ to,
128
+ label,
129
+ icon: Icon,
130
+ collapsed,
131
+ onNavigate,
132
+ }: {
133
+ to: string;
134
+ label: string;
135
+ icon: typeof IconList;
136
+ collapsed: boolean;
137
+ onNavigate?: () => void;
138
+ }) {
139
+ return (
140
+ <CollapsibleTooltip collapsed={collapsed} label={label}>
141
+ <NavLink
142
+ to={to}
143
+ onClick={onNavigate}
144
+ className={({ isActive }) => itemClasses(isActive, collapsed)}
145
+ >
146
+ <Icon className="size-4 shrink-0" />
147
+ {!collapsed && <span className="truncate">{label}</span>}
148
+ </NavLink>
149
+ </CollapsibleTooltip>
150
+ );
151
+ }
152
+
153
+ export function CrmSidebar({ onNavigate }: { onNavigate?: () => void }) {
154
+ const t = useT();
155
+ const location = useLocation();
156
+ const [collapsed, setCollapsed] = useState(() =>
157
+ readSidebarCollapsed(getCollapseStorage()),
158
+ );
159
+
160
+ useEffect(() => {
161
+ writeSidebarCollapsed(getCollapseStorage(), collapsed);
162
+ }, [collapsed]);
163
+
164
+ const listsQuery = useActionQuery<unknown>(
165
+ "list-crm-lists" as never,
166
+ { limit: 50 } as never,
167
+ { staleTime: 30_000, retry: false } as never,
168
+ );
169
+ const viewsQuery = useActionQuery<unknown>(
170
+ "list-crm-saved-views" as never,
171
+ { limit: 50 } as never,
172
+ { staleTime: 30_000, retry: false } as never,
173
+ );
174
+
175
+ const lists: CrmSidebarList[] = normalizeCrmLists(listsQuery.data);
176
+ const views: CrmSidebarSavedView[] = normalizeCrmSavedViews(viewsQuery.data);
177
+ const favorites = views.filter((view) => view.pinned);
178
+ const unpinnedViews = views.filter((view) => !view.pinned);
179
+ // An action that has not shipped yet and one that failed are the same 404 to
180
+ // the browser, so surface the failure instead of rendering an empty section.
181
+ const loadFailed = listsQuery.isError || viewsQuery.isError;
182
+ const settled = !listsQuery.isPending && !viewsQuery.isPending;
183
+
184
+ const collapseButton = (
185
+ <Tooltip delayDuration={0}>
186
+ <TooltipTrigger asChild>
187
+ <button
188
+ type="button"
189
+ onClick={() => setCollapsed((value) => !value)}
190
+ className="flex size-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground"
191
+ aria-label={
192
+ collapsed
193
+ ? t("navigation.expandSidebar")
194
+ : t("navigation.collapseSidebar")
195
+ }
196
+ >
197
+ {collapsed ? (
198
+ <IconLayoutSidebarLeftExpand className="size-4 rtl:-scale-x-100" />
199
+ ) : (
200
+ <IconLayoutSidebarLeftCollapse className="size-4 rtl:-scale-x-100" />
201
+ )}
202
+ </button>
203
+ </TooltipTrigger>
204
+ <TooltipContent side="right">
205
+ {collapsed
206
+ ? t("navigation.expandSidebar")
207
+ : t("navigation.collapseSidebar")}
208
+ </TooltipContent>
209
+ </Tooltip>
210
+ );
211
+
212
+ const searchButton = (
213
+ <Tooltip delayDuration={0}>
214
+ <TooltipTrigger asChild>
215
+ <button
216
+ type="button"
217
+ onClick={openCommandMenu}
218
+ className="flex size-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground"
219
+ aria-label={t("navigation.search")}
220
+ >
221
+ <IconSearch className="size-4" />
222
+ </button>
223
+ </TooltipTrigger>
224
+ <TooltipContent side="right">{t("navigation.search")}</TooltipContent>
225
+ </Tooltip>
226
+ );
227
+
228
+ return (
229
+ <TooltipProvider>
230
+ <nav
231
+ className={cn(
232
+ "flex h-full shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar py-3 text-sidebar-foreground transition-[width] duration-200 ease-out",
233
+ collapsed ? "w-14 px-1.5" : "w-60 px-2",
234
+ )}
235
+ >
236
+ <div
237
+ className={cn(
238
+ "flex h-8 items-center text-sm font-semibold tracking-tight text-sidebar-primary",
239
+ collapsed ? "justify-center" : "px-2",
240
+ )}
241
+ >
242
+ {collapsed ? <IconLayoutBoard className="size-4" /> : "CRM"}
243
+ </div>
244
+
245
+ <div className="mt-4 min-h-0 flex-1 overflow-y-auto">
246
+ <div className="grid gap-0.5">
247
+ {primaryNav.map((item) => {
248
+ const Icon = item.icon;
249
+ const label = t(item.labelKey);
250
+ const isActive = item.end
251
+ ? location.pathname === "/"
252
+ : location.pathname.startsWith(item.to);
253
+ return (
254
+ <CollapsibleTooltip
255
+ key={item.to}
256
+ collapsed={collapsed}
257
+ label={label}
258
+ >
259
+ <Link
260
+ to={item.to}
261
+ onClick={onNavigate}
262
+ aria-current={isActive ? "page" : undefined}
263
+ className={itemClasses(isActive, collapsed)}
264
+ >
265
+ <Icon className="size-4 shrink-0" />
266
+ {!collapsed && <span className="truncate">{label}</span>}
267
+ </Link>
268
+ </CollapsibleTooltip>
269
+ );
270
+ })}
271
+ </div>
272
+
273
+ {favorites.length > 0 && (
274
+ <SidebarSection
275
+ heading={collapsed ? "" : t("navigation.favorites")}
276
+ >
277
+ {favorites.map((view) => (
278
+ <ListRow
279
+ key={`favorite-${view.id}`}
280
+ to={savedViewHref(view)}
281
+ label={view.name}
282
+ icon={IconStar}
283
+ collapsed={collapsed}
284
+ onNavigate={onNavigate}
285
+ />
286
+ ))}
287
+ </SidebarSection>
288
+ )}
289
+
290
+ <SidebarSection
291
+ heading={collapsed ? "" : t("navigation.listsAndViews")}
292
+ >
293
+ {lists.map((list) => (
294
+ <ListRow
295
+ key={`list-${list.id}`}
296
+ to={`/lists/${encodeURIComponent(list.id)}`}
297
+ label={list.name}
298
+ icon={IconList}
299
+ collapsed={collapsed}
300
+ onNavigate={onNavigate}
301
+ />
302
+ ))}
303
+ {unpinnedViews.map((view) => (
304
+ <ListRow
305
+ key={`view-${view.id}`}
306
+ to={savedViewHref(view)}
307
+ label={view.name}
308
+ icon={view.viewKind === "board" ? IconLayoutBoard : IconTable}
309
+ collapsed={collapsed}
310
+ onNavigate={onNavigate}
311
+ />
312
+ ))}
313
+ {loadFailed ? (
314
+ <div
315
+ className={cn(
316
+ "flex items-center gap-2 rounded-md px-2 py-1.5 text-xs text-destructive",
317
+ collapsed && "justify-center px-0",
318
+ )}
319
+ >
320
+ <IconAlertTriangle className="size-4 shrink-0" />
321
+ {!collapsed && (
322
+ <>
323
+ <span className="min-w-0 flex-1">
324
+ {t("navigation.listsLoadFailed")}
325
+ </span>
326
+ <Button
327
+ type="button"
328
+ variant="ghost"
329
+ size="sm"
330
+ className="h-6 px-2 text-xs"
331
+ onClick={() => {
332
+ void listsQuery.refetch();
333
+ void viewsQuery.refetch();
334
+ }}
335
+ >
336
+ {t("navigation.retry")}
337
+ </Button>
338
+ </>
339
+ )}
340
+ </div>
341
+ ) : settled &&
342
+ lists.length === 0 &&
343
+ unpinnedViews.length === 0 &&
344
+ !collapsed ? (
345
+ <p className="px-2 py-1 text-xs text-sidebar-foreground/50">
346
+ {t("navigation.listsEmpty")}
347
+ </p>
348
+ ) : null}
349
+ </SidebarSection>
350
+ </div>
351
+
352
+ <div className="mt-2 shrink-0 border-t border-sidebar-border pt-2">
353
+ <div className="grid gap-0.5">
354
+ {footerNav.map((item) => {
355
+ const Icon = item.icon;
356
+ const label = t(item.labelKey);
357
+ const isActive = location.pathname.startsWith(item.to);
358
+ return (
359
+ <CollapsibleTooltip
360
+ key={item.to}
361
+ collapsed={collapsed}
362
+ label={label}
363
+ >
364
+ <Link
365
+ to={item.to}
366
+ onClick={onNavigate}
367
+ aria-current={isActive ? "page" : undefined}
368
+ className={itemClasses(isActive, collapsed)}
369
+ >
370
+ <Icon className="size-4 shrink-0" />
371
+ {!collapsed && <span className="truncate">{label}</span>}
372
+ </Link>
373
+ </CollapsibleTooltip>
374
+ );
375
+ })}
376
+ </div>
377
+
378
+ {!collapsed && (
379
+ <div className="mt-2 grid gap-2 px-1">
380
+ <OrgSwitcher reserveSpace />
381
+ <DevDatabaseLink />
382
+ </div>
383
+ )}
384
+
385
+ <SidebarFooterActions
386
+ collapsed={collapsed}
387
+ feedback={
388
+ <FeedbackButton
389
+ variant={collapsed ? "icon" : "sidebar"}
390
+ side="right"
391
+ className={collapsed ? "size-8" : "min-w-0"}
392
+ />
393
+ }
394
+ translate={
395
+ <LanguagePicker
396
+ variant="ghost-icon"
397
+ label={t("navigation.settings")}
398
+ />
399
+ }
400
+ search={searchButton}
401
+ collapse={collapseButton}
402
+ />
403
+ </div>
404
+ </nav>
405
+ </TooltipProvider>
406
+ );
407
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Ranked command-palette search, ported from the Analytics palette so CRM
3
+ * scores records, lists, and commands the same way the rest of the workspace
4
+ * does. `commandPaletteFilter` returns a score rather than a boolean because
5
+ * cmdk is driven with `shouldFilter: false` — ordering is ours to decide.
6
+ */
7
+
8
+ export function commandPaletteKeywords(
9
+ ...parts: Array<string | null | undefined>
10
+ ): string[] {
11
+ const variants = new Set<string>();
12
+
13
+ const add = (value: string) => {
14
+ const trimmed = value.trim();
15
+ if (!trimmed) return;
16
+ variants.add(trimmed);
17
+ variants.add(trimmed.toLowerCase());
18
+ };
19
+
20
+ for (const part of parts) {
21
+ if (!part) continue;
22
+
23
+ add(part);
24
+
25
+ const spaced = part.replace(/[-_/]+/g, " ").replace(/\s+/g, " ");
26
+ const hyphenated = spaced.trim().replace(/\s+/g, "-");
27
+ const compact = spaced.trim().replace(/\s+/g, "");
28
+
29
+ add(spaced);
30
+ add(hyphenated);
31
+ add(compact);
32
+ }
33
+
34
+ return Array.from(variants);
35
+ }
36
+
37
+ function normalizeSearchText(value: string): string {
38
+ return value
39
+ .normalize("NFKD")
40
+ .replace(/[\u0300-\u036f]/g, "")
41
+ .toLowerCase()
42
+ .replace(/[^\p{L}\p{N}]+/gu, " ")
43
+ .trim();
44
+ }
45
+
46
+ function fuzzySubsequenceScore(candidate: string, query: string): number {
47
+ const compactCandidate = candidate.replace(/\s/g, "");
48
+ const compactQuery = query.replace(/\s/g, "");
49
+ let queryIndex = 0;
50
+
51
+ for (const character of compactCandidate) {
52
+ if (character === compactQuery[queryIndex]) queryIndex += 1;
53
+ if (queryIndex === compactQuery.length) {
54
+ return 0.2 * (compactQuery.length / compactCandidate.length);
55
+ }
56
+ }
57
+
58
+ return 0;
59
+ }
60
+
61
+ export function commandPaletteFilter(
62
+ value: string,
63
+ search: string,
64
+ keywords: string[] = [],
65
+ ): number {
66
+ const query = normalizeSearchText(search);
67
+ if (!query) return 1;
68
+
69
+ const queryWords = query.split(" ");
70
+ let bestScore = 0;
71
+
72
+ for (const rawCandidate of [...keywords, value]) {
73
+ const candidate = normalizeSearchText(rawCandidate);
74
+ if (!candidate) continue;
75
+
76
+ const words = candidate.split(" ");
77
+ let score = 0;
78
+
79
+ if (candidate === query) score = 1;
80
+ else if (candidate.startsWith(query)) score = 0.96;
81
+ else if (words.includes(query)) score = 0.94;
82
+ else if (words.some((word) => word.startsWith(query))) score = 0.9;
83
+ else if (candidate.includes(query)) score = 0.8;
84
+ else if (
85
+ queryWords.every((queryWord) =>
86
+ words.some((word) => word.startsWith(queryWord)),
87
+ )
88
+ ) {
89
+ score = 0.72;
90
+ } else {
91
+ score = fuzzySubsequenceScore(candidate, query);
92
+ }
93
+
94
+ bestScore = Math.max(bestScore, score);
95
+ }
96
+
97
+ return bestScore;
98
+ }
99
+
100
+ export function rankCommandPaletteEntries<T>(
101
+ entries: T[],
102
+ search: string,
103
+ getSearchData: (entry: T) => { value: string; keywords?: string[] },
104
+ ): Array<{ entry: T; score: number }> {
105
+ return entries
106
+ .map((entry, index) => {
107
+ const { value, keywords } = getSearchData(entry);
108
+ return {
109
+ entry,
110
+ index,
111
+ score: commandPaletteFilter(value, search, keywords),
112
+ };
113
+ })
114
+ .filter(({ score }) => score > 0)
115
+ .sort((a, b) => b.score - a.score || a.index - b.index)
116
+ .map(({ entry, score }) => ({ entry, score }));
117
+ }
118
+
119
+ export function uniqueCommandItems<T extends { id: string }>(items: T[]): T[] {
120
+ const seenIds = new Set<string>();
121
+ return items.filter((item) => {
122
+ if (seenIds.has(item.id)) return false;
123
+ seenIds.add(item.id);
124
+ return true;
125
+ });
126
+ }
@@ -0,0 +1,41 @@
1
+ import { openCommandMenu } from "@agent-native/core/client/navigation";
2
+
3
+ /**
4
+ * Keyboard shortcuts live in the shell but the dialogs they open belong to
5
+ * whichever page is mounted, so the shell announces an intent and the page
6
+ * decides. A page that never listens simply does nothing — no shell change is
7
+ * needed to add or drop a surface.
8
+ */
9
+
10
+ export const CRM_NEW_RECORD_EVENT = "crm:new-record";
11
+ export const CRM_NEW_TASK_EVENT = "crm:new-task";
12
+ export const CRM_EDIT_RECORD_EVENT = "crm:edit-record";
13
+
14
+ export type CrmUiIntent =
15
+ | typeof CRM_NEW_RECORD_EVENT
16
+ | typeof CRM_NEW_TASK_EVENT
17
+ | typeof CRM_EDIT_RECORD_EVENT;
18
+
19
+ export function emitCrmUiIntent(intent: CrmUiIntent) {
20
+ if (typeof window === "undefined") return;
21
+ window.dispatchEvent(new CustomEvent(intent));
22
+ }
23
+
24
+ /** Marks the page's own search field as the `/` shortcut target. */
25
+ export const CRM_SEARCH_ATTRIBUTE = "data-crm-search";
26
+
27
+ /**
28
+ * `/` prefers the page's own search box and falls back to the command menu,
29
+ * which is the app-wide search when a page has none.
30
+ */
31
+ export function focusCrmSearch() {
32
+ if (typeof document === "undefined") return;
33
+ const field = document.querySelector<HTMLElement>(
34
+ `[${CRM_SEARCH_ATTRIBUTE}]`,
35
+ );
36
+ if (field) {
37
+ field.focus();
38
+ return;
39
+ }
40
+ openCommandMenu();
41
+ }
@@ -0,0 +1,40 @@
1
+ export const CRM_SIDEBAR_COLLAPSE_KEY = "crm.sidebar.collapsed";
2
+
3
+ /** The slice of `window.localStorage` the sidebar depends on. */
4
+ export interface CollapseStorage {
5
+ getItem(key: string): string | null;
6
+ setItem(key: string, value: string): void;
7
+ }
8
+
9
+ export function getCollapseStorage(): CollapseStorage | null {
10
+ if (typeof window === "undefined") return null;
11
+ try {
12
+ return window.localStorage;
13
+ } catch {
14
+ // Storage can be disabled outright (private mode, blocked cookies).
15
+ return null;
16
+ }
17
+ }
18
+
19
+ export function readSidebarCollapsed(
20
+ storage: CollapseStorage | null | undefined,
21
+ ): boolean {
22
+ if (!storage) return false;
23
+ try {
24
+ return storage.getItem(CRM_SIDEBAR_COLLAPSE_KEY) === "1";
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ export function writeSidebarCollapsed(
31
+ storage: CollapseStorage | null | undefined,
32
+ collapsed: boolean,
33
+ ): void {
34
+ if (!storage) return;
35
+ try {
36
+ storage.setItem(CRM_SIDEBAR_COLLAPSE_KEY, collapsed ? "1" : "0");
37
+ } catch {
38
+ // Quota or a blocked store — the preference simply does not persist.
39
+ }
40
+ }