@agent-native/core 0.84.21 → 0.84.23
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 +16 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/model-config.ts +1 -1
- package/corpus/core/src/cli/skills.ts +17 -3
- package/corpus/core/src/client/AssistantChat.tsx +27 -4
- package/corpus/core/src/client/guided-questions.tsx +11 -6
- package/corpus/core/src/client/tool-display.ts +8 -0
- package/corpus/core/src/file-upload/builder.ts +7 -2
- package/corpus/core/src/notifications/channels.ts +235 -38
- package/corpus/core/src/notifications/index.ts +1 -0
- package/corpus/core/src/notifications/registry.ts +13 -1
- package/corpus/core/src/server/analytics.ts +32 -6
- package/corpus/core/src/server/ssr-handler.ts +31 -12
- package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/AGENTS.md +21 -0
- package/corpus/templates/analytics/README.md +35 -0
- package/corpus/templates/analytics/actions/delete-analytics-alert-rule.ts +23 -0
- package/corpus/templates/analytics/actions/list-analytics-alert-rules.ts +20 -0
- package/corpus/templates/analytics/actions/run-analytics-alerts.ts +23 -0
- package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +69 -0
- package/corpus/templates/analytics/changelog/2026-07-01-analytics-can-alert-when-first-party-events-spike.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-01-session-replays-play-back-in-production-instead-of-showing.md +6 -0
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +126 -0
- package/corpus/templates/analytics/server/db/schema.ts +55 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +5 -2
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +74 -0
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +803 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +10 -3
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +54 -0
- package/corpus/templates/analytics/server/plugins/db.ts +100 -0
- package/corpus/templates/analytics/server/routes/api/analytics-alerts/run.post.ts +60 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/assets/README.md +30 -8
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/brain/README.md +20 -552
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/calendar/README.md +22 -28
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/chat/README.md +32 -0
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +30 -1
- package/corpus/templates/clips/README.md +37 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +84 -4
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +256 -0
- package/corpus/templates/clips/actions/reprocess-recording.ts +160 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +18 -7
- package/corpus/templates/clips/app/components/library/recording-card.tsx +42 -5
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +41 -1
- package/corpus/templates/clips/changelog/2026-07-01-move-to-folder-now-opens-folder-choices-directly-instead-of.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-shared-clips-now-start-playing-and-scrub-instantly-instead-o.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/server/lib/video-remux.ts +222 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +37 -0
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/content/AGENTS.md +7 -3
- package/corpus/templates/content/README.md +22 -34
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +4 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +58 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +257 -24
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +41 -15
- package/corpus/templates/content/app/i18n/zh-TW.ts +25 -7
- package/corpus/templates/content/app/i18n-data.ts +278 -40
- package/corpus/templates/content/changelog/2026-06-30-database-once-again-creates-an-inline-database-in-the-curren.md +6 -0
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +75 -19
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/design/AGENTS.md +7 -6
- package/corpus/templates/design/README.md +33 -0
- package/corpus/templates/design/actions/delete-file.ts +86 -10
- package/corpus/templates/design/actions/get-design-snapshot.ts +13 -0
- package/corpus/templates/design/actions/present-design-variants.ts +11 -13
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +13 -3
- package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-an-empty-state-selection-chip-in-the-composer.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/dispatch/README.md +34 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/forms/README.md +34 -0
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/macros/README.md +28 -0
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/mail/README.md +35 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/plan/README.md +30 -118
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/slides/README.md +34 -0
- package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/videos/README.md +22 -201
- package/dist/agent/model-config.js +1 -1
- package/dist/agent/model-config.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +17 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +28 -4
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +10 -6
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/tool-display.d.ts.map +1 -1
- package/dist/client/tool-display.js +8 -0
- package/dist/client/tool-display.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +6 -2
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/notifications/channels.d.ts +10 -0
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +172 -20
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/registry.d.ts +5 -0
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +4 -1
- package/dist/notifications/registry.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/analytics.d.ts +16 -0
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +30 -7
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +28 -12
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useSendToAgentChat, useT } from "@agent-native/core/client";
|
|
2
|
+
import type { CreateInlineDatabaseResponse } from "@shared/api";
|
|
2
3
|
import { collapseExactRepeatedNfm, docToNfm } from "@shared/nfm";
|
|
4
|
+
import { serializeRegistryBlockToMdx } from "@shared/nfm-registry";
|
|
3
5
|
import {
|
|
4
6
|
IconTypography,
|
|
5
7
|
IconH1,
|
|
@@ -33,7 +35,7 @@ import {
|
|
|
33
35
|
PopoverContent,
|
|
34
36
|
PopoverTrigger,
|
|
35
37
|
} from "@/components/ui/popover";
|
|
36
|
-
import {
|
|
38
|
+
import { useCreateInlineContentDatabase } from "@/hooks/use-content-database";
|
|
37
39
|
import { useCreatePage } from "@/hooks/use-create-page";
|
|
38
40
|
import { cn } from "@/lib/utils";
|
|
39
41
|
import { localContentComponents } from "@/local-components";
|
|
@@ -146,6 +148,36 @@ export function parseSlashCommandQuery(textBeforeCursor: string) {
|
|
|
146
148
|
return textBeforeCursor.match(/^\s*\/([a-zA-Z0-9]*)$/)?.[1] ?? null;
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
export function inlineDatabaseBlockContent(
|
|
152
|
+
block: CreateInlineDatabaseResponse["block"],
|
|
153
|
+
) {
|
|
154
|
+
return {
|
|
155
|
+
type: "registryBlock",
|
|
156
|
+
attrs: {
|
|
157
|
+
blockType: "inline-database",
|
|
158
|
+
blockId: block.ownerBlockId,
|
|
159
|
+
title: null,
|
|
160
|
+
summary: null,
|
|
161
|
+
__raw: serializeRegistryBlockToMdx("inline-database", {
|
|
162
|
+
id: block.ownerBlockId,
|
|
163
|
+
data: block,
|
|
164
|
+
}),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function insertInlineDatabaseBlock(
|
|
170
|
+
editor: Editor,
|
|
171
|
+
block: CreateInlineDatabaseResponse["block"],
|
|
172
|
+
position?: number | { from: number; to: number } | null,
|
|
173
|
+
) {
|
|
174
|
+
const content = inlineDatabaseBlockContent(block);
|
|
175
|
+
const chain = editor.chain().focus();
|
|
176
|
+
return position != null
|
|
177
|
+
? chain.insertContentAt(position, content).run()
|
|
178
|
+
: chain.insertContent(content).run();
|
|
179
|
+
}
|
|
180
|
+
|
|
149
181
|
const commands: CommandTemplate[] = [
|
|
150
182
|
{
|
|
151
183
|
titleKey: "editor.slash.text",
|
|
@@ -407,7 +439,9 @@ export function SlashCommandMenu({
|
|
|
407
439
|
const { send } = useSendToAgentChat();
|
|
408
440
|
const navigate = useNavigate();
|
|
409
441
|
const createPage = useCreatePage({ navigate: false, awaitPersist: true });
|
|
410
|
-
const
|
|
442
|
+
const createInlineDatabase = useCreateInlineContentDatabase(
|
|
443
|
+
documentId ?? null,
|
|
444
|
+
);
|
|
411
445
|
|
|
412
446
|
const [isOpen, setIsOpen] = useState(false);
|
|
413
447
|
const [isTurnInto, setIsTurnInto] = useState(false);
|
|
@@ -609,18 +643,36 @@ export function SlashCommandMenu({
|
|
|
609
643
|
title: t("editor.slash.database"),
|
|
610
644
|
description: t("editor.slash.databaseDescription"),
|
|
611
645
|
icon: IconDatabase,
|
|
612
|
-
|
|
646
|
+
preserveSlashRange: true,
|
|
647
|
+
action: async (editor, { slashRange }) => {
|
|
613
648
|
if (!documentId) {
|
|
614
649
|
toast.error(t("editor.noDocumentSelected"));
|
|
615
650
|
return;
|
|
616
651
|
}
|
|
652
|
+
if (slashRange) {
|
|
653
|
+
editor.chain().focus().deleteRange(slashRange).run();
|
|
654
|
+
}
|
|
617
655
|
const toastId = toast.loading(t("editor.creatingDatabase"));
|
|
618
656
|
try {
|
|
619
|
-
const result = await
|
|
620
|
-
|
|
657
|
+
const result = await createInlineDatabase.mutateAsync({
|
|
658
|
+
hostDocumentId: documentId,
|
|
621
659
|
title: t("editor.untitledDatabase"),
|
|
622
660
|
});
|
|
623
|
-
|
|
661
|
+
const inserted = insertInlineDatabaseBlock(editor, result.block);
|
|
662
|
+
if (!inserted) throw new Error(t("empty.genericError"));
|
|
663
|
+
await waitForEditorUpdateFrame();
|
|
664
|
+
const content = collapseExactRepeatedNfm(
|
|
665
|
+
docToNfm(editor.getJSON() as any),
|
|
666
|
+
{
|
|
667
|
+
requiredText: result.block.ownerBlockId,
|
|
668
|
+
},
|
|
669
|
+
);
|
|
670
|
+
if (onDraftPersisted) {
|
|
671
|
+
const persisted = await onDraftPersisted(content);
|
|
672
|
+
if (!persisted) throw new Error(t("empty.genericError"));
|
|
673
|
+
} else {
|
|
674
|
+
await onDraftCommitted?.();
|
|
675
|
+
}
|
|
624
676
|
toast.success(t("editor.databaseCreated"), { id: toastId });
|
|
625
677
|
} catch (error) {
|
|
626
678
|
toast.error(t("editor.failedToCreateDatabase"), {
|
|
@@ -27,10 +27,14 @@ import {
|
|
|
27
27
|
} from "@dnd-kit/sortable";
|
|
28
28
|
import type { Document, DocumentTreeNode } from "@shared/api";
|
|
29
29
|
import {
|
|
30
|
+
IconDatabase,
|
|
31
|
+
IconFileText,
|
|
30
32
|
IconPlus,
|
|
33
|
+
IconRestore,
|
|
31
34
|
IconSearch,
|
|
32
35
|
IconStar,
|
|
33
36
|
IconSettings,
|
|
37
|
+
IconTrashX,
|
|
34
38
|
IconLayoutSidebarLeftCollapse,
|
|
35
39
|
IconLayoutSidebarLeftExpand,
|
|
36
40
|
IconFolderOpen,
|
|
@@ -42,12 +46,34 @@ import { useLocation, useNavigate } from "react-router";
|
|
|
42
46
|
import { toast } from "sonner";
|
|
43
47
|
|
|
44
48
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
|
49
|
+
import {
|
|
50
|
+
AlertDialog,
|
|
51
|
+
AlertDialogAction,
|
|
52
|
+
AlertDialogCancel,
|
|
53
|
+
AlertDialogContent,
|
|
54
|
+
AlertDialogDescription,
|
|
55
|
+
AlertDialogFooter,
|
|
56
|
+
AlertDialogHeader,
|
|
57
|
+
AlertDialogTitle,
|
|
58
|
+
AlertDialogTrigger,
|
|
59
|
+
} from "@/components/ui/alert-dialog";
|
|
60
|
+
import {
|
|
61
|
+
DropdownMenu,
|
|
62
|
+
DropdownMenuContent,
|
|
63
|
+
DropdownMenuItem,
|
|
64
|
+
DropdownMenuTrigger,
|
|
65
|
+
} from "@/components/ui/dropdown-menu";
|
|
45
66
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
46
67
|
import {
|
|
47
68
|
Tooltip,
|
|
48
69
|
TooltipContent,
|
|
49
70
|
TooltipTrigger,
|
|
50
71
|
} from "@/components/ui/tooltip";
|
|
72
|
+
import {
|
|
73
|
+
useCreateContentDatabase,
|
|
74
|
+
useRestoreContentDatabase,
|
|
75
|
+
useTrashedContentDatabases,
|
|
76
|
+
} from "@/hooks/use-content-database";
|
|
51
77
|
import {
|
|
52
78
|
useDocuments,
|
|
53
79
|
useCreateDocument,
|
|
@@ -122,7 +148,8 @@ type SidebarSectionId =
|
|
|
122
148
|
| "local-files"
|
|
123
149
|
| "shared-copies"
|
|
124
150
|
| "private"
|
|
125
|
-
| "organization"
|
|
151
|
+
| "organization"
|
|
152
|
+
| "trash";
|
|
126
153
|
|
|
127
154
|
export function DocumentSidebar({
|
|
128
155
|
activeDocumentId,
|
|
@@ -138,8 +165,11 @@ export function DocumentSidebar({
|
|
|
138
165
|
const t = useT();
|
|
139
166
|
const { data: documents = [], isLoading } = useDocuments();
|
|
140
167
|
const createDocument = useCreateDocument();
|
|
168
|
+
const createDatabase = useCreateContentDatabase(null);
|
|
141
169
|
const deleteDocument = useDeleteDocument();
|
|
142
170
|
const moveDocument = useMoveDocument();
|
|
171
|
+
const restoreContentDatabase = useRestoreContentDatabase();
|
|
172
|
+
const { data: trashedDatabases } = useTrashedContentDatabases();
|
|
143
173
|
const { isCodeMode } = useCodeMode();
|
|
144
174
|
const updateDocument = useUpdateDocument();
|
|
145
175
|
const [searchQuery, setSearchQuery] = useState("");
|
|
@@ -156,6 +186,7 @@ export function DocumentSidebar({
|
|
|
156
186
|
"shared-copies": false,
|
|
157
187
|
private: false,
|
|
158
188
|
organization: false,
|
|
189
|
+
trash: false,
|
|
159
190
|
});
|
|
160
191
|
const localFilesActive = location.pathname.startsWith("/local-files");
|
|
161
192
|
const settingsActive = location.pathname.startsWith("/settings");
|
|
@@ -218,6 +249,7 @@ export function DocumentSidebar({
|
|
|
218
249
|
const activeDocument = activeDocumentId
|
|
219
250
|
? documents.find((doc) => doc.id === activeDocumentId)
|
|
220
251
|
: null;
|
|
252
|
+
const trashItems = trashedDatabases?.databases ?? [];
|
|
221
253
|
const parentByDocumentId = useMemo(
|
|
222
254
|
() => new Map(documents.map((doc) => [doc.id, doc.parentId])),
|
|
223
255
|
[documents],
|
|
@@ -364,6 +396,25 @@ export function DocumentSidebar({
|
|
|
364
396
|
],
|
|
365
397
|
);
|
|
366
398
|
|
|
399
|
+
const handleCreateDatabase = useCallback(
|
|
400
|
+
async (parentId?: string | null) => {
|
|
401
|
+
try {
|
|
402
|
+
const result = await createDatabase.mutateAsync({
|
|
403
|
+
parentId: parentId ?? null,
|
|
404
|
+
title: t("editor.untitledDatabase"),
|
|
405
|
+
});
|
|
406
|
+
navigateToDocument(result.database.documentId);
|
|
407
|
+
onNavigate?.();
|
|
408
|
+
} catch (err) {
|
|
409
|
+
toast.error(t("sidebar.failedCreateDatabase"), {
|
|
410
|
+
description:
|
|
411
|
+
err instanceof Error ? err.message : t("empty.genericError"),
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
[createDatabase, navigateToDocument, onNavigate, t],
|
|
416
|
+
);
|
|
417
|
+
|
|
367
418
|
const handleDelete = useCallback(
|
|
368
419
|
async (id: string) => {
|
|
369
420
|
const deletedIds = collectDocumentSubtreeIds(documents, id);
|
|
@@ -524,6 +575,42 @@ export function DocumentSidebar({
|
|
|
524
575
|
[updateDocument],
|
|
525
576
|
);
|
|
526
577
|
|
|
578
|
+
const handleRestoreDatabase = useCallback(
|
|
579
|
+
async (databaseId: string) => {
|
|
580
|
+
try {
|
|
581
|
+
await restoreContentDatabase.mutateAsync({ databaseId });
|
|
582
|
+
toast.success(t("sidebar.databaseRestored"));
|
|
583
|
+
} catch (err) {
|
|
584
|
+
toast.error(t("sidebar.failedRestoreDatabase"), {
|
|
585
|
+
description:
|
|
586
|
+
err instanceof Error ? err.message : t("empty.genericError"),
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
[restoreContentDatabase, t],
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const handlePermanentDeleteDatabase = useCallback(
|
|
594
|
+
async (documentId: string) => {
|
|
595
|
+
try {
|
|
596
|
+
await deleteDocument.mutateAsync({ id: documentId });
|
|
597
|
+
queryClient.invalidateQueries({
|
|
598
|
+
queryKey: ["action", "list-documents"],
|
|
599
|
+
});
|
|
600
|
+
queryClient.invalidateQueries({
|
|
601
|
+
queryKey: ["action", "list-trashed-content-databases"],
|
|
602
|
+
});
|
|
603
|
+
toast.success(t("sidebar.databasePermanentlyDeleted"));
|
|
604
|
+
} catch (err) {
|
|
605
|
+
toast.error(t("sidebar.failedPermanentDeleteDatabase"), {
|
|
606
|
+
description:
|
|
607
|
+
err instanceof Error ? err.message : t("empty.genericError"),
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
[deleteDocument, queryClient, t],
|
|
612
|
+
);
|
|
613
|
+
|
|
527
614
|
const filteredDocuments = searchQuery
|
|
528
615
|
? documents.filter((d) =>
|
|
529
616
|
d.title.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
@@ -548,7 +635,8 @@ export function DocumentSidebar({
|
|
|
548
635
|
navigateToDocument(id);
|
|
549
636
|
onNavigate?.();
|
|
550
637
|
}}
|
|
551
|
-
|
|
638
|
+
onCreateChildPage={(parentId) => handleCreatePage(parentId)}
|
|
639
|
+
onCreateChildDatabase={(parentId) => handleCreateDatabase(parentId)}
|
|
552
640
|
onDelete={handleDelete}
|
|
553
641
|
onToggleFavorite={handleToggleFavorite}
|
|
554
642
|
/>
|
|
@@ -556,14 +644,70 @@ export function DocumentSidebar({
|
|
|
556
644
|
</SortableContext>
|
|
557
645
|
);
|
|
558
646
|
|
|
559
|
-
const
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
647
|
+
const renderNewButton = () => (
|
|
648
|
+
<DropdownMenu>
|
|
649
|
+
<DropdownMenuTrigger asChild>
|
|
650
|
+
<button
|
|
651
|
+
type="button"
|
|
652
|
+
className="flex w-full items-center gap-2 rounded-md px-3 py-[5px] text-sm text-muted-foreground hover:bg-accent/50 hover:text-foreground"
|
|
653
|
+
disabled={createDocument.isPending || createDatabase.isPending}
|
|
654
|
+
>
|
|
655
|
+
<IconPlus size={14} className="shrink-0" />
|
|
656
|
+
<span>{t("sidebar.new")}</span>
|
|
657
|
+
</button>
|
|
658
|
+
</DropdownMenuTrigger>
|
|
659
|
+
<DropdownMenuContent align="start" className="w-44">
|
|
660
|
+
<DropdownMenuItem
|
|
661
|
+
disabled={createDocument.isPending}
|
|
662
|
+
onClick={() => void handleCreatePage()}
|
|
663
|
+
>
|
|
664
|
+
<IconFileText className="me-2 size-4" />
|
|
665
|
+
{t("sidebar.page")}
|
|
666
|
+
</DropdownMenuItem>
|
|
667
|
+
<DropdownMenuItem
|
|
668
|
+
disabled={createDatabase.isPending}
|
|
669
|
+
onClick={() => void handleCreateDatabase(null)}
|
|
670
|
+
>
|
|
671
|
+
<IconDatabase className="me-2 size-4" />
|
|
672
|
+
{t("sidebar.database")}
|
|
673
|
+
</DropdownMenuItem>
|
|
674
|
+
</DropdownMenuContent>
|
|
675
|
+
</DropdownMenu>
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
const renderCollapsedNewButton = () => (
|
|
679
|
+
<DropdownMenu>
|
|
680
|
+
<Tooltip>
|
|
681
|
+
<TooltipTrigger asChild>
|
|
682
|
+
<DropdownMenuTrigger asChild>
|
|
683
|
+
<button
|
|
684
|
+
type="button"
|
|
685
|
+
className="w-10 h-10 flex items-center justify-center rounded-lg hover:bg-accent text-muted-foreground hover:text-foreground"
|
|
686
|
+
disabled={createDocument.isPending || createDatabase.isPending}
|
|
687
|
+
>
|
|
688
|
+
<IconPlus size={16} />
|
|
689
|
+
</button>
|
|
690
|
+
</DropdownMenuTrigger>
|
|
691
|
+
</TooltipTrigger>
|
|
692
|
+
<TooltipContent>{t("sidebar.new")}</TooltipContent>
|
|
693
|
+
</Tooltip>
|
|
694
|
+
<DropdownMenuContent align="start" className="w-44">
|
|
695
|
+
<DropdownMenuItem
|
|
696
|
+
disabled={createDocument.isPending}
|
|
697
|
+
onClick={() => void handleCreatePage()}
|
|
698
|
+
>
|
|
699
|
+
<IconFileText className="me-2 size-4" />
|
|
700
|
+
{t("sidebar.page")}
|
|
701
|
+
</DropdownMenuItem>
|
|
702
|
+
<DropdownMenuItem
|
|
703
|
+
disabled={createDatabase.isPending}
|
|
704
|
+
onClick={() => void handleCreateDatabase(null)}
|
|
705
|
+
>
|
|
706
|
+
<IconDatabase className="me-2 size-4" />
|
|
707
|
+
{t("sidebar.database")}
|
|
708
|
+
</DropdownMenuItem>
|
|
709
|
+
</DropdownMenuContent>
|
|
710
|
+
</DropdownMenu>
|
|
567
711
|
);
|
|
568
712
|
|
|
569
713
|
const renderLocalFilesNavButton = () => (
|
|
@@ -686,6 +830,103 @@ export function DocumentSidebar({
|
|
|
686
830
|
);
|
|
687
831
|
};
|
|
688
832
|
|
|
833
|
+
const renderTrashSection = () => {
|
|
834
|
+
if (trashItems.length === 0) return null;
|
|
835
|
+
const collapsed = collapsedSections.trash;
|
|
836
|
+
|
|
837
|
+
return (
|
|
838
|
+
<div className="mt-3 border-t border-border/60 pt-2">
|
|
839
|
+
{renderSectionHeader("trash", t("sidebar.trash"))}
|
|
840
|
+
{!collapsed && (
|
|
841
|
+
<div className="px-1 py-1">
|
|
842
|
+
{trashItems.map((database) => {
|
|
843
|
+
const title = database.title || t("editor.untitledDatabase");
|
|
844
|
+
return (
|
|
845
|
+
<div
|
|
846
|
+
key={database.databaseId}
|
|
847
|
+
className="group flex min-w-0 items-center gap-1 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-accent/50 hover:text-foreground"
|
|
848
|
+
>
|
|
849
|
+
<span className="min-w-0 flex-1 truncate">{title}</span>
|
|
850
|
+
<Tooltip>
|
|
851
|
+
<TooltipTrigger asChild>
|
|
852
|
+
<button
|
|
853
|
+
type="button"
|
|
854
|
+
aria-label={t("sidebar.restoreDatabaseNamed", {
|
|
855
|
+
title,
|
|
856
|
+
})}
|
|
857
|
+
className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background hover:text-foreground disabled:opacity-50"
|
|
858
|
+
disabled={restoreContentDatabase.isPending}
|
|
859
|
+
onClick={() =>
|
|
860
|
+
void handleRestoreDatabase(database.databaseId)
|
|
861
|
+
}
|
|
862
|
+
>
|
|
863
|
+
<IconRestore size={14} />
|
|
864
|
+
</button>
|
|
865
|
+
</TooltipTrigger>
|
|
866
|
+
<TooltipContent>
|
|
867
|
+
{t("sidebar.restoreDatabase")}
|
|
868
|
+
</TooltipContent>
|
|
869
|
+
</Tooltip>
|
|
870
|
+
{database.canPermanentlyDelete && (
|
|
871
|
+
<AlertDialog>
|
|
872
|
+
<Tooltip>
|
|
873
|
+
<TooltipTrigger asChild>
|
|
874
|
+
<AlertDialogTrigger asChild>
|
|
875
|
+
<button
|
|
876
|
+
type="button"
|
|
877
|
+
aria-label={t(
|
|
878
|
+
"sidebar.deleteDatabaseNamedPermanently",
|
|
879
|
+
{ title },
|
|
880
|
+
)}
|
|
881
|
+
className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
|
|
882
|
+
disabled={deleteDocument.isPending}
|
|
883
|
+
>
|
|
884
|
+
<IconTrashX size={14} />
|
|
885
|
+
</button>
|
|
886
|
+
</AlertDialogTrigger>
|
|
887
|
+
</TooltipTrigger>
|
|
888
|
+
<TooltipContent>
|
|
889
|
+
{t("sidebar.deletePermanently")}
|
|
890
|
+
</TooltipContent>
|
|
891
|
+
</Tooltip>
|
|
892
|
+
<AlertDialogContent>
|
|
893
|
+
<AlertDialogHeader>
|
|
894
|
+
<AlertDialogTitle>
|
|
895
|
+
{t("sidebar.deleteDatabasePermanentlyQuestion")}
|
|
896
|
+
</AlertDialogTitle>
|
|
897
|
+
<AlertDialogDescription>
|
|
898
|
+
{t("sidebar.deleteDatabasePermanentlyDescription", {
|
|
899
|
+
title,
|
|
900
|
+
})}
|
|
901
|
+
</AlertDialogDescription>
|
|
902
|
+
</AlertDialogHeader>
|
|
903
|
+
<AlertDialogFooter>
|
|
904
|
+
<AlertDialogCancel>
|
|
905
|
+
{t("comments.cancel")}
|
|
906
|
+
</AlertDialogCancel>
|
|
907
|
+
<AlertDialogAction
|
|
908
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
909
|
+
onClick={() =>
|
|
910
|
+
void handlePermanentDeleteDatabase(
|
|
911
|
+
database.documentId,
|
|
912
|
+
)
|
|
913
|
+
}
|
|
914
|
+
>
|
|
915
|
+
{t("sidebar.deletePermanently")}
|
|
916
|
+
</AlertDialogAction>
|
|
917
|
+
</AlertDialogFooter>
|
|
918
|
+
</AlertDialogContent>
|
|
919
|
+
</AlertDialog>
|
|
920
|
+
)}
|
|
921
|
+
</div>
|
|
922
|
+
);
|
|
923
|
+
})}
|
|
924
|
+
</div>
|
|
925
|
+
)}
|
|
926
|
+
</div>
|
|
927
|
+
);
|
|
928
|
+
};
|
|
929
|
+
|
|
689
930
|
if (collapsed) {
|
|
690
931
|
return (
|
|
691
932
|
<div className="flex h-full w-12 flex-col items-center gap-1 border-e border-border bg-muted/30 py-3 transition-[width] duration-200 ease-out">
|
|
@@ -700,17 +941,7 @@ export function DocumentSidebar({
|
|
|
700
941
|
</TooltipTrigger>
|
|
701
942
|
<TooltipContent>{t("sidebar.expand")}</TooltipContent>
|
|
702
943
|
</Tooltip>
|
|
703
|
-
|
|
704
|
-
<TooltipTrigger asChild>
|
|
705
|
-
<button
|
|
706
|
-
className="w-10 h-10 flex items-center justify-center rounded-lg hover:bg-accent text-muted-foreground hover:text-foreground"
|
|
707
|
-
onClick={() => handleCreatePage()}
|
|
708
|
-
>
|
|
709
|
-
<IconPlus size={16} />
|
|
710
|
-
</button>
|
|
711
|
-
</TooltipTrigger>
|
|
712
|
-
<TooltipContent>{t("sidebar.newPage")}</TooltipContent>
|
|
713
|
-
</Tooltip>
|
|
944
|
+
{renderCollapsedNewButton()}
|
|
714
945
|
<Tooltip>
|
|
715
946
|
<TooltipTrigger asChild>
|
|
716
947
|
<button
|
|
@@ -860,7 +1091,7 @@ export function DocumentSidebar({
|
|
|
860
1091
|
))
|
|
861
1092
|
)}
|
|
862
1093
|
</div>
|
|
863
|
-
{
|
|
1094
|
+
{renderNewButton()}
|
|
864
1095
|
</>
|
|
865
1096
|
) : (
|
|
866
1097
|
<>
|
|
@@ -911,7 +1142,7 @@ export function DocumentSidebar({
|
|
|
911
1142
|
label: t("sidebar.localFiles"),
|
|
912
1143
|
nodes: localFileTree,
|
|
913
1144
|
emptyLabel: t("sidebar.noFilesYet"),
|
|
914
|
-
footer:
|
|
1145
|
+
footer: renderNewButton(),
|
|
915
1146
|
})}
|
|
916
1147
|
{databaseTree.length > 0
|
|
917
1148
|
? renderTreeSection({
|
|
@@ -922,6 +1153,7 @@ export function DocumentSidebar({
|
|
|
922
1153
|
className: "mt-3",
|
|
923
1154
|
})
|
|
924
1155
|
: null}
|
|
1156
|
+
{renderTrashSection()}
|
|
925
1157
|
</>
|
|
926
1158
|
) : (
|
|
927
1159
|
<>
|
|
@@ -939,7 +1171,7 @@ export function DocumentSidebar({
|
|
|
939
1171
|
label: t("sidebar.private"),
|
|
940
1172
|
nodes: privateTree,
|
|
941
1173
|
emptyLabel: t("sidebar.noPrivatePagesYet"),
|
|
942
|
-
footer:
|
|
1174
|
+
footer: renderNewButton(),
|
|
943
1175
|
})}
|
|
944
1176
|
|
|
945
1177
|
{!isLoading &&
|
|
@@ -950,6 +1182,7 @@ export function DocumentSidebar({
|
|
|
950
1182
|
emptyLabel: t("sidebar.noOrganizationPagesYet"),
|
|
951
1183
|
className: "mt-3",
|
|
952
1184
|
})}
|
|
1185
|
+
{renderTrashSection()}
|
|
953
1186
|
</>
|
|
954
1187
|
)}
|
|
955
1188
|
</>
|
|
@@ -50,7 +50,8 @@ interface DocumentTreeItemProps {
|
|
|
50
50
|
expandedIds: Set<string>;
|
|
51
51
|
onToggleExpanded: (id: string) => void;
|
|
52
52
|
onSelect: (id: string) => void;
|
|
53
|
-
|
|
53
|
+
onCreateChildPage: (parentId: string) => void;
|
|
54
|
+
onCreateChildDatabase: (parentId: string) => void;
|
|
54
55
|
onDelete: (id: string) => void;
|
|
55
56
|
onToggleFavorite: (id: string, isFavorite: boolean) => void;
|
|
56
57
|
}
|
|
@@ -94,7 +95,8 @@ export function DocumentTreeItem({
|
|
|
94
95
|
expandedIds,
|
|
95
96
|
onToggleExpanded,
|
|
96
97
|
onSelect,
|
|
97
|
-
|
|
98
|
+
onCreateChildPage,
|
|
99
|
+
onCreateChildDatabase,
|
|
98
100
|
onDelete,
|
|
99
101
|
onToggleFavorite,
|
|
100
102
|
}: DocumentTreeItemProps) {
|
|
@@ -262,22 +264,45 @@ export function DocumentTreeItem({
|
|
|
262
264
|
)}
|
|
263
265
|
|
|
264
266
|
{canCreateChild && (
|
|
265
|
-
<
|
|
266
|
-
<
|
|
267
|
-
<
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
<DropdownMenu>
|
|
268
|
+
<Tooltip>
|
|
269
|
+
<TooltipTrigger asChild>
|
|
270
|
+
<DropdownMenuTrigger asChild>
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
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"
|
|
274
|
+
aria-label={t("sidebar.addChildTo", {
|
|
275
|
+
title: node.title || t("sidebar.untitled"),
|
|
276
|
+
})}
|
|
277
|
+
onClick={(e) => e.stopPropagation()}
|
|
278
|
+
>
|
|
279
|
+
<IconPlus size={14} />
|
|
280
|
+
</button>
|
|
281
|
+
</DropdownMenuTrigger>
|
|
282
|
+
</TooltipTrigger>
|
|
283
|
+
<TooltipContent>{t("sidebar.addChild")}</TooltipContent>
|
|
284
|
+
</Tooltip>
|
|
285
|
+
<DropdownMenuContent align="start" className="w-44">
|
|
286
|
+
<DropdownMenuItem
|
|
271
287
|
onClick={(e) => {
|
|
272
288
|
e.stopPropagation();
|
|
273
|
-
|
|
289
|
+
onCreateChildPage(node.id);
|
|
274
290
|
}}
|
|
275
291
|
>
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
292
|
+
<IconFileText className="me-2 size-4" />
|
|
293
|
+
{t("sidebar.page")}
|
|
294
|
+
</DropdownMenuItem>
|
|
295
|
+
<DropdownMenuItem
|
|
296
|
+
onClick={(e) => {
|
|
297
|
+
e.stopPropagation();
|
|
298
|
+
onCreateChildDatabase(node.id);
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
<IconDatabase className="me-2 size-4" />
|
|
302
|
+
{t("sidebar.database")}
|
|
303
|
+
</DropdownMenuItem>
|
|
304
|
+
</DropdownMenuContent>
|
|
305
|
+
</DropdownMenu>
|
|
281
306
|
)}
|
|
282
307
|
</div>
|
|
283
308
|
</div>
|
|
@@ -297,7 +322,8 @@ export function DocumentTreeItem({
|
|
|
297
322
|
expandedIds={expandedIds}
|
|
298
323
|
onToggleExpanded={onToggleExpanded}
|
|
299
324
|
onSelect={onSelect}
|
|
300
|
-
|
|
325
|
+
onCreateChildPage={onCreateChildPage}
|
|
326
|
+
onCreateChildDatabase={onCreateChildDatabase}
|
|
301
327
|
onDelete={onDelete}
|
|
302
328
|
onToggleFavorite={onToggleFavorite}
|
|
303
329
|
/>
|
|
@@ -98,11 +98,11 @@ const messages = {
|
|
|
98
98
|
reorderField: "重新排序 {{name}}",
|
|
99
99
|
title: "標題",
|
|
100
100
|
toggleField: "切換 {{name}}",
|
|
101
|
-
creatingDatabase: "
|
|
102
|
-
databaseCreated: "
|
|
101
|
+
creatingDatabase: "正在建立內嵌資料庫...",
|
|
102
|
+
databaseCreated: "內嵌資料庫已建立",
|
|
103
103
|
describeWhatToGenerate: "描述要生成什麼...",
|
|
104
104
|
enterToSubmit: "輸入提交",
|
|
105
|
-
failedToCreateDatabase: "
|
|
105
|
+
failedToCreateDatabase: "建立內嵌資料庫失敗",
|
|
106
106
|
failedToCreatePage: "建立頁面失敗",
|
|
107
107
|
generateWithAi: "用人工智慧生成",
|
|
108
108
|
dropMedia: "拖放媒體",
|
|
@@ -386,7 +386,7 @@ const messages = {
|
|
|
386
386
|
codeBlockDescription: "程式碼片段",
|
|
387
387
|
collapsibleBlockDescription: "可折疊塊",
|
|
388
388
|
database: "資料庫",
|
|
389
|
-
databaseDescription: "
|
|
389
|
+
databaseDescription: "此頁面中的內嵌資料庫",
|
|
390
390
|
divider: "分頻器",
|
|
391
391
|
dividerDescription: "水平尺",
|
|
392
392
|
generate: "生成",
|
|
@@ -939,7 +939,9 @@ const messages = {
|
|
|
939
939
|
},
|
|
940
940
|
sidebar: {
|
|
941
941
|
cannotReorderPages: "無法重新排序頁面",
|
|
942
|
-
|
|
942
|
+
addChild: "新增子項目",
|
|
943
|
+
addChildTo: "新增子項目至 {{title}}",
|
|
944
|
+
addSubPage: "新增子頁面",
|
|
943
945
|
collapse: "折疊側邊欄",
|
|
944
946
|
completeStepsAboveFirst: "請先完成上面的步驟",
|
|
945
947
|
connectWorkspace: "連線工作區",
|
|
@@ -949,13 +951,25 @@ const messages = {
|
|
|
949
951
|
copy: "複製",
|
|
950
952
|
disconnectWorkspace: "斷開工作區連線",
|
|
951
953
|
expand: "展開側邊欄",
|
|
954
|
+
database: "資料庫",
|
|
955
|
+
databasePermanentlyDeleted: "資料庫已永久刪除",
|
|
956
|
+
databaseRestored: "資料庫已還原",
|
|
957
|
+
deleteDatabaseNamedPermanently: "永久刪除 {{title}}",
|
|
958
|
+
deleteDatabasePermanentlyDescription:
|
|
959
|
+
"這會永久刪除「{{title}}」及其頁面。此操作無法復原。",
|
|
960
|
+
deleteDatabasePermanentlyQuestion: "永久刪除資料庫?",
|
|
961
|
+
deletePermanently: "永久刪除",
|
|
962
|
+
failedCreateDatabase: "建立資料庫失敗",
|
|
952
963
|
failedCreatePage: "建立頁面失敗",
|
|
953
964
|
failedDeletePage: "刪除頁面失敗",
|
|
965
|
+
failedPermanentDeleteDatabase: "永久刪除資料庫失敗",
|
|
954
966
|
failedMovePage: "行動頁面失敗",
|
|
967
|
+
failedRestoreDatabase: "還原資料庫失敗",
|
|
955
968
|
deletePageDescription:
|
|
956
|
-
"
|
|
957
|
-
deletePageQuestion: "
|
|
969
|
+
"「{{title}}」及其所有子頁面將被永久刪除。此操作無法復原。",
|
|
970
|
+
deletePageQuestion: "刪除頁面?",
|
|
958
971
|
localFiles: "本機檔案",
|
|
972
|
+
new: "新增",
|
|
959
973
|
newPage: "新頁面",
|
|
960
974
|
nextStep: "下一步",
|
|
961
975
|
notionConfigureOAuthAuthorize: "設定 OAuth,然後授權你的工作區。",
|
|
@@ -991,14 +1005,18 @@ const messages = {
|
|
|
991
1005
|
noSharedCopiesYet: "還沒有共用副本",
|
|
992
1006
|
oneAffectedPageReadOnly: "受影響的頁面之一是唯讀的。",
|
|
993
1007
|
organization: "組織",
|
|
1008
|
+
page: "頁面",
|
|
994
1009
|
private: "私人",
|
|
995
1010
|
results: "結果",
|
|
996
1011
|
search: "搜尋",
|
|
997
1012
|
searchPages: "搜尋頁面...",
|
|
998
1013
|
refreshConnection: "重新整理連線",
|
|
1014
|
+
restoreDatabase: "還原",
|
|
1015
|
+
restoreDatabaseNamed: "還原 {{title}}",
|
|
999
1016
|
saving: "正在儲存...",
|
|
1000
1017
|
sharedCopies: "共用副本",
|
|
1001
1018
|
synced: "已同步",
|
|
1019
|
+
trash: "垃圾桶",
|
|
1002
1020
|
favorites: "釘選",
|
|
1003
1021
|
untitled: "無標題",
|
|
1004
1022
|
},
|