@agent-native/core 0.90.11 → 0.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +22 -0
  3. package/corpus/core/docs/content/toolkit-settings.mdx +56 -3
  4. package/corpus/core/package.json +2 -1
  5. package/corpus/core/src/agent/run-ownership.ts +29 -1
  6. package/corpus/core/src/changelog/parse.ts +78 -0
  7. package/corpus/core/src/chat-threads/schema.ts +43 -0
  8. package/corpus/core/src/chat-threads/store.ts +144 -12
  9. package/corpus/core/src/cli/workspace-dev.ts +25 -5
  10. package/corpus/core/src/client/AgentPanel.tsx +79 -14
  11. package/corpus/core/src/client/AssistantChat.tsx +359 -425
  12. package/corpus/core/src/client/analytics-session.ts +70 -0
  13. package/corpus/core/src/client/analytics.ts +175 -49
  14. package/corpus/core/src/client/chat/message-components.tsx +22 -2
  15. package/corpus/core/src/client/components/ui/message-scroller.tsx +131 -0
  16. package/corpus/core/src/client/conversation/AgentConversation.tsx +63 -41
  17. package/corpus/core/src/client/error-capture.ts +616 -0
  18. package/corpus/core/src/client/index.ts +12 -0
  19. package/corpus/core/src/client/session-replay.ts +57 -7
  20. package/corpus/core/src/client/settings/SettingsPanel.tsx +123 -1
  21. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +292 -45
  22. package/corpus/core/src/client/settings/index.ts +1 -0
  23. package/corpus/core/src/client/sharing/ShareButton.tsx +14 -11
  24. package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -10
  25. package/corpus/core/src/mcp/builtin-tools.ts +29 -2
  26. package/corpus/core/src/server/agent-chat-plugin.ts +174 -79
  27. package/corpus/core/src/server/onboarding-html.ts +28 -16
  28. package/corpus/core/src/sharing/actions/list-resource-shares.ts +45 -1
  29. package/corpus/core/src/sharing/actions/share-resource.ts +9 -0
  30. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  31. package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  32. package/corpus/core/src/vite/client.ts +217 -35
  33. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +8 -7
  34. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +14 -10
  35. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +15 -15
  36. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +1 -0
  37. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -22
  38. package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +114 -61
  39. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -0
  40. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +7 -7
  41. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +1 -0
  42. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +1 -1
  43. package/corpus/templates/analytics/.builder/skills/bigquery/SKILL.md +17 -17
  44. package/corpus/templates/analytics/.builder/skills/gcloud/SKILL.md +5 -5
  45. package/corpus/templates/analytics/.builder/skills/github/SKILL.md +7 -7
  46. package/corpus/templates/analytics/.builder/skills/gong/SKILL.md +12 -12
  47. package/corpus/templates/analytics/.builder/skills/grafana/SKILL.md +7 -7
  48. package/corpus/templates/analytics/.builder/skills/sentry/SKILL.md +7 -7
  49. package/corpus/templates/analytics/.builder/skills/slack/SKILL.md +6 -6
  50. package/corpus/templates/analytics/.builder/skills/stripe/SKILL.md +7 -7
  51. package/corpus/templates/analytics/AGENTS.md +47 -2
  52. package/corpus/templates/analytics/actions/add-status-page-monitor.ts +36 -0
  53. package/corpus/templates/analytics/actions/capture-test-error.ts +33 -0
  54. package/corpus/templates/analytics/actions/delete-monitor.ts +24 -0
  55. package/corpus/templates/analytics/actions/delete-status-page.ts +23 -0
  56. package/corpus/templates/analytics/actions/get-error-issue.ts +36 -0
  57. package/corpus/templates/analytics/actions/get-monitor-stats.ts +55 -0
  58. package/corpus/templates/analytics/actions/get-monitor.ts +27 -0
  59. package/corpus/templates/analytics/actions/get-public-status-page.ts +41 -0
  60. package/corpus/templates/analytics/actions/get-status-page.ts +29 -0
  61. package/corpus/templates/analytics/actions/list-error-issues.ts +40 -0
  62. package/corpus/templates/analytics/actions/list-monitors.ts +21 -0
  63. package/corpus/templates/analytics/actions/list-status-pages.ts +21 -0
  64. package/corpus/templates/analytics/actions/match-error-issues.ts +51 -0
  65. package/corpus/templates/analytics/actions/navigate.ts +58 -4
  66. package/corpus/templates/analytics/actions/remove-status-page-monitor.ts +22 -0
  67. package/corpus/templates/analytics/actions/reorder-status-page-monitors.ts +25 -0
  68. package/corpus/templates/analytics/actions/resolve-error-issue.ts +38 -0
  69. package/corpus/templates/analytics/actions/run-monitor-check.ts +23 -0
  70. package/corpus/templates/analytics/actions/save-analysis.ts +1 -0
  71. package/corpus/templates/analytics/actions/save-monitor.ts +145 -0
  72. package/corpus/templates/analytics/actions/save-status-page.ts +47 -0
  73. package/corpus/templates/analytics/actions/view-screen.ts +212 -0
  74. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  75. package/corpus/templates/analytics/app/components/layout/Layout.tsx +10 -1
  76. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +134 -39
  77. package/corpus/templates/analytics/app/components/monitoring/PublicStatusView.tsx +351 -0
  78. package/corpus/templates/analytics/app/components/monitoring/ResponseTimeChart.tsx +174 -0
  79. package/corpus/templates/analytics/app/components/monitoring/UptimeStatCards.tsx +85 -0
  80. package/corpus/templates/analytics/app/components/monitoring/UptimeTimelineBars.tsx +106 -0
  81. package/corpus/templates/analytics/app/components/monitoring/chart-utils.ts +101 -0
  82. package/corpus/templates/analytics/app/components/monitoring/index.ts +28 -0
  83. package/corpus/templates/analytics/app/components/monitoring/types.ts +42 -0
  84. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +34 -0
  85. package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -0
  86. package/corpus/templates/analytics/app/i18n-data.ts +33 -0
  87. package/corpus/templates/analytics/app/pages/DataSources.tsx +28 -0
  88. package/corpus/templates/analytics/app/pages/Settings.tsx +77 -9
  89. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -3
  90. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +7 -0
  91. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +205 -0
  92. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +100 -0
  93. package/corpus/templates/analytics/app/pages/monitoring/UptimePanel.tsx +475 -0
  94. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +372 -0
  95. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +360 -0
  96. package/corpus/templates/analytics/app/pages/monitoring/errors/Sparkline.tsx +55 -0
  97. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +106 -0
  98. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +75 -0
  99. package/corpus/templates/analytics/app/pages/monitoring/errors/utils.ts +132 -0
  100. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +535 -0
  101. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +1026 -0
  102. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorList.tsx +311 -0
  103. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +235 -0
  104. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPageEditor.tsx +661 -0
  105. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPagesView.tsx +355 -0
  106. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/i18n.ts +106 -0
  107. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/slug.ts +24 -0
  108. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/types.ts +20 -0
  109. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-summary.ts +87 -0
  110. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +161 -0
  111. package/corpus/templates/analytics/app/pages/monitoring/uptime/utils.ts +210 -0
  112. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +76 -1
  113. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +74 -9
  114. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +197 -20
  115. package/corpus/templates/analytics/app/root.tsx +33 -1
  116. package/corpus/templates/analytics/app/routes/monitoring._index.tsx +10 -0
  117. package/corpus/templates/analytics/app/routes/status.$slug.tsx +118 -0
  118. package/corpus/templates/analytics/changelog/2026-07-08-add-uptime-monitors-that-ping-your-urls-and-alert-you-when-t.md +6 -0
  119. package/corpus/templates/analytics/changelog/2026-07-08-analytics-chats-keep-large-data-dictionaries-compact-so-prov.md +6 -0
  120. package/corpus/templates/analytics/changelog/2026-07-08-create-public-status-pages-that-share-the-live-health-of-cho.md +6 -0
  121. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-extension-panels-use-clearer-open-copy.md +6 -0
  122. package/corpus/templates/analytics/changelog/2026-07-08-error-capture.md +6 -0
  123. package/corpus/templates/analytics/changelog/2026-07-08-fixed-single-panel-dashboard-drags-so-they-no-longer-rewrite.md +6 -0
  124. package/corpus/templates/analytics/changelog/2026-07-08-jump-straight-from-an-error-in-a-session-recording-to-its-fu.md +6 -0
  125. package/corpus/templates/analytics/changelog/2026-07-08-monitor-detail-and-list-now-show-colorful-uptime-timelines-r.md +6 -0
  126. package/corpus/templates/analytics/changelog/2026-07-08-pinned-ask-chats-now-show-a-pin-icon-in-the-sidebar.md +6 -0
  127. package/corpus/templates/analytics/changelog/2026-07-08-publish-a-public-uptime-status-page-with-colorful-uptime-tim.md +6 -0
  128. package/corpus/templates/analytics/changelog/2026-07-08-sessions-now-hide-visitor-emails-in-demo-mode-and-show-clean.md +6 -0
  129. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +5 -0
  130. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +5 -0
  131. package/corpus/templates/analytics/changelog/2026-07-08-the-uptime-monitor-list-now-has-a-current-status-overview-wi.md +6 -0
  132. package/corpus/templates/analytics/docs/error-capture.md +172 -0
  133. package/corpus/templates/analytics/docs/uptime-monitoring.md +150 -0
  134. package/corpus/templates/analytics/server/db/schema-errors.ts +150 -0
  135. package/corpus/templates/analytics/server/db/schema-monitoring.ts +177 -0
  136. package/corpus/templates/analytics/server/db/schema.ts +6 -0
  137. package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +123 -0
  138. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +41 -0
  139. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +55 -12
  140. package/corpus/templates/analytics/server/lib/error-capture.ts +1331 -0
  141. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +47 -1
  142. package/corpus/templates/analytics/server/lib/monitor-stats.ts +494 -0
  143. package/corpus/templates/analytics/server/lib/status-pages.ts +798 -0
  144. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +1665 -0
  145. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -37
  146. package/corpus/templates/analytics/server/plugins/auth.ts +11 -1
  147. package/corpus/templates/analytics/server/plugins/db.ts +408 -0
  148. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +63 -0
  149. package/corpus/templates/assets/app/routes/_index.tsx +1 -1
  150. package/corpus/templates/assets/app/routes/settings.tsx +22 -1
  151. package/corpus/templates/assets/changelog/2026-07-08-recent-drafts-keep-comfortable-side-padding.md +6 -0
  152. package/corpus/templates/assets/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  153. package/corpus/templates/brain/app/routes/settings.tsx +43 -5
  154. package/corpus/templates/brain/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  155. package/corpus/templates/calendar/app/pages/Settings.tsx +44 -6
  156. package/corpus/templates/calendar/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  157. package/corpus/templates/chat/app/routes/settings.tsx +16 -1
  158. package/corpus/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  159. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +79 -37
  160. package/corpus/templates/clips/app/components/player/player-controls.tsx +35 -0
  161. package/corpus/templates/clips/app/components/player/video-player.tsx +162 -18
  162. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +63 -6
  163. package/corpus/templates/clips/app/routes/record.tsx +18 -1
  164. package/corpus/templates/clips/changelog/2026-07-07-shared-clips-start-playback-reliably-from-the-first-play-click.md +6 -0
  165. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-camera-and-microphone-menus-now-show-which-.md +6 -0
  166. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-copies-new-clip-links-after-saving.md +6 -0
  167. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-recordings-now-continue-after-granting-came.md +6 -0
  168. package/corpus/templates/clips/changelog/2026-07-08-mobile-shared-clips-now-have-loom-style-tap-and-skip-controls.md +6 -0
  169. package/corpus/templates/clips/changelog/2026-07-08-recordings-and-uploads-copy-links-after-saving.md +6 -0
  170. package/corpus/templates/clips/changelog/2026-07-08-settings-are-cleaner-searchable-and-collapse-s3-with-builder.md +5 -0
  171. package/corpus/templates/clips/changelog/2026-07-08-transcripts-are-more-reliable-for-longer-desktop-recordings-.md +6 -0
  172. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -0
  173. package/corpus/templates/clips/chrome-extension/src/background.ts +162 -13
  174. package/corpus/templates/clips/chrome-extension/src/content-script.ts +14 -0
  175. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +36 -0
  176. package/corpus/templates/clips/chrome-extension/src/overlay.ts +99 -25
  177. package/corpus/templates/clips/chrome-extension/src/permission.ts +63 -14
  178. package/corpus/templates/clips/chrome-extension/src/popup.ts +123 -23
  179. package/corpus/templates/content/actions/_builder-cms-read-client.ts +47 -0
  180. package/corpus/templates/content/actions/_database-source-utils.ts +33 -4
  181. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +214 -29
  182. package/corpus/templates/content/actions/attach-content-database-source.ts +3 -0
  183. package/corpus/templates/content/actions/change-content-database-source-role.ts +1 -0
  184. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +42 -32
  185. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +352 -81
  186. package/corpus/templates/content/app/components/editor/database/settings.tsx +49 -24
  187. package/corpus/templates/content/app/routes/_app.settings.tsx +19 -1
  188. package/corpus/templates/content/changelog/2026-07-07-builder-source-tag-fields-now-import-as-multi-select.md +6 -0
  189. package/corpus/templates/content/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  190. package/corpus/templates/content/shared/api.ts +3 -0
  191. package/corpus/templates/content/shared/properties.ts +42 -0
  192. package/corpus/templates/design/actions/generate-design.ts +3 -0
  193. package/corpus/templates/design/actions/get-design-system.ts +147 -1
  194. package/corpus/templates/design/app/routes/settings.tsx +16 -1
  195. package/corpus/templates/design/changelog/2026-07-08-design-generation-now-follows-hydrated-builder-design-system.md +6 -0
  196. package/corpus/templates/design/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  197. package/corpus/templates/dispatch/app/routes/settings.tsx +23 -2
  198. package/corpus/templates/dispatch/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  199. package/corpus/templates/forms/app/routes/_app.settings.tsx +16 -1
  200. package/corpus/templates/forms/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  201. package/corpus/templates/macros/app/routes/settings.tsx +16 -1
  202. package/corpus/templates/macros/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  203. package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +1 -1
  204. package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +1 -1
  205. package/corpus/templates/mail/app/pages/SettingsPage.tsx +123 -105
  206. package/corpus/templates/mail/changelog/2026-07-08-settings-redesigned-with-a-consistent-searchable-layout.md +5 -0
  207. package/corpus/templates/plan/app/routes/settings.tsx +23 -2
  208. package/corpus/templates/plan/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  209. package/corpus/templates/slides/actions/add-slide.ts +1 -0
  210. package/corpus/templates/slides/actions/get-design-system.ts +147 -1
  211. package/corpus/templates/slides/app/pages/Index.tsx +46 -1
  212. package/corpus/templates/slides/app/routes/settings.tsx +16 -1
  213. package/corpus/templates/slides/changelog/2026-07-08-deck-generation-now-follows-hydrated-builder-design-system-g.md +6 -0
  214. package/corpus/templates/slides/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  215. package/dist/agent/run-ownership.d.ts +6 -0
  216. package/dist/agent/run-ownership.d.ts.map +1 -1
  217. package/dist/agent/run-ownership.js +11 -1
  218. package/dist/agent/run-ownership.js.map +1 -1
  219. package/dist/changelog/parse.d.ts +7 -0
  220. package/dist/changelog/parse.d.ts.map +1 -1
  221. package/dist/changelog/parse.js +64 -0
  222. package/dist/changelog/parse.js.map +1 -1
  223. package/dist/chat-threads/schema.d.ts +445 -0
  224. package/dist/chat-threads/schema.d.ts.map +1 -0
  225. package/dist/chat-threads/schema.js +39 -0
  226. package/dist/chat-threads/schema.js.map +1 -0
  227. package/dist/chat-threads/store.d.ts +12 -0
  228. package/dist/chat-threads/store.d.ts.map +1 -1
  229. package/dist/chat-threads/store.js +99 -10
  230. package/dist/chat-threads/store.js.map +1 -1
  231. package/dist/cli/workspace-dev.d.ts.map +1 -1
  232. package/dist/cli/workspace-dev.js +20 -5
  233. package/dist/cli/workspace-dev.js.map +1 -1
  234. package/dist/client/AgentPanel.d.ts.map +1 -1
  235. package/dist/client/AgentPanel.js +35 -7
  236. package/dist/client/AgentPanel.js.map +1 -1
  237. package/dist/client/AssistantChat.d.ts.map +1 -1
  238. package/dist/client/AssistantChat.js +105 -183
  239. package/dist/client/AssistantChat.js.map +1 -1
  240. package/dist/client/analytics-session.d.ts +3 -0
  241. package/dist/client/analytics-session.d.ts.map +1 -0
  242. package/dist/client/analytics-session.js +66 -0
  243. package/dist/client/analytics-session.js.map +1 -0
  244. package/dist/client/analytics.d.ts +32 -0
  245. package/dist/client/analytics.d.ts.map +1 -1
  246. package/dist/client/analytics.js +116 -46
  247. package/dist/client/analytics.js.map +1 -1
  248. package/dist/client/chat/message-components.d.ts +2 -1
  249. package/dist/client/chat/message-components.d.ts.map +1 -1
  250. package/dist/client/chat/message-components.js +16 -6
  251. package/dist/client/chat/message-components.js.map +1 -1
  252. package/dist/client/components/ui/message-scroller.d.ts +16 -0
  253. package/dist/client/components/ui/message-scroller.d.ts.map +1 -0
  254. package/dist/client/components/ui/message-scroller.js +25 -0
  255. package/dist/client/components/ui/message-scroller.js.map +1 -0
  256. package/dist/client/conversation/AgentConversation.d.ts +1 -1
  257. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  258. package/dist/client/conversation/AgentConversation.js +3 -8
  259. package/dist/client/conversation/AgentConversation.js.map +1 -1
  260. package/dist/client/error-capture.d.ts +99 -0
  261. package/dist/client/error-capture.d.ts.map +1 -0
  262. package/dist/client/error-capture.js +437 -0
  263. package/dist/client/error-capture.js.map +1 -0
  264. package/dist/client/index.d.ts +2 -2
  265. package/dist/client/index.d.ts.map +1 -1
  266. package/dist/client/index.js +3 -1
  267. package/dist/client/index.js.map +1 -1
  268. package/dist/client/session-replay.d.ts +22 -0
  269. package/dist/client/session-replay.d.ts.map +1 -1
  270. package/dist/client/session-replay.js +45 -4
  271. package/dist/client/session-replay.js.map +1 -1
  272. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  273. package/dist/client/settings/SettingsPanel.js +100 -5
  274. package/dist/client/settings/SettingsPanel.js.map +1 -1
  275. package/dist/client/settings/SettingsTabsPage.d.ts +49 -1
  276. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  277. package/dist/client/settings/SettingsTabsPage.js +131 -19
  278. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  279. package/dist/client/settings/index.d.ts +1 -1
  280. package/dist/client/settings/index.d.ts.map +1 -1
  281. package/dist/client/settings/index.js.map +1 -1
  282. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  283. package/dist/client/sharing/ShareButton.js +13 -8
  284. package/dist/client/sharing/ShareButton.js.map +1 -1
  285. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  286. package/dist/client/sharing/ShareDialog.js +12 -7
  287. package/dist/client/sharing/ShareDialog.js.map +1 -1
  288. package/dist/collab/struct-routes.d.ts +1 -1
  289. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  290. package/dist/mcp/builtin-tools.js +21 -2
  291. package/dist/mcp/builtin-tools.js.map +1 -1
  292. package/dist/notifications/routes.d.ts +1 -1
  293. package/dist/org/schema.d.ts +15 -15
  294. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  295. package/dist/resources/handlers.d.ts +3 -3
  296. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  297. package/dist/server/agent-chat-plugin.js +109 -73
  298. package/dist/server/agent-chat-plugin.js.map +1 -1
  299. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  300. package/dist/server/onboarding-html.d.ts.map +1 -1
  301. package/dist/server/onboarding-html.js +28 -16
  302. package/dist/server/onboarding-html.js.map +1 -1
  303. package/dist/sharing/actions/list-resource-shares.js +30 -1
  304. package/dist/sharing/actions/list-resource-shares.js.map +1 -1
  305. package/dist/sharing/actions/share-resource.js +6 -0
  306. package/dist/sharing/actions/share-resource.js.map +1 -1
  307. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  308. package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  309. package/dist/vite/client.d.ts.map +1 -1
  310. package/dist/vite/client.js +192 -30
  311. package/dist/vite/client.js.map +1 -1
  312. package/docs/content/toolkit-settings.mdx +56 -3
  313. package/package.json +2 -1
  314. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  315. package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
