@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,4 +1,5 @@
|
|
|
1
1
|
import { useSendToAgentChat, useT } from "@agent-native/core/client";
|
|
2
|
+
import { collapseExactRepeatedNfm, docToNfm } from "@shared/nfm";
|
|
2
3
|
import {
|
|
3
4
|
IconTypography,
|
|
4
5
|
IconH1,
|
|
@@ -44,6 +45,8 @@ import { buildRegistrySlashItems } from "./registrySlashItems";
|
|
|
44
45
|
interface SlashCommandMenuProps {
|
|
45
46
|
editor: Editor;
|
|
46
47
|
documentId?: string;
|
|
48
|
+
onDraftCommitted?: () => void | Promise<void>;
|
|
49
|
+
onDraftPersisted?: (markdown: string) => boolean | Promise<boolean>;
|
|
47
50
|
/**
|
|
48
51
|
* The open document's linked Notion page id, when it has one. When set, the
|
|
49
52
|
* registry-derived block slash items are filtered to specs that round-trip to
|
|
@@ -65,7 +68,35 @@ interface CommandItem {
|
|
|
65
68
|
searchText?: string;
|
|
66
69
|
shortcut?: string;
|
|
67
70
|
icon: React.ElementType;
|
|
68
|
-
|
|
71
|
+
preserveSlashRange?: boolean;
|
|
72
|
+
action: (
|
|
73
|
+
editor: Editor,
|
|
74
|
+
context: { slashRange: { from: number; to: number } | null },
|
|
75
|
+
) => void | boolean | Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getActiveSlashCommandRange(editor: Editor) {
|
|
79
|
+
const { state } = editor;
|
|
80
|
+
if (!state.selection.empty) return null;
|
|
81
|
+
const { from, $from } = state.selection;
|
|
82
|
+
if (!$from.parent.isTextblock) return null;
|
|
83
|
+
|
|
84
|
+
const blockStart = $from.start();
|
|
85
|
+
const textBefore = state.doc.textBetween(blockStart, from, "\n");
|
|
86
|
+
const slashQuery = parseSlashCommandQuery(textBefore);
|
|
87
|
+
if (slashQuery === null) return null;
|
|
88
|
+
|
|
89
|
+
const slashIndex = textBefore.lastIndexOf("/");
|
|
90
|
+
return {
|
|
91
|
+
from: blockStart + slashIndex,
|
|
92
|
+
to: from,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function waitForEditorUpdateFrame() {
|
|
97
|
+
return new Promise<void>((resolve) => {
|
|
98
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
99
|
+
});
|
|
69
100
|
}
|
|
70
101
|
|
|
71
102
|
interface CommandTemplate extends Omit<CommandItem, "title" | "description"> {
|
|
@@ -369,11 +400,13 @@ export function SlashCommandMenu({
|
|
|
369
400
|
editor,
|
|
370
401
|
documentId,
|
|
371
402
|
notionPageId,
|
|
403
|
+
onDraftCommitted,
|
|
404
|
+
onDraftPersisted,
|
|
372
405
|
}: SlashCommandMenuProps) {
|
|
373
406
|
const t = useT();
|
|
374
407
|
const { send } = useSendToAgentChat();
|
|
375
408
|
const navigate = useNavigate();
|
|
376
|
-
const createPage = useCreatePage();
|
|
409
|
+
const createPage = useCreatePage({ navigate: false, awaitPersist: true });
|
|
377
410
|
const createDatabase = useCreateContentDatabase(documentId ?? null);
|
|
378
411
|
|
|
379
412
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -505,12 +538,70 @@ export function SlashCommandMenu({
|
|
|
505
538
|
title: t("editor.slash.page"),
|
|
506
539
|
description: t("editor.slash.pageDescription"),
|
|
507
540
|
icon: IconFileText,
|
|
508
|
-
|
|
541
|
+
preserveSlashRange: true,
|
|
542
|
+
action: async (_editor, { slashRange }) => {
|
|
509
543
|
if (!documentId) {
|
|
510
544
|
toast.error(t("editor.noDocumentSelected"));
|
|
511
545
|
return;
|
|
512
546
|
}
|
|
513
|
-
|
|
547
|
+
let pageId: string;
|
|
548
|
+
try {
|
|
549
|
+
pageId = await createPage(documentId);
|
|
550
|
+
} catch {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const pageReference = {
|
|
554
|
+
type: "notionBlockAtom",
|
|
555
|
+
attrs: {
|
|
556
|
+
tagName: "page",
|
|
557
|
+
attrsJson: JSON.stringify({
|
|
558
|
+
id: pageId,
|
|
559
|
+
}),
|
|
560
|
+
label: "Untitled",
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
const insertContent = [pageReference, { type: "paragraph" }];
|
|
564
|
+
const range = slashRange
|
|
565
|
+
? (() => {
|
|
566
|
+
const $from = editor.state.doc.resolve(slashRange.from);
|
|
567
|
+
return $from.parent.isTextblock
|
|
568
|
+
? { from: $from.before(), to: $from.after() }
|
|
569
|
+
: slashRange;
|
|
570
|
+
})()
|
|
571
|
+
: null;
|
|
572
|
+
|
|
573
|
+
if (range) {
|
|
574
|
+
editor.chain().focus().insertContentAt(range, insertContent).run();
|
|
575
|
+
} else {
|
|
576
|
+
const { $from } = editor.state.selection;
|
|
577
|
+
editor
|
|
578
|
+
.chain()
|
|
579
|
+
.focus()
|
|
580
|
+
.insertContentAt($from.after(), insertContent)
|
|
581
|
+
.run();
|
|
582
|
+
}
|
|
583
|
+
await waitForEditorUpdateFrame();
|
|
584
|
+
try {
|
|
585
|
+
const content = collapseExactRepeatedNfm(
|
|
586
|
+
docToNfm(editor.getJSON() as any),
|
|
587
|
+
{
|
|
588
|
+
requiredText: `id="${pageId}"`,
|
|
589
|
+
},
|
|
590
|
+
);
|
|
591
|
+
if (onDraftPersisted) {
|
|
592
|
+
const persisted = await onDraftPersisted(content);
|
|
593
|
+
if (!persisted) throw new Error(t("empty.genericError"));
|
|
594
|
+
} else {
|
|
595
|
+
await onDraftCommitted?.();
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
toast.error(t("editor.failedToCreatePage"), {
|
|
599
|
+
description:
|
|
600
|
+
error instanceof Error ? error.message : t("empty.genericError"),
|
|
601
|
+
});
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
navigate(`/page/${pageId}`, { flushSync: true });
|
|
514
605
|
},
|
|
515
606
|
};
|
|
516
607
|
|
|
@@ -621,19 +712,19 @@ export function SlashCommandMenu({
|
|
|
621
712
|
|
|
622
713
|
const executeCommand = useCallback(
|
|
623
714
|
async (cmd: CommandItem) => {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
715
|
+
const slashRange =
|
|
716
|
+
getActiveSlashCommandRange(editor) ??
|
|
717
|
+
(slashPosRef.current !== null
|
|
718
|
+
? { from: slashPosRef.current, to: editor.state.selection.from }
|
|
719
|
+
: null);
|
|
720
|
+
if (slashRange && !cmd.preserveSlashRange) {
|
|
721
|
+
editor.chain().focus().deleteRange(slashRange).run();
|
|
631
722
|
}
|
|
632
723
|
setIsOpen(false);
|
|
633
724
|
setIsTurnInto(false);
|
|
634
725
|
setQuery("");
|
|
635
726
|
slashPosRef.current = null;
|
|
636
|
-
await cmd.action(editor);
|
|
727
|
+
await cmd.action(editor, { slashRange });
|
|
637
728
|
},
|
|
638
729
|
[editor],
|
|
639
730
|
);
|
|
@@ -688,24 +779,29 @@ export function SlashCommandMenu({
|
|
|
688
779
|
|
|
689
780
|
if (e.key === "ArrowDown") {
|
|
690
781
|
e.preventDefault();
|
|
782
|
+
e.stopPropagation();
|
|
691
783
|
if (filteredCommands.length === 0) return;
|
|
692
784
|
setSelectedIndex((i) => (i + 1) % filteredCommands.length);
|
|
693
785
|
} else if (e.key === "ArrowUp") {
|
|
694
786
|
e.preventDefault();
|
|
787
|
+
e.stopPropagation();
|
|
695
788
|
if (filteredCommands.length === 0) return;
|
|
696
789
|
setSelectedIndex(
|
|
697
790
|
(i) => (i - 1 + filteredCommands.length) % filteredCommands.length,
|
|
698
791
|
);
|
|
699
792
|
} else if (e.key === "Enter") {
|
|
700
793
|
e.preventDefault();
|
|
794
|
+
e.stopPropagation();
|
|
701
795
|
if (filteredCommands[selectedIndex]) {
|
|
702
796
|
executeCommand(filteredCommands[selectedIndex]);
|
|
703
797
|
}
|
|
704
798
|
} else if (e.key === "Escape") {
|
|
799
|
+
e.stopPropagation();
|
|
705
800
|
setIsOpen(false);
|
|
706
801
|
setIsTurnInto(false);
|
|
707
802
|
setQuery("");
|
|
708
803
|
slashPosRef.current = null;
|
|
804
|
+
onDraftCommitted?.();
|
|
709
805
|
}
|
|
710
806
|
};
|
|
711
807
|
|
|
@@ -717,6 +813,8 @@ export function SlashCommandMenu({
|
|
|
717
813
|
filteredCommands,
|
|
718
814
|
executeCommand,
|
|
719
815
|
editor,
|
|
816
|
+
onDraftCommitted,
|
|
817
|
+
onDraftPersisted,
|
|
720
818
|
openGeneratePopover,
|
|
721
819
|
readInlineGenerateCommand,
|
|
722
820
|
submitGeneratePrompt,
|
|
@@ -956,8 +1054,10 @@ function CommandButton({
|
|
|
956
1054
|
return (
|
|
957
1055
|
<button
|
|
958
1056
|
ref={buttonRef}
|
|
959
|
-
onMouseDown={(event) =>
|
|
960
|
-
|
|
1057
|
+
onMouseDown={(event) => {
|
|
1058
|
+
event.preventDefault();
|
|
1059
|
+
onExecute();
|
|
1060
|
+
}}
|
|
961
1061
|
onMouseEnter={onHover}
|
|
962
1062
|
className={cn(
|
|
963
1063
|
"flex min-h-9 w-full items-center gap-3 px-3 py-1 text-left transition-colors",
|
|
@@ -735,6 +735,7 @@ interface VisualEditorProps {
|
|
|
735
735
|
*/
|
|
736
736
|
contentUpdatedAt?: string | null;
|
|
737
737
|
onChange: (markdown: string) => void;
|
|
738
|
+
onSaveContent?: (markdown: string) => boolean | Promise<boolean>;
|
|
738
739
|
/** Yjs document for collaborative editing. */
|
|
739
740
|
ydoc?: YDoc | null;
|
|
740
741
|
/** Shared awareness instance for collaborative cursors/presence. */
|
|
@@ -870,6 +871,17 @@ export function shouldPersistLocalFileEditorUpdate({
|
|
|
870
871
|
return Boolean(transactionUiEvent);
|
|
871
872
|
}
|
|
872
873
|
|
|
874
|
+
function isActiveSlashCommandDraft(editor: CoreEditor): boolean {
|
|
875
|
+
const { state } = editor;
|
|
876
|
+
if (!state.selection.empty) return false;
|
|
877
|
+
const { from, $from } = state.selection;
|
|
878
|
+
if (!$from.parent.isTextblock) return false;
|
|
879
|
+
|
|
880
|
+
const blockStart = $from.start();
|
|
881
|
+
const textBefore = state.doc.textBetween(blockStart, from, "\n");
|
|
882
|
+
return /^\s*\/[a-zA-Z0-9]*$/.test(textBefore);
|
|
883
|
+
}
|
|
884
|
+
|
|
873
885
|
interface VisualEditorExtensionOptions {
|
|
874
886
|
documentId?: string;
|
|
875
887
|
ydoc?: YDoc | null;
|
|
@@ -1550,6 +1562,7 @@ export function VisualEditor({
|
|
|
1550
1562
|
content,
|
|
1551
1563
|
contentUpdatedAt,
|
|
1552
1564
|
onChange,
|
|
1565
|
+
onSaveContent,
|
|
1553
1566
|
ydoc,
|
|
1554
1567
|
awareness,
|
|
1555
1568
|
user,
|
|
@@ -1569,6 +1582,8 @@ export function VisualEditor({
|
|
|
1569
1582
|
const [isDraggingMedia, setIsDraggingMedia] = useState(false);
|
|
1570
1583
|
const onChangeRef = useRef(onChange);
|
|
1571
1584
|
onChangeRef.current = onChange;
|
|
1585
|
+
const onSaveContentRef = useRef(onSaveContent);
|
|
1586
|
+
onSaveContentRef.current = onSaveContent;
|
|
1572
1587
|
const notionPageLinksRef = useRef(notionPageLinks);
|
|
1573
1588
|
notionPageLinksRef.current = notionPageLinks;
|
|
1574
1589
|
const resolveNotionPageLink = useCallback((notionPageId: string) => {
|
|
@@ -1576,6 +1591,7 @@ export function VisualEditor({
|
|
|
1576
1591
|
return (
|
|
1577
1592
|
notionPageLinksRef.current.find(
|
|
1578
1593
|
(link) =>
|
|
1594
|
+
link.notionPageId === notionPageId ||
|
|
1579
1595
|
link.notionPageId.replace(/-/g, "").toLowerCase() === normalized,
|
|
1580
1596
|
) ?? null
|
|
1581
1597
|
);
|
|
@@ -1604,9 +1620,10 @@ export function VisualEditor({
|
|
|
1604
1620
|
// Clean up awareness on unmount
|
|
1605
1621
|
useEffect(() => {
|
|
1606
1622
|
return () => {
|
|
1623
|
+
localAwareness?.setLocalState(null);
|
|
1607
1624
|
fallbackAwareness?.destroy();
|
|
1608
1625
|
};
|
|
1609
|
-
}, [fallbackAwareness]);
|
|
1626
|
+
}, [fallbackAwareness, localAwareness]);
|
|
1610
1627
|
|
|
1611
1628
|
const extensions = useMemo(
|
|
1612
1629
|
() =>
|
|
@@ -1643,6 +1660,36 @@ export function VisualEditor({
|
|
|
1643
1660
|
const markUserEditIntent = useCallback(() => {
|
|
1644
1661
|
lastUserEditIntentAtRef.current = Date.now();
|
|
1645
1662
|
}, []);
|
|
1663
|
+
const persistEditorContent = useCallback(
|
|
1664
|
+
(
|
|
1665
|
+
editorToPersist: CoreEditor,
|
|
1666
|
+
options?: { markdown?: string; immediate?: boolean },
|
|
1667
|
+
) => {
|
|
1668
|
+
const guards = guardsRef.current;
|
|
1669
|
+
if (!guards) return false;
|
|
1670
|
+
try {
|
|
1671
|
+
const normalized =
|
|
1672
|
+
options?.markdown ?? docToNfm(editorToPersist.getJSON() as any);
|
|
1673
|
+
if (localFileMode && normalized === content) return true;
|
|
1674
|
+
if (options?.immediate && onSaveContentRef.current) {
|
|
1675
|
+
return onSaveContentRef.current(normalized);
|
|
1676
|
+
}
|
|
1677
|
+
// Don't persist an empty doc before Collaboration has seeded (would
|
|
1678
|
+
// clobber DB content with an empty string). `registerEmitted` records
|
|
1679
|
+
// this as the last-emitted value and returns false to skip the save.
|
|
1680
|
+
if (!guards.registerEmitted(normalized)) return true;
|
|
1681
|
+
queueMicrotask(() => onChangeRef.current(normalized));
|
|
1682
|
+
return true;
|
|
1683
|
+
} catch (err: any) {
|
|
1684
|
+
toast.error(
|
|
1685
|
+
t("editor.markdownSerializationError", { message: err.message }),
|
|
1686
|
+
);
|
|
1687
|
+
console.error("Markdown serialization error:", err);
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
},
|
|
1691
|
+
[content, localFileMode, t],
|
|
1692
|
+
);
|
|
1646
1693
|
|
|
1647
1694
|
const editor = useEditor({
|
|
1648
1695
|
extensions,
|
|
@@ -1796,20 +1843,8 @@ export function VisualEditor({
|
|
|
1796
1843
|
) {
|
|
1797
1844
|
return;
|
|
1798
1845
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
if (localFileMode && normalized === content) return;
|
|
1802
|
-
// Don't persist an empty doc before Collaboration has seeded (would
|
|
1803
|
-
// clobber DB content with an empty string). `registerEmitted` records
|
|
1804
|
-
// this as the last-emitted value and returns false to skip the save.
|
|
1805
|
-
if (!guards.registerEmitted(normalized)) return;
|
|
1806
|
-
queueMicrotask(() => onChangeRef.current(normalized));
|
|
1807
|
-
} catch (err: any) {
|
|
1808
|
-
toast.error(
|
|
1809
|
-
t("editor.markdownSerializationError", { message: err.message }),
|
|
1810
|
-
);
|
|
1811
|
-
console.error("Markdown serialization error:", err);
|
|
1812
|
-
}
|
|
1846
|
+
if (isActiveSlashCommandDraft(editor)) return;
|
|
1847
|
+
persistEditorContent(editor);
|
|
1813
1848
|
},
|
|
1814
1849
|
});
|
|
1815
1850
|
|
|
@@ -2038,6 +2073,12 @@ export function VisualEditor({
|
|
|
2038
2073
|
editor={editor}
|
|
2039
2074
|
documentId={documentId}
|
|
2040
2075
|
notionPageId={notionPageId}
|
|
2076
|
+
onDraftCommitted={() => {
|
|
2077
|
+
void persistEditorContent(editor);
|
|
2078
|
+
}}
|
|
2079
|
+
onDraftPersisted={(markdown) =>
|
|
2080
|
+
persistEditorContent(editor, { markdown, immediate: true })
|
|
2081
|
+
}
|
|
2041
2082
|
/>
|
|
2042
2083
|
) : null}
|
|
2043
2084
|
<LinkHoverPreview editor={editor} editable={editable} />
|
|
@@ -144,11 +144,12 @@ function normalizeNotionPageId(input: string | null | undefined) {
|
|
|
144
144
|
|
|
145
145
|
function getNotionPageId(attrs: Record<string, string>) {
|
|
146
146
|
return (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
normalizeNotionPageId(attrs.
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
attrs["data-agent-native-document-id"] ||
|
|
148
|
+
attrs.id ||
|
|
149
|
+
(normalizeNotionPageId(attrs.url) ??
|
|
150
|
+
normalizeNotionPageId(attrs.href) ??
|
|
151
|
+
normalizeNotionPageId(attrs.pageId) ??
|
|
152
|
+
normalizeNotionPageId(attrs.page_id))
|
|
152
153
|
);
|
|
153
154
|
}
|
|
154
155
|
|
|
@@ -148,7 +148,7 @@ export function DocumentTreeItem({
|
|
|
148
148
|
isDragging && "bg-accent/70 text-accent-foreground shadow-sm",
|
|
149
149
|
isActive
|
|
150
150
|
? "bg-accent text-accent-foreground"
|
|
151
|
-
: "text-muted-foreground hover:bg-accent
|
|
151
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
152
152
|
)}
|
|
153
153
|
style={{
|
|
154
154
|
paddingInlineStart: `${indent}px`,
|
|
@@ -208,14 +208,20 @@ export function DocumentTreeItem({
|
|
|
208
208
|
</span>
|
|
209
209
|
|
|
210
210
|
<div
|
|
211
|
-
className=
|
|
211
|
+
className={cn(
|
|
212
|
+
"pointer-events-none absolute right-1 top-1/2 flex flex-shrink-0 -translate-y-1/2 items-center gap-0.5 rounded-md pl-1 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100",
|
|
213
|
+
"bg-accent text-foreground",
|
|
214
|
+
isActive && "text-accent-foreground",
|
|
215
|
+
)}
|
|
212
216
|
onPointerDown={(e) => e.stopPropagation()}
|
|
213
217
|
>
|
|
214
218
|
{hasMenuActions && (
|
|
215
219
|
<DropdownMenu>
|
|
216
220
|
<DropdownMenuTrigger asChild>
|
|
217
221
|
<button
|
|
218
|
-
|
|
222
|
+
type="button"
|
|
223
|
+
className="flex h-6 w-6 items-center justify-center rounded text-current hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
224
|
+
aria-label={`More actions for ${node.title || "Untitled"}`}
|
|
219
225
|
onClick={(e) => e.stopPropagation()}
|
|
220
226
|
>
|
|
221
227
|
<IconDots size={14} />
|
|
@@ -259,7 +265,9 @@ export function DocumentTreeItem({
|
|
|
259
265
|
<Tooltip>
|
|
260
266
|
<TooltipTrigger asChild>
|
|
261
267
|
<button
|
|
262
|
-
|
|
268
|
+
type="button"
|
|
269
|
+
className="flex h-7 w-7 items-center justify-center rounded text-current hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
270
|
+
aria-label={`Add sub-page to ${node.title || "Untitled"}`}
|
|
263
271
|
onClick={(e) => {
|
|
264
272
|
e.stopPropagation();
|
|
265
273
|
onCreateChild(node.id);
|
|
@@ -2119,6 +2119,15 @@ audio.media-block__content {
|
|
|
2119
2119
|
background: hsl(210 100% 52% / 0.2);
|
|
2120
2120
|
}
|
|
2121
2121
|
|
|
2122
|
+
.notion-editor .notion-selection-fill {
|
|
2123
|
+
background: hsl(210 100% 52% / 0.08);
|
|
2124
|
+
box-decoration-break: clone;
|
|
2125
|
+
-webkit-box-decoration-break: clone;
|
|
2126
|
+
box-shadow:
|
|
2127
|
+
0 0.24em 0 hsl(210 100% 52% / 0.08),
|
|
2128
|
+
0 -0.08em 0 hsl(210 100% 52% / 0.08);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2122
2131
|
@media print {
|
|
2123
2132
|
@page {
|
|
2124
2133
|
margin: 0.65in;
|
|
@@ -19,11 +19,17 @@ function nanoid(size = 12): string {
|
|
|
19
19
|
return Array.from(bytes, (b) => chars[b % chars.length]).join("");
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function useCreatePage(opts?: {
|
|
22
|
+
export function useCreatePage(opts?: {
|
|
23
|
+
onAfterNavigate?: () => void;
|
|
24
|
+
navigate?: boolean;
|
|
25
|
+
awaitPersist?: boolean;
|
|
26
|
+
}) {
|
|
23
27
|
const navigate = useNavigate();
|
|
24
28
|
const queryClient = useQueryClient();
|
|
25
29
|
const createDocument = useCreateDocument();
|
|
26
30
|
const onAfterNavigate = opts?.onAfterNavigate;
|
|
31
|
+
const shouldNavigate = opts?.navigate ?? true;
|
|
32
|
+
const shouldAwaitPersist = opts?.awaitPersist ?? true;
|
|
27
33
|
|
|
28
34
|
return useCallback(
|
|
29
35
|
async (parentId?: string) => {
|
|
@@ -50,10 +56,12 @@ export function useCreatePage(opts?: { onAfterNavigate?: () => void }) {
|
|
|
50
56
|
});
|
|
51
57
|
queryClient.setQueryData(["action", "get-document", { id }], tempDoc);
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
if (shouldNavigate) {
|
|
60
|
+
navigate(`/page/${id}`, { flushSync: true });
|
|
61
|
+
onAfterNavigate?.();
|
|
62
|
+
}
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
const persist = async () => {
|
|
57
65
|
await createDocument.mutateAsync({
|
|
58
66
|
id,
|
|
59
67
|
title: "",
|
|
@@ -67,21 +75,42 @@ export function useCreatePage(opts?: { onAfterNavigate?: () => void }) {
|
|
|
67
75
|
queryClient.invalidateQueries({
|
|
68
76
|
queryKey: ["action", "list-documents"],
|
|
69
77
|
});
|
|
70
|
-
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const onPersistError = (err: unknown) => {
|
|
71
81
|
queryClient.invalidateQueries({
|
|
72
82
|
queryKey: ["action", "list-documents"],
|
|
73
83
|
});
|
|
74
84
|
queryClient.removeQueries({
|
|
75
85
|
queryKey: ["action", "get-document", { id }],
|
|
76
86
|
});
|
|
77
|
-
navigate("/");
|
|
87
|
+
if (shouldNavigate) navigate("/");
|
|
78
88
|
toast.error("Failed to create page", {
|
|
79
89
|
description:
|
|
80
90
|
err instanceof Error ? err.message : "Something went wrong",
|
|
81
91
|
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (shouldAwaitPersist) {
|
|
95
|
+
try {
|
|
96
|
+
await persist();
|
|
97
|
+
} catch (err) {
|
|
98
|
+
onPersistError(err);
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
void persist().catch(onPersistError);
|
|
82
103
|
}
|
|
104
|
+
|
|
83
105
|
return id;
|
|
84
106
|
},
|
|
85
|
-
[
|
|
107
|
+
[
|
|
108
|
+
createDocument,
|
|
109
|
+
navigate,
|
|
110
|
+
onAfterNavigate,
|
|
111
|
+
queryClient,
|
|
112
|
+
shouldAwaitPersist,
|
|
113
|
+
shouldNavigate,
|
|
114
|
+
],
|
|
86
115
|
);
|
|
87
116
|
}
|