@agent-native/core-corpus 0.212.1 → 0.214.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 (90) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/onboarding-journeys/SKILL.md +29 -14
  3. package/corpus/templates/analytics/CHANGELOG.md +11 -0
  4. package/corpus/templates/analytics/actions/estimate-onboarding-journey-cost.ts +23 -6
  5. package/corpus/templates/analytics/actions/get-onboarding-journey.ts +20 -5
  6. package/corpus/templates/analytics/actions/provider-api-register.ts +2 -7
  7. package/corpus/templates/analytics/changelog/2026-10-21-analytics-recognizes-questions-about-connected-data-product-.md +6 -0
  8. package/corpus/templates/analytics/changelog/2026-10-21-onboarding-journeys-now-honor-larger-event-row-budgets.md +6 -0
  9. package/corpus/templates/analytics/changelog/2026-10-21-preserve-unknown-replay-availability-in-partial-journeys.md +6 -0
  10. package/corpus/templates/analytics/package.json +6 -0
  11. package/corpus/templates/analytics/scripts/journey-capture-plan.ts +18 -7
  12. package/corpus/templates/analytics/scripts/journey-capture.ts +3 -1
  13. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +19 -12
  14. package/corpus/templates/analytics/server/lib/journey-tree.ts +24 -9
  15. package/corpus/templates/analytics/server/lib/onboarding-journey-cost.ts +163 -94
  16. package/corpus/templates/analytics/server/lib/onboarding-journey.ts +162 -74
  17. package/corpus/templates/assets/server/lib/org-admin.ts +20 -16
  18. package/corpus/templates/brain/server/lib/zoom-governance.ts +11 -6
  19. package/corpus/templates/clips/CHANGELOG.md +1 -0
  20. package/corpus/templates/clips/changelog/2026-10-16-rewind-status-says-when-it-remembers-audio.md +6 -0
  21. package/corpus/templates/clips/desktop/src/app.tsx +7 -4
  22. package/corpus/templates/clips/desktop/src/components/RewindCaptureStatus.tsx +25 -0
  23. package/corpus/templates/clips/desktop/src/lib/rewind-status.ts +6 -1
  24. package/corpus/templates/clips/desktop/src/shared/config.ts +3 -0
  25. package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +2 -0
  26. package/corpus/templates/clips/server/lib/recordings.ts +4 -19
  27. package/corpus/templates/community/qr/package.json +1 -1
  28. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +15 -1
  29. package/corpus/templates/content/CHANGELOG.md +13 -0
  30. package/corpus/templates/content/actions/_content-space-access.ts +95 -6
  31. package/corpus/templates/content/actions/_database-utils.ts +20 -16
  32. package/corpus/templates/content/actions/_database-view-setup.ts +305 -11
  33. package/corpus/templates/content/actions/_github-source-freshness.ts +72 -17
  34. package/corpus/templates/content/actions/_github-source-metadata.ts +63 -17
  35. package/corpus/templates/content/actions/_github-source.ts +141 -77
  36. package/corpus/templates/content/actions/_property-utils.ts +3 -0
  37. package/corpus/templates/content/actions/check-github-connection.ts +15 -0
  38. package/corpus/templates/content/actions/create-github-collection.ts +180 -0
  39. package/corpus/templates/content/actions/disconnect-content-database-source.ts +5 -0
  40. package/corpus/templates/content/actions/link-github-folder.ts +104 -36
  41. package/corpus/templates/content/actions/refresh-content-database-source.ts +37 -4
  42. package/corpus/templates/content/actions/sync-github-folder.ts +23 -5
  43. package/corpus/templates/content/actions/update-content-database-view.ts +18 -2
  44. package/corpus/templates/content/actions/view-screen.ts +12 -0
  45. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +263 -35
  46. package/corpus/templates/content/app/components/editor/database/GitHubLinkDialog.tsx +938 -0
  47. package/corpus/templates/content/app/components/editor/database/GitHubSourcePanel.tsx +228 -0
  48. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +561 -0
  49. package/corpus/templates/content/app/components/editor/database/GitHubUnlinkDialog.tsx +96 -0
  50. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +410 -0
  51. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +426 -0
  52. package/corpus/templates/content/app/components/editor/database/ListView.tsx +136 -18
  53. package/corpus/templates/content/app/components/editor/database/github-availability.ts +147 -0
  54. package/corpus/templates/content/app/components/editor/database/github-source-text.ts +280 -0
  55. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
  56. package/corpus/templates/content/app/components/editor/database/settings.tsx +3 -3
  57. package/corpus/templates/content/app/components/editor/database/view-config.ts +4 -0
  58. package/corpus/templates/content/app/components/layout/Layout.tsx +2 -0
  59. package/corpus/templates/content/app/global.css +13 -0
  60. package/corpus/templates/content/app/hooks/content-action-refresh.ts +6 -0
  61. package/corpus/templates/content/app/hooks/use-content-database.ts +3 -1
  62. package/corpus/templates/content/app/hooks/use-github-folder.ts +199 -0
  63. package/corpus/templates/content/app/i18n-data.ts +186 -1
  64. package/corpus/templates/content/app/lib/github-link-dialog.ts +86 -0
  65. package/corpus/templates/content/changelog/2026-10-18-connected-ai-assistants-can-link-a-new-collection-to-a-github.md +1 -1
  66. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +6 -0
  67. package/corpus/templates/content/changelog/2026-10-19-list-rows-can-be-customized.md +6 -0
  68. package/corpus/templates/content/changelog/2026-10-19-list-views-fit-phone-screens.md +6 -0
  69. package/corpus/templates/content/changelog/2026-10-21-organization-favorites-include-documents-shared-with-the-cur.md +6 -0
  70. package/corpus/templates/content/docs/product/capabilities/content.source.sync-policy.md +32 -7
  71. package/corpus/templates/content/docs/product/encyclopedia.md +3 -3
  72. package/corpus/templates/content/docs/product/features/content.feature.trust-your-connected-sources.md +2 -2
  73. package/corpus/templates/content/docs/product/roadmap.md +1 -1
  74. package/corpus/templates/content/parity/matrix.md +42 -42
  75. package/corpus/templates/content/parity/matrix.ts +14 -0
  76. package/corpus/templates/content/server/lib/github-source-client.ts +50 -1
  77. package/corpus/templates/content/shared/api.ts +21 -0
  78. package/corpus/templates/content/shared/database-item-layout.ts +591 -0
  79. package/corpus/templates/content/shared/github-source.ts +68 -0
  80. package/corpus/templates/design/CHANGELOG.md +1 -0
  81. package/corpus/templates/design/actions/connect-assets-mcp.ts +5 -9
  82. package/corpus/templates/design/changelog/2026-10-21-journey-canvases-accept-partial-analytics-samples-when-repla.md +6 -0
  83. package/corpus/templates/design/shared/journey-canvas.ts +6 -4
  84. package/corpus/templates/dispatch/.agents/skills/a2a-protocol/SKILL.md +16 -13
  85. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  86. package/corpus/templates/dispatch/changelog/2026-10-21-customer-ai-credit-questions-route-to-account-health-instead.md +6 -0
  87. package/corpus/templates/factory/server/lib/require-workspace-member.ts +5 -17
  88. package/corpus/templates/mail/server/lib/queued-drafts.ts +9 -4
  89. package/corpus/templates/slides/server/workspace-defaults.ts +12 -15
  90. package/package.json +1 -1
