@agent-native/core-corpus 0.210.2 → 0.211.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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
  3. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
  4. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
  5. package/corpus/templates/analytics/CHANGELOG.md +18 -0
  6. package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
  7. package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
  8. package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
  9. package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
  10. package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
  11. package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
  12. package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
  13. package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
  14. package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
  15. package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
  16. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  17. package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
  18. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
  19. package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
  20. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
  21. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
  22. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
  23. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
  24. package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
  25. package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
  26. package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
  30. package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
  37. package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
  38. package/corpus/templates/analytics/server/db/schema.ts +1 -0
  39. package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
  40. package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
  41. package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
  42. package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
  43. package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
  44. package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
  45. package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
  46. package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
  47. package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
  48. package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
  49. package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
  50. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
  51. package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
  52. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
  53. package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
  54. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
  55. package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
  56. package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
  57. package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
  58. package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
  59. package/corpus/templates/analytics/server/plugins/db.ts +9 -1
  60. package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
  61. package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
  62. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
  63. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
  64. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
  65. package/corpus/templates/calendar/actions/delete-event.ts +1 -3
  66. package/corpus/templates/calendar/actions/delete-events.ts +1 -2
  67. package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
  68. package/corpus/templates/calendar/actions/update-event.ts +2 -6
  69. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
  70. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
  71. package/corpus/templates/clips/CHANGELOG.md +11 -24
  72. package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
  73. package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
  74. package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
  75. package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
  76. package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
  77. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  78. package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
  79. package/corpus/templates/clips/app/root.tsx +22 -10
  80. package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
  81. package/corpus/templates/clips/app/routes/import.tsx +115 -110
  82. package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
  88. package/corpus/templates/clips/desktop/src/app.tsx +29 -186
  89. package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
  90. package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
  91. package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
  92. package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
  93. package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
  94. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
  95. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
  96. package/corpus/templates/content/CHANGELOG.md +12 -0
  97. package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
  98. package/corpus/templates/content/actions/_database-navigation.ts +3 -3
  99. package/corpus/templates/content/actions/_database-utils.ts +10 -22
  100. package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
  101. package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
  102. package/corpus/templates/content/actions/export-document.ts +2 -14
  103. package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
  104. package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
  105. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
  106. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
  107. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
  108. package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
  109. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
  110. package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
  111. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
  112. package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
  113. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
  114. package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
  115. package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
  116. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
  117. package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
  118. package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
  119. package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
  120. package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
  121. package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
  122. package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
  123. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
  124. package/corpus/templates/content/app/global.css +23 -0
  125. package/corpus/templates/content/app/i18n-data.ts +1 -0
  126. package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
  127. package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
  128. package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
  129. package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
  130. package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
  131. package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
  132. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
  133. package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
  134. package/corpus/templates/content/e2e/helpers.ts +10 -0
  135. package/corpus/templates/content/shared/api.ts +18 -12
  136. package/corpus/templates/content/shared/database-query.ts +107 -13
  137. package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
  138. package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
  139. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
  140. package/corpus/templates/design/CHANGELOG.md +2 -0
  141. package/corpus/templates/design/actions/delete-file.ts +1 -4
  142. package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
  143. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
  144. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
  145. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
  146. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
  147. package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
  148. package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
  149. package/corpus/templates/design/e2e/global-setup.ts +185 -85
  150. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
  151. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  152. package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
  153. package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
  154. package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
  155. package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
  156. package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
  157. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
  158. package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
  159. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
  160. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
  161. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
  162. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
  163. package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
  164. package/corpus/templates/mail/AGENTS.md +1 -1
  165. package/corpus/templates/mail/CHANGELOG.md +14 -0
  166. package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
  167. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
  168. package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
  169. package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
  170. package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
  171. package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
  172. package/corpus/templates/mail/app/root.tsx +10 -2
  173. package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
  174. package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
  175. package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
  176. package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
  177. package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
  178. package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
  179. package/corpus/templates/mail/server/handlers/emails.ts +9 -0
  180. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
  181. package/corpus/templates/plan/CHANGELOG.md +4 -0
  182. package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
  183. package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
  184. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
  185. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
  186. package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
  187. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
  188. package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
  189. package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
  190. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
  191. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
  192. package/package.json +1 -1
