@agent-native/core-corpus 0.211.0-nightly-20261020183616 → 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 (41) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +18 -0
  3. package/corpus/templates/clips/CHANGELOG.md +11 -24
  4. package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
  5. package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
  6. package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
  7. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
  8. package/corpus/templates/content/CHANGELOG.md +12 -0
  9. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +4 -0
  10. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -2
  11. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
  12. package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
  13. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +28 -13
  14. package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
  15. package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
  16. package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
  17. package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
  18. package/corpus/templates/content/app/global.css +23 -0
  19. package/corpus/templates/content/app/i18n-data.ts +1 -0
  20. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
  21. package/corpus/templates/design/CHANGELOG.md +2 -0
  22. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  23. package/corpus/templates/mail/CHANGELOG.md +14 -0
  24. package/corpus/templates/plan/CHANGELOG.md +4 -0
  25. package/package.json +1 -1
  26. package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  27. package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  28. package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  29. package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  30. package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  31. package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  32. package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  33. package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  34. package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  35. package/corpus/templates/design/.generated/bridge/native-builtin-metadata.generated.ts +0 -301
  36. package/corpus/templates/design/.generated/bridge/native-worker-source.generated.ts +0 -6
  37. package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  38. package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  39. package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  40. package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
  41. package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
package/corpus/README.md CHANGED
@@ -32,4 +32,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core-corpus/corpu
32
32
 
33
33
  ## Generated Counts
34
34
 
35
- - template files: 13257
35
+ - template files: 13210
@@ -7,6 +7,13 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Fixed
9
9
 
10
+ - Blog signup attribution excludes signups before the first visit and URLs on unrelated hosts
11
+ - Collapsed Analytics navigation starts at the top with more space between icons.
12
+ - Content keeps successful coverage dates and missing-day gaps, and Search Console setup validates paths before saving
13
+ - Private GitHub repositories can be indexed with an organization-scoped GitHub token
14
+ - Search Console preserves historical backfill after empty results and keeps daily collection paused when editing properties.
15
+ - Search Console refreshes historical coverage when properties change and advances normally after empty results
16
+ - Search Console setup keeps saved properties readable and supports disabling collection with invalid drafts.
10
17
  - Blog article credits include direct and wrapped author references.
11
18
  - Content collection recovers abandoned runs, avoids duplicate work, and preserves missing author names.
12
19
  - Daily content metrics recover collection gaps and keep omitted Search Console values uncollected.
@@ -14,6 +21,10 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
14
21
 
15
22
  ## 2026-10-19
16
23
 
24
+ ### Added
25
+
26
+ - Import from Sigma lists every workbook you can see in Sigma, with search. Workbooks already migrated show greyed out with their owner and open the dashboard in a new tab, so nobody migrates the same workbook twice.
27
+
17
28
  ### Fixed
18
29
 
19
30
  - Composer focus remains safe when an editor changes during navigation.
@@ -25,6 +36,10 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
25
36
 
26
37
  - Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.
27
38
 
39
+ ### Security
40
+
41
+ - Creating or revoking a public analytics key now asks you to approve it every time the agent or another app tries it.
42
+
28
43
  ## 2026-10-17
29
44
 
30
45
  ### Added
@@ -47,6 +62,7 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
47
62
 
48
63
  ### Added
49
64
 
65
+ - The Content dashboard can show signups per blog article, collected once a day behind a BigQuery cost cap that a dry run measures first
50
66
  - Click a table row to see its numbers broken down by app, or another column, in a side panel.
51
67
  - Connect a personal Sigma account to import its dashboards
52
68
  - First-party charts can read a scheduled warehouse events model instead of the raw event table, so repeat loads reuse BigQuery's cache between model rebuilds.
@@ -104,6 +120,8 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
104
120
 
105
121
  ### Added
106
122
 
123
+ - A Content dashboard lists every blog article with its author, pageviews, and search clicks, and says when a source failed or isn't set up instead of showing zero
124
+ - Data sources has a Google Search Console card that shows the service account to grant and turns on daily collection
107
125
  - Analytics can collect daily GA4 pageviews and Search Console clicks and impressions for each blog article
108
126
  - Analytics can collect your blog article list once a day into its own tables, ready for the Content view
109
127
  - Click any bar, line point, funnel stage or slice on the first-party dashboard to open the underlying records in a pop-up, with filter, SQL and CSV export.