@@ -3525,7 +3525,7 @@ function DatabaseTableView({
3525
3525
 
3526
3526
  async function setSelectedPropertyValue(
3527
3527
  property: DocumentProperty,
3528
- value: DocumentPropertyValue,
3528
+ operation: DatabaseBulkPropertyValueOperation,
3529
3529
  ) {
3530
3530
  if (selectedItems.length === 0) return;
3531
3531
  const selectedSnapshot = selectedItems;
@@ -3537,7 +3537,7 @@ function DatabaseTableView({
3537
3537
  await setProperty.mutateAsync({
3538
3538
  documentId: item.document.id,
3539
3539
  propertyId: property.definition.id,
3540
- value,
3540
+ value: databaseBulkPropertyValueForItem(item, property, operation),
3541
3541
  });
3542
3542
  updatedCount += 1;
3543
3543
  } catch {
@@ -3575,6 +3575,7 @@ function DatabaseTableView({
3575
3575
  selectedCount={selectedCount}
3576
3576
  canEdit={canEdit}
3577
3577
  properties={bulkEditableProperties}
3578
+ selectedItems={selectedItems}
3578
3579
  duplicateDisabled={
3579
3580
  isDuplicatingSelected ||
3580
3581
  duplicateItems.isPending ||
@@ -5923,6 +5924,25 @@ function shouldPreselectDetailField(
5923
5924
  return DETAIL_FIELD_NAME_HINTS.some((hint) => key.includes(hint));
5924
5925
  }
5925
5926
 
5927
+ function sourceFieldIconType(
5928
+ sourceFieldType: string,
5929
+ ): DocumentPropertyType | "name" {
5930
+ const normalized = sourceFieldType.trim().toLowerCase();
5931
+ if (normalized === "number") return "number";
5932
+ if (normalized === "datetime" || normalized === "date") return "date";
5933
+ if (normalized === "url") return "url";
5934
+ if (normalized === "boolean" || normalized === "checkbox") return "checkbox";
5935
+ if (
5936
+ normalized === "list" ||
5937
+ normalized === "array" ||
5938
+ normalized === "tags" ||
5939
+ normalized === "multi_select"
5940
+ ) {
5941
+ return "multi_select";
5942
+ }
5943
+ return "text";
5944
+ }
5945
+
5926
5946
  function SourceDetailsFieldPicker({
5927
5947
  documentId,
5928
5948
  source,
@@ -6024,31 +6044,37 @@ function SourceDetailsFieldPicker({
6024
6044
  </div>
6025
6045
  ) : (
6026
6046
  <div className="grid min-w-0 gap-1">
6027
- {fields.map((field) => (
6028
- <button
6029
- key={field.id}
6030
- type="button"
6031
- className="flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
6032
- onClick={() => toggleField(field.id)}
6033
- >
6034
- <span
6035
- className={cn(
6036
- "flex size-4 shrink-0 items-center justify-center rounded border",
6037
- selected.has(field.id)
6038
- ? "border-[#2383e2] bg-[#2383e2] text-white"
6039
- : "border-muted-foreground/40 text-transparent",
6040
- )}
6047
+ {fields.map((field) => {
6048
+ const iconType = sourceFieldIconType(field.sourceFieldType);
6049
+ const Icon =
6050
+ iconType === "name" ? IconFileText : TYPE_ICONS[iconType];
6051
+ return (
6052
+ <button
6053
+ key={field.id}
6054
+ type="button"
6055
+ className="flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
6056
+ onClick={() => toggleField(field.id)}
6041
6057
  >
6042
- <IconCheck className="size-3" />
6043
- </span>
6044
- <span className="min-w-0 flex-1 truncate">
6045
- {field.sourceFieldLabel}
6046
- </span>
6047
- <span className="shrink-0 text-[11px] text-muted-foreground">
6048
- {field.sourceFieldType}
6049
- </span>
6050
- </button>
6051
- ))}
6058
+ <span
6059
+ className={cn(
6060
+ "flex size-4 shrink-0 items-center justify-center rounded border",
6061
+ selected.has(field.id)
6062
+ ? "border-[#2383e2] bg-[#2383e2] text-white"
6063
+ : "border-muted-foreground/40 text-transparent",
6064
+ )}
6065
+ >
6066
+ <IconCheck className="size-3" />
6067
+ </span>
6068
+ <Icon className="size-3.5 shrink-0 text-muted-foreground" />
6069
+ <span className="min-w-0 flex-1 truncate">
6070
+ {field.sourceFieldLabel}
6071
+ </span>
6072
+ <span className="shrink-0 text-[11px] text-muted-foreground">
6073
+ {field.sourceFieldType}
6074
+ </span>
6075
+ </button>
6076
+ );
6077
+ })}
6052
6078
  </div>
6053
6079
  )}
6054
6080
  <div className="flex justify-end gap-2">
@@ -11220,6 +11246,156 @@ function databaseItemPropertyById(
11220
11246
  );
11221
11247
  }
11222
11248
 
11249
+ export type DatabaseBulkMultiSelectOperation =
11250
+ | { kind: "multi_select_add"; optionIds: string[] }
11251
+ | { kind: "multi_select_remove"; optionIds: string[] }
11252
+ | {
11253
+ kind: "multi_select_batch";
11254
+ addOptionIds: string[];
11255
+ removeOptionIds: string[];
11256
+ };
11257
+
11258
+ export type DatabaseBulkPropertyValueOperation =
11259
+ | { kind: "set"; value: DocumentPropertyValue }
11260
+ | DatabaseBulkMultiSelectOperation;
11261
+
11262
+ export function databaseBulkPropertyValueForItem(
11263
+ item: ContentDatabaseItem,
11264
+ property: DocumentProperty,
11265
+ operation: DatabaseBulkPropertyValueOperation,
11266
+ ): DocumentPropertyValue {
11267
+ if (operation.kind === "set") return operation.value;
11268
+ const itemProperty = databaseItemPropertyById(
11269
+ item,
11270
+ [property],
11271
+ property.definition.id,
11272
+ );
11273
+ return databaseBulkMultiSelectValueAfterOperation(
11274
+ itemProperty?.value ?? null,
11275
+ operation,
11276
+ );
11277
+ }
11278
+
11279
+ function databaseBulkMultiSelectValue(value: DocumentPropertyValue) {
11280
+ return Array.isArray(value)
11281
+ ? value.filter((item): item is string => typeof item === "string")
11282
+ : [];
11283
+ }
11284
+
11285
+ export function databaseBulkMultiSelectValueAfterOperation(
11286
+ value: DocumentPropertyValue,
11287
+ operation: DatabaseBulkMultiSelectOperation,
11288
+ ) {
11289
+ const current = databaseBulkMultiSelectValue(value);
11290
+ const addOptionIds =
11291
+ operation.kind === "multi_select_batch"
11292
+ ? operation.addOptionIds
11293
+ : operation.kind === "multi_select_add"
11294
+ ? operation.optionIds
11295
+ : [];
11296
+ const removeOptionIds =
11297
+ operation.kind === "multi_select_batch"
11298
+ ? operation.removeOptionIds
11299
+ : operation.kind === "multi_select_remove"
11300
+ ? operation.optionIds
11301
+ : [];
11302
+ const removedIds = new Set(removeOptionIds);
11303
+ const kept = current.filter((optionId) => !removedIds.has(optionId));
11304
+ const keptIds = new Set(kept);
11305
+ return [
11306
+ ...kept,
11307
+ ...Array.from(new Set(addOptionIds)).filter(
11308
+ (optionId) => !keptIds.has(optionId),
11309
+ ),
11310
+ ];
11311
+ }
11312
+
11313
+ export function databaseBulkMultiSelectOptionPresence(
11314
+ items: ContentDatabaseItem[],
11315
+ property: DocumentProperty,
11316
+ optionId: string,
11317
+ operation?: DatabaseBulkMultiSelectOperation,
11318
+ ) {
11319
+ const values = items.map((item) =>
11320
+ operation
11321
+ ? databaseBulkMultiSelectValueAfterOperation(
11322
+ databaseItemPropertyById(item, [property], property.definition.id)
11323
+ ?.value ?? null,
11324
+ operation,
11325
+ )
11326
+ : databaseBulkMultiSelectValue(
11327
+ databaseItemPropertyById(item, [property], property.definition.id)
11328
+ ?.value ?? null,
11329
+ ),
11330
+ );
11331
+ return {
11332
+ presentInAny: values.some((value) => value.includes(optionId)),
11333
+ presentInAll:
11334
+ values.length > 0 && values.every((value) => value.includes(optionId)),
11335
+ };
11336
+ }
11337
+
11338
+ export function databaseBulkMultiSelectToggleOperation(
11339
+ items: ContentDatabaseItem[],
11340
+ property: DocumentProperty,
11341
+ optionId: string,
11342
+ operation: DatabaseBulkMultiSelectOperation = {
11343
+ kind: "multi_select_batch",
11344
+ addOptionIds: [],
11345
+ removeOptionIds: [],
11346
+ },
11347
+ ): DatabaseBulkMultiSelectOperation {
11348
+ const presence = databaseBulkMultiSelectOptionPresence(
11349
+ items,
11350
+ property,
11351
+ optionId,
11352
+ operation,
11353
+ );
11354
+ const addOptionIds =
11355
+ operation.kind === "multi_select_batch"
11356
+ ? new Set(operation.addOptionIds)
11357
+ : operation.kind === "multi_select_add"
11358
+ ? new Set(operation.optionIds)
11359
+ : new Set<string>();
11360
+ const removeOptionIds =
11361
+ operation.kind === "multi_select_batch"
11362
+ ? new Set(operation.removeOptionIds)
11363
+ : operation.kind === "multi_select_remove"
11364
+ ? new Set(operation.optionIds)
11365
+ : new Set<string>();
11366
+ if (presence.presentInAll) {
11367
+ addOptionIds.delete(optionId);
11368
+ removeOptionIds.add(optionId);
11369
+ } else {
11370
+ removeOptionIds.delete(optionId);
11371
+ addOptionIds.add(optionId);
11372
+ }
11373
+ return {
11374
+ kind: "multi_select_batch",
11375
+ addOptionIds: Array.from(addOptionIds),
11376
+ removeOptionIds: Array.from(removeOptionIds),
11377
+ };
11378
+ }
11379
+
11380
+ function databaseBulkMultiSelectOperationHasChanges(
11381
+ operation: DatabaseBulkMultiSelectOperation,
11382
+ ) {
11383
+ return operation.kind === "multi_select_batch"
11384
+ ? operation.addOptionIds.length > 0 || operation.removeOptionIds.length > 0
11385
+ : operation.optionIds.length > 0;
11386
+ }
11387
+
11388
+ export function databaseBulkMultiSelectFilteredOptions(
11389
+ options: DocumentPropertyOption[],
11390
+ query: string,
11391
+ ) {
11392
+ const normalizedQuery = query.trim().toLowerCase();
11393
+ if (!normalizedQuery) return options;
11394
+ return options.filter((option) =>
11395
+ option.name.trim().toLowerCase().includes(normalizedQuery),
11396
+ );
11397
+ }
11398
+
11223
11399
  function databaseTimelineRangeLabel(days: Date[]) {
11224
11400
  const first = days[0] ?? new Date();
11225
11401
  const last = days[days.length - 1] ?? first;
@@ -12022,6 +12198,7 @@ function DatabaseSelectionBar({
12022
12198
  selectedCount,
12023
12199
  canEdit,
12024
12200
  properties,
12201
+ selectedItems,
12025
12202
  duplicateDisabled,
12026
12203
  deleteDisabled,
12027
12204
  updateDisabled,
@@ -12033,13 +12210,14 @@ function DatabaseSelectionBar({
12033
12210
  selectedCount: number;
12034
12211
  canEdit: boolean;
12035
12212
  properties: DocumentProperty[];
12213
+ selectedItems: ContentDatabaseItem[];
12036
12214
  duplicateDisabled: boolean;
12037
12215
  deleteDisabled: boolean;
12038
12216
  updateDisabled: boolean;
12039
12217
  onClearSelection: () => void;
12040
12218
  onSetPropertyValue: (
12041
12219
  property: DocumentProperty,
12042
- value: DocumentPropertyValue,
12220
+ operation: DatabaseBulkPropertyValueOperation,
12043
12221
  ) => Promise<void>;
12044
12222
  onDuplicateSelected: () => void;
12045
12223
  onDeleteSelected: () => void;
@@ -12055,6 +12233,7 @@ function DatabaseSelectionBar({
12055
12233
  <DatabaseBulkEditPopover
12056
12234
  properties={properties}
12057
12235
  selectedCount={selectedCount}
12236
+ selectedItems={selectedItems}
12058
12237
  disabled={updateDisabled || properties.length === 0}
12059
12238
  onSetPropertyValue={onSetPropertyValue}
12060
12239
  />
@@ -12099,15 +12278,17 @@ function DatabaseSelectionBar({
12099
12278
  function DatabaseBulkEditPopover({
12100
12279
  properties,
12101
12280
  selectedCount,
12281
+ selectedItems,
12102
12282
  disabled,
12103
12283
  onSetPropertyValue,
12104
12284
  }: {
12105
12285
  properties: DocumentProperty[];
12106
12286
  selectedCount: number;
12287
+ selectedItems: ContentDatabaseItem[];
12107
12288
  disabled: boolean;
12108
12289
  onSetPropertyValue: (
12109
12290
  property: DocumentProperty,
12110
- value: DocumentPropertyValue,
12291
+ operation: DatabaseBulkPropertyValueOperation,
12111
12292
  ) => Promise<void>;
12112
12293
  }) {
12113
12294
  const [open, setOpen] = useState(false);
@@ -12128,9 +12309,9 @@ function DatabaseBulkEditPopover({
12128
12309
 
12129
12310
  async function applyValue(
12130
12311
  property: DocumentProperty,
12131
- value: DocumentPropertyValue,
12312
+ operation: DatabaseBulkPropertyValueOperation,
12132
12313
  ) {
12133
- await onSetPropertyValue(property, value);
12314
+ await onSetPropertyValue(property, operation);
12134
12315
  setOpen(false);
12135
12316
  }
12136
12317
 
@@ -12181,6 +12362,7 @@ function DatabaseBulkEditPopover({
12181
12362
  {selectedProperty ? (
12182
12363
  <DatabaseBulkPropertyValueEditor
12183
12364
  property={selectedProperty}
12365
+ selectedItems={selectedItems}
12184
12366
  disabled={disabled}
12185
12367
  onApply={(value) => applyValue(selectedProperty, value)}
12186
12368
  onCancel={() => setOpen(false)}
@@ -12200,13 +12382,15 @@ function DatabaseBulkEditPopover({
12200
12382
 
12201
12383
  function DatabaseBulkPropertyValueEditor({
12202
12384
  property,
12385
+ selectedItems,
12203
12386
  disabled,
12204
12387
  onApply,
12205
12388
  onCancel,
12206
12389
  }: {
12207
12390
  property: DocumentProperty;
12391
+ selectedItems: ContentDatabaseItem[];
12208
12392
  disabled: boolean;
12209
- onApply: (value: DocumentPropertyValue) => Promise<void>;
12393
+ onApply: (operation: DatabaseBulkPropertyValueOperation) => Promise<void>;
12210
12394
  onCancel: () => void;
12211
12395
  }) {
12212
12396
  const type = property.definition.type;
@@ -12220,7 +12404,7 @@ function DatabaseBulkPropertyValueEditor({
12220
12404
  variant="secondary"
12221
12405
  className="justify-start"
12222
12406
  disabled={disabled}
12223
- onClick={() => void onApply(true)}
12407
+ onClick={() => void onApply({ kind: "set", value: true })}
12224
12408
  >
12225
12409
  <IconCheck className="mr-1.5 size-3.5" />
12226
12410
  Checked
@@ -12231,7 +12415,7 @@ function DatabaseBulkPropertyValueEditor({
12231
12415
  variant="secondary"
12232
12416
  className="justify-start"
12233
12417
  disabled={disabled}
12234
- onClick={() => void onApply(false)}
12418
+ onClick={() => void onApply({ kind: "set", value: false })}
12235
12419
  >
12236
12420
  <IconMinus className="mr-1.5 size-3.5" />
12237
12421
  Unchecked
@@ -12242,7 +12426,7 @@ function DatabaseBulkPropertyValueEditor({
12242
12426
  variant="ghost"
12243
12427
  className="justify-start"
12244
12428
  disabled={disabled}
12245
- onClick={() => void onApply(null)}
12429
+ onClick={() => void onApply({ kind: "set", value: null })}
12246
12430
  >
12247
12431
  {dbText("clearValue")}
12248
12432
  </Button>
@@ -12254,6 +12438,7 @@ function DatabaseBulkPropertyValueEditor({
12254
12438
  return (
12255
12439
  <DatabaseBulkOptionValueEditor
12256
12440
  property={property}
12441
+ selectedItems={selectedItems}
12257
12442
  disabled={disabled}
12258
12443
  onApply={onApply}
12259
12444
  onCancel={onCancel}
@@ -12289,7 +12474,7 @@ function DatabaseBulkScalarValueEditor({
12289
12474
  }: {
12290
12475
  property: DocumentProperty;
12291
12476
  disabled: boolean;
12292
- onApply: (value: DocumentPropertyValue) => Promise<void>;
12477
+ onApply: (operation: DatabaseBulkPropertyValueOperation) => Promise<void>;
12293
12478
  onCancel: () => void;
12294
12479
  }) {
12295
12480
  const type = property.definition.type;
@@ -12314,7 +12499,7 @@ function DatabaseBulkScalarValueEditor({
12314
12499
  onSubmit={(event) => {
12315
12500
  event.preventDefault();
12316
12501
  if (!valueState.isValid) return;
12317
- void onApply(valueState.value);
12502
+ void onApply({ kind: "set", value: valueState.value });
12318
12503
  }}
12319
12504
  >
12320
12505
  {type === "date" ? (
@@ -12327,8 +12512,11 @@ function DatabaseBulkScalarValueEditor({
12327
12512
  disabled={disabled}
12328
12513
  onClick={() =>
12329
12514
  void onApply({
12330
- start: dateInputValueForOffset(new Date(), 0),
12331
- includeTime: false,
12515
+ kind: "set",
12516
+ value: {
12517
+ start: dateInputValueForOffset(new Date(), 0),
12518
+ includeTime: false,
12519
+ },
12332
12520
  })
12333
12521
  }
12334
12522
  >
@@ -12343,8 +12531,11 @@ function DatabaseBulkScalarValueEditor({
12343
12531
  disabled={disabled}
12344
12532
  onClick={() =>
12345
12533
  void onApply({
12346
- start: dateInputValueForOffset(new Date(), 1),
12347
- includeTime: false,
12534
+ kind: "set",
12535
+ value: {
12536
+ start: dateInputValueForOffset(new Date(), 1),
12537
+ includeTime: false,
12538
+ },
12348
12539
  })
12349
12540
  }
12350
12541
  >
@@ -12378,7 +12569,7 @@ function DatabaseBulkScalarValueEditor({
12378
12569
  variant="ghost"
12379
12570
  size="sm"
12380
12571
  disabled={disabled}
12381
- onClick={() => void onApply(null)}
12572
+ onClick={() => void onApply({ kind: "set", value: null })}
12382
12573
  >
12383
12574
  Clear
12384
12575
  </Button>
@@ -12403,7 +12594,7 @@ function DatabaseBulkFilesValueEditor({
12403
12594
  onCancel,
12404
12595
  }: {
12405
12596
  disabled: boolean;
12406
- onApply: (value: DocumentPropertyValue) => Promise<void>;
12597
+ onApply: (operation: DatabaseBulkPropertyValueOperation) => Promise<void>;
12407
12598
  onCancel: () => void;
12408
12599
  }) {
12409
12600
  const [value, setValue] = useState("");
@@ -12414,7 +12605,7 @@ function DatabaseBulkFilesValueEditor({
12414
12605
  onSubmit={(event) => {
12415
12606
  event.preventDefault();
12416
12607
  const items = filesMediaItems(value);
12417
- void onApply(items.length > 0 ? items : null);
12608
+ void onApply({ kind: "set", value: items.length > 0 ? items : null });
12418
12609
  }}
12419
12610
  >
12420
12611
  <textarea
@@ -12438,7 +12629,7 @@ function DatabaseBulkFilesValueEditor({
12438
12629
  variant="ghost"
12439
12630
  size="sm"
12440
12631
  disabled={disabled}
12441
- onClick={() => void onApply(null)}
12632
+ onClick={() => void onApply({ kind: "set", value: null })}
12442
12633
  >
12443
12634
  Clear
12444
12635
  </Button>
@@ -12455,18 +12646,35 @@ function DatabaseBulkFilesValueEditor({
12455
12646
 
12456
12647
  function DatabaseBulkOptionValueEditor({
12457
12648
  property,
12649
+ selectedItems,
12458
12650
  disabled,
12459
12651
  onApply,
12460
12652
  onCancel,
12461
12653
  }: {
12462
12654
  property: DocumentProperty;
12655
+ selectedItems: ContentDatabaseItem[];
12463
12656
  disabled: boolean;
12464
- onApply: (value: DocumentPropertyValue) => Promise<void>;
12657
+ onApply: (operation: DatabaseBulkPropertyValueOperation) => Promise<void>;
12465
12658
  onCancel: () => void;
12466
12659
  }) {
12467
12660
  const options = property.definition.options.options ?? [];
12468
- const [selectedIds, setSelectedIds] = useState<string[]>([]);
12469
12661
  const multi = property.definition.type === "multi_select";
12662
+ const selectedItemsKey = selectedItems.map((item) => item.id).join("|");
12663
+ const [pendingMultiSelectOperation, setPendingMultiSelectOperation] =
12664
+ useState<DatabaseBulkMultiSelectOperation>({
12665
+ kind: "multi_select_batch",
12666
+ addOptionIds: [],
12667
+ removeOptionIds: [],
12668
+ });
12669
+ const [optionSearchQuery, setOptionSearchQuery] = useState("");
12670
+
12671
+ useEffect(() => {
12672
+ setPendingMultiSelectOperation({
12673
+ kind: "multi_select_batch",
12674
+ addOptionIds: [],
12675
+ removeOptionIds: [],
12676
+ });
12677
+ }, [property.definition.id, selectedItemsKey]);
12470
12678
 
12471
12679
  if (options.length === 0) {
12472
12680
  return (
@@ -12474,68 +12682,130 @@ function DatabaseBulkOptionValueEditor({
12474
12682
  <div className="rounded bg-muted/40 px-2 py-3 text-sm text-muted-foreground">
12475
12683
  {dbText("thisPropertyHasNoOptionsYet")}
12476
12684
  </div>
12477
- <Button
12478
- type="button"
12479
- size="sm"
12480
- variant="ghost"
12481
- className="justify-start"
12482
- disabled={disabled}
12483
- onClick={() => void onApply(multi ? [] : null)}
12484
- >
12485
- {dbText("clearValue")}
12486
- </Button>
12685
+ {multi ? (
12686
+ <Button
12687
+ type="button"
12688
+ size="sm"
12689
+ variant="ghost"
12690
+ className="justify-start"
12691
+ onClick={onCancel}
12692
+ >
12693
+ Cancel
12694
+ </Button>
12695
+ ) : (
12696
+ <Button
12697
+ type="button"
12698
+ size="sm"
12699
+ variant="ghost"
12700
+ className="justify-start"
12701
+ disabled={disabled}
12702
+ onClick={() => void onApply({ kind: "set", value: null })}
12703
+ >
12704
+ {dbText("clearValue")}
12705
+ </Button>
12706
+ )}
12487
12707
  </div>
12488
12708
  );
12489
12709
  }
12490
12710
 
12491
12711
  if (multi) {
12712
+ const hasPendingChanges = databaseBulkMultiSelectOperationHasChanges(
12713
+ pendingMultiSelectOperation,
12714
+ );
12715
+ const filteredOptions = databaseBulkMultiSelectFilteredOptions(
12716
+ options,
12717
+ optionSearchQuery,
12718
+ );
12492
12719
  return (
12493
12720
  <div className="grid gap-2">
12721
+ <div className="flex h-8 items-center gap-1 rounded border border-border bg-background px-2">
12722
+ <IconSearch className="size-3.5 shrink-0 text-muted-foreground" />
12723
+ <Input
12724
+ autoFocus
12725
+ value={optionSearchQuery}
12726
+ placeholder={dbText("searchOptions")}
12727
+ aria-label={`Search ${property.definition.name} options`}
12728
+ className="h-7 border-0 bg-transparent px-0 text-xs shadow-none focus-visible:ring-0"
12729
+ onChange={(event) => setOptionSearchQuery(event.target.value)}
12730
+ onKeyDown={(event) => {
12731
+ if (event.key === "Escape") {
12732
+ event.preventDefault();
12733
+ if (optionSearchQuery) {
12734
+ setOptionSearchQuery("");
12735
+ } else {
12736
+ onCancel();
12737
+ }
12738
+ }
12739
+ }}
12740
+ />
12741
+ </div>
12494
12742
  <div className="max-h-52 overflow-auto">
12495
- {options.map((option) => {
12496
- const checked = selectedIds.includes(option.id);
12743
+ {filteredOptions.length === 0 ? (
12744
+ <div className="rounded px-2 py-3 text-sm text-muted-foreground">
12745
+ {dbText("noMatchingOptions")}
12746
+ </div>
12747
+ ) : null}
12748
+ {filteredOptions.map((option) => {
12749
+ const presence = databaseBulkMultiSelectOptionPresence(
12750
+ selectedItems,
12751
+ property,
12752
+ option.id,
12753
+ pendingMultiSelectOperation,
12754
+ );
12497
12755
  return (
12498
12756
  <button
12499
12757
  key={option.id}
12500
12758
  type="button"
12501
- className="flex w-full items-center justify-between gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-accent"
12759
+ aria-pressed={presence.presentInAll}
12760
+ aria-label={`${
12761
+ presence.presentInAll ? "Remove" : "Add"
12762
+ } ${option.name} for selected rows`}
12763
+ className="flex w-full items-center justify-between gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-accent disabled:opacity-50"
12764
+ disabled={disabled}
12502
12765
  onClick={() =>
12503
- setSelectedIds((current) =>
12504
- current.includes(option.id)
12505
- ? current.filter((id) => id !== option.id)
12506
- : [...current, option.id],
12766
+ setPendingMultiSelectOperation((current) =>
12767
+ databaseBulkMultiSelectToggleOperation(
12768
+ selectedItems,
12769
+ property,
12770
+ option.id,
12771
+ current,
12772
+ ),
12507
12773
  )
12508
12774
  }
12509
12775
  >
12510
12776
  <DatabaseBulkOptionPill option={option} />
12511
- {checked ? (
12512
- <IconCheck className="size-4 text-muted-foreground" />
12777
+ {presence.presentInAll ? (
12778
+ <IconCheck className="size-4 shrink-0 text-muted-foreground" />
12779
+ ) : presence.presentInAny ? (
12780
+ <IconMinus className="size-4 shrink-0 text-muted-foreground" />
12513
12781
  ) : null}
12514
12782
  </button>
12515
12783
  );
12516
12784
  })}
12517
12785
  </div>
12518
- <div className="flex justify-end gap-2">
12786
+ <div className="flex items-center justify-between gap-2">
12519
12787
  <Button
12520
12788
  type="button"
12521
12789
  variant="ghost"
12522
12790
  size="sm"
12523
12791
  disabled={disabled}
12524
- onClick={() => void onApply([])}
12525
- >
12526
- Clear
12527
- </Button>
12528
- <Button type="button" variant="ghost" size="sm" onClick={onCancel}>
12529
- Cancel
12530
- </Button>
12531
- <Button
12532
- type="button"
12533
- size="sm"
12534
- disabled={disabled}
12535
- onClick={() => void onApply(selectedIds)}
12792
+ onClick={() => void onApply({ kind: "set", value: [] })}
12536
12793
  >
12537
- Apply
12794
+ {dbText("clearValue")}
12538
12795
  </Button>
12796
+ <div className="flex justify-end gap-2">
12797
+ <Button type="button" variant="ghost" size="sm" onClick={onCancel}>
12798
+ Cancel
12799
+ </Button>
12800
+ <Button
12801
+ type="button"
12802
+ size="sm"
12803
+ disabled={disabled || !hasPendingChanges}
12804
+ onClick={() => void onApply(pendingMultiSelectOperation)}
12805
+ >
12806
+ Apply
12807
+ </Button>
12808
+ </div>
12539
12809
  </div>
12540
12810
  </div>
12541
12811
  );
@@ -12550,7 +12820,7 @@ function DatabaseBulkOptionValueEditor({
12550
12820
  type="button"
12551
12821
  className="flex w-full items-center rounded px-2 py-1.5 text-left text-sm hover:bg-accent disabled:opacity-50"
12552
12822
  disabled={disabled}
12553
- onClick={() => void onApply(option.id)}
12823
+ onClick={() => void onApply({ kind: "set", value: option.id })}
12554
12824
  >
12555
12825
  <DatabaseBulkOptionPill option={option} />
12556
12826
  </button>
@@ -12562,7 +12832,7 @@ function DatabaseBulkOptionValueEditor({
12562
12832
  variant="ghost"
12563
12833
  className="justify-start"
12564
12834
  disabled={disabled}
12565
- onClick={() => void onApply(null)}
12835
+ onClick={() => void onApply({ kind: "set", value: null })}
12566
12836
  >
12567
12837
  {dbText("clearValue")}
12568
12838
  </Button>
@@ -14557,6 +14827,7 @@ function DatabaseTableRow({
14557
14827
  item.properties.find(
14558
14828
  (candidate) => candidate.definition.id === property.definition.id,
14559
14829
  ) ?? property;
14830
+
14560
14831
  const value = (
14561
14832
  <div
14562
14833
  className={cn(