@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/onboarding-journeys/SKILL.md +29 -14
- package/corpus/templates/analytics/CHANGELOG.md +11 -0
- package/corpus/templates/analytics/actions/estimate-onboarding-journey-cost.ts +23 -6
- package/corpus/templates/analytics/actions/get-onboarding-journey.ts +20 -5
- package/corpus/templates/analytics/actions/provider-api-register.ts +2 -7
- package/corpus/templates/analytics/changelog/2026-10-21-analytics-recognizes-questions-about-connected-data-product-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-21-onboarding-journeys-now-honor-larger-event-row-budgets.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-21-preserve-unknown-replay-availability-in-partial-journeys.md +6 -0
- package/corpus/templates/analytics/package.json +6 -0
- package/corpus/templates/analytics/scripts/journey-capture-plan.ts +18 -7
- package/corpus/templates/analytics/scripts/journey-capture.ts +3 -1
- package/corpus/templates/analytics/server/lib/db-admin-connections.ts +19 -12
- package/corpus/templates/analytics/server/lib/journey-tree.ts +24 -9
- package/corpus/templates/analytics/server/lib/onboarding-journey-cost.ts +163 -94
- package/corpus/templates/analytics/server/lib/onboarding-journey.ts +162 -74
- package/corpus/templates/assets/server/lib/org-admin.ts +20 -16
- package/corpus/templates/brain/server/lib/zoom-governance.ts +11 -6
- package/corpus/templates/clips/CHANGELOG.md +1 -0
- package/corpus/templates/clips/changelog/2026-10-16-rewind-status-says-when-it-remembers-audio.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +7 -4
- package/corpus/templates/clips/desktop/src/components/RewindCaptureStatus.tsx +25 -0
- package/corpus/templates/clips/desktop/src/lib/rewind-status.ts +6 -1
- package/corpus/templates/clips/desktop/src/shared/config.ts +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +2 -0
- package/corpus/templates/clips/server/lib/recordings.ts +4 -19
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +15 -1
- package/corpus/templates/content/CHANGELOG.md +13 -0
- package/corpus/templates/content/actions/_content-space-access.ts +95 -6
- package/corpus/templates/content/actions/_database-utils.ts +20 -16
- package/corpus/templates/content/actions/_database-view-setup.ts +305 -11
- package/corpus/templates/content/actions/_github-source-freshness.ts +72 -17
- package/corpus/templates/content/actions/_github-source-metadata.ts +63 -17
- package/corpus/templates/content/actions/_github-source.ts +141 -77
- package/corpus/templates/content/actions/_property-utils.ts +3 -0
- package/corpus/templates/content/actions/check-github-connection.ts +15 -0
- package/corpus/templates/content/actions/create-github-collection.ts +180 -0
- package/corpus/templates/content/actions/disconnect-content-database-source.ts +5 -0
- package/corpus/templates/content/actions/link-github-folder.ts +104 -36
- package/corpus/templates/content/actions/refresh-content-database-source.ts +37 -4
- package/corpus/templates/content/actions/sync-github-folder.ts +23 -5
- package/corpus/templates/content/actions/update-content-database-view.ts +18 -2
- package/corpus/templates/content/actions/view-screen.ts +12 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +263 -35
- package/corpus/templates/content/app/components/editor/database/GitHubLinkDialog.tsx +938 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourcePanel.tsx +228 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +561 -0
- package/corpus/templates/content/app/components/editor/database/GitHubUnlinkDialog.tsx +96 -0
- package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +410 -0
- package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +426 -0
- package/corpus/templates/content/app/components/editor/database/ListView.tsx +136 -18
- package/corpus/templates/content/app/components/editor/database/github-availability.ts +147 -0
- package/corpus/templates/content/app/components/editor/database/github-source-text.ts +280 -0
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
- package/corpus/templates/content/app/components/editor/database/settings.tsx +3 -3
- package/corpus/templates/content/app/components/editor/database/view-config.ts +4 -0
- package/corpus/templates/content/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/content/app/global.css +13 -0
- package/corpus/templates/content/app/hooks/content-action-refresh.ts +6 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +3 -1
- package/corpus/templates/content/app/hooks/use-github-folder.ts +199 -0
- package/corpus/templates/content/app/i18n-data.ts +186 -1
- package/corpus/templates/content/app/lib/github-link-dialog.ts +86 -0
- package/corpus/templates/content/changelog/2026-10-18-connected-ai-assistants-can-link-a-new-collection-to-a-github.md +1 -1
- package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-list-rows-can-be-customized.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-list-views-fit-phone-screens.md +6 -0
- package/corpus/templates/content/changelog/2026-10-21-organization-favorites-include-documents-shared-with-the-cur.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.sync-policy.md +32 -7
- package/corpus/templates/content/docs/product/encyclopedia.md +3 -3
- package/corpus/templates/content/docs/product/features/content.feature.trust-your-connected-sources.md +2 -2
- package/corpus/templates/content/docs/product/roadmap.md +1 -1
- package/corpus/templates/content/parity/matrix.md +42 -42
- package/corpus/templates/content/parity/matrix.ts +14 -0
- package/corpus/templates/content/server/lib/github-source-client.ts +50 -1
- package/corpus/templates/content/shared/api.ts +21 -0
- package/corpus/templates/content/shared/database-item-layout.ts +591 -0
- package/corpus/templates/content/shared/github-source.ts +68 -0
- package/corpus/templates/design/CHANGELOG.md +1 -0
- package/corpus/templates/design/actions/connect-assets-mcp.ts +5 -9
- package/corpus/templates/design/changelog/2026-10-21-journey-canvases-accept-partial-analytics-samples-when-repla.md +6 -0
- package/corpus/templates/design/shared/journey-canvas.ts +6 -4
- package/corpus/templates/dispatch/.agents/skills/a2a-protocol/SKILL.md +16 -13
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-21-customer-ai-credit-questions-route-to-account-health-instead.md +6 -0
- package/corpus/templates/factory/server/lib/require-workspace-member.ts +5 -17
- package/corpus/templates/mail/server/lib/queued-drafts.ts +9 -4
- package/corpus/templates/slides/server/workspace-defaults.ts +12 -15
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
|
|
8
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
9
|
import { documentChangeResource } from "../server/lib/document-change-resource.js";
|
|
10
|
+
import type { GitHubLinkBlockedReason } from "../shared/github-source.js";
|
|
10
11
|
import {
|
|
11
12
|
getContentDatabaseSourceSnapshot,
|
|
12
13
|
resolveDatabaseForSourceMutation,
|
|
@@ -18,6 +19,8 @@ import {
|
|
|
18
19
|
previewGitHubFolder,
|
|
19
20
|
resolveGitHubFolder,
|
|
20
21
|
syncGitHubSource,
|
|
22
|
+
unexpectedSyncFailure,
|
|
23
|
+
type GitHubSyncResult,
|
|
21
24
|
} from "./_github-source.js";
|
|
22
25
|
import { serializeDatabase } from "./_property-utils.js";
|
|
23
26
|
|
|
@@ -62,15 +65,10 @@ const linkGitHubFolderSchema = z
|
|
|
62
65
|
"Provide exactly one databaseId or documentId",
|
|
63
66
|
);
|
|
64
67
|
|
|
65
|
-
type LinkBlocked =
|
|
66
|
-
| "collection-has-rows"
|
|
67
|
-
| "already-linked"
|
|
68
|
-
| "system-collection";
|
|
69
|
-
|
|
70
68
|
async function linkBlockedReason(
|
|
71
69
|
database: { id: string; systemRole: string | null },
|
|
72
70
|
db: ReturnType<typeof getDb> = getDb(),
|
|
73
|
-
): Promise<
|
|
71
|
+
): Promise<GitHubLinkBlockedReason | null> {
|
|
74
72
|
if (database.systemRole) return "system-collection";
|
|
75
73
|
const [source] = await db
|
|
76
74
|
.select({ id: schema.contentDatabaseSources.id })
|
|
@@ -95,14 +93,14 @@ async function linkBlockedReason(
|
|
|
95
93
|
return row ? "collection-has-rows" : null;
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
const BLOCKED_MESSAGES: Record<
|
|
96
|
+
const BLOCKED_MESSAGES: Record<GitHubLinkBlockedReason, string> = {
|
|
99
97
|
"collection-has-rows":
|
|
100
98
|
"This collection already has rows. Link a GitHub folder from a new, empty collection.",
|
|
101
99
|
"already-linked": "This collection is already linked to a source.",
|
|
102
100
|
"system-collection": "This collection can't be linked to a GitHub folder.",
|
|
103
101
|
};
|
|
104
102
|
|
|
105
|
-
function blockedError(blocked:
|
|
103
|
+
function blockedError(blocked: GitHubLinkBlockedReason) {
|
|
106
104
|
return new ActionContractError(BLOCKED_MESSAGES[blocked], {
|
|
107
105
|
errorCode:
|
|
108
106
|
blocked === "collection-has-rows"
|
|
@@ -114,6 +112,96 @@ function blockedError(blocked: LinkBlocked) {
|
|
|
114
112
|
});
|
|
115
113
|
}
|
|
116
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Links a collection to a folder already read and previewed, then brings in
|
|
117
|
+
* the first files. The link is saved before the sync, so a sync that fails is
|
|
118
|
+
* reported on the link rather than thrown.
|
|
119
|
+
*/
|
|
120
|
+
export async function linkPreviewedGitHubFolder({
|
|
121
|
+
database,
|
|
122
|
+
resolved,
|
|
123
|
+
preview,
|
|
124
|
+
connectionId,
|
|
125
|
+
userEmail,
|
|
126
|
+
}: {
|
|
127
|
+
database: Parameters<typeof createGitHubSourceLink>[0]["database"];
|
|
128
|
+
resolved: Awaited<ReturnType<typeof resolveGitHubFolder>>;
|
|
129
|
+
preview: Awaited<ReturnType<typeof previewGitHubFolder>>;
|
|
130
|
+
connectionId: string | null;
|
|
131
|
+
userEmail: string;
|
|
132
|
+
}) {
|
|
133
|
+
const sourceId = await createGitHubSourceLink({
|
|
134
|
+
database,
|
|
135
|
+
resolved,
|
|
136
|
+
preview,
|
|
137
|
+
connectionId,
|
|
138
|
+
userEmail,
|
|
139
|
+
// Reading GitHub takes seconds, so someone may have added a row since
|
|
140
|
+
// the check above; this runs again under the collection's lock.
|
|
141
|
+
assertLinkable: async (tx) => {
|
|
142
|
+
const blockedNow = await linkBlockedReason(database, tx);
|
|
143
|
+
if (blockedNow) throw blockedError(blockedNow);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
return bringInGitHubLink({ database, preview, sourceId });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Brings in a saved link's first files, or the rest of them on a retry, and
|
|
151
|
+
* says how far it got.
|
|
152
|
+
*/
|
|
153
|
+
export async function bringInGitHubLink({
|
|
154
|
+
database,
|
|
155
|
+
preview,
|
|
156
|
+
sourceId,
|
|
157
|
+
}: {
|
|
158
|
+
database: Parameters<typeof createGitHubSourceLink>[0]["database"];
|
|
159
|
+
preview: Awaited<ReturnType<typeof previewGitHubFolder>>;
|
|
160
|
+
sourceId: string;
|
|
161
|
+
}) {
|
|
162
|
+
// The link is saved by now, so anything that fails from here is reported
|
|
163
|
+
// on it rather than thrown, which would leave the caller thinking it failed.
|
|
164
|
+
// A contract error is the exception: the link or its collection went away
|
|
165
|
+
// during the sync, so there's nothing left to report on.
|
|
166
|
+
let sync: GitHubSyncResult;
|
|
167
|
+
try {
|
|
168
|
+
sync = await syncGitHubSource({
|
|
169
|
+
sourceId,
|
|
170
|
+
budgetMs: FIRST_SYNC_BUDGET_MS,
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error instanceof ActionContractError) throw error;
|
|
174
|
+
console.warn(
|
|
175
|
+
"[github-source] first sync failed:",
|
|
176
|
+
error instanceof Error ? error.message : String(error),
|
|
177
|
+
);
|
|
178
|
+
sync = unexpectedSyncFailure(new Date().toISOString());
|
|
179
|
+
}
|
|
180
|
+
await writeAppState("refresh-signal", { ts: Date.now() }).catch(
|
|
181
|
+
(error: unknown) => {
|
|
182
|
+
// The change event below still reaches open pages.
|
|
183
|
+
console.warn(
|
|
184
|
+
"[github-source] refresh signal failed:",
|
|
185
|
+
error instanceof Error ? error.message : String(error),
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
// coercion-ok: the status line reads the link on its own; this is a courtesy copy.
|
|
190
|
+
const source = await getContentDatabaseSourceSnapshot(database).catch(
|
|
191
|
+
() => null,
|
|
192
|
+
);
|
|
193
|
+
return {
|
|
194
|
+
sync,
|
|
195
|
+
source,
|
|
196
|
+
summary:
|
|
197
|
+
sync.state === "partial"
|
|
198
|
+
? `Linked ${preview.label}. ${sync.created} of ${preview.markdownFiles} rows are in; the rest follow within about a minute.`
|
|
199
|
+
: sync.state === "error"
|
|
200
|
+
? `Linked ${preview.label}, but the first sync failed: ${sync.problem?.message ?? "GitHub sync failed."}`
|
|
201
|
+
: `Linked ${preview.label}: ${sync.created} rows from commit ${sync.commitSha?.slice(0, 7) ?? "unknown"}.${sync.problem ? ` ${sync.problem.message}` : ""}`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
117
205
|
export default defineAction({
|
|
118
206
|
description:
|
|
119
207
|
"Link an empty content collection to a GitHub folder, view only: each Markdown file becomes a row, frontmatter becomes read-only columns, and pushes to the branch arrive on their own. Run with dryRun first to see the file count, skipped files with reasons, and the columns and types the frontmatter becomes.",
|
|
@@ -163,44 +251,24 @@ export default defineAction({
|
|
|
163
251
|
preview,
|
|
164
252
|
canLink: blocked === null,
|
|
165
253
|
blocked: blocked ? BLOCKED_MESSAGES[blocked] : null,
|
|
254
|
+
blockedReason: blocked,
|
|
166
255
|
summary: blocked
|
|
167
256
|
? BLOCKED_MESSAGES[blocked]
|
|
168
257
|
: `${githubPreviewSummary(preview)} Run again without dryRun to link it.`,
|
|
169
258
|
};
|
|
170
259
|
}
|
|
171
260
|
|
|
172
|
-
const sourceId = await createGitHubSourceLink({
|
|
173
|
-
database,
|
|
174
|
-
resolved,
|
|
175
|
-
preview,
|
|
176
|
-
connectionId,
|
|
177
|
-
userEmail,
|
|
178
|
-
// Reading GitHub takes seconds, so someone may have added a row since
|
|
179
|
-
// the check above; this runs again under the collection's lock.
|
|
180
|
-
assertLinkable: async (tx) => {
|
|
181
|
-
const blockedNow = await linkBlockedReason(database, tx);
|
|
182
|
-
if (blockedNow) throw blockedError(blockedNow);
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
const sync = await syncGitHubSource({
|
|
186
|
-
sourceId,
|
|
187
|
-
budgetMs: FIRST_SYNC_BUDGET_MS,
|
|
188
|
-
throwConnectionErrors: true,
|
|
189
|
-
});
|
|
190
|
-
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
191
|
-
const source = await getContentDatabaseSourceSnapshot(database);
|
|
192
261
|
return {
|
|
193
262
|
dryRun: false,
|
|
194
263
|
database: serializeDatabase(database),
|
|
195
264
|
preview,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
: `Linked ${preview.label}: ${sync.created} rows from commit ${sync.commitSha?.slice(0, 7) ?? "unknown"}.${sync.problem ? ` ${sync.problem.message}` : ""}`,
|
|
265
|
+
...(await linkPreviewedGitHubFolder({
|
|
266
|
+
database,
|
|
267
|
+
resolved,
|
|
268
|
+
preview,
|
|
269
|
+
connectionId,
|
|
270
|
+
userEmail,
|
|
271
|
+
})),
|
|
204
272
|
};
|
|
205
273
|
},
|
|
206
274
|
// Everyone reading the collection gets its new rows, not just the caller.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
2
3
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
|
|
@@ -18,7 +19,11 @@ import {
|
|
|
18
19
|
storeSecondarySourceRows,
|
|
19
20
|
updateReadOnlySourceMetadata,
|
|
20
21
|
} from "./_database-source-utils.js";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
announceGitHubSyncChange,
|
|
24
|
+
githubSyncChangedRows,
|
|
25
|
+
readGitHubStatusShown,
|
|
26
|
+
} from "./_github-source-freshness.js";
|
|
22
27
|
import { githubSyncSummary, syncGitHubSource } from "./_github-source.js";
|
|
23
28
|
import { serializeDatabase } from "./_property-utils.js";
|
|
24
29
|
|
|
@@ -80,13 +85,41 @@ export default defineAction({
|
|
|
80
85
|
let githubSummary: string | null = null;
|
|
81
86
|
let githubChanged = false;
|
|
82
87
|
if (source.sourceType === "github") {
|
|
88
|
+
const shownBefore = await readGitHubStatusShown(source.id);
|
|
83
89
|
const result = await syncGitHubSource({
|
|
84
90
|
sourceId: source.id,
|
|
85
91
|
budgetMs: args.fullRefresh === true ? 25_000 : 6_000,
|
|
86
92
|
throwConnectionErrors: true,
|
|
93
|
+
throwUnexpectedErrors: true,
|
|
94
|
+
}).catch((error: unknown) => {
|
|
95
|
+
// A sync that fails may have written rows first, and it stores the
|
|
96
|
+
// problem on the link, so readers hear about it either way.
|
|
97
|
+
try {
|
|
98
|
+
announceGitHubSyncChange(
|
|
99
|
+
database.documentId,
|
|
100
|
+
getRequestUserEmail() ?? null,
|
|
101
|
+
);
|
|
102
|
+
} catch (announceError) {
|
|
103
|
+
// The caller still gets the sync's error; other tabs catch up on their next read.
|
|
104
|
+
console.warn(
|
|
105
|
+
"[github-source] change event failed:",
|
|
106
|
+
announceError instanceof Error
|
|
107
|
+
? announceError.message
|
|
108
|
+
: String(announceError),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
87
112
|
});
|
|
88
113
|
githubSummary = githubSyncSummary(source.sourceName, result);
|
|
89
|
-
|
|
114
|
+
// The rows are written by now, so a status that can't be read back
|
|
115
|
+
// counts as changed rather than failing the refresh.
|
|
116
|
+
// coercion-ok: null differs from every reading, so readers look again.
|
|
117
|
+
const shownAfter = await readGitHubStatusShown(source.id).catch(
|
|
118
|
+
() => null,
|
|
119
|
+
);
|
|
120
|
+
githubChanged =
|
|
121
|
+
githubSyncChangedRows(result) ||
|
|
122
|
+
(result.state !== "busy" && shownAfter !== shownBefore);
|
|
90
123
|
} else if (source.sourceType === "mock-local") {
|
|
91
124
|
await resyncMockSourceSnapshot({ database, source, now });
|
|
92
125
|
} else if (source.sourceType === "builder-cms") {
|
|
@@ -182,8 +215,8 @@ export default defineAction({
|
|
|
182
215
|
...(source.sourceType === "github" ? { githubChanged } : {}),
|
|
183
216
|
};
|
|
184
217
|
},
|
|
185
|
-
// A GitHub sync that changed rows changed them for
|
|
186
|
-
// collection, as with Sync now.
|
|
218
|
+
// A GitHub sync that changed rows or the status line changed them for
|
|
219
|
+
// everyone reading the collection, as with Sync now.
|
|
187
220
|
changeResource: (_input, result) =>
|
|
188
221
|
result.githubChanged
|
|
189
222
|
? documentChangeResource(result.database.documentId)
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
getContentDatabaseSourceSnapshot,
|
|
9
9
|
resolveDatabaseForSourceMutation,
|
|
10
10
|
} from "./_database-source-utils.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
githubSyncChangedRows,
|
|
13
|
+
readGitHubStatusShown,
|
|
14
|
+
} from "./_github-source-freshness.js";
|
|
12
15
|
import {
|
|
13
16
|
githubSyncSummary,
|
|
14
17
|
readLinkedGitHubSource,
|
|
@@ -64,12 +67,23 @@ export default defineAction({
|
|
|
64
67
|
{ errorCode: "NOT_GITHUB_LINKED", statusCode: 409 },
|
|
65
68
|
);
|
|
66
69
|
}
|
|
70
|
+
const shownBefore = await readGitHubStatusShown(linked.id);
|
|
67
71
|
const sync = await syncGitHubSource({
|
|
68
72
|
sourceId: linked.id,
|
|
69
73
|
budgetMs: SYNC_BUDGET_MS,
|
|
70
74
|
});
|
|
71
75
|
const failed: SyncFollowUp[] = [];
|
|
72
|
-
|
|
76
|
+
// A busy sync changed nothing; the run already going reports for itself.
|
|
77
|
+
// A status that can't be read back counts as changed, so readers check.
|
|
78
|
+
const statusChanged =
|
|
79
|
+
sync.state !== "busy" &&
|
|
80
|
+
(await afterSync("status_read", failed, () =>
|
|
81
|
+
readGitHubStatusShown(linked.id),
|
|
82
|
+
)) !== shownBefore;
|
|
83
|
+
if (
|
|
84
|
+
statusChanged ||
|
|
85
|
+
(sync.state !== "unchanged" && sync.state !== "busy")
|
|
86
|
+
) {
|
|
73
87
|
await afterSync("refresh_signal", failed, () =>
|
|
74
88
|
writeAppState("refresh-signal", { ts: Date.now() }),
|
|
75
89
|
);
|
|
@@ -80,6 +94,7 @@ export default defineAction({
|
|
|
80
94
|
return {
|
|
81
95
|
database: serializeDatabase(database),
|
|
82
96
|
sync,
|
|
97
|
+
statusChanged,
|
|
83
98
|
source,
|
|
84
99
|
followUpFailed: failed,
|
|
85
100
|
summary: [
|
|
@@ -88,16 +103,19 @@ export default defineAction({
|
|
|
88
103
|
].join(" "),
|
|
89
104
|
};
|
|
90
105
|
},
|
|
91
|
-
// Everyone reading the collection gets the synced rows, not just
|
|
106
|
+
// Everyone reading the collection gets the synced rows and status, not just
|
|
107
|
+
// the caller.
|
|
92
108
|
changeResource: (_input, result) =>
|
|
93
|
-
githubSyncChangedRows(result.sync)
|
|
109
|
+
githubSyncChangedRows(result.sync) || result.statusChanged
|
|
94
110
|
? documentChangeResource(result.database.documentId)
|
|
95
111
|
: null,
|
|
96
112
|
});
|
|
97
113
|
|
|
98
|
-
type SyncFollowUp = "refresh_signal" | "source_read";
|
|
114
|
+
type SyncFollowUp = "status_read" | "refresh_signal" | "source_read";
|
|
99
115
|
|
|
100
116
|
const FOLLOW_UP_FAILED: Record<SyncFollowUp, string> = {
|
|
117
|
+
status_read:
|
|
118
|
+
"Whether the link's status changed couldn't be read, so readers were told to check it again.",
|
|
101
119
|
refresh_signal:
|
|
102
120
|
"Other open tabs couldn't be told about the changes; they show them once they reload.",
|
|
103
121
|
source_read:
|
|
@@ -10,6 +10,7 @@ import { z } from "zod";
|
|
|
10
10
|
import { getDb, schema } from "../server/db/index.js";
|
|
11
11
|
import { syncPrivateViewIconReferences } from "../server/lib/private-icon-references.js";
|
|
12
12
|
import { CONTENT_DATABASE_FILTER_OPERATORS } from "../shared/api.js";
|
|
13
|
+
import { withSavedContentDatabaseItemLayout } from "../shared/database-item-layout.js";
|
|
13
14
|
import { withSavedTableColumnPresentation } from "../shared/database-table-columns.js";
|
|
14
15
|
import { lockContentDatabaseMutation } from "./_content-database-mutation-lock.js";
|
|
15
16
|
import {
|
|
@@ -19,9 +20,11 @@ import {
|
|
|
19
20
|
} from "./_database-setup-mutation.js";
|
|
20
21
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
21
22
|
import {
|
|
23
|
+
contentDatabaseItemLayoutSchema,
|
|
22
24
|
runReplaceContentDatabaseViews,
|
|
23
25
|
runUpdateContentDatabaseView,
|
|
24
26
|
updateContentDatabaseViewAgentSchema,
|
|
27
|
+
withSavableItemLayout,
|
|
25
28
|
} from "./_database-view-setup.js";
|
|
26
29
|
import {
|
|
27
30
|
parseDatabaseViewConfig,
|
|
@@ -114,6 +117,7 @@ const viewSchema = z
|
|
|
114
117
|
.default("default"),
|
|
115
118
|
openPagesIn: z.enum(["preview", "full_page"]).default("preview"),
|
|
116
119
|
formQuestions: z.array(formQuestionSchema).default([]),
|
|
120
|
+
itemLayout: contentDatabaseItemLayoutSchema.nullable().optional(),
|
|
117
121
|
})
|
|
118
122
|
.strict();
|
|
119
123
|
|
|
@@ -143,7 +147,7 @@ const guardedViewReplacementSchema = setupGuardSchema
|
|
|
143
147
|
|
|
144
148
|
export default defineAction({
|
|
145
149
|
description:
|
|
146
|
-
"Create one named ordinary table view or sparsely update one exact saved table view using stable property IDs, fresh schema and configuration revisions, and an idempotency key.",
|
|
150
|
+
"Create one named ordinary table view, or sparsely update one exact saved table view or a list view's name, icon, and row layout (itemLayout, itemLayoutPreset), using stable property IDs, fresh schema and configuration revisions, and an idempotency key.",
|
|
147
151
|
mcpTool: true,
|
|
148
152
|
mcpApp: { structuredContent: true },
|
|
149
153
|
agentInputSchema: updateContentDatabaseViewAgentSchema,
|
|
@@ -213,10 +217,22 @@ export default defineAction({
|
|
|
213
217
|
const currentViewConfig = parseDatabaseViewConfig(
|
|
214
218
|
lockedDatabase.viewConfigJson,
|
|
215
219
|
);
|
|
220
|
+
const definitions = await tx
|
|
221
|
+
.select()
|
|
222
|
+
.from(schema.documentPropertyDefinitions)
|
|
223
|
+
.where(eq(schema.documentPropertyDefinitions.databaseId, databaseId));
|
|
216
224
|
const nextViewConfig = {
|
|
217
225
|
...viewConfig,
|
|
218
226
|
views: viewConfig.views?.map((view) =>
|
|
219
|
-
|
|
227
|
+
withSavableItemLayout(
|
|
228
|
+
withSavedContentDatabaseItemLayout(
|
|
229
|
+
withSavedTableColumnPresentation(view, currentViewConfig.views),
|
|
230
|
+
currentViewConfig.views,
|
|
231
|
+
),
|
|
232
|
+
currentViewConfig.views.find((saved) => saved.id === view.id)
|
|
233
|
+
?.itemLayout,
|
|
234
|
+
definitions,
|
|
235
|
+
),
|
|
220
236
|
),
|
|
221
237
|
};
|
|
222
238
|
const nextViewConfigJson = serializeDatabaseViewConfig(nextViewConfig);
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
ContentDatabaseViewType,
|
|
25
25
|
DocumentProperty,
|
|
26
26
|
} from "../shared/api.js";
|
|
27
|
+
import { normalizeContentDatabaseItemLayout } from "../shared/database-item-layout.js";
|
|
27
28
|
import {
|
|
28
29
|
databaseColumnWraps,
|
|
29
30
|
databaseFrozenColumnIds,
|
|
@@ -782,6 +783,17 @@ export function databaseCurrentViewSnapshot(
|
|
|
782
783
|
"preview",
|
|
783
784
|
formQuestions:
|
|
784
785
|
arrayValue(nav.databaseFormQuestions) ?? activeView?.formQuestions ?? [],
|
|
786
|
+
...(viewType === "list"
|
|
787
|
+
? {
|
|
788
|
+
// An explicit null is a reset to default rows that may not be saved yet.
|
|
789
|
+
itemLayout:
|
|
790
|
+
nav.databaseItemLayout === null
|
|
791
|
+
? null
|
|
792
|
+
: (normalizeContentDatabaseItemLayout(nav.databaseItemLayout) ??
|
|
793
|
+
activeView?.itemLayout ??
|
|
794
|
+
null),
|
|
795
|
+
}
|
|
796
|
+
: {}),
|
|
785
797
|
visibleItemCount:
|
|
786
798
|
numberValue(nav.databaseVisibleItemCount) ??
|
|
787
799
|
response.pagination?.returnedItems ??
|