@@ -3,10 +3,20 @@
3
3
  All notable user-facing changes to Clips are documented here. Open it any time
4
4
  from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-10-20
7
+
8
+ ### Fixed
9
+
10
+ - Locally saved desktop recordings now appear in the recovery list with upload and storage setup actions.
11
+
6
12
  ## 2026-10-19
7
13
 
8
14
  ### Fixed
9
15
 
16
+ - Agents see a clip's frames as soon as it finishes processing instead of a cached "still processing" answer.
17
+ - Chapter list times now match the player's clock, and each chapter shows a focus ring when you tab to it.
18
+ - Editing a comment puts the cursor after its text, and Reply on another notification moves the cursor to its reply box.
19
+ - Video imports use clearer labels, YouTube links work without connected storage, and embedded players stay clear of timeline hover previews.
10
20
  - Screen Memory can transcribe desktop recordings without a separate FFmpeg install.
11
21
 
12
22
  ### Changed
@@ -57,6 +67,7 @@ from the command menu (Cmd+K → "What's new") or from Settings.
57
67
 
58
68
  ### Fixed
59
69
 
70
+ - Recordings uploaded by a connected agent can now use free Clips storage, like recordings made in the app.
60
71
  - An agent upload too large for free hosted storage is now refused before it starts, with the size limit that applies.
61
72
  - Agents connected to Clips over MCP can now import a transcript file, instead of getting an unknown-tool error.
62
73
  - Clips keeps meeting transcripts recoverable while a save or notes finalization is still in progress.
@@ -1616,28 +1627,4 @@ from the command menu (Cmd+K → "What's new") or from Settings.
1616
1627
  - Fixed the Mac menu bar popover so it opens reliably after launch at login.
1617
1628
  - Localized Clips Settings changelog and extension sidebar labels in every supported interface language.
1618
1629
 
1619
- ## 2026-06-24
1620
-
1621
- ### Added
1622
-
1623
- - Added a language picker and localized app chrome for supported languages.
1624
- - Clips now includes a language picker with Simplified Chinese support for core navigation, empty states, and settings.
1625
-
1626
- ### Improved
1627
-
1628
- - Chrome extension setup now shows a clear all-done confirmation when permissions are ready.
1629
- - Settings now link directly to Agent settings for model, API key, automation, and voice preferences.
1630
- - Settings now puts What's new in a compact sidebar, prioritizes Builder.io setup, and lets you manage S3 storage and AI provider keys in place.
1631
- - Shared clips now expose their full console log stream (all levels, redacted) to connected agents, not just warnings and errors
1632
- - Shared clips now expose their full fetch/XHR network request stream (method, sanitized URL, status, duration) to connected agents, not just failed requests
1633
-
1634
- ### Fixed
1635
-
1636
- - Archive and spaces navigation is more resilient when sidebar count loading fails.
1637
- - Inline Slack playback controls no longer shift when adjusting volume, and compact clip times stay on one line.
1638
-
1639
- ### Changed
1640
-
1641
- - The Chrome extension option now only appears on supported Clips hosts unless a custom deployment explicitly enables it.
1642
-
1643
1630
  For the full list of updates, see the [changelog folder](./changelog/).