@@ -32,6 +32,7 @@ import {
32
32
  type ContentDatabaseFilterMode,
33
33
  type ContentDatabaseFilterOperator,
34
34
  type ContentDatabaseFormQuestion,
35
+ type ContentDatabaseItemLayout,
35
36
  type ContentDatabaseOpenPagesIn,
36
37
  type ContentDatabaseRowDensity,
37
38
  type ContentDatabaseSort,
@@ -46,6 +47,7 @@ import {
46
47
  } from "@shared/api";
47
48
  import { contentRecentHref } from "@shared/content-personal-navigation";
48
49
  import { contentDatabaseFormQuestions } from "@shared/database-form";
50
+ import { contentDatabaseItemLayoutField } from "@shared/database-item-layout";
49
51
  import {
50
52
  applyContentDatabaseTableQuery,
51
53
  contentDatabaseFilterDateRangeValues,
@@ -57,6 +59,7 @@ import {
57
59
  databaseFrozenColumnIds,
58
60
  reorderDatabaseTableColumn,
59
61
  } from "@shared/database-table-columns";
62
+ import { GITHUB_SOURCE_TYPE } from "@shared/github-source";
60
63
  import {
61
64
  type DocumentPropertyOptionColor,
62
65
  countWords,
@@ -75,6 +78,7 @@ import {
75
78
  IconArrowUp,
76
79
  IconAdjustmentsHorizontal,
77
80
  IconArrowsDiagonal,
81
+ IconBrandGithub,
78
82
  IconArrowsSort,
79
83
  IconCalendar,
80
84
  IconCalendarDue,
@@ -114,6 +118,7 @@ import { useQueryClient } from "@tanstack/react-query";
114
118
  import {
115
119
  useEffect,
116
120
  useCallback,
121
+ useId,
117
122
  useMemo,
118
123
  useRef,
119
124
  useState,
@@ -326,6 +331,10 @@ import {
326
331
  } from "./DatabaseViewSkeleton";
327
332
  import { DatabaseFormView } from "./FormView";
328
333
  import { DatabaseGalleryView } from "./GalleryView";
334
+ import type { GitHubCollectionPlace } from "./github-availability";
335
+ import { GitHubSourcePanel, useGitHubSourcesRow } from "./GitHubSourcePanel";
336
+ import { GitHubSourceStatusLine } from "./GitHubSourceStatusLine";
337
+ import { DatabaseListRowsSetting } from "./ListItemLayout";
329
338
  import { DatabaseListView } from "./ListView";
330
339
  import {
331
340
  DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT,
@@ -1119,6 +1128,9 @@ function DatabaseTable({
1119
1128
  >(null);
1120
1129
  const [searchOpen, setSearchOpen] = useState(false);
1121
1130
  const [settingsOpen, setSettingsOpen] = useState(false);
1131
+ const [editingListRowsViewId, setEditingListRowsViewId] = useState<
1132
+ string | null
1133
+ >(null);
1122
1134
  const settingsTriggerRef = useRef<HTMLButtonElement | null>(null);
1123
1135
  const [preserveSourceNavigationOnClose, setPreserveSourceNavigationOnClose] =
1124
1136
  useState(false);
@@ -2543,6 +2555,10 @@ function DatabaseTable({
2543
2555
  updateActiveView((view) => ({ ...view, formQuestions }));
2544
2556
  }
2545
2557
 
2558
+ function setItemLayout(itemLayout: ContentDatabaseItemLayout | null) {
2559
+ updateActiveView((view) => ({ ...view, itemLayout }));
2560
+ }
2561
+
2546
2562
  function setGroupCollapsed(groupId: string, collapsed: boolean) {
2547
2563
  updateActiveView((view) =>
2548
2564
  setDatabaseViewCollapsedGroup(view, groupId, collapsed),
@@ -3493,6 +3509,39 @@ function DatabaseTable({
3493
3509
  );
3494
3510
  })}
3495
3511
 
3512
+ <GitHubSourceStatusLine
3513
+ source={
3514
+ sources.find(
3515
+ (candidate) => candidate.sourceType === GITHUB_SOURCE_TYPE,
3516
+ ) ?? null
3517
+ }
3518
+ documentId={document.id}
3519
+ canEdit={canEdit}
3520
+ pageCount={totalItemCount}
3521
+ />
3522
+ {canEdit &&
3523
+ databaseId &&
3524
+ data &&
3525
+ !data.database.systemRole &&
3526
+ !source &&
3527
+ sources.length === 0 &&
3528
+ totalItemCount === 0 ? (
3529
+ // One line for every source, so the list of them stays in Sources.
3530
+ <div className="mb-2 text-xs" data-empty-collection-source-hint>
3531
+ <button
3532
+ type="button"
3533
+ className="inline-flex items-center gap-1.5 rounded-sm text-muted-foreground underline-offset-2 hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
3534
+ onClick={() => {
3535
+ changeDatabaseSettingsPanel("source");
3536
+ setSettingsOpen(true);
3537
+ }}
3538
+ >
3539
+ <IconPlugConnected className="size-3.5" aria-hidden />
3540
+ {dbText("emptyCollectionSourceHint")}
3541
+ </button>
3542
+ </div>
3543
+ ) : null}
3544
+
3496
3545
  {database.itemsFailed ? (
3497
3546
  <QueryErrorState
3498
3547
  compact
@@ -3552,8 +3601,15 @@ function DatabaseTable({
3552
3601
  <DatabaseListView
3553
3602
  properties={tableProperties}
3554
3603
  groupableProperties={orderedProperties}
3604
+ layoutProperties={databaseListLayoutProperties(
3605
+ orderedProperties,
3606
+ activeView,
3607
+ )}
3555
3608
  items={visibleItems}
3609
+ databaseId={databaseId}
3556
3610
  databaseDocumentId={document.id}
3611
+ itemLayout={activeView.itemLayout ?? null}
3612
+ editingRows={editingListRowsViewId === activeView.id}
3557
3613
  canEdit={effectiveCanEdit}
3558
3614
  canCreateItems={canCreateItems}
3559
3615
  isLoading={isDatabaseViewLoading}
@@ -3571,6 +3627,10 @@ function DatabaseTable({
3571
3627
  onPreview={previewItemPage}
3572
3628
  onDeletedPreviewItem={handleDeletedPreviewItem}
3573
3629
  onOpenPage={openItemPage}
3630
+ onItemLayoutChange={setItemLayout}
3631
+ onEditingRowsChange={(editing) =>
3632
+ setEditingListRowsViewId(editing ? activeView.id : null)
3633
+ }
3574
3634
  />
3575
3635
  ) : activeView.type === "gallery" ? (
3576
3636
  <DatabaseGalleryView
@@ -3850,6 +3910,15 @@ function DatabaseTable({
3850
3910
  databaseId={databaseId}
3851
3911
  documentId={document.id}
3852
3912
  isFilesDatabase={document.database?.systemRole === "files"}
3913
+ githubPlace={{
3914
+ databaseId,
3915
+ documentId: document.id,
3916
+ spaceId: document.spaceId ?? undefined,
3917
+ parentId: document.parentId,
3918
+ systemRole: data?.database.systemRole ?? null,
3919
+ pageCount: totalItemCount,
3920
+ canEdit,
3921
+ }}
3853
3922
  canEdit={canEdit}
3854
3923
  canManage={document.canManage === true}
3855
3924
  activeView={activeView}
@@ -4005,6 +4074,12 @@ function DatabaseTable({
4005
4074
  onWrapCellsChange={setWrapCells}
4006
4075
  onOpenPagesInChange={setOpenPagesIn}
4007
4076
  onFormQuestionsChange={setFormQuestions}
4077
+ editingListRows={editingListRowsViewId === activeView.id}
4078
+ onItemLayoutChange={setItemLayout}
4079
+ onEditListRows={() => {
4080
+ setEditingListRowsViewId(activeView.id);
4081
+ closeDatabaseSettings();
4082
+ }}
4008
4083
  onPropertyHiddenChange={setPropertyHiddenInActiveView}
4009
4084
  onPropertiesHiddenChange={setPropertiesHiddenInActiveView}
4010
4085
  onGroupByChange={(propertyId) =>
@@ -4132,6 +4207,7 @@ export function databaseNavigationState({
4132
4207
  | "tableColumnOrderIds"
4133
4208
  | "rowDensity"
4134
4209
  | "openPagesIn"
4210
+ | "itemLayout"
4135
4211
  >;
4136
4212
  effectiveFrozenColumnIds?: string[];
4137
4213
  searchQuery?: string;
@@ -4246,6 +4322,8 @@ export function databaseNavigationState({
4246
4322
  activeView.openPagesIn === "full_page"
4247
4323
  ? activeView.openPagesIn
4248
4324
  : undefined,
4325
+ databaseItemLayout:
4326
+ activeView.type === "list" ? (activeView.itemLayout ?? null) : undefined,
4249
4327
  databaseVisibleItemCount: visibleItemCount,
4250
4328
  databaseTotalItemCount: totalItemCount,
4251
4329
  databaseVisibleItems: databaseVisibleItemSummaries(
@@ -6773,8 +6851,8 @@ function DatabaseInlineSortControl({
6773
6851
  size="sm"
6774
6852
  variant="ghost"
6775
6853
  className={cn(
6776
- "relative h-7 max-w-[16rem] rounded border border-[#2383e2]/30 bg-[#2383e2]/10 px-2 text-xs font-normal text-[#0f5ea8] shadow-none hover:bg-[#2383e2]/15 focus-visible:border-[#2383e2]/40 focus-visible:ring-[#2383e2]/25 dark:border-[#529cca]/35 dark:bg-[#529cca]/15 dark:text-[#8ec7ff] dark:hover:bg-[#529cca]/20",
6777
- open && "border-[#2383e2]/40 bg-[#2383e2]/15",
6854
+ "relative h-7 max-w-[16rem] rounded border border-database-chip/30 bg-database-chip/10 px-2 text-xs font-normal text-database-chip-foreground shadow-none hover:bg-database-chip/15 focus-visible:border-database-blue/40 focus-visible:ring-database-blue/25 dark:border-database-chip/35 dark:bg-database-chip/15 dark:hover:bg-database-chip/20",
6855
+ open && "border-database-blue/40 bg-database-blue/15",
6778
6856
  )}
6779
6857
  >
6780
6858
  {sort.direction === "asc" ? (
@@ -7091,8 +7169,8 @@ function DatabaseAdvancedFilterGroupControl({
7091
7169
  size="sm"
7092
7170
  variant="ghost"
7093
7171
  className={cn(
7094
- "relative h-7 rounded border border-[#2383e2]/30 bg-[#2383e2]/10 px-2 text-xs font-normal text-[#0f5ea8] shadow-none hover:bg-[#2383e2]/15 focus-visible:border-[#2383e2]/40 focus-visible:ring-[#2383e2]/25 dark:border-[#529cca]/35 dark:bg-[#529cca]/15 dark:text-[#8ec7ff] dark:hover:bg-[#529cca]/20",
7095
- open && "border-[#2383e2]/40 bg-[#2383e2]/15",
7172
+ "relative h-7 rounded border border-database-chip/30 bg-database-chip/10 px-2 text-xs font-normal text-database-chip-foreground shadow-none hover:bg-database-chip/15 focus-visible:border-database-blue/40 focus-visible:ring-database-blue/25 dark:border-database-chip/35 dark:bg-database-chip/15 dark:hover:bg-database-chip/20",
7173
+ open && "border-database-blue/40 bg-database-blue/15",
7096
7174
  )}
7097
7175
  >
7098
7176
  <IconFilter className="size-3.5 shrink-0" />
@@ -7592,10 +7670,10 @@ function DatabaseInlineFilterControl({
7592
7670
  size="sm"
7593
7671
  variant="ghost"
7594
7672
  className={cn(
7595
- "relative h-7 max-w-[16rem] rounded border border-border/70 bg-background px-2 text-xs font-normal text-foreground shadow-none hover:bg-muted focus-visible:border-[#2383e2]/40 focus-visible:ring-[#2383e2]/25",
7673
+ "relative h-7 max-w-[16rem] rounded border border-border/70 bg-background px-2 text-xs font-normal text-foreground shadow-none hover:bg-muted focus-visible:border-database-blue/40 focus-visible:ring-database-blue/25",
7596
7674
  filterIsComplete &&
7597
- "border-[#2383e2]/30 bg-[#2383e2]/10 text-[#0f5ea8] hover:bg-[#2383e2]/15 dark:border-[#529cca]/35 dark:bg-[#529cca]/15 dark:text-[#8ec7ff] dark:hover:bg-[#529cca]/20",
7598
- open && "border-[#2383e2]/40 bg-[#2383e2]/15",
7675
+ "border-database-chip/30 bg-database-chip/10 text-database-chip-foreground hover:bg-database-chip/15 dark:border-database-chip/35 dark:bg-database-chip/15 dark:hover:bg-database-chip/20",
7676
+ open && "border-database-blue/40 bg-database-blue/15",
7599
7677
  !filterIsComplete && "text-muted-foreground",
7600
7678
  )}
7601
7679
  >
@@ -7787,7 +7865,8 @@ type SourceNavStep =
7787
7865
  | { kind: "addSource" }
7788
7866
  | { kind: "secondarySource"; sourceId: string; sourceName: string }
7789
7867
  | { kind: "keyConfirm"; candidate: PendingSourceCandidate }
7790
- | { kind: "fieldPicker"; sourceId: string; sourceName: string };
7868
+ | { kind: "fieldPicker"; sourceId: string; sourceName: string }
7869
+ | { kind: "github" };
7791
7870
 
7792
7871
  function sourceNavTitle(stack: SourceNavStep[]): string {
7793
7872
  const top = stack[stack.length - 1];
@@ -7798,6 +7877,7 @@ function sourceNavTitle(stack: SourceNavStep[]): string {
7798
7877
  if (top.kind === "secondarySource") return top.sourceName;
7799
7878
  if (top.kind === "keyConfirm") return dbText("matchExistingItemsToDetails");
7800
7879
  if (top.kind === "fieldPicker") return dbText("chooseFields");
7880
+ if (top.kind === "github") return "GitHub";
7801
7881
  return top.model?.displayName ?? top.sourceName ?? "Builder";
7802
7882
  }
7803
7883
 
@@ -7844,6 +7924,7 @@ function DatabaseSettingsPanelSheet({
7844
7924
  databaseId,
7845
7925
  documentId,
7846
7926
  isFilesDatabase,
7927
+ githubPlace,
7847
7928
  canEdit,
7848
7929
  canManage,
7849
7930
  activeView,
@@ -7871,6 +7952,9 @@ function DatabaseSettingsPanelSheet({
7871
7952
  onWrapCellsChange,
7872
7953
  onOpenPagesInChange,
7873
7954
  onFormQuestionsChange,
7955
+ editingListRows,
7956
+ onItemLayoutChange,
7957
+ onEditListRows,
7874
7958
  onPropertyHiddenChange,
7875
7959
  onPropertiesHiddenChange,
7876
7960
  onGroupByChange,
@@ -7883,6 +7967,7 @@ function DatabaseSettingsPanelSheet({
7883
7967
  databaseId: string;
7884
7968
  documentId: string;
7885
7969
  isFilesDatabase: boolean;
7970
+ githubPlace: GitHubCollectionPlace;
7886
7971
  canEdit: boolean;
7887
7972
  canManage: boolean;
7888
7973
  activeView: ContentDatabaseView;
@@ -7920,6 +8005,9 @@ function DatabaseSettingsPanelSheet({
7920
8005
  onWrapCellsChange: (wrapCells: boolean) => void;
7921
8006
  onOpenPagesInChange: (openPagesIn: ContentDatabaseOpenPagesIn) => void;
7922
8007
  onFormQuestionsChange: (formQuestions: ContentDatabaseFormQuestion[]) => void;
8008
+ editingListRows: boolean;
8009
+ onItemLayoutChange: (itemLayout: ContentDatabaseItemLayout | null) => void;
8010
+ onEditListRows: () => void;
7923
8011
  onPropertyHiddenChange: (propertyId: string, hidden: boolean) => void;
7924
8012
  onPropertiesHiddenChange: (propertyIds: string[], hidden: boolean) => void;
7925
8013
  onGroupByChange: (propertyId: string | null) => void;
@@ -8053,6 +8141,7 @@ function DatabaseSettingsPanelSheet({
8053
8141
  databaseId={databaseId}
8054
8142
  documentId={documentId}
8055
8143
  isFilesDatabase={isFilesDatabase}
8144
+ githubPlace={githubPlace}
8056
8145
  itemCount={items.length}
8057
8146
  canEdit={canEdit}
8058
8147
  canManage={canManage}
@@ -8081,10 +8170,14 @@ function DatabaseSettingsPanelSheet({
8081
8170
  <DatabaseSettingsLayoutPanel
8082
8171
  activeView={activeView}
8083
8172
  properties={properties}
8173
+ items={items}
8174
+ editingListRows={editingListRows}
8084
8175
  onViewTypeChange={onViewTypeChange}
8085
8176
  onWrapCellsChange={onWrapCellsChange}
8086
8177
  onOpenPagesInChange={onOpenPagesInChange}
8087
8178
  onFormQuestionsChange={onFormQuestionsChange}
8179
+ onItemLayoutChange={onItemLayoutChange}
8180
+ onEditListRows={onEditListRows}
8088
8181
  />
8089
8182
  ) : panel === "property_visibility" ? (
8090
8183
  <DatabaseSettingsPropertyVisibilityPanel
@@ -8461,6 +8554,7 @@ function DatabaseSettingsSourcePanel({
8461
8554
  databaseId,
8462
8555
  documentId,
8463
8556
  isFilesDatabase,
8557
+ githubPlace,
8464
8558
  itemCount,
8465
8559
  canEdit,
8466
8560
  canManage,
@@ -8485,6 +8579,7 @@ function DatabaseSettingsSourcePanel({
8485
8579
  databaseId: string;
8486
8580
  documentId: string;
8487
8581
  isFilesDatabase: boolean;
8582
+ githubPlace: GitHubCollectionPlace;
8488
8583
  itemCount: number;
8489
8584
  canEdit: boolean;
8490
8585
  canManage: boolean;
@@ -8571,6 +8666,8 @@ function DatabaseSettingsSourcePanel({
8571
8666
  navigate(`/local-files?databaseId=${databaseId}`)
8572
8667
  }
8573
8668
  showLocalFolder={isFilesDatabase}
8669
+ githubPlace={githubPlace}
8670
+ onOpenGitHubStep={() => onNavPush({ kind: "github" })}
8574
8671
  onOpenSecondary={(secondary) =>
8575
8672
  onNavPush({
8576
8673
  kind: "secondarySource",
@@ -8583,6 +8680,16 @@ function DatabaseSettingsSourcePanel({
8583
8680
  );
8584
8681
  }
8585
8682
 
8683
+ if (top.kind === "github") {
8684
+ return (
8685
+ <GitHubSourcePanel
8686
+ place={githubPlace}
8687
+ sources={sources.length > 0 ? sources : source ? [source] : []}
8688
+ onUnlinked={() => onNavReplace([])}
8689
+ />
8690
+ );
8691
+ }
8692
+
8586
8693
  if (top.kind === "addSource") {
8587
8694
  return (
8588
8695
  <AddSourceView
@@ -9014,7 +9121,7 @@ function DatabaseSettingsSourcePanel({
9014
9121
  className={cn(
9015
9122
  "min-w-0 rounded px-2 py-1.5 text-[11px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:opacity-60",
9016
9123
  selected
9017
- ? "bg-[#2383e2] text-white shadow-sm"
9124
+ ? "bg-database-blue text-database-blue-foreground shadow-sm"
9018
9125
  : "text-muted-foreground hover:bg-background hover:text-foreground",
9019
9126
  )}
9020
9127
  onClick={() =>
@@ -9055,7 +9162,7 @@ function DatabaseSettingsSourcePanel({
9055
9162
  className={cn(
9056
9163
  "mt-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded border",
9057
9164
  liveWriteControl.allowPublicationTransitions
9058
- ? "border-[#2383e2] bg-[#2383e2] text-white"
9165
+ ? "border-database-blue bg-database-blue text-database-blue-foreground"
9059
9166
  : "border-muted-foreground/40 bg-background text-transparent",
9060
9167
  )}
9061
9168
  >
@@ -9219,7 +9326,7 @@ function DatabaseSettingsSourcePanel({
9219
9326
  );
9220
9327
  }
9221
9328
 
9222
- function SourcesListView({
9329
+ export function SourcesListView({
9223
9330
  source,
9224
9331
  sources,
9225
9332
  builderConfigured,
@@ -9229,6 +9336,8 @@ function SourcesListView({
9229
9336
  onOpenNotion,
9230
9337
  onOpenLocalFolder,
9231
9338
  showLocalFolder,
9339
+ githubPlace,
9340
+ onOpenGitHubStep,
9232
9341
  onOpenSecondary,
9233
9342
  onAddSource,
9234
9343
  }: {
@@ -9241,6 +9350,9 @@ function SourcesListView({
9241
9350
  onOpenNotion: () => void;
9242
9351
  onOpenLocalFolder: () => void;
9243
9352
  showLocalFolder: boolean;
9353
+ githubPlace: GitHubCollectionPlace;
9354
+ /** Opens the GitHub step: the linked folder, or why one can't link here. */
9355
+ onOpenGitHubStep: () => void;
9244
9356
  onOpenSecondary: (source: ContentDatabaseSource) => void;
9245
9357
  onAddSource: () => void;
9246
9358
  }) {
@@ -9248,6 +9360,20 @@ function SourcesListView({
9248
9360
  const isBuilderSource = source?.sourceType === "builder-cms";
9249
9361
  const connectedSources =
9250
9362
  sources.length > 0 ? sources : source ? [source] : [];
9363
+ const github = useGitHubSourcesRow(
9364
+ githubPlace,
9365
+ connectedSources,
9366
+ onOpenGitHubStep,
9367
+ );
9368
+ const githubSource =
9369
+ github.availability.kind === "linked"
9370
+ ? github.availability.source
9371
+ : undefined;
9372
+ // A GitHub folder is the collection's whole content, so other apps
9373
+ // explain that instead of leading to a refusal.
9374
+ const lockedByGitHub = githubSource
9375
+ ? dbText("githubRowUnavailableWhileLinked")
9376
+ : undefined;
9251
9377
  return (
9252
9378
  <div className="grid min-w-0 gap-4">
9253
9379
  {connectedSources.length === 0 ? (
@@ -9265,30 +9391,51 @@ function SourcesListView({
9265
9391
  icon={
9266
9392
  connected.sourceType === "builder-cms" ? (
9267
9393
  <BuilderLogoMark className="size-4" />
9394
+ ) : connected.sourceType === GITHUB_SOURCE_TYPE ? (
9395
+ <IconBrandGithub className="size-4" />
9268
9396
  ) : (
9269
9397
  <IconLayoutGrid className="size-4" />
9270
9398
  )
9271
9399
  }
9272
- label={connected.sourceName}
9273
- value={sourceRoleLabel(connected, index)}
9400
+ label={
9401
+ connected.sourceType === GITHUB_SOURCE_TYPE
9402
+ ? "GitHub"
9403
+ : connected.sourceName
9404
+ }
9405
+ value={
9406
+ connected.sourceType === GITHUB_SOURCE_TYPE
9407
+ ? undefined
9408
+ : sourceRoleLabel(connected, index)
9409
+ }
9410
+ description={
9411
+ connected.sourceType === GITHUB_SOURCE_TYPE
9412
+ ? github.description
9413
+ : undefined
9414
+ }
9274
9415
  onClick={
9275
9416
  connected.metadata.federation?.role === "secondary"
9276
9417
  ? () => onOpenSecondary(connected)
9277
9418
  : connected.sourceType === "builder-cms"
9278
9419
  ? () => onOpenBuilder(connected)
9279
- : undefined
9420
+ : connected.sourceType === GITHUB_SOURCE_TYPE
9421
+ ? onOpenGitHubStep
9422
+ : undefined
9280
9423
  }
9281
9424
  disabled={
9282
9425
  connected.metadata.federation?.role !== "secondary" &&
9283
- connected.sourceType !== "builder-cms"
9426
+ connected.sourceType !== "builder-cms" &&
9427
+ connected.sourceType !== GITHUB_SOURCE_TYPE
9284
9428
  }
9285
9429
  />
9286
9430
  ))}
9287
- <DatabaseSettingsRow
9288
- icon={<IconPlus className="size-4" />}
9289
- label={dbText("addAnotherSource")}
9290
- onClick={onAddSource}
9291
- />
9431
+ {/* A GitHub folder is the collection's whole content, so nothing joins it. */}
9432
+ {githubSource ? null : (
9433
+ <DatabaseSettingsRow
9434
+ icon={<IconPlus className="size-4" />}
9435
+ label={dbText("addAnotherSource")}
9436
+ onClick={onAddSource}
9437
+ />
9438
+ )}
9292
9439
  </div>
9293
9440
  )}
9294
9441
  <div className="grid min-w-0 gap-1.5">
@@ -9306,7 +9453,9 @@ function SourcesListView({
9306
9453
  : undefined
9307
9454
  }
9308
9455
  badgeCount={reviewableCount}
9309
- onClick={onOpenBuilder}
9456
+ description={lockedByGitHub}
9457
+ muted={!!lockedByGitHub}
9458
+ onClick={lockedByGitHub ? onOpenGitHubStep : () => onOpenBuilder()}
9310
9459
  />
9311
9460
  <DatabaseSettingsRow
9312
9461
  icon={<NotionLogoMark className="size-4" />}
@@ -9318,8 +9467,19 @@ function SourcesListView({
9318
9467
  ? dbText("connected")
9319
9468
  : undefined
9320
9469
  }
9321
- onClick={onOpenNotion}
9470
+ description={lockedByGitHub}
9471
+ muted={!!lockedByGitHub}
9472
+ onClick={lockedByGitHub ? onOpenGitHubStep : onOpenNotion}
9322
9473
  />
9474
+ {githubSource ? null : (
9475
+ <DatabaseSettingsRow
9476
+ icon={<IconBrandGithub className="size-4" />}
9477
+ label="GitHub"
9478
+ description={github.description}
9479
+ muted={github.muted}
9480
+ onClick={github.open}
9481
+ />
9482
+ )}
9323
9483
  {showLocalFolder ? (
9324
9484
  <DatabaseSettingsRow
9325
9485
  icon={<IconFolder className="size-4" />}
@@ -10416,7 +10576,7 @@ function SourceDetailsFieldPicker({
10416
10576
  className={cn(
10417
10577
  "flex size-4 shrink-0 items-center justify-center rounded border",
10418
10578
  selected.has(field.id)
10419
- ? "border-[#2383e2] bg-[#2383e2] text-white"
10579
+ ? "border-database-blue bg-database-blue text-database-blue-foreground"
10420
10580
  : "border-muted-foreground/40 text-transparent",
10421
10581
  )}
10422
10582
  >
@@ -11000,37 +11160,70 @@ function DatabaseSettingsRow({
11000
11160
  icon,
11001
11161
  label,
11002
11162
  value,
11163
+ description,
11003
11164
  badgeCount = 0,
11004
11165
  disabled = false,
11166
+ muted = false,
11005
11167
  onClick,
11006
11168
  }: {
11007
11169
  icon: ReactNode;
11008
11170
  label: string;
11009
11171
  value?: string;
11172
+ /** A second line that's always shown, like why the row can't be used. */
11173
+ description?: string;
11010
11174
  badgeCount?: number;
11011
11175
  disabled?: boolean;
11012
- onClick?: () => void;
11176
+ /**
11177
+ * Looks unavailable but stays focusable and clickable, so its reason can
11178
+ * still be opened by keyboard, touch and screen readers.
11179
+ */
11180
+ muted?: boolean;
11181
+ onClick?: (button: HTMLButtonElement) => void;
11013
11182
  }) {
11014
11183
  const badgeLabel = formatCompactCountBadge(badgeCount);
11184
+ const descriptionId = useId();
11015
11185
  return (
11016
11186
  <button
11017
11187
  type="button"
11018
11188
  disabled={disabled || !onClick}
11189
+ aria-disabled={muted || undefined}
11190
+ aria-describedby={description ? descriptionId : undefined}
11019
11191
  className={cn(
11020
- "flex h-9 w-full min-w-0 items-center gap-2 rounded-md px-2 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
11192
+ "flex w-full min-w-0 items-center gap-2 rounded-md px-2 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
11193
+ description ? "min-h-9 py-1.5" : "h-9",
11021
11194
  disabled || !onClick
11022
11195
  ? "cursor-default text-muted-foreground/60"
11023
- : "text-foreground hover:bg-muted",
11196
+ : muted
11197
+ ? "text-muted-foreground/60 hover:bg-muted"
11198
+ : "text-foreground hover:bg-muted",
11024
11199
  )}
11025
11200
  onClick={(event: ReactMouseEvent<HTMLButtonElement>) => {
11026
11201
  event.stopPropagation();
11027
- onClick?.();
11202
+ onClick?.(event.currentTarget);
11028
11203
  }}
11029
11204
  >
11030
- <span className="flex size-5 shrink-0 items-center justify-center text-muted-foreground">
11205
+ <span
11206
+ className={cn(
11207
+ "flex size-5 shrink-0 items-center justify-center text-muted-foreground",
11208
+ muted && "opacity-60",
11209
+ description && "self-start",
11210
+ )}
11211
+ >
11031
11212
  {icon}
11032
11213
  </span>
11033
- <span className="min-w-0 flex-1 truncate">{label}</span>
11214
+ {description ? (
11215
+ <span className="grid min-w-0 flex-1">
11216
+ <span className="truncate">{label}</span>
11217
+ <span
11218
+ id={descriptionId}
11219
+ className="break-words text-xs text-muted-foreground"
11220
+ >
11221
+ {description}
11222
+ </span>
11223
+ </span>
11224
+ ) : (
11225
+ <span className="min-w-0 flex-1 truncate">{label}</span>
11226
+ )}
11034
11227
  {value ? (
11035
11228
  <span className="max-w-28 shrink-0 truncate text-xs text-muted-foreground">
11036
11229
  {value}
@@ -11076,7 +11269,7 @@ function DatabaseSettingsSwitch({
11076
11269
  <span
11077
11270
  className={cn(
11078
11271
  "relative h-5 w-9 rounded-full transition-colors",
11079
- checked ? "bg-[#2383e2]" : "bg-muted-foreground/25",
11272
+ checked ? "bg-database-blue" : "bg-muted-foreground/25",
11080
11273
  )}
11081
11274
  >
11082
11275
  <span
@@ -11093,17 +11286,25 @@ function DatabaseSettingsSwitch({
11093
11286
  function DatabaseSettingsLayoutPanel({
11094
11287
  activeView,
11095
11288
  properties,
11289
+ items,
11290
+ editingListRows,
11096
11291
  onViewTypeChange,
11097
11292
  onWrapCellsChange,
11098
11293
  onOpenPagesInChange,
11099
11294
  onFormQuestionsChange,
11295
+ onItemLayoutChange,
11296
+ onEditListRows,
11100
11297
  }: {
11101
11298
  activeView: ContentDatabaseView;
11102
11299
  properties: DocumentProperty[];
11300
+ items: ContentDatabaseItem[];
11301
+ editingListRows: boolean;
11103
11302
  onViewTypeChange: (type: ContentDatabaseViewType) => void;
11104
11303
  onWrapCellsChange: (wrapCells: boolean) => void;
11105
11304
  onOpenPagesInChange: (openPagesIn: ContentDatabaseOpenPagesIn) => void;
11106
11305
  onFormQuestionsChange: (formQuestions: ContentDatabaseFormQuestion[]) => void;
11306
+ onItemLayoutChange: (itemLayout: ContentDatabaseItemLayout | null) => void;
11307
+ onEditListRows: () => void;
11107
11308
  }) {
11108
11309
  const wrapCells = activeView.wrapCells === true;
11109
11310
  const openPagesIn = activeView.openPagesIn ?? "preview";
@@ -11119,7 +11320,7 @@ function DatabaseSettingsLayoutPanel({
11119
11320
  className={cn(
11120
11321
  "flex h-16 flex-col items-center justify-center gap-1 rounded-md border text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
11121
11322
  activeView.type === type
11122
- ? "border-[#2383e2] bg-[#2383e2]/5 text-[#2383e2]"
11323
+ ? "border-database-blue bg-database-blue/5 text-database-blue"
11123
11324
  : "border-border text-muted-foreground hover:bg-muted hover:text-foreground",
11124
11325
  )}
11125
11326
  onClick={() => onViewTypeChange(type)}
@@ -11148,6 +11349,18 @@ function DatabaseSettingsLayoutPanel({
11148
11349
  onChange={onFormQuestionsChange}
11149
11350
  />
11150
11351
  ) : null}
11352
+ {activeView.type === "list" ? (
11353
+ <DatabaseListRowsSetting
11354
+ itemLayout={activeView.itemLayout ?? null}
11355
+ properties={databaseListLayoutProperties(properties, activeView)}
11356
+ visibleProperties={properties.filter((property) =>
11357
+ isDatabasePropertyVisibleInView(property, items, activeView),
11358
+ )}
11359
+ editingOnList={editingListRows}
11360
+ onChange={onItemLayoutChange}
11361
+ onEditOnList={onEditListRows}
11362
+ />
11363
+ ) : null}
11151
11364
  </div>
11152
11365
  );
11153
11366
  }
@@ -13715,6 +13928,7 @@ export function createDatabaseView(
13715
13928
  rowDensity: normalizeClientDatabaseRowDensity(values.rowDensity),
13716
13929
  openPagesIn: normalizeClientDatabaseOpenPagesIn(values.openPagesIn),
13717
13930
  formQuestions: normalizeClientDatabaseFormQuestions(values.formQuestions),
13931
+ ...contentDatabaseItemLayoutField(values.itemLayout),
13718
13932
  };
13719
13933
  }
13720
13934
 
@@ -13984,6 +14198,7 @@ export function duplicateDatabaseView(
13984
14198
  rowDensity: view.rowDensity,
13985
14199
  openPagesIn: view.openPagesIn,
13986
14200
  formQuestions: view.formQuestions,
14201
+ itemLayout: view.itemLayout,
13987
14202
  },
13988
14203
  view.type,
13989
14204
  );
@@ -14126,6 +14341,7 @@ function normalizeClientDatabaseView(
14126
14341
  rowDensity: normalizeClientDatabaseRowDensity(value.rowDensity),
14127
14342
  openPagesIn: normalizeClientDatabaseOpenPagesIn(value.openPagesIn),
14128
14343
  formQuestions: normalizeClientDatabaseFormQuestions(value.formQuestions),
14344
+ itemLayout: value.itemLayout,
14129
14345
  },
14130
14346
  type,
14131
14347
  );
@@ -14483,6 +14699,18 @@ export function isDatabasePropertyVisibleInView(
14483
14699
  );
14484
14700
  }
14485
14701
 
14702
+ /**
14703
+ * The properties a list's custom rows can show: all but those hidden in this
14704
+ * view. Always-hidden properties are left out by the row layout itself.
14705
+ */
14706
+ function databaseListLayoutProperties(
14707
+ properties: DocumentProperty[],
14708
+ view: Pick<ContentDatabaseView, "hiddenPropertyIds">,
14709
+ ) {
14710
+ const hidden = new Set(view.hiddenPropertyIds ?? []);
14711
+ return properties.filter((property) => !hidden.has(property.definition.id));
14712
+ }
14713
+
14486
14714
  export function setDatabaseViewHiddenPropertyIds(
14487
14715
  view: ContentDatabaseView,
14488
14716
  propertyIds: string[],
@@ -17839,7 +18067,7 @@ function DatabaseFilterValueControl({
17839
18067
  return (
17840
18068
  <div className="overflow-hidden rounded-sm">
17841
18069
  <div className="px-1 pb-1">
17842
- <div className="flex min-h-9 flex-wrap items-center gap-1 rounded border border-input bg-background px-2 py-1 focus-within:border-[#2383e2] focus-within:ring-2 focus-within:ring-[#2383e2]/25">
18070
+ <div className="flex min-h-9 flex-wrap items-center gap-1 rounded border border-input bg-background px-2 py-1 focus-within:border-database-blue focus-within:ring-2 focus-within:ring-database-blue/25">
17843
18071
  {showCompactOptionValueInput ? null : (
17844
18072
  <IconSearch className="size-3.5 shrink-0 text-muted-foreground" />
17845
18073
  )}
@@ -17961,7 +18189,7 @@ function DatabaseFilterValueControl({
17961
18189
  value={filter.value}
17962
18190
  placeholder={filterValuePlaceholder(filter.key, properties)}
17963
18191
  onChange={(event) => onValueChange(event.target.value)}
17964
- className="h-9 text-sm focus-visible:border-[#2383e2] focus-visible:ring-[#2383e2]/25"
18192
+ className="h-9 text-sm focus-visible:border-database-blue focus-visible:ring-database-blue/25"
17965
18193
  />
17966
18194
  );
17967
18195
  }
@@ -18102,7 +18330,7 @@ function DatabaseBetweenDateFilterValueControl({
18102
18330
  value={start}
18103
18331
  aria-label={`Start ${filter.label} filter date`}
18104
18332
  onChange={(event) => updateRange(event.target.value, end)}
18105
- className="h-9 text-sm focus-visible:border-[#2383e2] focus-visible:ring-[#2383e2]/25"
18333
+ className="h-9 text-sm focus-visible:border-database-blue focus-visible:ring-database-blue/25"
18106
18334
  />
18107
18335
  <span className="text-xs text-muted-foreground">to</span>
18108
18336
  <Input
@@ -18110,7 +18338,7 @@ function DatabaseBetweenDateFilterValueControl({
18110
18338
  value={end}
18111
18339
  aria-label={`End ${filter.label} filter date`}
18112
18340
  onChange={(event) => updateRange(start, event.target.value)}
18113
- className="h-9 text-sm focus-visible:border-[#2383e2] focus-visible:ring-[#2383e2]/25"
18341
+ className="h-9 text-sm focus-visible:border-database-blue focus-visible:ring-database-blue/25"
18114
18342
  />
18115
18343
  </div>
18116
18344
  );