@agent-native/core 0.128.0 → 0.128.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +27 -0
  3. package/corpus/core/package.json +3 -1
  4. package/corpus/core/src/server/builder-design-systems.ts +299 -82
  5. package/corpus/core/src/server/email-template.ts +33 -0
  6. package/corpus/core/src/server/index.ts +8 -0
  7. package/corpus/templates/analytics/_gitignore +2 -0
  8. package/corpus/templates/assets/_gitignore +2 -0
  9. package/corpus/templates/calendar/_gitignore +2 -0
  10. package/corpus/templates/chat/_gitignore +2 -0
  11. package/corpus/templates/clips/AGENTS.md +6 -1
  12. package/corpus/templates/clips/_gitignore +2 -0
  13. package/corpus/templates/clips/actions/complete-transactional-email-summary.ts +56 -0
  14. package/corpus/templates/clips/actions/import-loom-recording.ts +258 -76
  15. package/corpus/templates/clips/actions/lib/direct-video.ts +96 -0
  16. package/corpus/templates/clips/actions/lib/loom-import-job.ts +53 -2
  17. package/corpus/templates/clips/actions/lib/loom-video.ts +2 -62
  18. package/corpus/templates/clips/actions/lib/video-download-limits.ts +62 -0
  19. package/corpus/templates/clips/actions/list-transactional-email-ai-requests.ts +249 -0
  20. package/corpus/templates/clips/app/components/library/library-grid.tsx +65 -0
  21. package/corpus/templates/clips/app/components/library/library-layout.tsx +55 -0
  22. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +28 -126
  23. package/corpus/templates/clips/app/global.css +23 -0
  24. package/corpus/templates/clips/app/hooks/use-transactional-email-bridge.ts +98 -0
  25. package/corpus/templates/clips/app/i18n/en-US.ts +20 -0
  26. package/corpus/templates/clips/app/routes/_app.tsx +2 -0
  27. package/corpus/templates/clips/app/routes/import.tsx +396 -0
  28. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
  29. package/corpus/templates/clips/app/routes/record.tsx +13 -71
  30. package/corpus/templates/clips/changelog/2026-07-25-two-clip-emails-now-include-a-concise-summary-generated-secu.md +6 -0
  31. package/corpus/templates/clips/data/clips-transactional-emails/config.json +3 -0
  32. package/corpus/templates/clips/server/jobs/transactional-emails.ts +1034 -0
  33. package/corpus/templates/clips/server/lib/transactional-email-store.ts +865 -0
  34. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +270 -0
  35. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  36. package/corpus/templates/clips/server/routes/api/view-event.post.ts +59 -2
  37. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +8 -2
  38. package/corpus/templates/content/AGENTS.md +8 -0
  39. package/corpus/templates/content/_gitignore +2 -0
  40. package/corpus/templates/content/actions/_content-database-personal-view.ts +28 -3
  41. package/corpus/templates/content/actions/_property-utils.ts +32 -2
  42. package/corpus/templates/content/actions/configure-document-property.ts +12 -2
  43. package/corpus/templates/content/actions/delete-document-property.ts +13 -3
  44. package/corpus/templates/content/actions/duplicate-document-property.ts +13 -3
  45. package/corpus/templates/content/actions/list-content-spaces.ts +5 -2
  46. package/corpus/templates/content/actions/list-document-properties.ts +12 -3
  47. package/corpus/templates/content/actions/move-database-item.ts +74 -55
  48. package/corpus/templates/content/actions/reorder-document-property.ts +20 -4
  49. package/corpus/templates/content/actions/set-document-property.ts +10 -1
  50. package/corpus/templates/content/actions/update-content-database-personal-view.ts +7 -0
  51. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +31 -21
  52. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +30 -3
  53. package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +8 -1
  54. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +72 -20
  55. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +79 -10
  56. package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +8 -1
  57. package/corpus/templates/content/app/components/editor/database/settings.tsx +6 -0
  58. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +246 -26
  59. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +491 -197
  60. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +3 -3
  61. package/corpus/templates/content/app/components/sidebar/sidebar-reorder.tsx +409 -0
  62. package/corpus/templates/content/app/hooks/use-content-database.ts +117 -3
  63. package/corpus/templates/content/app/hooks/use-content-spaces.ts +1 -0
  64. package/corpus/templates/content/app/hooks/use-document-properties.ts +62 -18
  65. package/corpus/templates/content/app/hooks/use-documents.ts +13 -5
  66. package/corpus/templates/content/app/i18n-data.ts +261 -3
  67. package/corpus/templates/content/app/routes/_app.page.$id.tsx +9 -2
  68. package/corpus/templates/content/changelog/2026-07-24-database-page-properties-keep-their-context.md +6 -0
  69. package/corpus/templates/content/changelog/2026-07-26-sidebar-references-can-now-be-personally-reordered.md +6 -0
  70. package/corpus/templates/content/docs/solutions/2026-07-24-database-property-context-switch-diagnosis.md +249 -0
  71. package/corpus/templates/content/docs/solutions/content-sidebar-order-shape.md +389 -0
  72. package/corpus/templates/content/shared/api.ts +28 -0
  73. package/corpus/templates/design/_gitignore +2 -0
  74. package/corpus/templates/design/app/i18n-data.ts +10 -0
  75. package/corpus/templates/design/app/lib/builder-design-system-upload.ts +280 -0
  76. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +90 -54
  77. package/corpus/templates/design/changelog/2026-07-28-figma-fig-uploads-now-show-indexing-progress-and-an-open-in-.md +6 -0
  78. package/corpus/templates/design/server/handlers/design-system-decode-job-status.ts +49 -0
  79. package/corpus/templates/design/server/handlers/design-system-upload-start.ts +87 -0
  80. package/corpus/templates/design/server/handlers/index-design-system-sources.ts +82 -0
  81. package/corpus/templates/design/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  82. package/corpus/templates/design/server/routes/api/design-system-upload-start.post.ts +1 -0
  83. package/corpus/templates/design/server/routes/api/index-design-system-sources.post.ts +1 -0
  84. package/corpus/templates/dispatch/_gitignore +2 -0
  85. package/corpus/templates/forms/_gitignore +2 -0
  86. package/corpus/templates/macros/_gitignore +2 -0
  87. package/corpus/templates/mail/_gitignore +2 -0
  88. package/corpus/templates/plan/_gitignore +2 -0
  89. package/corpus/templates/slides/_gitignore +2 -0
  90. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +126 -53
  91. package/corpus/templates/slides/app/components/design-system/builder-design-system-upload.ts +271 -0
  92. package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +1 -1
  93. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  94. package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +49 -0
  95. package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +87 -0
  96. package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +79 -0
  97. package/corpus/templates/slides/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  98. package/corpus/templates/slides/server/routes/api/design-system-upload-start.post.ts +1 -0
  99. package/corpus/templates/slides/server/routes/api/index-design-system-sources.post.ts +1 -0
  100. package/corpus/templates/tasks/_gitignore +2 -0
  101. package/corpus/toolkit/CHANGELOG.md +6 -0
  102. package/corpus/toolkit/package.json +1 -1
  103. package/corpus/toolkit/src/editor/DragHandle.ts +63 -9
  104. package/dist/collab/routes.d.ts +1 -1
  105. package/dist/collab/struct-routes.d.ts +1 -1
  106. package/dist/observability/routes.d.ts +3 -3
  107. package/dist/progress/routes.d.ts +1 -1
  108. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  109. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  110. package/dist/server/builder-design-systems.d.ts +49 -0
  111. package/dist/server/builder-design-systems.d.ts.map +1 -1
  112. package/dist/server/builder-design-systems.js +221 -69
  113. package/dist/server/builder-design-systems.js.map +1 -1
  114. package/dist/server/email-template.d.ts +7 -0
  115. package/dist/server/email-template.d.ts.map +1 -1
  116. package/dist/server/email-template.js +24 -0
  117. package/dist/server/email-template.js.map +1 -1
  118. package/dist/server/index.d.ts +1 -1
  119. package/dist/server/index.d.ts.map +1 -1
  120. package/dist/server/index.js +1 -1
  121. package/dist/server/index.js.map +1 -1
  122. package/dist/server/transcribe-voice.d.ts +1 -1
  123. package/dist/templates/chat/_gitignore +2 -0
  124. package/package.json +5 -3
  125. package/src/server/builder-design-systems.ts +299 -82
  126. package/src/server/email-template.ts +33 -0
  127. package/src/server/index.ts +8 -0
  128. package/src/templates/chat/_gitignore +2 -0
  129. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +0 -151
  130. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +0 -1
  131. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +0 -119
  132. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +0 -1