@@ -82,7 +82,11 @@ export default defineAction({
82
82
  // The ticket comes first: if it is refused or fails to save, no recording
83
83
  // is left stranded in "uploading" with nothing able to finish it.
84
84
  const recordingId = nanoid();
85
- const ticket = await createRecordingUploadTicket(ownerEmail, recordingId);
85
+ const ticket = await createRecordingUploadTicket(
86
+ ownerEmail,
87
+ recordingId,
88
+ getRequestContext()?.authUserId,
89
+ );
86
90
  let created: Awaited<ReturnType<typeof createRecording.run>>;
87
91
  try {
88
92
  created = await createRecording.run(
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-16
4
+ ---
5
+
6
+ Recordings uploaded by a connected agent can now use free Clips storage, like recordings made in the app.
@@ -27,6 +27,8 @@ export interface RecordingUploadTicket extends Record<string, unknown> {
27
27
  tokenHash: string;
28
28
  reservedAt: number;
29
29
  expiresAt: number;
30
+ /** The creator's auth user id, which a hosted-storage reservation is held under. */
31
+ authUserId?: string;
30
32
  }
31
33
 
32
34
  interface RecordingUploadTickets extends Record<string, unknown> {
@@ -37,6 +39,7 @@ export interface RecordingUploadTicketOwner {
37
39
  recordingId: string;
38
40
  ownerEmail: string;
39
41
  orgId: string;
42
+ authUserId?: string;
40
43
  }
41
44
 
42
45
  function tokenHash(token: string): string {
@@ -180,6 +183,7 @@ async function compareAndSwapTickets(
180
183
  export async function createRecordingUploadTicket(
181
184
  ownerEmail: string,
182
185
  recordingId: string,
186
+ authUserId?: string,
183
187
  ): Promise<RecordingUploadTicket & { token: string }> {
184
188
  const token = `${TOKEN_PREFIX}${encodeOwner(ownerEmail)}.${crypto.randomBytes(32).toString("base64url")}`;
185
189
  const reservedAt = Date.now();
@@ -188,6 +192,7 @@ export async function createRecordingUploadTicket(
188
192
  tokenHash: tokenHash(token),
189
193
  reservedAt,
190
194
  expiresAt: reservedAt + RECORDING_UPLOAD_TICKET_TTL_MS,
195
+ ...(authUserId ? { authUserId } : {}),
191
196
  };
192
197
 
193
198
  for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt += 1) {
@@ -296,5 +301,8 @@ export async function requireRecordingUploadTicketOwner(
296
301
  recordingId,
297
302
  ownerEmail: verified.ownerEmail,
298
303
  orgId: recording.organizationId,
304
+ ...(typeof verified.ticket.authUserId === "string"
305
+ ? { authUserId: verified.ticket.authUserId }
306
+ : {}),
299
307
  };
300
308
  }
@@ -510,6 +510,7 @@ export async function handleRecordingChunk(
510
510
  recordingId?: string;
511
511
  ownerEmail?: string;
512
512
  orgId?: string;
513
+ authUserId?: string;
513
514
  },
514
515
  ) {
515
516
  const recordingId =
@@ -579,6 +580,7 @@ export async function handleRecordingChunk(
579
580
  if (override?.ownerEmail) {
580
581
  ownerEmail = override.ownerEmail;
581
582
  orgId = override.orgId;
583
+ authUserId = override.authUserId;
582
584
  } else {
583
585
  try {
584
586
  const context = await getEventOwnerContext(event);
@@ -5,6 +5,16 @@ time from the command menu (Cmd+K → "What's new").
5
5
 
6
6
  ## 2026-10-19
7
7
 
8
+ ### Improved
9
+
10
+ - Database views can filter one property more than once, number filters add ≥ and ≤, and filter chips name their operator, like "Status: is not done".
11
+
12
+ ### Fixed
13
+
14
+ - Edits made from a block's menu or a table's row and column menus, such as deleting a table, adding a row or turning off the header row, are now saved instead of coming back after a reload
15
+ - Starting a comment puts the cursor in the comment box, ready to type.
16
+ - Table row and column options now appear for the row or column under the pointer, Escape selects the cell before leaving the editor, wide tables keep their add-column control on screen on phones, and a tap near a table's edge on a touch screen edits the cell there instead of adding a row or column
17
+
8
18
  ### Changed
9
19
 
10
20
  - Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.
@@ -32,6 +42,8 @@ time from the command menu (Cmd+K → "What's new").
32
42
 
33
43
  ### Fixed
34
44
 
45
+ - Opening a page in a new tab while another tab is still saving no longer sends a second save or files a copy to History.
46
+ - Typing steadily in one tab no longer makes another open tab show "This page changed elsewhere" after its edits saved.
35
47
  - Reopening a page after someone else renamed it no longer adds a copy of your edits to Version History
36
48
 
37
49
  ## 2026-10-12
@@ -742,6 +742,8 @@ interface CommentsSidebarOptions {
742
742
  surface?: CommentSurface;
743
743
  /** Closes the anchored popover; shown as its header close button. */
744
744
  onClose?: () => void;
745
+ /** Extra header actions for each comment thread's card. */
746
+ renderThreadActions?: (thread: CommentThread) => ReactNode;
745
747
  }
746
748
 
747
749
  type CommentsSidebarProps = CommentsSidebarOptions &
@@ -813,6 +815,7 @@ export function CommentsSidebar({
813
815
  presentation = "inline",
814
816
  surface: surfaceProp,
815
817
  onClose,
818
+ renderThreadActions,
816
819
  }: CommentsSidebarProps) {
817
820
  const surface: CommentSurface =
818
821
  surfaceProp ?? (presentation === "history" ? "panel" : "rail");
@@ -1846,6 +1849,7 @@ export function CommentsSidebar({
1846
1849
  resolved={Boolean(thread.resolved)}
1847
1850
  surface={surface}
1848
1851
  onClose={onClose}
1852
+ threadActions={renderThreadActions?.(thread)}
1849
1853
  currentUserEmail={currentUserEmail}
1850
1854
  quote={thread.quotedText}
1851
1855
  propertyTarget={
@@ -448,12 +448,14 @@ export interface PageEditorSession {
448
448
 
449
449
  /**
450
450
  * Opens the Page with its Comments showing: one thread when `threadId` is
451
- * set, otherwise the list. A new `key` asks again.
451
+ * set, a new comment on the Property `propertyId` names, otherwise the list.
452
+ * A new `key` asks again.
452
453
  */
453
454
  export interface PageCommentIntent {
454
455
  key: number;
455
456
  documentId: string;
456
457
  threadId?: string | null;
458
+ propertyId?: string;
457
459
  }
458
460
 
459
461
  export interface PageEditorSurfaceProps extends DocumentEditorProps {
@@ -7390,6 +7392,7 @@ function PageEditorSessionBody({
7390
7392
  )
7391
7393
  ? pagePropertiesQuery.data.properties
7392
7394
  : undefined;
7395
+ const pagePropertiesFailed = !pageProperties && pagePropertiesQuery.isError;
7393
7396
  const documentLayoutRef = useRef<HTMLDivElement>(null);
7394
7397
  const commentLaneRef = useRef<HTMLElement>(null);
7395
7398
  const anchoredCommentRef = useRef<HTMLElement>(null);
@@ -7774,6 +7777,7 @@ function PageEditorSessionBody({
7774
7777
  ],
7775
7778
  );
7776
7779
  const handledCommentDeepLinkRef = useRef<string | null>(null);
7780
+ const handledPropertyCommentLinkRef = useRef<string | null>(null);
7777
7781
 
7778
7782
  const startPropertyComment = useCallback(
7779
7783
  (property: DocumentProperty) => {
@@ -7910,6 +7914,40 @@ function PageEditorSessionBody({
7910
7914
  activateCommentThread(threadId, true);
7911
7915
  }, [activateCommentThread, documentId, location.search, threads]);
7912
7916
 
7917
+ // A Collection that opens rows as full pages links here to start a comment
7918
+ // on one Property, as the row's peek does on a touch screen.
7919
+ useEffect(() => {
7920
+ const propertyId = new URLSearchParams(location.search).get(
7921
+ "commentProperty",
7922
+ );
7923
+ if (!propertyId) {
7924
+ handledPropertyCommentLinkRef.current = null;
7925
+ return;
7926
+ }
7927
+ const linkKey = `${documentId}:${propertyId}`;
7928
+ // Wait for the Properties; a failed read opens the list instead.
7929
+ if (
7930
+ handledPropertyCommentLinkRef.current === linkKey ||
7931
+ (!pageProperties && !pagePropertiesFailed)
7932
+ ) {
7933
+ return;
7934
+ }
7935
+ handledPropertyCommentLinkRef.current = linkKey;
7936
+ const property = pageProperties?.find(
7937
+ (candidate) => candidate.definition.id === propertyId,
7938
+ );
7939
+ if (property && canComment) startPropertyComment(property);
7940
+ else handleUtilityPanelChange("comments");
7941
+ }, [
7942
+ canComment,
7943
+ documentId,
7944
+ handleUtilityPanelChange,
7945
+ location.search,
7946
+ pageProperties,
7947
+ pagePropertiesFailed,
7948
+ startPropertyComment,
7949
+ ]);
7950
+
7913
7951
  const handledCommentIntentRef = useRef<number | null>(null);
7914
7952
  useEffect(() => {
7915
7953
  if (
@@ -7917,7 +7955,7 @@ function PageEditorSessionBody({
7917
7955
  handledCommentIntentRef.current === commentIntent.key
7918
7956
  )
7919
7957
  return;
7920
- const threadId = commentIntent.threadId;
7958
+ const { threadId, propertyId } = commentIntent;
7921
7959
  if (threadId) {
7922
7960
  // Wait for the thread; a thread that is gone opens the list instead.
7923
7961
  if (!threads) return;
@@ -7926,14 +7964,30 @@ function PageEditorSessionBody({
7926
7964
  activateCommentThread(threadId);
7927
7965
  return;
7928
7966
  }
7967
+ } else if (propertyId) {
7968
+ // Wait for the Properties; one that is gone, or a failed read, opens
7969
+ // the list instead.
7970
+ if (!pageProperties && !pagePropertiesFailed) return;
7971
+ handledCommentIntentRef.current = commentIntent.key;
7972
+ const property = pageProperties?.find(
7973
+ (candidate) => candidate.definition.id === propertyId,
7974
+ );
7975
+ if (property && canComment) {
7976
+ startPropertyComment(property);
7977
+ return;
7978
+ }
7929
7979
  }
7930
7980
  handledCommentIntentRef.current = commentIntent.key;
7931
7981
  handleUtilityPanelChange("comments");
7932
7982
  }, [
7933
7983
  activateCommentThread,
7984
+ canComment,
7934
7985
  commentIntent,
7935
7986
  documentId,
7936
7987
  handleUtilityPanelChange,
7988
+ pageProperties,
7989
+ pagePropertiesFailed,
7990
+ startPropertyComment,
7937
7991
  threads,
7938
7992
  ]);
7939
7993
 
@@ -171,6 +171,10 @@ import {
171
171
  PropertyCommentControl,
172
172
  usePropertyCommentRowState,
173
173
  } from "./property-comments";
174
+ import {
175
+ formatPropertyDate,
176
+ formatPropertyDateTime,
177
+ } from "./property-date-format";
174
178
 
175
179
  type TFunction = ReturnType<typeof useT>;
176
180
 
@@ -282,56 +286,12 @@ function slugify(value: string) {
282
286
  return slug || `option-${Date.now()}`;
283
287
  }
284
288
 
285
- function formatDate(value: string) {
286
- if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
287
- const [year, month, day] = value.split("-").map(Number);
288
- return new Intl.DateTimeFormat(undefined, {
289
- month: "short",
290
- day: "numeric",
291
- year: "numeric",
292
- }).format(new Date(year, month - 1, day));
293
- }
294
-
295
- const date = new Date(value);
296
- if (Number.isNaN(date.getTime())) return value;
297
- return new Intl.DateTimeFormat(undefined, {
298
- month: "short",
299
- day: "numeric",
300
- year: "numeric",
301
- }).format(date);
302
- }
303
-
304
- function formatDateTime(value: string) {
305
- if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
306
- const [datePart, timePart] = value.split("T");
307
- const [year, month, day] = datePart.split("-").map(Number);
308
- const [hour, minute] = timePart.split(":").map(Number);
309
- return new Intl.DateTimeFormat(undefined, {
310
- month: "short",
311
- day: "numeric",
312
- year: "numeric",
313
- hour: "numeric",
314
- minute: "2-digit",
315
- }).format(new Date(year, month - 1, day, hour, minute));
316
- }
317
-
318
- const date = new Date(value);
319
- if (Number.isNaN(date.getTime())) return value;
320
- return new Intl.DateTimeFormat(undefined, {
321
- month: "short",
322
- day: "numeric",
323
- year: "numeric",
324
- hour: "numeric",
325
- minute: "2-digit",
326
- }).format(date);
327
- }
328
-
329
289
  function formatPropertyDateDisplayValue(
330
290
  value: DocumentProperty["value"],
331
291
  t?: TFunction,
332
292
  ) {
333
293
  const includeTime = documentPropertyDateIncludesTime(value);
334
- const formatter = includeTime ? formatDateTime : formatDate;
294
+ const formatter = includeTime ? formatPropertyDateTime : formatPropertyDate;
335
295
  const start = documentPropertyDatePart(value, "start");
336
296
  const end = documentPropertyDatePart(value, "end");
337
297
  if (!start) return tWithFallback(t, "editor.properties.empty", "Empty");
@@ -392,7 +352,7 @@ export function displayValue(
392
352
  }
393
353
 
394
354
  if (type === "created_time" || type === "last_edited_time") {
395
- return <span>{formatDateTime(propertyText(value))}</span>;
355
+ return <span>{formatPropertyDateTime(propertyText(value))}</span>;
396
356
  }
397
357
 
398
358
  if (type === "person") {
@@ -42,7 +42,8 @@ function subscribeToNoHover(onChange: () => void) {
42
42
  return () => media.removeEventListener("change", onChange);
43
43
  }
44
44
 
45
- function deviceCannotHover() {
45
+ /** Whether no pointer on this device can hover, as on a phone. */
46
+ export function deviceCannotHover() {
46
47
  return (
47
48
  typeof window !== "undefined" &&
48
49
  typeof window.matchMedia === "function" &&
@@ -301,8 +301,8 @@ import {
301
301
  useContentTableSelectionGutter,
302
302
  } from "./ContentTable";
303
303
  import { databaseCanCreateItems, databaseCreateTarget } from "./create-target";
304
+ import { DatabaseCellComments } from "./database-cell-comments";
304
305
  import {
305
- DatabaseCellCommentMarker,
306
306
  DatabaseItemCommentsContext,
307
307
  DatabaseRowCommentCount,
308
308
  type OpenDatabaseItemComments,
@@ -1931,7 +1931,11 @@ function DatabaseTable({
1931
1931
  }
1932
1932
 
1933
1933
  // Opens the row with its Comments showing, from a count or a cell marker.
1934
- const previewItemComments: OpenDatabaseItemComments = (item, threadId) => {
1934
+ const previewItemComments: OpenDatabaseItemComments = (
1935
+ item,
1936
+ threadId,
1937
+ propertyId,
1938
+ ) => {
1935
1939
  if (openWorkspaceFiles(item)) return;
1936
1940
  seedDatabaseItemDocumentCaches(queryClient, item);
1937
1941
  prioritizeBuilderBodyHydrationForItem(item);
@@ -1941,18 +1945,19 @@ function DatabaseTable({
1941
1945
  databaseId,
1942
1946
  document.id,
1943
1947
  );
1944
- runPreviewTransition(
1945
- () =>
1946
- void navigate(
1947
- threadId ? `${path}&comment=${encodeURIComponent(threadId)}` : path,
1948
- ),
1949
- );
1948
+ const link = threadId
1949
+ ? `${path}&comment=${encodeURIComponent(threadId)}`
1950
+ : propertyId
1951
+ ? `${path}&commentProperty=${encodeURIComponent(propertyId)}`
1952
+ : path;
1953
+ runPreviewTransition(() => void navigate(link));
1950
1954
  return;
1951
1955
  }
1952
1956
  const intent: PageCommentIntent = {
1953
1957
  key: ++previewCommentIntentSequenceRef.current,
1954
1958
  documentId: item.document.id,
1955
1959
  threadId,
1960
+ propertyId,
1956
1961
  };
1957
1962
  if (item.document.id === previewDocumentId) {
1958
1963
  setPreviewCommentIntent(intent);
@@ -3672,7 +3677,12 @@ function DatabaseTable({
3672
3677
  onOpenPage={openItemPage}
3673
3678
  />
3674
3679
  ) : (
3675
- <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
+ >
3676
3686
  <DatabaseTableView
3677
3687
  startupAnchored={renderMode === "page"}
3678
3688
  columnOrderIds={activeView.tableColumnOrderIds ?? []}
@@ -18687,7 +18697,7 @@ function DatabaseTableRow({
18687
18697
  <div
18688
18698
  key={property.definition.id}
18689
18699
  className={cn(
18690
- "flex min-w-0 hover:bg-muted/25",
18700
+ "group/cell relative flex min-w-0 hover:bg-muted/25",
18691
18701
  databaseTableCellDensityClass(rowDensity),
18692
18702
  wrapCells ? "items-start" : "items-center",
18693
18703
  )}
@@ -18716,10 +18726,15 @@ function DatabaseTableRow({
18716
18726
  ) : (
18717
18727
  value
18718
18728
  )}
18719
- <DatabaseCellCommentMarker
18729
+ <DatabaseCellComments
18720
18730
  item={item}
18721
- propertyId={itemProperty.definition.id}
18722
- propertyName={itemProperty.definition.name}
18731
+ property={itemProperty}
18732
+ rowSelected={selected}
18733
+ besideEditButton={
18734
+ canEdit &&
18735
+ itemProperty.editable &&
18736
+ itemProperty.definition.type === "relation"
18737
+ }
18723
18738
  />
18724
18739
  </div>
18725
18740
  );