@agent-native/core 0.77.10 → 0.77.12
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 +12 -0
- package/corpus/core/package.json +3 -3
- package/corpus/core/src/cli/create.ts +65 -15
- package/corpus/core/src/cli/sync-builder-starter-manifest.ts +261 -65
- package/corpus/core/src/collab/awareness.ts +12 -9
- package/corpus/core/src/collab/client.ts +4 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -1
- package/corpus/templates/clips/chrome-extension/package.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +35 -19
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +38 -1
- package/corpus/templates/clips/desktop/src/styles.css +35 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +343 -14
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +36 -2
- package/corpus/templates/content/actions/move-document.ts +79 -33
- package/corpus/templates/content/actions/restore-content-database.ts +40 -3
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +84 -2
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +177 -49
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +393 -219
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +632 -467
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +114 -14
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +56 -15
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +6 -5
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +12 -4
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +36 -7
- package/corpus/templates/content/app/i18n-data.ts +140 -10
- package/corpus/templates/content/changelog/2026-06-25-comment-cards-now-track-their-highlighted-text-while-scrolli.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-comments-now-scroll-with-the-document-and-stay-below-the-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-creating-a-database-from-the-slash-menu-no-longer-leaves-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-hover-controls-no-longer-leave-a-lingering-fade-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-page-actions-are-now-easier-to-see-when-hovering-ina.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-text-selections-in-the-editor-no-longer-show-white-gaps-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-has-a-one-click-page-link-copy-button.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-shows-page-breadcrumbs-and-the-latest.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-page-command-now-leaves-a-notion-style-page-reference-an.md +6 -0
- package/corpus/templates/content/shared/nfm.ts +16 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +53 -15
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.d.ts +36 -2
- package/dist/cli/sync-builder-starter-manifest.d.ts.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.js +180 -37
- package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
- package/dist/collab/awareness.d.ts +1 -1
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +12 -8
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +4 -3
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
|
-
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
|
+
import { assertAccess, type Visibility } from "@agent-native/core/sharing";
|
|
4
4
|
import { and, eq, sql } from "drizzle-orm";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
@@ -54,19 +54,17 @@ async function assertParentIsNotDescendant({
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
async function
|
|
57
|
+
async function preflightBlockDatabaseOwnershipClearance({
|
|
58
58
|
db,
|
|
59
59
|
documentId,
|
|
60
60
|
ownerEmail,
|
|
61
61
|
parentId,
|
|
62
|
-
updatedAt,
|
|
63
62
|
}: {
|
|
64
63
|
db: ReturnType<typeof getDb>;
|
|
65
64
|
documentId: string;
|
|
66
65
|
ownerEmail: string;
|
|
67
66
|
parentId: string | null;
|
|
68
|
-
|
|
69
|
-
}) {
|
|
67
|
+
}): Promise<string | null> {
|
|
70
68
|
const [database] = await db
|
|
71
69
|
.select({
|
|
72
70
|
id: schema.contentDatabases.id,
|
|
@@ -81,11 +79,24 @@ async function clearBlockDatabaseOwnershipAfterParentChange({
|
|
|
81
79
|
);
|
|
82
80
|
|
|
83
81
|
if (!database?.ownerDocumentId || database.ownerDocumentId === parentId) {
|
|
84
|
-
return;
|
|
82
|
+
return null;
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
await assertAccess("document", database.ownerDocumentId, "editor");
|
|
86
|
+
return database.id;
|
|
87
|
+
}
|
|
88
88
|
|
|
89
|
+
async function clearBlockDatabaseOwnership({
|
|
90
|
+
db,
|
|
91
|
+
databaseId,
|
|
92
|
+
ownerEmail,
|
|
93
|
+
updatedAt,
|
|
94
|
+
}: {
|
|
95
|
+
db: Pick<ReturnType<typeof getDb>, "update">;
|
|
96
|
+
databaseId: string;
|
|
97
|
+
ownerEmail: string;
|
|
98
|
+
updatedAt: string;
|
|
99
|
+
}) {
|
|
89
100
|
await db
|
|
90
101
|
.update(schema.contentDatabases)
|
|
91
102
|
.set({
|
|
@@ -95,23 +106,44 @@ async function clearBlockDatabaseOwnershipAfterParentChange({
|
|
|
95
106
|
})
|
|
96
107
|
.where(
|
|
97
108
|
and(
|
|
98
|
-
eq(schema.contentDatabases.id,
|
|
109
|
+
eq(schema.contentDatabases.id, databaseId),
|
|
99
110
|
eq(schema.contentDatabases.ownerEmail, ownerEmail),
|
|
100
111
|
),
|
|
101
112
|
);
|
|
102
113
|
}
|
|
103
114
|
|
|
115
|
+
function sameRootSection(
|
|
116
|
+
left: { visibility: Visibility; orgId?: string | null },
|
|
117
|
+
right: { visibility: Visibility; orgId?: string | null },
|
|
118
|
+
) {
|
|
119
|
+
return left.visibility === right.visibility && left.orgId === right.orgId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function rootSectionFilter(document: {
|
|
123
|
+
visibility: Visibility;
|
|
124
|
+
orgId?: string | null;
|
|
125
|
+
}) {
|
|
126
|
+
return and(
|
|
127
|
+
eq(schema.documents.visibility, document.visibility),
|
|
128
|
+
document.orgId
|
|
129
|
+
? eq(schema.documents.orgId, document.orgId)
|
|
130
|
+
: sql`${schema.documents.orgId} IS NULL`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
104
134
|
async function resolveSiblingPositionsAfterMove({
|
|
105
135
|
db,
|
|
106
136
|
ownerEmail,
|
|
107
137
|
id,
|
|
108
138
|
parentId,
|
|
139
|
+
rootSection,
|
|
109
140
|
position,
|
|
110
141
|
}: {
|
|
111
142
|
db: ReturnType<typeof getDb>;
|
|
112
143
|
ownerEmail: string;
|
|
113
144
|
id: string;
|
|
114
145
|
parentId: string | null;
|
|
146
|
+
rootSection: { visibility: Visibility; orgId?: string | null };
|
|
115
147
|
position: number;
|
|
116
148
|
}) {
|
|
117
149
|
const siblings = await db
|
|
@@ -129,6 +161,7 @@ async function resolveSiblingPositionsAfterMove({
|
|
|
129
161
|
)
|
|
130
162
|
: and(
|
|
131
163
|
eq(schema.documents.ownerEmail, ownerEmail),
|
|
164
|
+
rootSectionFilter(rootSection),
|
|
132
165
|
sql`parent_id IS NULL`,
|
|
133
166
|
),
|
|
134
167
|
);
|
|
@@ -209,6 +242,9 @@ export default defineAction({
|
|
|
209
242
|
if (parentAccess.resource.ownerEmail !== ownerEmail) {
|
|
210
243
|
throw new Error("Parent document must belong to the same owner");
|
|
211
244
|
}
|
|
245
|
+
if (!sameRootSection(parentAccess.resource, existing)) {
|
|
246
|
+
throw new Error("Parent document must be in the same section");
|
|
247
|
+
}
|
|
212
248
|
await assertParentIsNotDescendant({
|
|
213
249
|
db,
|
|
214
250
|
ownerEmail,
|
|
@@ -221,6 +257,15 @@ export default defineAction({
|
|
|
221
257
|
|
|
222
258
|
const targetParentId =
|
|
223
259
|
args.parentId !== undefined ? args.parentId : existing.parentId;
|
|
260
|
+
const blockDatabaseIdToDetach =
|
|
261
|
+
args.parentId !== undefined
|
|
262
|
+
? await preflightBlockDatabaseOwnershipClearance({
|
|
263
|
+
db,
|
|
264
|
+
documentId: id,
|
|
265
|
+
ownerEmail,
|
|
266
|
+
parentId: args.parentId,
|
|
267
|
+
})
|
|
268
|
+
: null;
|
|
224
269
|
const normalizedSiblingPositions =
|
|
225
270
|
args.position !== undefined
|
|
226
271
|
? await resolveSiblingPositionsAfterMove({
|
|
@@ -228,6 +273,7 @@ export default defineAction({
|
|
|
228
273
|
ownerEmail,
|
|
229
274
|
id,
|
|
230
275
|
parentId: targetParentId,
|
|
276
|
+
rootSection: existing,
|
|
231
277
|
position: args.position,
|
|
232
278
|
})
|
|
233
279
|
: null;
|
|
@@ -249,36 +295,36 @@ export default defineAction({
|
|
|
249
295
|
)
|
|
250
296
|
: and(
|
|
251
297
|
eq(schema.documents.ownerEmail, ownerEmail),
|
|
298
|
+
rootSectionFilter(existing),
|
|
252
299
|
sql`parent_id IS NULL`,
|
|
253
300
|
),
|
|
254
301
|
);
|
|
255
302
|
updates.position = (maxPos[0]?.max ?? -1) + 1;
|
|
256
303
|
}
|
|
257
304
|
|
|
258
|
-
await db
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
305
|
+
await db.transaction(async (tx) => {
|
|
306
|
+
await tx
|
|
307
|
+
.update(schema.documents)
|
|
308
|
+
.set(updates)
|
|
309
|
+
.where(
|
|
310
|
+
and(
|
|
311
|
+
eq(schema.documents.id, id),
|
|
312
|
+
eq(schema.documents.ownerEmail, ownerEmail),
|
|
313
|
+
),
|
|
314
|
+
);
|
|
267
315
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
}
|
|
316
|
+
if (args.parentId !== undefined && blockDatabaseIdToDetach) {
|
|
317
|
+
await clearBlockDatabaseOwnership({
|
|
318
|
+
db: tx,
|
|
319
|
+
databaseId: blockDatabaseIdToDetach,
|
|
320
|
+
ownerEmail,
|
|
321
|
+
updatedAt,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
277
324
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
db
|
|
325
|
+
if (normalizedSiblingPositions) {
|
|
326
|
+
for (const document of normalizedSiblingPositions) {
|
|
327
|
+
await tx
|
|
282
328
|
.update(schema.documents)
|
|
283
329
|
.set({ position: document.position })
|
|
284
330
|
.where(
|
|
@@ -286,10 +332,10 @@ export default defineAction({
|
|
|
286
332
|
eq(schema.documents.id, document.id),
|
|
287
333
|
eq(schema.documents.ownerEmail, ownerEmail),
|
|
288
334
|
),
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
293
339
|
|
|
294
340
|
const [doc] = await db
|
|
295
341
|
.select()
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
|
+
import { resolveAccess } from "@agent-native/core/sharing";
|
|
3
4
|
import { eq } from "drizzle-orm";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
|
|
6
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
assertContentDatabaseLifecycleAccess,
|
|
10
|
+
collectInlineDatabaseOwnerBlockIds,
|
|
11
|
+
} from "./_content-database-lifecycle.js";
|
|
12
|
+
import pullDocumentAction from "./pull-document.js";
|
|
13
|
+
|
|
14
|
+
async function shouldClearStaleInlineOwnership(args: {
|
|
15
|
+
ownerDocumentId: string | null;
|
|
16
|
+
ownerBlockId: string | null;
|
|
17
|
+
}) {
|
|
18
|
+
if (!args.ownerDocumentId || !args.ownerBlockId) return false;
|
|
19
|
+
let content: string | null = null;
|
|
20
|
+
try {
|
|
21
|
+
const host = await pullDocumentAction.run({
|
|
22
|
+
id: args.ownerDocumentId,
|
|
23
|
+
format: "markdown",
|
|
24
|
+
});
|
|
25
|
+
content = String(host.content ?? "");
|
|
26
|
+
} catch {
|
|
27
|
+
const hostAccess = await resolveAccess("document", args.ownerDocumentId);
|
|
28
|
+
if (!hostAccess) return false;
|
|
29
|
+
content = String(hostAccess.resource.content ?? "");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parsed = await collectInlineDatabaseOwnerBlockIds(content);
|
|
33
|
+
return parsed.ok && !parsed.ownerBlockIds.has(args.ownerBlockId);
|
|
34
|
+
}
|
|
8
35
|
|
|
9
36
|
export default defineAction({
|
|
10
37
|
description: "Restore a soft-deleted content database.",
|
|
@@ -12,13 +39,23 @@ export default defineAction({
|
|
|
12
39
|
databaseId: z.string().describe("Content database ID"),
|
|
13
40
|
}),
|
|
14
41
|
run: async ({ databaseId }) => {
|
|
15
|
-
await assertContentDatabaseLifecycleAccess(databaseId);
|
|
42
|
+
const ownership = await assertContentDatabaseLifecycleAccess(databaseId);
|
|
16
43
|
const db = getDb();
|
|
17
44
|
const now = new Date().toISOString();
|
|
45
|
+
const clearInlineOwnership = await shouldClearStaleInlineOwnership({
|
|
46
|
+
ownerDocumentId: ownership.database.ownerDocumentId,
|
|
47
|
+
ownerBlockId: ownership.database.ownerBlockId,
|
|
48
|
+
});
|
|
18
49
|
|
|
19
50
|
await db
|
|
20
51
|
.update(schema.contentDatabases)
|
|
21
|
-
.set({
|
|
52
|
+
.set({
|
|
53
|
+
deletedAt: null,
|
|
54
|
+
updatedAt: now,
|
|
55
|
+
...(clearInlineOwnership
|
|
56
|
+
? { ownerDocumentId: null, ownerBlockId: null }
|
|
57
|
+
: {}),
|
|
58
|
+
})
|
|
22
59
|
.where(eq(schema.contentDatabases.id, databaseId));
|
|
23
60
|
|
|
24
61
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
@@ -11,10 +11,16 @@ import {
|
|
|
11
11
|
IconH3,
|
|
12
12
|
IconH4,
|
|
13
13
|
} from "@tabler/icons-react";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
NodeSelection,
|
|
16
|
+
Plugin,
|
|
17
|
+
PluginKey,
|
|
18
|
+
type EditorState,
|
|
19
|
+
} from "@tiptap/pm/state";
|
|
20
|
+
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
15
21
|
import type { Editor } from "@tiptap/react";
|
|
16
22
|
import { BubbleMenu } from "@tiptap/react/menus";
|
|
17
|
-
import { useState } from "react";
|
|
23
|
+
import { useEffect, useState } from "react";
|
|
18
24
|
|
|
19
25
|
import {
|
|
20
26
|
Tooltip,
|
|
@@ -44,6 +50,15 @@ const BUBBLE_TOOLBAR_EXCLUDED_NODE_TYPES = new Set([
|
|
|
44
50
|
"localMdxComponent",
|
|
45
51
|
]);
|
|
46
52
|
|
|
53
|
+
type SelectionFillRange = {
|
|
54
|
+
from: number;
|
|
55
|
+
to: number;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const selectionFillPluginKey = new PluginKey<SelectionFillRange | null>(
|
|
59
|
+
"contentSelectionFill",
|
|
60
|
+
);
|
|
61
|
+
|
|
47
62
|
function selectionIncludesBubbleToolbarExcludedNode(
|
|
48
63
|
state: EditorState,
|
|
49
64
|
from: number,
|
|
@@ -72,6 +87,73 @@ export function BubbleToolbar({ editor, onComment }: BubbleToolbarProps) {
|
|
|
72
87
|
const [showLinkInput, setShowLinkInput] = useState(false);
|
|
73
88
|
const [linkUrl, setLinkUrl] = useState("");
|
|
74
89
|
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const plugin = new Plugin<SelectionFillRange | null>({
|
|
92
|
+
key: selectionFillPluginKey,
|
|
93
|
+
state: {
|
|
94
|
+
init: () => null,
|
|
95
|
+
apply: (tr, value) => {
|
|
96
|
+
const meta = tr.getMeta(selectionFillPluginKey);
|
|
97
|
+
if (meta !== undefined) return meta;
|
|
98
|
+
return value
|
|
99
|
+
? {
|
|
100
|
+
from: tr.mapping.map(value.from),
|
|
101
|
+
to: tr.mapping.map(value.to),
|
|
102
|
+
}
|
|
103
|
+
: null;
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
props: {
|
|
107
|
+
decorations(state) {
|
|
108
|
+
const range = selectionFillPluginKey.getState(state);
|
|
109
|
+
if (!range || range.from === range.to) return DecorationSet.empty;
|
|
110
|
+
return DecorationSet.create(state.doc, [
|
|
111
|
+
Decoration.inline(range.from, range.to, {
|
|
112
|
+
class: "notion-selection-fill",
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
editor.registerPlugin(plugin);
|
|
120
|
+
|
|
121
|
+
const syncSelectionFill = () => {
|
|
122
|
+
const { state } = editor;
|
|
123
|
+
const { from, to } = state.selection;
|
|
124
|
+
const nextRange =
|
|
125
|
+
editor.isFocused &&
|
|
126
|
+
from !== to &&
|
|
127
|
+
!selectionIncludesBubbleToolbarExcludedNode(state, from, to)
|
|
128
|
+
? { from, to }
|
|
129
|
+
: null;
|
|
130
|
+
const currentRange = selectionFillPluginKey.getState(state);
|
|
131
|
+
if (
|
|
132
|
+
currentRange?.from === nextRange?.from &&
|
|
133
|
+
currentRange?.to === nextRange?.to
|
|
134
|
+
) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
editor.view.dispatch(
|
|
138
|
+
state.tr
|
|
139
|
+
.setMeta(selectionFillPluginKey, nextRange)
|
|
140
|
+
.setMeta("addToHistory", false),
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
editor.on("selectionUpdate", syncSelectionFill);
|
|
145
|
+
editor.on("focus", syncSelectionFill);
|
|
146
|
+
editor.on("blur", syncSelectionFill);
|
|
147
|
+
syncSelectionFill();
|
|
148
|
+
|
|
149
|
+
return () => {
|
|
150
|
+
editor.off("selectionUpdate", syncSelectionFill);
|
|
151
|
+
editor.off("focus", syncSelectionFill);
|
|
152
|
+
editor.off("blur", syncSelectionFill);
|
|
153
|
+
editor.unregisterPlugin(selectionFillPluginKey);
|
|
154
|
+
};
|
|
155
|
+
}, [editor]);
|
|
156
|
+
|
|
75
157
|
const handleSetLink = () => {
|
|
76
158
|
if (linkUrl.trim()) {
|
|
77
159
|
editor
|