@agent-native/core 0.101.7 → 0.101.10
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/core/CHANGELOG.md +22 -0
- package/corpus/core/docs/content/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
- package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
- package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
- package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
- package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
- package/corpus/core/src/server/auth.ts +25 -1
- package/corpus/core/src/server/builder-browser.ts +353 -1
- package/corpus/core/src/server/core-routes-plugin.ts +601 -223
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
- package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
- package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
- package/corpus/templates/content/AGENTS.md +3 -2
- package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
- package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
- package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
- package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
- package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +1041 -154
- package/corpus/templates/content/actions/_database-utils.ts +89 -12
- package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
- package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
- package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
- package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
- package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +27 -15
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
- package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2353 -576
- package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
- package/corpus/templates/content/app/global.css +18 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
- package/corpus/templates/content/app/i18n/zh-TW.ts +25 -0
- package/corpus/templates/content/app/i18n-data.ts +287 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
- package/corpus/templates/content/parity/matrix.md +27 -25
- package/corpus/templates/content/parity/matrix.ts +50 -2
- package/corpus/templates/content/server/db/schema.ts +47 -0
- package/corpus/templates/content/server/plugins/auth.ts +1 -0
- package/corpus/templates/content/server/plugins/db.ts +53 -0
- package/corpus/templates/content/shared/api.ts +70 -1
- package/corpus/templates/content/shared/builder-mdx.ts +705 -15
- package/corpus/templates/content/shared/nfm.ts +3 -3
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
- package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
- package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/design/app/i18n/en-US.ts +1 -0
- package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
- package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
- package/corpus/templates/design/shared/review-anchor.ts +48 -0
- package/corpus/templates/plan/server/plan-mdx.ts +53 -7
- package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
- package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
- package/dist/client/review/ReviewCommentComposer.js +3 -3
- package/dist/client/review/ReviewCommentComposer.js.map +1 -1
- package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
- package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
- package/dist/client/review/ReviewThreadPanel.js +6 -4
- package/dist/client/review/ReviewThreadPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +3 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +11 -4
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +8 -4
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +19 -6
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
- package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +19 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts +78 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +246 -1
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +41 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +260 -14
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/docs/content/deployment.mdx +9 -7
- package/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/package.json +2 -2
|
@@ -61,12 +61,39 @@ export interface PreviewDocumentPayload {
|
|
|
61
61
|
loadedContentWasEmpty?: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export interface
|
|
65
|
-
|
|
64
|
+
export interface PreviewDocumentSaveDeferred {
|
|
65
|
+
outcome: "deferred";
|
|
66
|
+
reason: "hydration" | "conflict";
|
|
67
|
+
conflictSnapshot?: PreviewDocumentDraftSnapshot;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
+
export interface PreviewDocumentSaveAdapter {
|
|
71
|
+
save: (
|
|
72
|
+
documentId: string,
|
|
73
|
+
payload: PreviewDocumentPayload,
|
|
74
|
+
baseline?: PreviewDocumentPayload,
|
|
75
|
+
) => Promise<unknown>;
|
|
76
|
+
onSaved?: (payload: PreviewDocumentPayload) => void;
|
|
77
|
+
onError?: (error: unknown) => void;
|
|
78
|
+
onDraftConflict?: (snapshot: PreviewDocumentDraftSnapshot) => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface PreviewDocumentDraftSnapshot {
|
|
82
|
+
lastSaved: PreviewDocumentPayload;
|
|
83
|
+
pending: PreviewDocumentPayload;
|
|
84
|
+
deferredReason: PreviewDocumentSaveDeferred["reason"] | null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The save could not run yet, but the payload is still user-owned and must stay
|
|
89
|
+
* dirty until a later flush can persist it. This is intentionally different
|
|
90
|
+
* from success: callers must never advance or reset the confirmed baseline.
|
|
91
|
+
*/
|
|
92
|
+
export function deferredPreviewDocumentSave(
|
|
93
|
+
reason: PreviewDocumentSaveDeferred["reason"] = "hydration",
|
|
94
|
+
conflictSnapshot?: PreviewDocumentDraftSnapshot,
|
|
95
|
+
): PreviewDocumentSaveDeferred {
|
|
96
|
+
return { outcome: "deferred", reason, conflictSnapshot };
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
export interface PreviewDocumentSaveController {
|
|
@@ -89,6 +116,19 @@ export interface PreviewDocumentSaveController {
|
|
|
89
116
|
cancel(): void;
|
|
90
117
|
/** Adopt `payload` as the confirmed-saved baseline (no save scheduled). */
|
|
91
118
|
mark(payload: PreviewDocumentPayload): void;
|
|
119
|
+
/**
|
|
120
|
+
* Adopt a fresher server baseline while retaining the user's pending title
|
|
121
|
+
* and content. Used only after an explicit "keep local draft" choice.
|
|
122
|
+
*/
|
|
123
|
+
rebasePending(payload: PreviewDocumentPayload): void;
|
|
124
|
+
/** Replace callbacks captured by an older preview mount. */
|
|
125
|
+
replaceSaveAdapter(adapter: PreviewDocumentSaveAdapter): void;
|
|
126
|
+
/** Serializable dirty state for bounded browser draft storage. */
|
|
127
|
+
draftSnapshot(): PreviewDocumentDraftSnapshot;
|
|
128
|
+
/** Restore a previously persisted dirty draft into a fresh controller. */
|
|
129
|
+
restoreDraft(snapshot: PreviewDocumentDraftSnapshot): void;
|
|
130
|
+
/** Notify whichever preview mount currently owns this controller. */
|
|
131
|
+
notifyDraftConflict(snapshot: PreviewDocumentDraftSnapshot): void;
|
|
92
132
|
/** The payload last CONFIRMED persisted. */
|
|
93
133
|
readonly lastSaved: PreviewDocumentPayload;
|
|
94
134
|
/** The latest payload the user has typed (may differ from lastSaved). */
|
|
@@ -97,6 +137,8 @@ export interface PreviewDocumentSaveController {
|
|
|
97
137
|
readonly hasPendingTimer: boolean;
|
|
98
138
|
/** Whether a save() call is currently outstanding (in flight). */
|
|
99
139
|
readonly isSaving: boolean;
|
|
140
|
+
/** Why the latest attempted save remains dirty, if it was deferred. */
|
|
141
|
+
readonly deferredReason: PreviewDocumentSaveDeferred["reason"] | null;
|
|
100
142
|
/**
|
|
101
143
|
* Whether this controller has confirmed at least one local save since creation.
|
|
102
144
|
* Until the server query echoes that payload, clean local state is newer than
|
|
@@ -109,25 +151,21 @@ function payloadsEqual(a: PreviewDocumentPayload, b: PreviewDocumentPayload) {
|
|
|
109
151
|
return a.title === b.title && a.content === b.content;
|
|
110
152
|
}
|
|
111
153
|
|
|
112
|
-
export function createPreviewDocumentSaveController(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
debounceMs?: number;
|
|
128
|
-
setTimeoutFn?: typeof setTimeout;
|
|
129
|
-
clearTimeoutFn?: typeof clearTimeout;
|
|
130
|
-
}): PreviewDocumentSaveController {
|
|
154
|
+
export function createPreviewDocumentSaveController(
|
|
155
|
+
args: PreviewDocumentSaveAdapter & {
|
|
156
|
+
/**
|
|
157
|
+
* The document id this controller persists to, fixed for its entire life. A
|
|
158
|
+
* controller NEVER changes which document it targets — switching rows acquires
|
|
159
|
+
* a different controller (see previewDocumentSaveRegistry).
|
|
160
|
+
*/
|
|
161
|
+
documentId: string;
|
|
162
|
+
initial: PreviewDocumentPayload;
|
|
163
|
+
/** Persist `payload` to this controller's document. */
|
|
164
|
+
debounceMs?: number;
|
|
165
|
+
setTimeoutFn?: typeof setTimeout;
|
|
166
|
+
clearTimeoutFn?: typeof clearTimeout;
|
|
167
|
+
},
|
|
168
|
+
): PreviewDocumentSaveController {
|
|
131
169
|
const documentId = args.documentId;
|
|
132
170
|
const debounceMs = args.debounceMs ?? 450;
|
|
133
171
|
const setTimeoutFn = args.setTimeoutFn ?? setTimeout;
|
|
@@ -137,6 +175,13 @@ export function createPreviewDocumentSaveController(args: {
|
|
|
137
175
|
let pending: PreviewDocumentPayload = { ...args.initial };
|
|
138
176
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
139
177
|
let hasSavedLocally = false;
|
|
178
|
+
let deferredReason: PreviewDocumentSaveDeferred["reason"] | null = null;
|
|
179
|
+
let saveAdapter: PreviewDocumentSaveAdapter = {
|
|
180
|
+
save: args.save,
|
|
181
|
+
onSaved: args.onSaved,
|
|
182
|
+
onError: args.onError,
|
|
183
|
+
onDraftConflict: args.onDraftConflict,
|
|
184
|
+
};
|
|
140
185
|
|
|
141
186
|
// The single in-flight save, or null when idle. A debounced edit made while
|
|
142
187
|
// this is set does NOT start a new save; it updates `pending` and a trailing
|
|
@@ -161,29 +206,42 @@ export function createPreviewDocumentSaveController(args: {
|
|
|
161
206
|
if (payloadsEqual(pending, lastSaved)) return; // nothing dirty.
|
|
162
207
|
|
|
163
208
|
const attempted = { ...pending };
|
|
164
|
-
const promise = Promise.resolve(
|
|
209
|
+
const promise = Promise.resolve(
|
|
210
|
+
saveAdapter.save(documentId, attempted, { ...lastSaved }),
|
|
211
|
+
)
|
|
165
212
|
.then((result) => {
|
|
166
213
|
if (
|
|
167
214
|
result &&
|
|
168
215
|
typeof result === "object" &&
|
|
169
|
-
"
|
|
170
|
-
result.
|
|
216
|
+
"outcome" in result &&
|
|
217
|
+
result.outcome === "deferred" &&
|
|
218
|
+
"reason" in result &&
|
|
219
|
+
(result.reason === "hydration" || result.reason === "conflict")
|
|
171
220
|
) {
|
|
172
|
-
|
|
221
|
+
// Hydration can begin after a keystroke but before the debounce fires.
|
|
222
|
+
// Keep the attempted payload dirty so the registry can retain it across
|
|
223
|
+
// close/reopen and a later flush can retry it. A deferred save is not a
|
|
224
|
+
// successful save, and user-authored content is never disposable.
|
|
225
|
+
deferredReason = result.reason;
|
|
173
226
|
inFlight = null;
|
|
227
|
+
const deferredResult = result as PreviewDocumentSaveDeferred;
|
|
228
|
+
if (deferredResult.conflictSnapshot) {
|
|
229
|
+
saveAdapter.onDraftConflict?.(deferredResult.conflictSnapshot);
|
|
230
|
+
}
|
|
174
231
|
return;
|
|
175
232
|
}
|
|
176
233
|
lastSaved = attempted;
|
|
177
234
|
hasSavedLocally = true;
|
|
235
|
+
deferredReason = null;
|
|
178
236
|
inFlight = null;
|
|
179
|
-
|
|
237
|
+
saveAdapter.onSaved?.(attempted);
|
|
180
238
|
// A trailing edit may have landed while this save was in flight. Issue
|
|
181
239
|
// exactly one more for the LATEST payload. Bounded: stops once quiescent.
|
|
182
240
|
kick();
|
|
183
241
|
})
|
|
184
242
|
.catch((error) => {
|
|
185
243
|
inFlight = null;
|
|
186
|
-
|
|
244
|
+
saveAdapter.onError?.(error);
|
|
187
245
|
});
|
|
188
246
|
inFlight = promise;
|
|
189
247
|
}
|
|
@@ -201,10 +259,12 @@ export function createPreviewDocumentSaveController(args: {
|
|
|
201
259
|
documentId,
|
|
202
260
|
changeTitle(title: string) {
|
|
203
261
|
pending = { ...pending, title };
|
|
262
|
+
deferredReason = null;
|
|
204
263
|
schedule();
|
|
205
264
|
},
|
|
206
265
|
changeContent(content: string) {
|
|
207
266
|
pending = { ...pending, content };
|
|
267
|
+
deferredReason = null;
|
|
208
268
|
schedule();
|
|
209
269
|
},
|
|
210
270
|
flush() {
|
|
@@ -233,6 +293,38 @@ export function createPreviewDocumentSaveController(args: {
|
|
|
233
293
|
lastSaved = { ...payload };
|
|
234
294
|
pending = { ...payload };
|
|
235
295
|
hasSavedLocally = false;
|
|
296
|
+
deferredReason = null;
|
|
297
|
+
},
|
|
298
|
+
rebasePending(payload: PreviewDocumentPayload) {
|
|
299
|
+
clearTimer();
|
|
300
|
+
lastSaved = { ...payload };
|
|
301
|
+
pending = {
|
|
302
|
+
...pending,
|
|
303
|
+
loadedUpdatedAt: payload.loadedUpdatedAt,
|
|
304
|
+
loadedContentWasEmpty: payload.loadedContentWasEmpty,
|
|
305
|
+
};
|
|
306
|
+
hasSavedLocally = false;
|
|
307
|
+
deferredReason = null;
|
|
308
|
+
},
|
|
309
|
+
replaceSaveAdapter(adapter: PreviewDocumentSaveAdapter) {
|
|
310
|
+
saveAdapter = adapter;
|
|
311
|
+
},
|
|
312
|
+
draftSnapshot() {
|
|
313
|
+
return {
|
|
314
|
+
lastSaved: { ...lastSaved },
|
|
315
|
+
pending: { ...pending },
|
|
316
|
+
deferredReason,
|
|
317
|
+
};
|
|
318
|
+
},
|
|
319
|
+
restoreDraft(snapshot: PreviewDocumentDraftSnapshot) {
|
|
320
|
+
clearTimer();
|
|
321
|
+
lastSaved = { ...snapshot.lastSaved };
|
|
322
|
+
pending = { ...snapshot.pending };
|
|
323
|
+
deferredReason = snapshot.deferredReason;
|
|
324
|
+
hasSavedLocally = false;
|
|
325
|
+
},
|
|
326
|
+
notifyDraftConflict(snapshot: PreviewDocumentDraftSnapshot) {
|
|
327
|
+
saveAdapter.onDraftConflict?.(snapshot);
|
|
236
328
|
},
|
|
237
329
|
get lastSaved() {
|
|
238
330
|
return { ...lastSaved };
|
|
@@ -246,6 +338,9 @@ export function createPreviewDocumentSaveController(args: {
|
|
|
246
338
|
get isSaving() {
|
|
247
339
|
return inFlight !== null;
|
|
248
340
|
},
|
|
341
|
+
get deferredReason() {
|
|
342
|
+
return deferredReason;
|
|
343
|
+
},
|
|
249
344
|
get hasSavedLocally() {
|
|
250
345
|
return hasSavedLocally;
|
|
251
346
|
},
|
|
@@ -67,12 +67,14 @@ function controllerIsDirty(controller: PreviewDocumentSaveController): boolean {
|
|
|
67
67
|
export function acquirePreviewDocumentSaveController(
|
|
68
68
|
documentId: string,
|
|
69
69
|
factory: () => PreviewDocumentSaveController,
|
|
70
|
+
refreshAdapter?: (controller: PreviewDocumentSaveController) => void,
|
|
70
71
|
): PreviewDocumentSaveController {
|
|
71
72
|
let entry = registry.get(documentId);
|
|
72
73
|
if (!entry) {
|
|
73
74
|
entry = { controller: factory(), refCount: 0, evicting: false };
|
|
74
75
|
registry.set(documentId, entry);
|
|
75
76
|
}
|
|
77
|
+
refreshAdapter?.(entry.controller);
|
|
76
78
|
entry.refCount += 1;
|
|
77
79
|
// A reopen before a pending eviction settled: keep the instance alive.
|
|
78
80
|
entry.evicting = false;
|
|
@@ -364,6 +364,21 @@
|
|
|
364
364
|
line-height: 1.45;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
.notion-editor h5 {
|
|
368
|
+
font-size: 1em;
|
|
369
|
+
font-weight: 600;
|
|
370
|
+
margin: 0.8em 0 0.15em;
|
|
371
|
+
line-height: 1.5;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.notion-editor h6 {
|
|
375
|
+
font-size: 0.95em;
|
|
376
|
+
font-weight: 600;
|
|
377
|
+
margin: 0.75em 0 0.15em;
|
|
378
|
+
line-height: 1.5;
|
|
379
|
+
color: hsl(var(--muted-foreground));
|
|
380
|
+
}
|
|
381
|
+
|
|
367
382
|
/* Paragraphs */
|
|
368
383
|
.notion-editor p {
|
|
369
384
|
margin: 0.5em 0;
|
|
@@ -408,7 +423,9 @@
|
|
|
408
423
|
.notion-editor h1.is-empty::before,
|
|
409
424
|
.notion-editor h2.is-empty::before,
|
|
410
425
|
.notion-editor h3.is-empty::before,
|
|
411
|
-
.notion-editor h4.is-empty::before
|
|
426
|
+
.notion-editor h4.is-empty::before,
|
|
427
|
+
.notion-editor h5.is-empty::before,
|
|
428
|
+
.notion-editor h6.is-empty::before {
|
|
412
429
|
content: attr(data-placeholder);
|
|
413
430
|
float: left;
|
|
414
431
|
color: hsl(var(--muted-foreground));
|
|
@@ -4,6 +4,8 @@ import type {
|
|
|
4
4
|
AddDatabaseItemRequest,
|
|
5
5
|
AttachContentDatabaseSourceRequest,
|
|
6
6
|
BuilderCmsModelsResponse,
|
|
7
|
+
CancelPreparedBuilderSourceUpdateRequest,
|
|
8
|
+
CancelPreparedBuilderSourceUpdateResponse,
|
|
7
9
|
ChangeContentDatabaseSourceRoleRequest,
|
|
8
10
|
ContentDatabaseResponse,
|
|
9
11
|
ContentDatabasePersonalViewResponse,
|
|
@@ -25,6 +27,7 @@ import type {
|
|
|
25
27
|
ExecuteBuilderSourceExecutionRequest,
|
|
26
28
|
MoveDatabaseItemRequest,
|
|
27
29
|
PrepareBuilderSourceExecutionRequest,
|
|
30
|
+
PreviewBuilderSourceReviewResponse,
|
|
28
31
|
PrepareBuilderSourceReviewRequest,
|
|
29
32
|
PrepareBuilderSourceReviewResponse,
|
|
30
33
|
ProcessBuilderBodyHydrationRequest,
|
|
@@ -815,6 +818,27 @@ export function useAddContentDatabaseSourceFieldProperty(documentId: string) {
|
|
|
815
818
|
});
|
|
816
819
|
}
|
|
817
820
|
|
|
821
|
+
export function useMaterializeBuilderRequiredFields(documentId: string) {
|
|
822
|
+
const queryClient = useQueryClient();
|
|
823
|
+
return useActionMutation<
|
|
824
|
+
ContentDatabaseResponse,
|
|
825
|
+
{ documentId: string; sourceId: string }
|
|
826
|
+
>("materialize-builder-required-fields", {
|
|
827
|
+
onSuccess: (data) => {
|
|
828
|
+
writeContentDatabaseResponseToCache(queryClient, documentId, data);
|
|
829
|
+
queryClient.invalidateQueries({
|
|
830
|
+
queryKey: contentDatabaseQueryKey(documentId),
|
|
831
|
+
});
|
|
832
|
+
queryClient.invalidateQueries({
|
|
833
|
+
queryKey: ["action", "get-content-database-source", { documentId }],
|
|
834
|
+
});
|
|
835
|
+
queryClient.invalidateQueries({
|
|
836
|
+
queryKey: ["action", "list-document-properties", { documentId }],
|
|
837
|
+
});
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
818
842
|
export function useBuilderCmsModels(enabled: boolean) {
|
|
819
843
|
return useActionQuery<BuilderCmsModelsResponse>(
|
|
820
844
|
"list-builder-cms-models",
|
|
@@ -1014,6 +1038,23 @@ export function usePrepareBuilderSourceExecution(documentId: string) {
|
|
|
1014
1038
|
});
|
|
1015
1039
|
}
|
|
1016
1040
|
|
|
1041
|
+
export function useCancelPreparedBuilderSourceUpdate(documentId: string) {
|
|
1042
|
+
const queryClient = useQueryClient();
|
|
1043
|
+
return useActionMutation<
|
|
1044
|
+
CancelPreparedBuilderSourceUpdateResponse,
|
|
1045
|
+
CancelPreparedBuilderSourceUpdateRequest
|
|
1046
|
+
>("cancel-prepared-builder-source-update", {
|
|
1047
|
+
onSuccess: () => {
|
|
1048
|
+
queryClient.invalidateQueries({
|
|
1049
|
+
queryKey: contentDatabaseQueryKey(documentId),
|
|
1050
|
+
});
|
|
1051
|
+
queryClient.invalidateQueries({
|
|
1052
|
+
queryKey: ["action", "get-content-database-source", { documentId }],
|
|
1053
|
+
});
|
|
1054
|
+
},
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1017
1058
|
export function useValidateBuilderSourceExecution(documentId: string) {
|
|
1018
1059
|
const queryClient = useQueryClient();
|
|
1019
1060
|
return useActionMutation<
|
|
@@ -1099,6 +1140,33 @@ export function usePrepareBuilderSourceReview(documentId: string) {
|
|
|
1099
1140
|
});
|
|
1100
1141
|
}
|
|
1101
1142
|
|
|
1143
|
+
export function usePreviewBuilderSourceReview(args: {
|
|
1144
|
+
documentId: string;
|
|
1145
|
+
sourceId: string | null;
|
|
1146
|
+
scope: "selected" | "all";
|
|
1147
|
+
documentIds?: string[];
|
|
1148
|
+
enabled: boolean;
|
|
1149
|
+
}) {
|
|
1150
|
+
return useActionQuery<PreviewBuilderSourceReviewResponse>(
|
|
1151
|
+
"preview-builder-source-review",
|
|
1152
|
+
args.enabled && args.sourceId
|
|
1153
|
+
? {
|
|
1154
|
+
documentId: args.documentId,
|
|
1155
|
+
sourceId: args.sourceId,
|
|
1156
|
+
scope: args.scope,
|
|
1157
|
+
documentIds: args.documentIds,
|
|
1158
|
+
}
|
|
1159
|
+
: undefined,
|
|
1160
|
+
{
|
|
1161
|
+
enabled: args.enabled && !!args.sourceId,
|
|
1162
|
+
retry: false,
|
|
1163
|
+
staleTime: 0,
|
|
1164
|
+
gcTime: 0,
|
|
1165
|
+
refetchOnWindowFocus: false,
|
|
1166
|
+
},
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1102
1170
|
export function useStageBuilderSourceBulkUpdate(documentId: string) {
|
|
1103
1171
|
const queryClient = useQueryClient();
|
|
1104
1172
|
return useActionMutation<
|
|
@@ -92,13 +92,15 @@ export function seedDatabaseItemDocumentCaches(
|
|
|
92
92
|
// Seed only cold caches. Overwriting an existing entry would bump its
|
|
93
93
|
// freshness with possibly older table-snapshot data (a background database
|
|
94
94
|
// refetch can lag a just-saved document edit) and suppress the correcting
|
|
95
|
-
// refetch for the whole staleTime window.
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
// an
|
|
95
|
+
// refetch for the whole staleTime window. Source-backed rows are never seeded:
|
|
96
|
+
// list snapshots are not authoritative enough to unlock the body editor, even
|
|
97
|
+
// when they happen to contain non-empty content. The dedicated get-document
|
|
98
|
+
// response owns that decision and prevents an edit from racing hydration.
|
|
99
99
|
if (
|
|
100
100
|
!databaseItemBodyHydrationIsPending(item) &&
|
|
101
101
|
!sourceBackedEmptyBody &&
|
|
102
|
+
!item.bodyHydration &&
|
|
103
|
+
!item.document.databaseMembership?.sourceId &&
|
|
102
104
|
queryClient.getQueryData(documentQueryKey(item.document.id)) === undefined
|
|
103
105
|
) {
|
|
104
106
|
queryClient.setQueryData<Document>(documentQueryKey(item.document.id), {
|
|
@@ -139,6 +141,53 @@ export function useDocument(id: string | null) {
|
|
|
139
141
|
});
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
export interface PreviewDocumentDraftRecord {
|
|
145
|
+
documentId: string;
|
|
146
|
+
title: string;
|
|
147
|
+
content: string;
|
|
148
|
+
baseDocumentUpdatedAt: string | null;
|
|
149
|
+
loadedContentWasEmpty: number;
|
|
150
|
+
deferredReason: string | null;
|
|
151
|
+
version: number;
|
|
152
|
+
updatedAt: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function usePreviewDocumentDraft(documentId: string | null) {
|
|
156
|
+
return useActionQuery<{ draft: PreviewDocumentDraftRecord | null }>(
|
|
157
|
+
"get-preview-document-draft",
|
|
158
|
+
documentId ? { documentId } : undefined,
|
|
159
|
+
{ enabled: !!documentId, retry: false },
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function useUpdatePreviewDocumentDraft() {
|
|
164
|
+
return useActionMutation<
|
|
165
|
+
{
|
|
166
|
+
status: "saved" | "deleted" | "conflict";
|
|
167
|
+
draft: PreviewDocumentDraftRecord | null;
|
|
168
|
+
},
|
|
169
|
+
| {
|
|
170
|
+
operation: "upsert";
|
|
171
|
+
documentId: string;
|
|
172
|
+
expectedVersion: number | null;
|
|
173
|
+
draft: {
|
|
174
|
+
title: string;
|
|
175
|
+
content: string;
|
|
176
|
+
baseDocumentUpdatedAt: string | null;
|
|
177
|
+
loadedContentWasEmpty: boolean;
|
|
178
|
+
deferredReason: "hydration" | "conflict" | null;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
| {
|
|
182
|
+
operation: "delete";
|
|
183
|
+
documentId: string;
|
|
184
|
+
expectedVersion: number;
|
|
185
|
+
expectedTitle: string;
|
|
186
|
+
expectedContent: string;
|
|
187
|
+
}
|
|
188
|
+
>("update-preview-document-draft");
|
|
189
|
+
}
|
|
190
|
+
|
|
142
191
|
export function useCreateDocument() {
|
|
143
192
|
return useActionMutation<Document, DocumentCreateRequest>("create-document");
|
|
144
193
|
}
|
|
@@ -578,6 +578,13 @@ const messages = {
|
|
|
578
578
|
addDetailsToExistingItems: "為現有項目新增詳細資料",
|
|
579
579
|
addedFieldsFromSource: "已從此來源新增 {{count}} 個欄位。",
|
|
580
580
|
addedOneFieldFromSource: "已從此來源新增 1 個欄位。",
|
|
581
|
+
addRequiredFields: "新增必填欄位",
|
|
582
|
+
publishingFieldsAdded: "已新增發布欄位",
|
|
583
|
+
requiredBuilderFieldsReady:
|
|
584
|
+
"{{count}} 個 Builder 必填欄位已可在 Content 中編輯。",
|
|
585
|
+
requiredPublishingFields: "必填發布欄位",
|
|
586
|
+
requiredPublishingFieldsDescription:
|
|
587
|
+
"新增此集合發布前 Builder 所需的中繼資料:{{fields}}。",
|
|
581
588
|
addingDetails: "正在新增詳細資料",
|
|
582
589
|
addingDetailsMatchedOn: "正在新增以 {{field}} 比對的詳細資料。",
|
|
583
590
|
addingItems: "正在新增項目",
|
|
@@ -628,6 +635,7 @@ const messages = {
|
|
|
628
635
|
noPendingLocalBuilderChanges: "尚無待處理的本機 Builder 變更。",
|
|
629
636
|
needsAttention: "需要處理",
|
|
630
637
|
failedYouCanRetry: "失敗 — 您可以重試",
|
|
638
|
+
reconciliationRequired: "需要核對 — 請勿重試",
|
|
631
639
|
needsAFreshReview: "需要重新審查",
|
|
632
640
|
working: "處理中…",
|
|
633
641
|
ready: "就緒",
|
|
@@ -653,6 +661,11 @@ const messages = {
|
|
|
653
661
|
builderBodySyncing: "內容仍在從 Builder 同步",
|
|
654
662
|
builderBodySyncingDescription:
|
|
655
663
|
"同步 Builder 正文完成前會暫停編輯,避免覆寫既有文章內容。",
|
|
664
|
+
builderDraftConflictTitle: "你編輯期間收到了新的 Builder 內容",
|
|
665
|
+
builderDraftConflictDescription:
|
|
666
|
+
"你的本機草稿已保留。你可以保留草稿以在本機取代新同步的正文,或重新載入 Builder 正文。",
|
|
667
|
+
keepLocalDraft: "保留本機草稿",
|
|
668
|
+
reloadBuilderBody: "重新載入 Builder 正文",
|
|
656
669
|
builderBodiesHydrated: "已同步 {{hydrated}} / {{total}} 個正文",
|
|
657
670
|
builderReviewShowingRows:
|
|
658
671
|
"此次審查顯示 {{total}} 個 Builder 列中的 {{shown}} 個。",
|
|
@@ -870,6 +883,8 @@ const messages = {
|
|
|
870
883
|
retry: "Retry",
|
|
871
884
|
reviewBeforeTheyReachBuilder: "在傳送到 Builder 前審核。",
|
|
872
885
|
reviewDiff: "審核差異",
|
|
886
|
+
reviewDiffDescription: "在任何內容傳送到 Builder 前載入完整的內容差異。",
|
|
887
|
+
loadingCompleteBuilderDiff: "正在載入完整的 Builder 差異…",
|
|
873
888
|
sampleMatches: "範例匹配",
|
|
874
889
|
showAll: "顯示全部",
|
|
875
890
|
sortAscending: "升序排序",
|
|
@@ -917,6 +932,7 @@ const messages = {
|
|
|
917
932
|
builderModel: "Builder 模型",
|
|
918
933
|
builderPreparingGate: "正在準備 Builder gate,並透過受保護路徑送出。",
|
|
919
934
|
builderPushFinished: "Builder 推送已完成。",
|
|
935
|
+
builderPushAlreadyRunning: "Builder 推送已在執行中。未送出第二次寫入。",
|
|
920
936
|
builderPushedReconciled: "已送出至 Builder 並完成本機對帳。",
|
|
921
937
|
builderPushWillSend: "送出會透過受保護的 Builder 路徑寫入 autosave。",
|
|
922
938
|
builderWritesDisabledCheckOnly: "Builder 寫入已停用。送出只會檢查更新。",
|
|
@@ -926,6 +942,15 @@ const messages = {
|
|
|
926
942
|
checksOnly: "僅檢查",
|
|
927
943
|
close: "關閉",
|
|
928
944
|
closeBuilderUpdateReview: "關閉 Builder 更新審查",
|
|
945
|
+
cancelPreparedUpdate: "取消已準備的更新",
|
|
946
|
+
cancelPreparedUpdateQuestion: "要取消這個已準備的更新嗎?",
|
|
947
|
+
cancelPreparedUpdateDescription:
|
|
948
|
+
"這會在送出前停止本機執行並保留稽核記錄。不會向 Builder 傳送任何內容。",
|
|
949
|
+
keepPreparedUpdate: "保留已準備的更新",
|
|
950
|
+
cancellingPreparedUpdate: "正在取消…",
|
|
951
|
+
preparedUpdateCancelled: "已取消準備好的 Builder 更新",
|
|
952
|
+
preparedUpdateAlreadyCancelled: "準備好的 Builder 更新已被取消",
|
|
953
|
+
cancelPreparedUpdateFailed: "未能取消準備好的 Builder 更新",
|
|
929
954
|
disabled: "已停用",
|
|
930
955
|
draft: "草稿",
|
|
931
956
|
enabled: "已啟用",
|