@agent-native/core 0.159.2 → 0.159.3
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/clips/.agents/skills/meetings/SKILL.md +11 -6
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/actions/lib/meeting-content.ts +15 -4
- package/corpus/templates/clips/actions/list-meetings.ts +179 -42
- package/corpus/templates/clips/actions/search-meetings.ts +277 -0
- package/corpus/templates/clips/app/components/meetings/agenda-card.tsx +273 -0
- package/corpus/templates/clips/app/components/meetings/day-grouped-card.tsx +113 -0
- package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +117 -0
- package/corpus/templates/clips/app/hooks/use-navigation-state.ts +12 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -2
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +263 -357
- package/corpus/templates/clips/changelog/2026-08-14-meetings-history-is-searchable-again.md +6 -0
- package/corpus/templates/clips/desktop/design-refs/granola-ux.md +17 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +29 -2
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +11 -11
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +11 -50
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +57 -20
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +30 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +204 -51
- package/corpus/templates/content/app/global.css +4 -1
- package/corpus/templates/content/app/i18n-data.ts +30 -0
- package/corpus/templates/content/changelog/2026-08-12-toggle-blocks-now-follow-notion-style-enter-and-shift-tab-be.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-08-12-toggle-summary-focus-persistence-shape.md +733 -0
- package/corpus/templates/content/shared/builder-mdx.ts +44 -4
- package/corpus/templates/slides/actions/get-layout-overflows.ts +65 -2
- package/dist/deploy/build.d.ts +6 -4
- package/dist/deploy/build.js +27 -11
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/package.json +2 -2
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +0 -333
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: improved
|
|
3
|
+
date: 2026-08-14
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Meetings now opens on your history instead of a wall of upcoming cards: past meetings with notes but no linked recording are back in the list, older ones keep loading as you go, and search reaches attendee names and transcript text so you can find a call by what was said in it.
|
|
@@ -91,6 +91,23 @@ Primary (cited inline below):
|
|
|
91
91
|
- **+N attendee overflow** is used in the homepage marketing example: `Jim, Michaela +5`. So attendees collapse past ~2 named to a `+N` count.
|
|
92
92
|
- Empty state copy and detail are not exposed publicly; design ours to match Apple-Notes minimalism (single illustrated/iconic prompt, no marketing fluff).
|
|
93
93
|
|
|
94
|
+
### Home layout, observed 2026-08-14
|
|
95
|
+
|
|
96
|
+
From a first-party screenshot of the current app (supersedes the review-derived
|
|
97
|
+
notes above where they conflict):
|
|
98
|
+
|
|
99
|
+
- Upcoming collapses into **one bordered "Coming up" card** at the top, roughly
|
|
100
|
+
300px tall for three days. Each day is a left column (`14 / August / Fri`)
|
|
101
|
+
beside its events; only the in-progress event carries a button (`Start now`).
|
|
102
|
+
- Everything below is history, in day groups (`Tue, Aug 11`, `Thu, Aug 6`) that
|
|
103
|
+
**skip empty days** and scroll back indefinitely — no pager, no visible cap.
|
|
104
|
+
- History rows are one line: avatar, title, attendee subtitle (`Elaine`,
|
|
105
|
+
`Lisa, Cody & 2 others`), right-aligned time. Notes with no attendees get a
|
|
106
|
+
document icon and `Me`. No status chips anywhere in the list.
|
|
107
|
+
- An **"Ask anything" composer is pinned to the bottom** of the home view with
|
|
108
|
+
a suggestion chip (`List recent todos`) — the same bar as the note view, so
|
|
109
|
+
the agent is reachable over the whole corpus, not just one meeting.
|
|
110
|
+
|
|
94
111
|
### Sidebar organisation
|
|
95
112
|
|
|
96
113
|
- Folders: "private" or "shared" (Business Dive, Wonder Tools).
|
|
@@ -2965,6 +2965,7 @@ export async function withBuilderBodiesSourceValues(
|
|
|
2965
2965
|
export async function builderBodyChangeForLocalContent(args: {
|
|
2966
2966
|
row: Pick<ContentDatabaseSourceRecordRowDb, "sourceValuesJson">;
|
|
2967
2967
|
localContent: string | null | undefined;
|
|
2968
|
+
usesCurrentHydrationCodec?: boolean;
|
|
2968
2969
|
}): Promise<ContentDatabaseSourceBodyChange | null> {
|
|
2969
2970
|
const sourceValues =
|
|
2970
2971
|
parseObject<Record<string, DocumentPropertyValue>>(
|
|
@@ -2995,6 +2996,14 @@ export async function builderBodyChangeForLocalContent(args: {
|
|
|
2995
2996
|
builderBodyUsesCurrentMediaConverter(localContent);
|
|
2996
2997
|
const normalizedLocalContent =
|
|
2997
2998
|
normalizeBuilderBodyBaselineContent(localContent);
|
|
2999
|
+
if (
|
|
3000
|
+
args.usesCurrentHydrationCodec &&
|
|
3001
|
+
normalizedLocalContent &&
|
|
3002
|
+
normalizedLocalContent ===
|
|
3003
|
+
normalizeBuilderBodyBaselineContent(currentContent)
|
|
3004
|
+
) {
|
|
3005
|
+
return null;
|
|
3006
|
+
}
|
|
2998
3007
|
if (
|
|
2999
3008
|
!usesCurrentMediaConverter &&
|
|
3000
3009
|
normalizedLocalContent &&
|
|
@@ -3027,7 +3036,11 @@ export async function builderBodyChangeForLocalContent(args: {
|
|
|
3027
3036
|
}
|
|
3028
3037
|
|
|
3029
3038
|
try {
|
|
3030
|
-
const
|
|
3039
|
+
const canMergeReadableBaseline =
|
|
3040
|
+
!!losslessContent &&
|
|
3041
|
+
!localContent.includes("<Builder") &&
|
|
3042
|
+
(args.usesCurrentHydrationCodec || !usesCurrentMediaConverter);
|
|
3043
|
+
const proposed = !canMergeReadableBaseline
|
|
3031
3044
|
? {
|
|
3032
3045
|
blocks: await builderMdxBodyToBuilderBlocks(
|
|
3033
3046
|
normalizeUnsourcedBuilderCreateMdx(localContent),
|
|
@@ -3035,7 +3048,7 @@ export async function builderBodyChangeForLocalContent(args: {
|
|
|
3035
3048
|
),
|
|
3036
3049
|
warnings: [] as string[],
|
|
3037
3050
|
}
|
|
3038
|
-
:
|
|
3051
|
+
: canMergeReadableBaseline
|
|
3039
3052
|
? await builderReadableBodyToBuilderBlocks({
|
|
3040
3053
|
localContent,
|
|
3041
3054
|
losslessContent,
|
|
@@ -3158,6 +3171,7 @@ export async function builderBodyChangeForSourceSnapshotDocument(args: {
|
|
|
3158
3171
|
sourceValuesJson: string;
|
|
3159
3172
|
};
|
|
3160
3173
|
isHydrated: boolean;
|
|
3174
|
+
bodyHydrationVersion?: string | null;
|
|
3161
3175
|
allowUnsourcedCreate: boolean;
|
|
3162
3176
|
localContent: string | null | undefined;
|
|
3163
3177
|
}): Promise<ContentDatabaseSourceBodyChange | null> {
|
|
@@ -3175,6 +3189,9 @@ export async function builderBodyChangeForSourceSnapshotDocument(args: {
|
|
|
3175
3189
|
return builderBodyChangeForLocalContent({
|
|
3176
3190
|
row: args.row,
|
|
3177
3191
|
localContent: args.localContent,
|
|
3192
|
+
usesCurrentHydrationCodec:
|
|
3193
|
+
Boolean(args.bodyHydrationVersion) &&
|
|
3194
|
+
!builderBodyHydrationIsCodecMigration(args.bodyHydrationVersion),
|
|
3178
3195
|
});
|
|
3179
3196
|
}
|
|
3180
3197
|
if (!args.allowUnsourcedCreate) return null;
|
|
@@ -3973,6 +3990,8 @@ async function readSourceSnapshotRowsOnce(args: {
|
|
|
3973
3990
|
id: schema.contentDatabaseItems.id,
|
|
3974
3991
|
documentId: schema.contentDatabaseItems.documentId,
|
|
3975
3992
|
bodyHydrationStatus: schema.contentDatabaseItems.bodyHydrationStatus,
|
|
3993
|
+
bodyHydrationVersion:
|
|
3994
|
+
schema.contentDatabaseItems.bodyHydrationVersion,
|
|
3976
3995
|
})
|
|
3977
3996
|
.from(schema.contentDatabaseItems)
|
|
3978
3997
|
.where(
|
|
@@ -4398,6 +4417,12 @@ async function loadSourceSnapshot(
|
|
|
4398
4417
|
.filter((item) => item.bodyHydrationStatus === "hydrated")
|
|
4399
4418
|
.map((item) => item.documentId),
|
|
4400
4419
|
);
|
|
4420
|
+
const bodyHydrationVersionByDocumentId = new Map(
|
|
4421
|
+
databaseItemRows.map((item) => [
|
|
4422
|
+
item.documentId,
|
|
4423
|
+
item.bodyHydrationVersion,
|
|
4424
|
+
]),
|
|
4425
|
+
);
|
|
4401
4426
|
await Promise.all(
|
|
4402
4427
|
allDocumentIds.map(async (documentId) => {
|
|
4403
4428
|
const row = sourceRowByDocumentId.get(documentId);
|
|
@@ -4412,6 +4437,8 @@ async function loadSourceSnapshot(
|
|
|
4412
4437
|
bodyChange = await builderBodyChangeForSourceSnapshotDocument({
|
|
4413
4438
|
row,
|
|
4414
4439
|
isHydrated: hydratedDocumentIds.has(documentId),
|
|
4440
|
+
bodyHydrationVersion:
|
|
4441
|
+
bodyHydrationVersionByDocumentId.get(documentId) ?? null,
|
|
4415
4442
|
allowUnsourcedCreate,
|
|
4416
4443
|
localContent: documentContentById.get(documentId),
|
|
4417
4444
|
});
|
|
@@ -439,16 +439,17 @@ const commands: CommandTemplate[] = [
|
|
|
439
439
|
descriptionKey: "editor.slash.toggleDescription",
|
|
440
440
|
shortcut: ">",
|
|
441
441
|
icon: IconChevronRight,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
.run();
|
|
442
|
+
preserveSlashRange: true,
|
|
443
|
+
action: (editor, { slashRange }) => {
|
|
444
|
+
const toggle = {
|
|
445
|
+
type: "notionToggle",
|
|
446
|
+
attrs: { summary: "", open: true },
|
|
447
|
+
};
|
|
448
|
+
if (slashRange) {
|
|
449
|
+
editor.chain().focus().insertContentAt(slashRange, toggle).run();
|
|
450
|
+
} else {
|
|
451
|
+
editor.chain().focus().insertContent(toggle).run();
|
|
452
|
+
}
|
|
452
453
|
focusMostRecentEmptyToggleSummary(editor);
|
|
453
454
|
},
|
|
454
455
|
},
|
|
@@ -553,7 +554,6 @@ const turnIntoCommands: CommandTemplate[] = [
|
|
|
553
554
|
.insertContent({
|
|
554
555
|
type: "notionToggle",
|
|
555
556
|
attrs: { summary: text, open: true },
|
|
556
|
-
content: [{ type: "paragraph" }],
|
|
557
557
|
})
|
|
558
558
|
.run();
|
|
559
559
|
if (!text) focusMostRecentEmptyToggleSummary(editor);
|
|
@@ -76,7 +76,6 @@ import {
|
|
|
76
76
|
LocalMdxComponentNode,
|
|
77
77
|
} from "./extensions/LocalMdxComponentNode";
|
|
78
78
|
import {
|
|
79
|
-
EMPTY_TOGGLE_BODY_PLACEHOLDER,
|
|
80
79
|
createNotionEditorExtensions,
|
|
81
80
|
focusMostRecentEmptyToggleSummary,
|
|
82
81
|
type NotionPageLink,
|
|
@@ -447,10 +446,7 @@ const NotionMarkdownShortcuts = Extension.create({
|
|
|
447
446
|
.replaceWith(
|
|
448
447
|
shortcut.blockFrom,
|
|
449
448
|
shortcut.blockTo,
|
|
450
|
-
toggle.create(
|
|
451
|
-
{ summary: "", open: true },
|
|
452
|
-
paragraph.create(),
|
|
453
|
-
),
|
|
449
|
+
toggle.create({ summary: "", open: true }),
|
|
454
450
|
)
|
|
455
451
|
.scrollIntoView(),
|
|
456
452
|
);
|
|
@@ -478,47 +474,6 @@ const NotionMarkdownShortcuts = Extension.create({
|
|
|
478
474
|
},
|
|
479
475
|
});
|
|
480
476
|
|
|
481
|
-
const NotionToggleBodyPlaceholder = Extension.create({
|
|
482
|
-
name: "notionToggleBodyPlaceholder",
|
|
483
|
-
|
|
484
|
-
addProseMirrorPlugins() {
|
|
485
|
-
return [
|
|
486
|
-
new Plugin({
|
|
487
|
-
key: new PluginKey("notionToggleBodyPlaceholder"),
|
|
488
|
-
props: {
|
|
489
|
-
decorations: ({ doc, selection }) => {
|
|
490
|
-
const decorations: Decoration[] = [];
|
|
491
|
-
|
|
492
|
-
doc.descendants((node, pos, parent) => {
|
|
493
|
-
const selectionIsInsideNode =
|
|
494
|
-
selection.from >= pos && selection.to <= pos + node.nodeSize;
|
|
495
|
-
|
|
496
|
-
if (
|
|
497
|
-
node.type.name !== "paragraph" ||
|
|
498
|
-
parent?.type.name !== "notionToggle" ||
|
|
499
|
-
node.content.size > 0 ||
|
|
500
|
-
node.textContent.trim() ||
|
|
501
|
-
selectionIsInsideNode
|
|
502
|
-
) {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
decorations.push(
|
|
507
|
-
Decoration.node(pos, pos + node.nodeSize, {
|
|
508
|
-
class: "is-empty notion-toggle__body-placeholder",
|
|
509
|
-
"data-placeholder": EMPTY_TOGGLE_BODY_PLACEHOLDER,
|
|
510
|
-
}),
|
|
511
|
-
);
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
return DecorationSet.create(doc, decorations);
|
|
515
|
-
},
|
|
516
|
-
},
|
|
517
|
-
}),
|
|
518
|
-
];
|
|
519
|
-
},
|
|
520
|
-
});
|
|
521
|
-
|
|
522
477
|
/**
|
|
523
478
|
* Tab / Shift-Tab indents any block (paragraph, heading, blockquote, etc.)
|
|
524
479
|
* by wrapping it in a blockquote — which the NFM pipeline already serializes
|
|
@@ -1363,9 +1318,7 @@ function getVisualEditorPlaceholder({
|
|
|
1363
1318
|
hasAncestorType(editor, pos, "notionToggle");
|
|
1364
1319
|
|
|
1365
1320
|
if (isToggleBody) {
|
|
1366
|
-
return hasAnchor && editor.isFocused
|
|
1367
|
-
? emptyBlockPlaceholder
|
|
1368
|
-
: EMPTY_TOGGLE_BODY_PLACEHOLDER;
|
|
1321
|
+
return hasAnchor && editor.isFocused ? emptyBlockPlaceholder : "";
|
|
1369
1322
|
}
|
|
1370
1323
|
|
|
1371
1324
|
if (node.type.name === "heading") {
|
|
@@ -1671,7 +1624,6 @@ export function createVisualEditorExtensions({
|
|
|
1671
1624
|
VisualEditorPlaceholder.configure({
|
|
1672
1625
|
emptyBlockPlaceholder,
|
|
1673
1626
|
}),
|
|
1674
|
-
NotionToggleBodyPlaceholder,
|
|
1675
1627
|
Link.configure({
|
|
1676
1628
|
openOnClick: false,
|
|
1677
1629
|
HTMLAttributes: { class: "notion-link" },
|
|
@@ -2044,6 +1996,14 @@ export function VisualEditor({
|
|
|
2044
1996
|
) ?? null
|
|
2045
1997
|
);
|
|
2046
1998
|
}, []);
|
|
1999
|
+
const isVisualEditorFocused = useCallback((editor: CoreEditor) => {
|
|
2000
|
+
if (editor.isFocused) return true;
|
|
2001
|
+
const activeElement = editor.view.dom.ownerDocument.activeElement;
|
|
2002
|
+
return Boolean(
|
|
2003
|
+
activeElement?.matches(".notion-toggle__summary") &&
|
|
2004
|
+
editor.view.dom.contains(activeElement),
|
|
2005
|
+
);
|
|
2006
|
+
}, []);
|
|
2047
2007
|
|
|
2048
2008
|
// Reuse the synced Awareness instance when provided; fall back for tests or
|
|
2049
2009
|
// non-template embedders that only pass a Y.Doc.
|
|
@@ -2468,6 +2428,7 @@ export function VisualEditor({
|
|
|
2468
2428
|
value: content,
|
|
2469
2429
|
contentUpdatedAt,
|
|
2470
2430
|
editable,
|
|
2431
|
+
isEditorFocused: isVisualEditorFocused,
|
|
2471
2432
|
getMarkdown: (e) => docToNfm(e.getJSON() as any),
|
|
2472
2433
|
// Read-only viewers join the shared Y.Doc purely to RECEIVE live edits and
|
|
2473
2434
|
// cursors; their editor content comes from the server state fetch + peer Yjs
|
|
@@ -890,6 +890,8 @@ function DatabaseTable({
|
|
|
890
890
|
>(null);
|
|
891
891
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
892
892
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
893
|
+
const [preserveSourceNavigationOnClose, setPreserveSourceNavigationOnClose] =
|
|
894
|
+
useState(false);
|
|
893
895
|
const sourceHandoffActiveRef = useRef(false);
|
|
894
896
|
const addPropertyOpenRequestSequenceRef = useRef(0);
|
|
895
897
|
const [addPropertyOpenRequestId, setAddPropertyOpenRequestId] = useState(0);
|
|
@@ -940,12 +942,35 @@ function DatabaseTable({
|
|
|
940
942
|
};
|
|
941
943
|
async function runSourceHandoffMutation<T>(
|
|
942
944
|
mutation: () => Promise<T>,
|
|
945
|
+
options: { revealOptimisticPreview?: boolean } = {},
|
|
943
946
|
): Promise<T | null> {
|
|
947
|
+
const returnToAddProperty = sourceHandoffActiveRef.current;
|
|
948
|
+
if (options.revealOptimisticPreview) {
|
|
949
|
+
sourceHandoffActiveRef.current = false;
|
|
950
|
+
setPreserveSourceNavigationOnClose(true);
|
|
951
|
+
setSettingsOpen(false);
|
|
952
|
+
}
|
|
944
953
|
try {
|
|
945
954
|
const result = await mutation();
|
|
946
|
-
|
|
955
|
+
if (options.revealOptimisticPreview) {
|
|
956
|
+
setPreserveSourceNavigationOnClose(false);
|
|
957
|
+
if (returnToAddProperty) {
|
|
958
|
+
addPropertyOpenRequestSequenceRef.current += 1;
|
|
959
|
+
setAddPropertyOpenRequestId(
|
|
960
|
+
addPropertyOpenRequestSequenceRef.current,
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
} else {
|
|
964
|
+
completeSourceHandoff();
|
|
965
|
+
}
|
|
947
966
|
return result;
|
|
948
967
|
} catch (error) {
|
|
968
|
+
if (options.revealOptimisticPreview) {
|
|
969
|
+
sourceHandoffActiveRef.current = returnToAddProperty;
|
|
970
|
+
setSettingsPanel("source");
|
|
971
|
+
setSettingsOpen(true);
|
|
972
|
+
setPreserveSourceNavigationOnClose(false);
|
|
973
|
+
}
|
|
949
974
|
toast.error(dbText("failedToAttachSource"), {
|
|
950
975
|
description:
|
|
951
976
|
error instanceof Error ? error.message : dbText("somethingWentWrong"),
|
|
@@ -3067,21 +3092,28 @@ function DatabaseTable({
|
|
|
3067
3092
|
onClose={closeDatabaseSettings}
|
|
3068
3093
|
onPanelChange={changeDatabaseSettingsPanel}
|
|
3069
3094
|
onAttachBuilderSource={(model, relationshipMode) =>
|
|
3070
|
-
runSourceHandoffMutation(
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3095
|
+
runSourceHandoffMutation(
|
|
3096
|
+
async () => {
|
|
3097
|
+
const result = await attachSource.mutateAsync({
|
|
3098
|
+
documentId: document.id,
|
|
3099
|
+
sourceType: "builder-cms",
|
|
3100
|
+
sourceName: model.displayName,
|
|
3101
|
+
sourceTable: model.name,
|
|
3102
|
+
builderFieldPaths: model.fields.map((field) => field.name),
|
|
3103
|
+
relationshipMode,
|
|
3104
|
+
mode:
|
|
3105
|
+
relationshipMode === "items"
|
|
3106
|
+
? "add"
|
|
3107
|
+
: sources.length > 0 || source
|
|
3108
|
+
? undefined
|
|
3109
|
+
: "replace",
|
|
3110
|
+
});
|
|
3111
|
+
if ("responseProjection" in result) {
|
|
3112
|
+
runBuilderHydration(result.sourceId);
|
|
3113
|
+
}
|
|
3114
|
+
return result;
|
|
3115
|
+
},
|
|
3116
|
+
{ revealOptimisticPreview: true },
|
|
3085
3117
|
)
|
|
3086
3118
|
}
|
|
3087
3119
|
onFederateSource={(candidate, join) =>
|
|
@@ -3194,6 +3226,7 @@ function DatabaseTable({
|
|
|
3194
3226
|
setSourceWriteMode.isPending
|
|
3195
3227
|
}
|
|
3196
3228
|
sourcePendingOperations={sourcePendingOperations}
|
|
3229
|
+
preserveSourceNavigationOnClose={preserveSourceNavigationOnClose}
|
|
3197
3230
|
onViewTypeChange={(type) =>
|
|
3198
3231
|
setViewConfig(updateDatabaseViewType(viewConfig, activeView.id, type))
|
|
3199
3232
|
}
|
|
@@ -7634,6 +7667,7 @@ function DatabaseSettingsPanelSheet({
|
|
|
7634
7667
|
onSetBuilderLiveWrites,
|
|
7635
7668
|
sourceActionPending,
|
|
7636
7669
|
sourcePendingOperations,
|
|
7670
|
+
preserveSourceNavigationOnClose,
|
|
7637
7671
|
onViewTypeChange,
|
|
7638
7672
|
onWrapCellsChange,
|
|
7639
7673
|
onOpenPagesInChange,
|
|
@@ -7681,6 +7715,7 @@ function DatabaseSettingsPanelSheet({
|
|
|
7681
7715
|
onSetBuilderLiveWrites: (settings: BuilderSourceWriteSettingsInput) => void;
|
|
7682
7716
|
sourceActionPending: boolean;
|
|
7683
7717
|
sourcePendingOperations: DatabaseSourcePendingOperations;
|
|
7718
|
+
preserveSourceNavigationOnClose: boolean;
|
|
7684
7719
|
onViewTypeChange: (type: ContentDatabaseViewType) => void;
|
|
7685
7720
|
onWrapCellsChange: (wrapCells: boolean) => void;
|
|
7686
7721
|
onOpenPagesInChange: (openPagesIn: ContentDatabaseOpenPagesIn) => void;
|
|
@@ -7730,10 +7765,12 @@ function DatabaseSettingsPanelSheet({
|
|
|
7730
7765
|
);
|
|
7731
7766
|
}, [builderAttachPreview.data, documentId, queryClient, sourceActionPending]);
|
|
7732
7767
|
useEffect(() => {
|
|
7733
|
-
//
|
|
7734
|
-
//
|
|
7735
|
-
if (
|
|
7736
|
-
|
|
7768
|
+
// Ordinary close/reopen returns to the root. The optimistic attach handoff
|
|
7769
|
+
// keeps the leaf only long enough to restore a failed mutation in place.
|
|
7770
|
+
if (panel !== "source" || (!open && !preserveSourceNavigationOnClose)) {
|
|
7771
|
+
setSourceNavStack([]);
|
|
7772
|
+
}
|
|
7773
|
+
}, [open, panel, preserveSourceNavigationOnClose]);
|
|
7737
7774
|
|
|
7738
7775
|
if (!open) return null;
|
|
7739
7776
|
|
|
@@ -801,6 +801,36 @@ export function BuilderSourceReviewDialog({
|
|
|
801
801
|
<div className="font-medium">
|
|
802
802
|
{row.bodyChange.summary}
|
|
803
803
|
</div>
|
|
804
|
+
{row.bodyChange.currentExcerpt !== null ||
|
|
805
|
+
row.bodyChange.proposedExcerpt !== null ? (
|
|
806
|
+
<div
|
|
807
|
+
className="mt-2 grid gap-2 text-muted-foreground sm:grid-cols-2"
|
|
808
|
+
aria-label={t(
|
|
809
|
+
"database.bodyChangeComparison",
|
|
810
|
+
)}
|
|
811
|
+
>
|
|
812
|
+
{row.bodyChange.currentExcerpt !== null ? (
|
|
813
|
+
<div className="min-w-0">
|
|
814
|
+
<div className="mb-1 font-medium text-foreground">
|
|
815
|
+
{t("database.currentBody")}
|
|
816
|
+
</div>
|
|
817
|
+
<div className="whitespace-pre-wrap break-words">
|
|
818
|
+
{row.bodyChange.currentExcerpt}
|
|
819
|
+
</div>
|
|
820
|
+
</div>
|
|
821
|
+
) : null}
|
|
822
|
+
{row.bodyChange.proposedExcerpt !== null ? (
|
|
823
|
+
<div className="min-w-0">
|
|
824
|
+
<div className="mb-1 font-medium text-foreground">
|
|
825
|
+
{t("database.proposedBody")}
|
|
826
|
+
</div>
|
|
827
|
+
<div className="whitespace-pre-wrap break-words">
|
|
828
|
+
{row.bodyChange.proposedExcerpt}
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
) : null}
|
|
832
|
+
</div>
|
|
833
|
+
) : null}
|
|
804
834
|
{row.bodyChange.warnings?.length ? (
|
|
805
835
|
<div className="mt-1 text-muted-foreground">
|
|
806
836
|
{row.bodyChange.warnings.join(" ")}
|