@@ -5,6 +5,8 @@ import type {
5
5
  ContentDatabasePersonalViewOverrides,
6
6
  ContentDatabaseResponse,
7
7
  ContentDatabaseViewConfig,
8
+ ContentSidebarOrderMode,
9
+ ContentSidebarViewOrder,
8
10
  } from "@shared/api";
9
11
  import {
10
12
  IconChevronDown,
@@ -51,6 +53,12 @@ import {
51
53
  } from "@/components/ui/tooltip";
52
54
  import { cn } from "@/lib/utils";
53
55
 
56
+ import {
57
+ SidebarDropIndicator,
58
+ SidebarReorderProvider,
59
+ useSidebarReorderItem,
60
+ type SidebarReorderLabels,
61
+ } from "../../sidebar/sidebar-reorder";
54
62
  import { applyDatabaseView } from "./filter-sort";
55
63
  import {
56
64
  databaseViewGroupingProperty,
@@ -64,6 +72,75 @@ import {
64
72
  normalizeClientDatabaseViewConfig,
65
73
  } from "./view-config";
66
74
 
75
+ export interface ContentFilesSidebarManualReorder {
76
+ onReorder: (
77
+ itemIds: string[],
78
+ moved: { itemId: string; position: number },
79
+ ) => void;
80
+ labels: SidebarReorderLabels;
81
+ }
82
+
83
+ export interface ContentFilesSidebarRenderReorder {
84
+ controls: ReturnType<typeof useSidebarReorderItem>;
85
+ labels: SidebarReorderLabels;
86
+ }
87
+
88
+ export function databaseSidebarReorderItems(
89
+ items: ContentDatabaseItem[],
90
+ untitledLabel: string,
91
+ hierarchical: boolean,
92
+ ) {
93
+ return items.map((item) => ({
94
+ id: item.id,
95
+ label: item.document.title || untitledLabel,
96
+ parentId: hierarchical ? item.document.parentId : null,
97
+ }));
98
+ }
99
+
100
+ export function contentSidebarOrderedItems(
101
+ items: ContentDatabaseItem[],
102
+ order: ContentSidebarViewOrder,
103
+ ) {
104
+ const itemIds = new Map(
105
+ order.itemIds.map((itemId, index) => [itemId, index]),
106
+ );
107
+ const stableItemId = (
108
+ left: ContentDatabaseItem,
109
+ right: ContentDatabaseItem,
110
+ ) => left.id.localeCompare(right.id);
111
+ const newestFirst = (left: string, right: string) =>
112
+ new Date(right).getTime() - new Date(left).getTime();
113
+
114
+ return [...items].sort((left, right) => {
115
+ if (order.mode === "custom") {
116
+ const leftIndex = itemIds.get(left.id);
117
+ const rightIndex = itemIds.get(right.id);
118
+ if (leftIndex !== undefined || rightIndex !== undefined) {
119
+ if (leftIndex === undefined) return 1;
120
+ if (rightIndex === undefined) return -1;
121
+ if (leftIndex !== rightIndex) return leftIndex - rightIndex;
122
+ }
123
+ return left.position - right.position || stableItemId(left, right);
124
+ }
125
+ if (order.mode === "last_edited") {
126
+ return (
127
+ newestFirst(left.document.updatedAt, right.document.updatedAt) ||
128
+ stableItemId(left, right)
129
+ );
130
+ }
131
+ if (order.mode === "created") {
132
+ return (
133
+ newestFirst(left.document.createdAt, right.document.createdAt) ||
134
+ stableItemId(left, right)
135
+ );
136
+ }
137
+ return (
138
+ (left.document.title || "").localeCompare(right.document.title || "") ||
139
+ stableItemId(left, right)
140
+ );
141
+ });
142
+ }
143
+
67
144
  function applyPersonalSidebarViewOverrides(
68
145
  savedViewConfig: ContentDatabaseViewConfig,
69
146
  overrides: ContentDatabasePersonalViewOverrides | null | undefined,
@@ -99,6 +176,8 @@ export function ContentFilesSidebarView({
99
176
  activeDocumentId,
100
177
  labels,
101
178
  onSelectView,
179
+ sidebarOrder,
180
+ manualReorder,
102
181
  onOpenItem,
103
182
  onCreateChildPage,
104
183
  onCreateChildDatabase,
@@ -114,6 +193,9 @@ export function ContentFilesSidebarView({
114
193
  isLoading: boolean;
115
194
  activeDocumentId?: string | null;
116
195
  onSelectView?: (viewId: string) => void;
196
+ /** A parent-owned, user-scoped Files order. It never writes database membership. */
197
+ sidebarOrder?: ContentSidebarViewOrder;
198
+ manualReorder?: ContentFilesSidebarManualReorder;
117
199
  onOpenItem?: (item: ContentDatabaseItem) => boolean;
118
200
  onCreateChildPage?: (item: ContentDatabaseItem) => void;
119
201
  onCreateChildDatabase?: (item: ContentDatabaseItem) => void;
@@ -121,7 +203,10 @@ export function ContentFilesSidebarView({
121
203
  onToggleFavorite?: (item: ContentDatabaseItem) => void;
122
204
  expandedDocumentIds?: ReadonlySet<string>;
123
205
  onDocumentExpandedChange?: (documentId: string, expanded: boolean) => void;
124
- renderItem?: (item: ContentDatabaseItem) => ReactNode;
206
+ renderItem?: (
207
+ item: ContentDatabaseItem,
208
+ reorder?: ContentFilesSidebarRenderReorder,
209
+ ) => ReactNode;
125
210
  scroll?: boolean;
126
211
  labels: Omit<
127
212
  Parameters<typeof DatabaseSidebarView>[0],
@@ -160,16 +245,19 @@ export function ContentFilesSidebarView({
160
245
  useEffect(() => {
161
246
  setConstraintsCleared(false);
162
247
  }, [activeFilterKey, activeView.id]);
163
- const items = usableData
248
+ const filteredItems = usableData
164
249
  ? applyDatabaseView(
165
250
  usableData.items,
166
251
  usableData.properties,
167
252
  "",
168
253
  constraintsCleared ? [] : activeView.filters,
169
- activeView.sorts,
254
+ sidebarOrder ? [] : activeView.sorts,
170
255
  activeView.filterMode ?? "and",
171
256
  )
172
257
  : [];
258
+ const items = sidebarOrder
259
+ ? contentSidebarOrderedItems(filteredItems, sidebarOrder)
260
+ : filteredItems;
173
261
  const groups = databaseVisibleGroups(
174
262
  databaseViewItemGroups(
175
263
  items,
@@ -185,6 +273,12 @@ export function ContentFilesSidebarView({
185
273
  const hierarchyUniverseItems = hasFilesHierarchy
186
274
  ? usableData?.items
187
275
  : undefined;
276
+ const manualReorderEnabled =
277
+ Boolean(manualReorder) &&
278
+ (sidebarOrder?.mode ?? "custom") === "custom" &&
279
+ activeView.sorts.length === 0 &&
280
+ activeView.filters.length === 0 &&
281
+ !databaseViewGroupingProperty(activeView, usableData?.properties ?? []);
188
282
  return (
189
283
  <div className="min-w-0">
190
284
  {viewConfig.views.length > 1 && (
@@ -235,6 +329,7 @@ export function ContentFilesSidebarView({
235
329
  renderItem={renderItem}
236
330
  hierarchyItems={hierarchyItems}
237
331
  hierarchyUniverseItems={hierarchyUniverseItems}
332
+ manualReorder={manualReorderEnabled ? manualReorder : undefined}
238
333
  scroll={scroll}
239
334
  />
240
335
  </div>
@@ -260,6 +355,7 @@ export function DatabaseSidebarView({
260
355
  renderItem,
261
356
  hierarchyItems,
262
357
  hierarchyUniverseItems,
358
+ manualReorder,
263
359
  scroll = true,
264
360
  noMatchesLabel,
265
361
  clearLabel,
@@ -281,9 +377,13 @@ export function DatabaseSidebarView({
281
377
  onToggleFavorite?: (item: ContentDatabaseItem) => void;
282
378
  expandedDocumentIds?: ReadonlySet<string>;
283
379
  onDocumentExpandedChange?: (documentId: string, expanded: boolean) => void;
284
- renderItem?: (item: ContentDatabaseItem) => ReactNode;
380
+ renderItem?: (
381
+ item: ContentDatabaseItem,
382
+ reorder?: ContentFilesSidebarRenderReorder,
383
+ ) => ReactNode;
285
384
  hierarchyItems?: ContentDatabaseItem[];
286
385
  hierarchyUniverseItems?: ContentDatabaseItem[];
386
+ manualReorder?: ContentFilesSidebarManualReorder;
287
387
  scroll?: boolean;
288
388
  noMatchesLabel: string;
289
389
  clearLabel: string;
@@ -336,7 +436,7 @@ export function DatabaseSidebarView({
336
436
  );
337
437
  return (
338
438
  <div key={node.item.id} className="min-w-0">
339
- <DatabaseSidebarRow
439
+ <SidebarDatabaseRow
340
440
  item={node.item}
341
441
  openPagesIn={openPagesIn}
342
442
  onPreview={onPreview}
@@ -353,6 +453,7 @@ export function DatabaseSidebarView({
353
453
  onToggleExpanded={(nextOpen) =>
354
454
  setDocumentOpen(node.item.document.id, nextOpen)
355
455
  }
456
+ manualReorder={manualReorder}
356
457
  />
357
458
  {open && node.children.length > 0 ? (
358
459
  <div>
@@ -401,7 +502,7 @@ export function DatabaseSidebarView({
401
502
  const navigation = (
402
503
  <nav
403
504
  aria-label={navigationLabel}
404
- className="grid min-w-0 gap-1 overflow-x-hidden p-1"
505
+ className="grid min-w-0 gap-1 overflow-x-hidden py-1 ps-1"
405
506
  >
406
507
  {grouped
407
508
  ? groups.map((group) => {
@@ -426,11 +527,14 @@ export function DatabaseSidebarView({
426
527
  <CollapsibleContent className="grid gap-0.5 pl-2">
427
528
  {group.items.map((item) =>
428
529
  renderItem ? (
429
- <div key={item.id} className="min-w-0">
430
- {renderItem(item)}
431
- </div>
530
+ <SidebarRenderedItem
531
+ key={item.id}
532
+ item={item}
533
+ renderItem={renderItem}
534
+ manualReorder={manualReorder}
535
+ />
432
536
  ) : (
433
- <DatabaseSidebarRow
537
+ <SidebarDatabaseRow
434
538
  key={item.id}
435
539
  item={item}
436
540
  openPagesIn={openPagesIn}
@@ -442,6 +546,7 @@ export function DatabaseSidebarView({
442
546
  onDeleteItem={onDeleteItem}
443
547
  onToggleFavorite={onToggleFavorite}
444
548
  untitledLabel={untitledLabel}
549
+ manualReorder={manualReorder}
445
550
  />
446
551
  ),
447
552
  )}
@@ -453,11 +558,14 @@ export function DatabaseSidebarView({
453
558
  ? itemTree.map((node) => renderTreeNode(node, 0))
454
559
  : items.map((item) =>
455
560
  renderItem ? (
456
- <div key={item.id} className="min-w-0">
457
- {renderItem(item)}
458
- </div>
561
+ <SidebarRenderedItem
562
+ key={item.id}
563
+ item={item}
564
+ renderItem={renderItem}
565
+ manualReorder={manualReorder}
566
+ />
459
567
  ) : (
460
- <DatabaseSidebarRow
568
+ <SidebarDatabaseRow
461
569
  key={item.id}
462
570
  item={item}
463
571
  openPagesIn={openPagesIn}
@@ -469,16 +577,117 @@ export function DatabaseSidebarView({
469
577
  onDeleteItem={onDeleteItem}
470
578
  onToggleFavorite={onToggleFavorite}
471
579
  untitledLabel={untitledLabel}
580
+ manualReorder={manualReorder}
472
581
  />
473
582
  ),
474
583
  )}
475
584
  </nav>
476
585
  );
477
- return scroll ? (
478
- <ScrollArea className="max-h-[32rem] w-full">{navigation}</ScrollArea>
586
+ const reorderItems = databaseSidebarReorderItems(
587
+ hierarchyItems ?? items,
588
+ untitledLabel,
589
+ Boolean(hierarchyItems),
590
+ );
591
+ const reorderableNavigation = manualReorder ? (
592
+ <SidebarReorderProvider
593
+ items={reorderItems}
594
+ labels={manualReorder.labels}
595
+ onReorder={manualReorder.onReorder}
596
+ >
597
+ {navigation}
598
+ </SidebarReorderProvider>
479
599
  ) : (
480
600
  navigation
481
601
  );
602
+ return scroll ? (
603
+ <ScrollArea className="max-h-[32rem] w-full">
604
+ {reorderableNavigation}
605
+ </ScrollArea>
606
+ ) : (
607
+ reorderableNavigation
608
+ );
609
+ }
610
+
611
+ function SidebarRenderedItem({
612
+ item,
613
+ renderItem,
614
+ manualReorder,
615
+ }: {
616
+ item: ContentDatabaseItem;
617
+ renderItem: (
618
+ item: ContentDatabaseItem,
619
+ reorder?: ContentFilesSidebarRenderReorder,
620
+ ) => ReactNode;
621
+ manualReorder?: ContentFilesSidebarManualReorder;
622
+ }) {
623
+ return manualReorder ? (
624
+ <ReorderableRenderedSidebarItem
625
+ item={item}
626
+ renderItem={renderItem}
627
+ labels={manualReorder.labels}
628
+ />
629
+ ) : (
630
+ <div className="min-w-0">{renderItem(item)}</div>
631
+ );
632
+ }
633
+
634
+ function ReorderableRenderedSidebarItem({
635
+ item,
636
+ renderItem,
637
+ labels,
638
+ }: {
639
+ item: ContentDatabaseItem;
640
+ renderItem: (
641
+ item: ContentDatabaseItem,
642
+ reorder?: ContentFilesSidebarRenderReorder,
643
+ ) => ReactNode;
644
+ labels: SidebarReorderLabels;
645
+ }) {
646
+ const controls = useSidebarReorderItem(item.id);
647
+ return (
648
+ <div
649
+ ref={controls.setNodeRef}
650
+ style={controls.style}
651
+ className="relative min-w-0"
652
+ >
653
+ <SidebarDropIndicator placement={controls.dropIndicator} />
654
+ {renderItem(item, { controls, labels })}
655
+ </div>
656
+ );
657
+ }
658
+
659
+ function SidebarDatabaseRow({
660
+ manualReorder,
661
+ ...props
662
+ }: Parameters<typeof DatabaseSidebarRow>[0] & {
663
+ manualReorder?: ContentFilesSidebarManualReorder;
664
+ }) {
665
+ return manualReorder ? (
666
+ <ReorderableDatabaseSidebarRow
667
+ {...props}
668
+ reorderLabels={manualReorder.labels}
669
+ />
670
+ ) : (
671
+ <DatabaseSidebarRow {...props} />
672
+ );
673
+ }
674
+
675
+ function ReorderableDatabaseSidebarRow({
676
+ reorderLabels,
677
+ ...props
678
+ }: Parameters<typeof DatabaseSidebarRow>[0] & {
679
+ reorderLabels: SidebarReorderLabels;
680
+ }) {
681
+ const reorder = useSidebarReorderItem(props.item.id);
682
+ return (
683
+ <div ref={reorder.setNodeRef} style={reorder.style} className="relative">
684
+ <SidebarDropIndicator placement={reorder.dropIndicator} />
685
+ <DatabaseSidebarRow
686
+ {...props}
687
+ reorder={{ controls: reorder, labels: reorderLabels }}
688
+ />
689
+ </div>
690
+ );
482
691
  }
483
692
 
484
693
  function DatabaseSidebarRow({
@@ -496,6 +705,7 @@ function DatabaseSidebarRow({
496
705
  hasChildren = false,
497
706
  expanded = false,
498
707
  onToggleExpanded,
708
+ reorder,
499
709
  }: {
500
710
  item: ContentDatabaseItem;
501
711
  openPagesIn: ContentDatabaseOpenPagesIn;
@@ -511,6 +721,10 @@ function DatabaseSidebarRow({
511
721
  hasChildren?: boolean;
512
722
  expanded?: boolean;
513
723
  onToggleExpanded?: (open: boolean) => void;
724
+ reorder?: {
725
+ controls: ReturnType<typeof useSidebarReorderItem>;
726
+ labels: SidebarReorderLabels;
727
+ };
514
728
  }) {
515
729
  const t = useT();
516
730
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
@@ -521,8 +735,7 @@ function DatabaseSidebarRow({
521
735
  item.document.accessRole === "admin";
522
736
  const canCreateChild = canEdit && Boolean(onCreateChildPage);
523
737
  const hasMenuActions =
524
- (canEdit && Boolean(onToggleFavorite)) ||
525
- (canManage && Boolean(onDeleteItem));
738
+ Boolean(onToggleFavorite) || (canManage && Boolean(onDeleteItem));
526
739
  function handleClick(event: MouseEvent<HTMLAnchorElement>) {
527
740
  if (
528
741
  event.defaultPrevented ||
@@ -555,7 +768,7 @@ function DatabaseSidebarRow({
555
768
  style={{
556
769
  insetInlineStart: `${databaseSidebarRowIndent(depth, hasChildren)}px`,
557
770
  }}
558
- aria-label={`${expanded ? "Collapse" : "Expand"} ${title}`}
771
+ aria-label={`${expanded ? t("sidebar.collapse") : t("sidebar.expand")} ${title}`}
559
772
  aria-expanded={expanded}
560
773
  onPointerUp={(event) => event.currentTarget.blur()}
561
774
  onClick={() => onToggleExpanded?.(!expanded)}
@@ -570,8 +783,14 @@ function DatabaseSidebarRow({
570
783
  ) : null}
571
784
  <Link
572
785
  to={`/page/${item.document.id}`}
786
+ {...reorder?.controls.attributes}
787
+ {...reorder?.controls.listeners}
788
+ data-sidebar-reorder-item-id={reorder?.controls.itemId}
789
+ role="link"
573
790
  className={cn(
574
791
  "flex h-7 min-w-0 items-center gap-1.5 rounded pe-1.5 text-sm text-foreground/85 hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
792
+ reorder && "touch-none cursor-pointer select-none",
793
+ reorder?.controls.isDragging && "cursor-grabbing",
575
794
  active && "font-semibold text-foreground",
576
795
  )}
577
796
  style={{
@@ -598,7 +817,8 @@ function DatabaseSidebarRow({
598
817
  <span
599
818
  className={cn(
600
819
  "min-w-0 flex-1 truncate",
601
- (hasMenuActions || canCreateChild) && "pe-12",
820
+ (hasMenuActions || canCreateChild) &&
821
+ "group-hover:pe-12 group-focus-within:pe-12",
602
822
  )}
603
823
  >
604
824
  {title}
@@ -606,20 +826,20 @@ function DatabaseSidebarRow({
606
826
  </Link>
607
827
 
608
828
  {(hasMenuActions || canCreateChild) && (
609
- <div className="pointer-events-none absolute end-1 top-1/2 z-10 flex -translate-y-1/2 items-center gap-0.5 rounded bg-sidebar px-0.5 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100">
829
+ <div className="pointer-events-none absolute end-0 top-1/2 z-10 flex -translate-y-1/2 items-center gap-0.5 rounded bg-sidebar px-0.5 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100">
610
830
  {hasMenuActions && (
611
831
  <DropdownMenu>
612
832
  <DropdownMenuTrigger asChild>
613
833
  <button
614
834
  type="button"
615
835
  className="flex size-6 items-center justify-center rounded text-foreground hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
616
- aria-label={`More actions for ${title}`}
836
+ aria-label={t("sidebar.moreActionsFor", { label: title })}
617
837
  >
618
838
  <IconDots size={14} />
619
839
  </button>
620
840
  </DropdownMenuTrigger>
621
841
  <DropdownMenuContent align="start" className="w-48">
622
- {canEdit && onToggleFavorite ? (
842
+ {onToggleFavorite ? (
623
843
  <DropdownMenuItem onSelect={() => onToggleFavorite(item)}>
624
844
  <IconStar
625
845
  className={cn(
@@ -628,11 +848,11 @@ function DatabaseSidebarRow({
628
848
  )}
629
849
  />
630
850
  {item.document.isFavorite
631
- ? "Remove from favorites"
632
- : "Add to favorites"}
851
+ ? t("sidebar.unpinFromSidebar")
852
+ : t("sidebar.pinToSidebar")}
633
853
  </DropdownMenuItem>
634
854
  ) : null}
635
- {canEdit && onToggleFavorite && canManage && onDeleteItem ? (
855
+ {onToggleFavorite && canManage && onDeleteItem ? (
636
856
  <DropdownMenuSeparator />
637
857
  ) : null}
638
858
  {canManage && onDeleteItem ? (