@@ -46,7 +46,12 @@ import {
46
46
  } from "@shared/api";
47
47
  import { contentRecentHref } from "@shared/content-personal-navigation";
48
48
  import { contentDatabaseFormQuestions } from "@shared/database-form";
49
- import { applyContentDatabaseTableQuery } from "@shared/database-query";
49
+ import {
50
+ applyContentDatabaseTableQuery,
51
+ contentDatabaseFilterDateRangeValues,
52
+ contentDatabaseFilterOperatorNeedsValue,
53
+ contentDatabaseFilterOperatorsForType,
54
+ } from "@shared/database-query";
50
55
  import {
51
56
  databaseTableColumnIds,
52
57
  databaseFrozenColumnIds,
@@ -296,8 +301,8 @@ import {
296
301
  useContentTableSelectionGutter,
297
302
  } from "./ContentTable";
298
303
  import { databaseCanCreateItems, databaseCreateTarget } from "./create-target";
304
+ import { DatabaseCellComments } from "./database-cell-comments";
299
305
  import {
300
- DatabaseCellCommentMarker,
301
306
  DatabaseItemCommentsContext,
302
307
  DatabaseRowCommentCount,
303
308
  type OpenDatabaseItemComments,
@@ -1926,7 +1931,11 @@ function DatabaseTable({
1926
1931
  }
1927
1932
 
1928
1933
  // Opens the row with its Comments showing, from a count or a cell marker.
1929
- const previewItemComments: OpenDatabaseItemComments = (item, threadId) => {
1934
+ const previewItemComments: OpenDatabaseItemComments = (
1935
+ item,
1936
+ threadId,
1937
+ propertyId,
1938
+ ) => {
1930
1939
  if (openWorkspaceFiles(item)) return;
1931
1940
  seedDatabaseItemDocumentCaches(queryClient, item);
1932
1941
  prioritizeBuilderBodyHydrationForItem(item);
@@ -1936,18 +1945,19 @@ function DatabaseTable({
1936
1945
  databaseId,
1937
1946
  document.id,
1938
1947
  );
1939
- runPreviewTransition(
1940
- () =>
1941
- void navigate(
1942
- threadId ? `${path}&comment=${encodeURIComponent(threadId)}` : path,
1943
- ),
1944
- );
1948
+ const link = threadId
1949
+ ? `${path}&comment=${encodeURIComponent(threadId)}`
1950
+ : propertyId
1951
+ ? `${path}&commentProperty=${encodeURIComponent(propertyId)}`
1952
+ : path;
1953
+ runPreviewTransition(() => void navigate(link));
1945
1954
  return;
1946
1955
  }
1947
1956
  const intent: PageCommentIntent = {
1948
1957
  key: ++previewCommentIntentSequenceRef.current,
1949
1958
  documentId: item.document.id,
1950
1959
  threadId,
1960
+ propertyId,
1951
1961
  };
1952
1962
  if (item.document.id === previewDocumentId) {
1953
1963
  setPreviewCommentIntent(intent);
@@ -3667,7 +3677,12 @@ function DatabaseTable({
3667
3677
  onOpenPage={openItemPage}
3668
3678
  />
3669
3679
  ) : (
3670
- <DatabaseItemCommentsContext.Provider value={previewItemComments}>
3680
+ // The read-only ChatGPT widget can't read or write Comments.
3681
+ <DatabaseItemCommentsContext.Provider
3682
+ value={
3683
+ document.mcpDirectoryWidgetReadOnly ? null : previewItemComments
3684
+ }
3685
+ >
3671
3686
  <DatabaseTableView
3672
3687
  startupAnchored={renderMode === "page"}
3673
3688
  columnOrderIds={activeView.tableColumnOrderIds ?? []}
@@ -6858,7 +6873,7 @@ function DatabaseInlineSortControl({
6858
6873
  );
6859
6874
  }
6860
6875
 
6861
- function DatabaseAddFilterButton({
6876
+ export function DatabaseAddFilterButton({
6862
6877
  open,
6863
6878
  filters,
6864
6879
  properties,
@@ -6873,19 +6888,10 @@ function DatabaseAddFilterButton({
6873
6888
  onAddFilter: (key: "name" | (string & {}), label: string) => void;
6874
6889
  onAddAdvancedFilter: (key: "name" | (string & {}), label: string) => void;
6875
6890
  }) {
6876
- const excludedFilterKeys = useMemo(
6877
- () => new Set(filters.filter(isActiveFilter).map((filter) => filter.key)),
6878
- [filters],
6891
+ const advancedFilterCandidate = databaseFilterRuleCandidate(
6892
+ properties,
6893
+ filters.filter(isActiveFilter),
6879
6894
  );
6880
- const advancedFilterCandidate =
6881
- databasePropertyPickerItems(properties, "", {
6882
- includeName: false,
6883
- excludeKeys: excludedFilterKeys,
6884
- })[0] ??
6885
- databasePropertyPickerItems(properties, "", {
6886
- includeName: true,
6887
- excludeKeys: excludedFilterKeys,
6888
- })[0];
6889
6895
 
6890
6896
  return (
6891
6897
  <DropdownMenu open={open} onOpenChange={onOpenChange}>
@@ -6905,17 +6911,14 @@ function DatabaseAddFilterButton({
6905
6911
  <DatabasePropertyPickerMenuItems
6906
6912
  properties={properties}
6907
6913
  includeName
6908
- excludeKeys={excludedFilterKeys}
6909
6914
  placeholder={dbText("filterBy")}
6910
6915
  closeOnSelect
6911
6916
  onSelect={onAddFilter}
6912
6917
  />
6913
6918
  <DropdownMenuSeparator />
6914
6919
  <DropdownMenuItem
6915
- disabled={!advancedFilterCandidate}
6916
6920
  onSelect={(event) => {
6917
6921
  event.preventDefault();
6918
- if (!advancedFilterCandidate) return;
6919
6922
  onAddAdvancedFilter(
6920
6923
  advancedFilterCandidate.key,
6921
6924
  advancedFilterCandidate.label,
@@ -7011,19 +7014,10 @@ function DatabaseAdvancedFilterGroupControl({
7011
7014
  ({ filter }) => !filter.parentFilterGroupId,
7012
7015
  );
7013
7016
  const nestedGroupEntries = advancedNestedFilterGroups(filterEntries);
7014
- const advancedKeys = useMemo(
7015
- () => new Set(advancedFilters.map((filter) => filter.key)),
7016
- [advancedFilters],
7017
- );
7018
- const addRuleCandidate =
7019
- databasePropertyPickerItems(properties, "", {
7020
- includeName: false,
7021
- excludeKeys: advancedKeys,
7022
- })[0] ??
7023
- databasePropertyPickerItems(properties, "", {
7024
- includeName: true,
7025
- excludeKeys: advancedKeys,
7026
- })[0];
7017
+ const addRuleCandidate = databaseFilterRuleCandidate(
7018
+ properties,
7019
+ advancedFilters,
7020
+ );
7027
7021
 
7028
7022
  function updateFilterAt(index: number, next: Partial<DatabaseFilter>) {
7029
7023
  const nextFilters = [...filters];
@@ -7035,7 +7029,7 @@ function DatabaseAdvancedFilterGroupControl({
7035
7029
  filterGroupId: next.filterGroupId ?? currentFilter.filterGroupId,
7036
7030
  parentFilterGroupId:
7037
7031
  next.parentFilterGroupId ?? currentFilter.parentFilterGroupId,
7038
- value: filterOperatorNeedsValue(nextOperator)
7032
+ value: contentDatabaseFilterOperatorNeedsValue(nextOperator)
7039
7033
  ? (next.value ?? currentFilter.value)
7040
7034
  : "",
7041
7035
  };
@@ -7158,10 +7152,8 @@ function DatabaseAdvancedFilterGroupControl({
7158
7152
  </DropdownMenuSubTrigger>
7159
7153
  <DropdownMenuSubContent className="w-56">
7160
7154
  <DropdownMenuItem
7161
- disabled={!addRuleCandidate}
7162
7155
  onSelect={(event) => {
7163
7156
  event.preventDefault();
7164
- if (!addRuleCandidate) return;
7165
7157
  addAdvancedRule(addRuleCandidate.key, addRuleCandidate.label);
7166
7158
  }}
7167
7159
  >
@@ -7169,10 +7161,8 @@ function DatabaseAdvancedFilterGroupControl({
7169
7161
  {dbText("addFilterRule")}
7170
7162
  </DropdownMenuItem>
7171
7163
  <DropdownMenuItem
7172
- disabled={!addRuleCandidate}
7173
7164
  onSelect={(event) => {
7174
7165
  event.preventDefault();
7175
- if (!addRuleCandidate) return;
7176
7166
  addNestedFilterGroup(
7177
7167
  addRuleCandidate.key,
7178
7168
  addRuleCandidate.label,
@@ -7344,7 +7334,7 @@ function DatabaseAdvancedFilterRuleRow({
7344
7334
  </DropdownMenuContent>
7345
7335
  </DropdownMenu>
7346
7336
 
7347
- {filterOperatorNeedsValue(filter.operator) ? (
7337
+ {contentDatabaseFilterOperatorNeedsValue(filter.operator) ? (
7348
7338
  <div className="min-w-0 flex-[1.35] [&>div>div:first-child]:px-0 [&>input]:h-9 [&>input]:text-xs">
7349
7339
  <DatabaseFilterValueControl
7350
7340
  documentId={documentId}
@@ -7415,19 +7405,10 @@ function DatabaseAdvancedNestedFilterGroup({
7415
7405
  onRemoveFilter: (index: number) => void;
7416
7406
  onRemoveGroup: () => void;
7417
7407
  }) {
7418
- const nestedKeys = useMemo(
7419
- () => new Set(group.entries.map(({ filter }) => filter.key)),
7420
- [group.entries],
7421
- );
7422
- const addRuleCandidate =
7423
- databasePropertyPickerItems(properties, "", {
7424
- includeName: false,
7425
- excludeKeys: nestedKeys,
7426
- })[0] ??
7427
- databasePropertyPickerItems(properties, "", {
7428
- includeName: true,
7429
- excludeKeys: nestedKeys,
7430
- })[0];
7408
+ const addRuleCandidate = databaseFilterRuleCandidate(
7409
+ properties,
7410
+ group.entries.map(({ filter }) => filter),
7411
+ );
7431
7412
 
7432
7413
  return (
7433
7414
  <div className="flex items-start gap-2">
@@ -7506,10 +7487,8 @@ function DatabaseAdvancedNestedFilterGroup({
7506
7487
  </DropdownMenuTrigger>
7507
7488
  <DropdownMenuContent align="start" className="w-56">
7508
7489
  <DropdownMenuItem
7509
- disabled={!addRuleCandidate}
7510
7490
  onSelect={(event) => {
7511
7491
  event.preventDefault();
7512
- if (!addRuleCandidate) return;
7513
7492
  onAddNestedFilterRule(
7514
7493
  group.id,
7515
7494
  addRuleCandidate.key,
@@ -7581,7 +7560,7 @@ function DatabaseInlineFilterControl({
7581
7560
  nextFilters[filterIndex] = {
7582
7561
  ...currentFilter,
7583
7562
  ...next,
7584
- value: filterOperatorNeedsValue(nextOperator)
7563
+ value: contentDatabaseFilterOperatorNeedsValue(nextOperator)
7585
7564
  ? (next.value ?? currentFilter.value)
7586
7565
  : "",
7587
7566
  };
@@ -7598,7 +7577,7 @@ function DatabaseInlineFilterControl({
7598
7577
  }
7599
7578
 
7600
7579
  const filterIsComplete =
7601
- !filterOperatorNeedsValue(filter.operator) ||
7580
+ !contentDatabaseFilterOperatorNeedsValue(filter.operator) ||
7602
7581
  filter.value.trim().length > 0;
7603
7582
  return (
7604
7583
  <DropdownMenu open={open} onOpenChange={onOpenChange}>
@@ -7753,7 +7732,7 @@ function DatabaseInlineFilterControl({
7753
7732
  </DropdownMenu>
7754
7733
  </div>
7755
7734
 
7756
- {filterOperatorNeedsValue(filter.operator) ? (
7735
+ {contentDatabaseFilterOperatorNeedsValue(filter.operator) ? (
7757
7736
  <DatabaseFilterValueControl
7758
7737
  autoFocus={!filter.value}
7759
7738
  documentId={documentId}
@@ -11599,6 +11578,12 @@ type DatabasePropertyPickerOption = {
11599
11578
  type: DocumentPropertyType | "name";
11600
11579
  };
11601
11580
 
11581
+ const DATABASE_NAME_PICKER_OPTION: DatabasePropertyPickerOption = {
11582
+ key: "name",
11583
+ label: "Name",
11584
+ type: "name",
11585
+ };
11586
+
11602
11587
  export function databasePropertyPickerItems(
11603
11588
  properties: DocumentProperty[],
11604
11589
  query: string,
@@ -11609,9 +11594,7 @@ export function databasePropertyPickerItems(
11609
11594
  ): DatabasePropertyPickerOption[] {
11610
11595
  const normalizedQuery = query.trim().toLowerCase();
11611
11596
  const items: DatabasePropertyPickerOption[] = [
11612
- ...(includeName
11613
- ? [{ key: "name", label: "Name", type: "name" as const }]
11614
- : []),
11597
+ ...(includeName ? [DATABASE_NAME_PICKER_OPTION] : []),
11615
11598
  ...properties.map((property) => ({
11616
11599
  key: property.definition.id,
11617
11600
  label: property.definition.name,
@@ -11627,6 +11610,23 @@ export function databasePropertyPickerItems(
11627
11610
  );
11628
11611
  }
11629
11612
 
11613
+ // Filters may repeat a property, so used properties are only deprioritized.
11614
+ // Excluding them would disable the add-rule actions once every one is used.
11615
+ export function databaseFilterRuleCandidate(
11616
+ properties: DocumentProperty[],
11617
+ filters: readonly DatabaseFilter[],
11618
+ ): DatabasePropertyPickerOption {
11619
+ const usedKeys = new Set(filters.map((filter) => filter.key));
11620
+ const candidates = [
11621
+ ...databasePropertyPickerItems(properties, "", { includeName: false }),
11622
+ DATABASE_NAME_PICKER_OPTION,
11623
+ ];
11624
+ return (
11625
+ candidates.find((candidate) => !usedKeys.has(candidate.key)) ??
11626
+ candidates[0]
11627
+ );
11628
+ }
11629
+
11630
11630
  function DatabasePropertyPickerSearch({
11631
11631
  value,
11632
11632
  onChange,
@@ -11683,7 +11683,6 @@ function DatabasePropertyPickerMenuItems({
11683
11683
  properties,
11684
11684
  selectedKey,
11685
11685
  includeName,
11686
- excludeKeys,
11687
11686
  placeholder,
11688
11687
  closeOnSelect,
11689
11688
  onSelect,
@@ -11691,7 +11690,6 @@ function DatabasePropertyPickerMenuItems({
11691
11690
  properties: DocumentProperty[];
11692
11691
  selectedKey?: string;
11693
11692
  includeName?: boolean;
11694
- excludeKeys?: ReadonlySet<string>;
11695
11693
  placeholder?: string;
11696
11694
  closeOnSelect?: boolean;
11697
11695
  onSelect: (key: string, label: string) => void;
@@ -11699,7 +11697,6 @@ function DatabasePropertyPickerMenuItems({
11699
11697
  const [query, setQuery] = useState("");
11700
11698
  const items = databasePropertyPickerItems(properties, query, {
11701
11699
  includeName,
11702
- excludeKeys,
11703
11700
  });
11704
11701
 
11705
11702
  return (
@@ -17270,7 +17267,7 @@ function isActiveFilter(
17270
17267
  filter: DatabaseFilter | null,
17271
17268
  ): filter is DatabaseFilter {
17272
17269
  if (!filter) return false;
17273
- if (filterOperatorNeedsValue(filter.operator)) {
17270
+ if (contentDatabaseFilterOperatorNeedsValue(filter.operator)) {
17274
17271
  return databaseFilterSelectedValues(filter.value).length > 0;
17275
17272
  }
17276
17273
  return true;
@@ -17295,11 +17292,18 @@ export function databasePropertyValuesForNewItem(
17295
17292
  );
17296
17293
  if (!property?.editable) continue;
17297
17294
  if (isComputedPropertyType(property.definition.type)) continue;
17298
- if (propertyValues[property.definition.id] !== undefined) continue;
17299
17295
 
17300
17296
  const value = databaseFilterDefaultValueForNewItem(filter, property);
17301
- if (value !== undefined) {
17297
+ if (value === undefined) continue;
17298
+ const current = propertyValues[property.definition.id];
17299
+ if (current === undefined) {
17302
17300
  propertyValues[property.definition.id] = value;
17301
+ } else if (Array.isArray(current) && Array.isArray(value)) {
17302
+ // "Tags contains A" and "Tags contains B" both have to match, so a
17303
+ // new row gets every value; one value would drop it from the view.
17304
+ propertyValues[property.definition.id] = [
17305
+ ...new Set([...current, ...value]),
17306
+ ];
17303
17307
  }
17304
17308
  }
17305
17309
 
@@ -17627,7 +17631,7 @@ function SortFieldIcon({
17627
17631
  return <Icon className="mr-2 size-4 text-muted-foreground" />;
17628
17632
  }
17629
17633
 
17630
- function FilterMenu({
17634
+ export function FilterMenu({
17631
17635
  filters,
17632
17636
  properties,
17633
17637
  inlineOpen,
@@ -17646,19 +17650,10 @@ function FilterMenu({
17646
17650
  }) {
17647
17651
  const activeFilters = filters.filter(isActiveFilter);
17648
17652
  const active = activeFilters.length > 0 || inlineOpen;
17649
- const excludedFilterKeys = useMemo(
17650
- () => new Set(activeFilters.map((filter) => filter.key)),
17651
- [activeFilters],
17652
- );
17653
- const advancedFilterCandidate =
17654
- databasePropertyPickerItems(properties, "", {
17655
- includeName: false,
17656
- excludeKeys: excludedFilterKeys,
17657
- })[0] ??
17658
- databasePropertyPickerItems(properties, "", {
17659
- includeName: true,
17660
- excludeKeys: excludedFilterKeys,
17661
- })[0];
17653
+ const advancedFilterCandidate = databaseFilterRuleCandidate(
17654
+ properties,
17655
+ activeFilters,
17656
+ );
17662
17657
 
17663
17658
  return (
17664
17659
  <DropdownMenu open={open} onOpenChange={onOpenChange}>
@@ -17681,17 +17676,14 @@ function FilterMenu({
17681
17676
  <DatabasePropertyPickerMenuItems
17682
17677
  properties={properties}
17683
17678
  includeName
17684
- excludeKeys={excludedFilterKeys}
17685
17679
  placeholder={dbText("filterBy")}
17686
17680
  closeOnSelect
17687
17681
  onSelect={onAddFilter}
17688
17682
  />
17689
17683
  <DropdownMenuSeparator />
17690
17684
  <DropdownMenuItem
17691
- disabled={!advancedFilterCandidate}
17692
17685
  onSelect={(event) => {
17693
17686
  event.preventDefault();
17694
- if (!advancedFilterCandidate) return;
17695
17687
  onAddAdvancedFilter(
17696
17688
  advancedFilterCandidate.key,
17697
17689
  advancedFilterCandidate.label,
@@ -17982,6 +17974,8 @@ const FILTER_OPERATOR_LABELS: Record<FilterOperator, string> = {
17982
17974
  does_not_equal: "Is not",
17983
17975
  greater_than: "Greater than",
17984
17976
  less_than: "Less than",
17977
+ greater_than_or_equal: "Greater than or equal to",
17978
+ less_than_or_equal: "Less than or equal to",
17985
17979
  before: "Before",
17986
17980
  after: "After",
17987
17981
  between: "Between",
@@ -18020,50 +18014,10 @@ function databaseFilterOperatorInlineLabel(
18020
18014
  function filterOperatorsForKey(
18021
18015
  key: string,
18022
18016
  properties: DocumentProperty[],
18023
- ): FilterOperator[] {
18024
- const type = filterPropertyTypeForKey(key, properties);
18025
-
18026
- if (type === "checkbox") {
18027
- return ["is_checked", "is_unchecked"];
18028
- }
18029
-
18030
- if (
18031
- type === "select" ||
18032
- type === "status" ||
18033
- type === "multi_select" ||
18034
- type === "person"
18035
- ) {
18036
- return ["contains", "does_not_equal", "is_empty", "is_not_empty"];
18037
- }
18038
-
18039
- if (type === "number") {
18040
- return [
18041
- "equals",
18042
- "does_not_equal",
18043
- "greater_than",
18044
- "less_than",
18045
- "is_empty",
18046
- "is_not_empty",
18047
- ];
18048
- }
18049
-
18050
- if (
18051
- type === "date" ||
18052
- type === "created_time" ||
18053
- type === "last_edited_time"
18054
- ) {
18055
- return [
18056
- "equals",
18057
- "does_not_equal",
18058
- "before",
18059
- "after",
18060
- "between",
18061
- "is_empty",
18062
- "is_not_empty",
18063
- ];
18064
- }
18065
-
18066
- return ["contains", "equals", "does_not_equal", "is_empty", "is_not_empty"];
18017
+ ): readonly FilterOperator[] {
18018
+ return contentDatabaseFilterOperatorsForType(
18019
+ filterPropertyTypeForKey(key, properties),
18020
+ );
18067
18021
  }
18068
18022
 
18069
18023
  function defaultFilterOperatorForKey(
@@ -18084,12 +18038,6 @@ function filterPropertyTypeForKey(
18084
18038
  );
18085
18039
  }
18086
18040
 
18087
- function filterOperatorNeedsValue(operator: FilterOperator) {
18088
- return !["is_empty", "is_not_empty", "is_checked", "is_unchecked"].includes(
18089
- operator,
18090
- );
18091
- }
18092
-
18093
18041
  function filterValuePlaceholder(key: string, properties: DocumentProperty[]) {
18094
18042
  const type = filterPropertyTypeForKey(key, properties);
18095
18043
  if (type === "number") return "Number";
@@ -18127,7 +18075,7 @@ function DatabaseBetweenDateFilterValueControl({
18127
18075
  filter: DatabaseFilter;
18128
18076
  onValueChange: (value: string) => void;
18129
18077
  }) {
18130
- const [start, end] = databaseFilterDateRangeValues(filter.value);
18078
+ const [start, end] = contentDatabaseFilterDateRangeValues(filter.value);
18131
18079
  const updateRange = (nextStart: string, nextEnd: string) => {
18132
18080
  onValueChange(databaseFilterEncodeDateRangeValues(nextStart, nextEnd));
18133
18081
  };
@@ -18244,7 +18192,7 @@ function databaseFilterValueLabel(
18244
18192
  properties: DocumentProperty[],
18245
18193
  ) {
18246
18194
  if (filter.operator === "between") {
18247
- const [start, end] = databaseFilterDateRangeValues(filter.value);
18195
+ const [start, end] = contentDatabaseFilterDateRangeValues(filter.value);
18248
18196
  if (start && end) return `${start} to ${end}`;
18249
18197
  return start || end || "Choose dates";
18250
18198
  }
@@ -18283,20 +18231,6 @@ function databaseFilterSelectedValues(value: string) {
18283
18231
  }
18284
18232
  }
18285
18233
 
18286
- function databaseFilterDateRangeValues(value: string): [string, string] {
18287
- const trimmed = value.trim();
18288
- if (!trimmed) return ["", ""];
18289
- try {
18290
- const parsed = JSON.parse(trimmed) as unknown;
18291
- if (!Array.isArray(parsed)) return [trimmed, ""];
18292
- const start = typeof parsed[0] === "string" ? parsed[0].trim() : "";
18293
- const end = typeof parsed[1] === "string" ? parsed[1].trim() : "";
18294
- return [start, end];
18295
- } catch {
18296
- return [trimmed, ""];
18297
- }
18298
- }
18299
-
18300
18234
  function databaseFilterEncodeDateRangeValues(start: string, end: string) {
18301
18235
  const normalizedStart = start.trim();
18302
18236
  const normalizedEnd = end.trim();
@@ -18329,17 +18263,62 @@ export function databaseInlineFilterLabel(
18329
18263
  filter: DatabaseFilter,
18330
18264
  properties: DocumentProperty[],
18331
18265
  ) {
18332
- if (!filterOperatorNeedsValue(filter.operator)) {
18333
- return `${filter.label}: ${databaseFilterOperatorLabel(
18334
- filter.operator,
18335
- filter.key,
18336
- properties,
18337
- )}`;
18266
+ const operator = databaseFilterChipOperatorLabel(
18267
+ filter.operator,
18268
+ filter.key,
18269
+ properties,
18270
+ );
18271
+ if (!contentDatabaseFilterOperatorNeedsValue(filter.operator)) {
18272
+ return `${filter.label}: ${operator}`;
18338
18273
  }
18339
18274
  if (databaseFilterSelectedValues(filter.value).length === 0) {
18340
18275
  return filter.label;
18341
18276
  }
18342
- return `${filter.label}: ${databaseFilterValueLabel(filter, properties)}`;
18277
+ const value = databaseFilterValueLabel(filter, properties);
18278
+ return `${filter.label}: ${operator ? `${operator} ${value}` : value}`;
18279
+ }
18280
+
18281
+ // Empty only for operators that read as a plain match ("Status: Done"). The
18282
+ // `never` default makes a new operator choose its wording; at runtime it
18283
+ // still catches unknown operators in saved views.
18284
+ function databaseFilterChipOperatorLabel(
18285
+ operator: FilterOperator,
18286
+ key: string,
18287
+ properties: DocumentProperty[],
18288
+ ): string {
18289
+ const type = filterPropertyTypeForKey(key, properties);
18290
+ switch (operator) {
18291
+ case "contains":
18292
+ case "between":
18293
+ return "";
18294
+ case "equals":
18295
+ if (type === "number") return "=";
18296
+ return databaseFilterOptionPropertyForKey(key, properties) ? "" : "is";
18297
+ case "does_not_equal":
18298
+ if (type === "number") return "≠";
18299
+ return type === "multi_select" || type === "person"
18300
+ ? "does not contain"
18301
+ : "is not";
18302
+ case "greater_than":
18303
+ return ">";
18304
+ case "less_than":
18305
+ return "<";
18306
+ case "greater_than_or_equal":
18307
+ return "≥";
18308
+ case "less_than_or_equal":
18309
+ return "≤";
18310
+ case "before":
18311
+ case "after":
18312
+ case "is_checked":
18313
+ case "is_unchecked":
18314
+ case "is_empty":
18315
+ case "is_not_empty":
18316
+ return databaseFilterOperatorInlineLabel(operator, key, properties);
18317
+ default: {
18318
+ const savedOperator: never = operator;
18319
+ return databaseFilterOperatorInlineLabel(savedOperator, key, properties);
18320
+ }
18321
+ }
18343
18322
  }
18344
18323
 
18345
18324
  function databaseFilterPropertyForKey(
@@ -18718,7 +18697,7 @@ function DatabaseTableRow({
18718
18697
  <div
18719
18698
  key={property.definition.id}
18720
18699
  className={cn(
18721
- "flex min-w-0 hover:bg-muted/25",
18700
+ "group/cell relative flex min-w-0 hover:bg-muted/25",
18722
18701
  databaseTableCellDensityClass(rowDensity),
18723
18702
  wrapCells ? "items-start" : "items-center",
18724
18703
  )}
@@ -18747,10 +18726,15 @@ function DatabaseTableRow({
18747
18726
  ) : (
18748
18727
  value
18749
18728
  )}
18750
- <DatabaseCellCommentMarker
18729
+ <DatabaseCellComments
18751
18730
  item={item}
18752
- propertyId={itemProperty.definition.id}
18753
- propertyName={itemProperty.definition.name}
18731
+ property={itemProperty}
18732
+ rowSelected={selected}
18733
+ besideEditButton={
18734
+ canEdit &&
18735
+ itemProperty.editable &&
18736
+ itemProperty.definition.type === "relation"
18737
+ }
18754
18738
  />
18755
18739
  </div>
18756
18740
  );