@agent-native/core 0.114.0 → 0.114.1
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 +2 -2
- package/corpus/core/CHANGELOG.md +9 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/build-compatibility.ts +77 -0
- package/corpus/core/src/client/route-chunk-recovery/index.ts +1 -0
- package/corpus/core/src/client/use-action.ts +32 -1
- package/corpus/core/src/deploy/build.ts +4 -1
- package/corpus/core/src/org/context.ts +56 -30
- package/corpus/core/src/server/action-routes.ts +52 -2
- package/corpus/core/src/server/auth-plugin.ts +9 -4
- package/corpus/core/src/server/better-auth-instance.ts +5 -1
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/vite/client.ts +17 -0
- package/corpus/templates/content/AGENTS.md +2 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +49 -5
- package/corpus/templates/content/actions/_content-document-access.ts +21 -0
- package/corpus/templates/content/actions/_content-favorites.ts +91 -0
- package/corpus/templates/content/actions/_content-files.ts +79 -23
- package/corpus/templates/content/actions/_content-sidebar-state.ts +24 -0
- package/corpus/templates/content/actions/_content-space-access.ts +15 -17
- package/corpus/templates/content/actions/_content-space-catalog-guards.ts +20 -0
- package/corpus/templates/content/actions/_content-spaces.ts +364 -167
- package/corpus/templates/content/actions/_database-source-utils.ts +8 -0
- package/corpus/templates/content/actions/_database-utils.ts +249 -15
- package/corpus/templates/content/actions/_delete-content-space.ts +84 -0
- package/corpus/templates/content/actions/_files-system-properties.ts +499 -0
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -1
- package/corpus/templates/content/actions/_property-utils.ts +18 -9
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +3 -0
- package/corpus/templates/content/actions/configure-document-property.ts +3 -0
- package/corpus/templates/content/actions/create-content-database.ts +2 -2
- package/corpus/templates/content/actions/create-content-space.ts +34 -0
- package/corpus/templates/content/actions/create-document.ts +4 -1
- package/corpus/templates/content/actions/delete-content-space.ts +19 -0
- package/corpus/templates/content/actions/delete-database-items.ts +28 -1
- package/corpus/templates/content/actions/delete-document-property.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +39 -2
- package/corpus/templates/content/actions/duplicate-database-item.ts +6 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +6 -0
- package/corpus/templates/content/actions/duplicate-document-property.ts +3 -0
- package/corpus/templates/content/actions/get-content-database.ts +11 -2
- package/corpus/templates/content/actions/get-content-sidebar-state.ts +23 -0
- package/corpus/templates/content/actions/get-document.ts +32 -7
- package/corpus/templates/content/actions/import-content-source.ts +33 -1
- package/corpus/templates/content/actions/list-content-spaces.ts +34 -2
- package/corpus/templates/content/actions/list-documents.ts +39 -7
- package/corpus/templates/content/actions/reorder-document-property.ts +9 -0
- package/corpus/templates/content/actions/set-document-property.ts +41 -23
- package/corpus/templates/content/actions/share-local-file-document.ts +15 -0
- package/corpus/templates/content/actions/submit-content-database-form.ts +7 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +14 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +1 -0
- package/corpus/templates/content/actions/update-content-sidebar-state.ts +27 -0
- package/corpus/templates/content/actions/update-document.ts +158 -66
- package/corpus/templates/content/app/components/EmptyState.tsx +4 -57
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +8 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +519 -178
- package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +39 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +266 -7
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +430 -152
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +467 -66
- package/corpus/templates/content/app/components/editor/database/view-config.ts +9 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +720 -196
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +173 -1
- package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +1 -1
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +91 -16
- package/corpus/templates/content/app/entry.client.tsx +5 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +84 -3
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +60 -2
- package/corpus/templates/content/app/hooks/use-create-page.ts +30 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +270 -19
- package/corpus/templates/content/app/i18n-data.ts +243 -20
- package/corpus/templates/content/app/lib/local-folder-picker-safety.ts +54 -0
- package/corpus/templates/content/app/lib/local-folder-picker-support.ts +18 -0
- package/corpus/templates/content/app/routes/_app.favorites.tsx +30 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +18 -6
- package/corpus/templates/content/changelog/2026-07-17-workspace-files-now-stay-in-a-loading-state-until-automatic-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspace-switching-now-opens-the-selected-files-database-pr.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspaces-now-expand-above-their-own-files-list-without-dup.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-create-named-workspaces-each-with-its-own-private-files-data.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-favorites-align-with-workspace-and-file-rows-in-the-sidebar.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-local-folder-selection-no-longer-invokes-the-unsafe-native-p.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-made-content-workspaces-independently-expandable-restored-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-page-details-now-live-in-a-shared-info-and-comments-rail-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-database-views-now-control-sidebar-workspace-navig.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-navigation-now-aligns-favorites-with-workspace-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-favorites-now-open-as-a-table-and-compact-breadcrumbs-make-w.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-files-databases-now-start-unfiltered-sidebar-rows-exactly-fo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-sidebar-page-trees-now-open-only-when-expanded-remember-thei.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-file-databases-now-use-the-workspace-name-open-as-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-navigation-now-stays-stable-on-hover-uses-folder-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-rows-now-use-folder-icons-and-deleting-a-user-crea.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspaces-can-be-created-without-leaving-the-workspaces-dat.md +6 -0
- package/corpus/templates/content/e2e/global-setup.ts +3 -2
- package/corpus/templates/content/parity/matrix.md +28 -28
- package/corpus/templates/content/parity/matrix.ts +8 -2
- package/corpus/templates/content/server/db/index.ts +1 -0
- package/corpus/templates/content/server/db/schema.ts +10 -0
- package/corpus/templates/content/server/lib/documents.ts +12 -7
- package/corpus/templates/content/server/plugins/db.ts +17 -0
- package/corpus/templates/content/shared/api.ts +9 -0
- package/corpus/templates/content/shared/database-form.ts +11 -2
- package/corpus/templates/content/vite.config.ts +1 -0
- package/dist/client/build-compatibility.d.ts +11 -0
- package/dist/client/build-compatibility.d.ts.map +1 -0
- package/dist/client/build-compatibility.js +56 -0
- package/dist/client/build-compatibility.js.map +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery/index.js +1 -0
- package/dist/client/route-chunk-recovery/index.js.map +1 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +19 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +2 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/context.d.ts +4 -2
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +44 -22
- package/dist/org/context.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +32 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth-plugin.d.ts.map +1 -1
- package/dist/server/auth-plugin.js +9 -5
- package/dist/server/auth-plugin.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +4 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/vite/client.d.ts +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +8 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/build-compatibility.ts +77 -0
- package/src/client/route-chunk-recovery/index.ts +1 -0
- package/src/client/use-action.ts +32 -1
- package/src/deploy/build.ts +4 -1
- package/src/org/context.ts +56 -30
- package/src/server/action-routes.ts +52 -2
- package/src/server/auth-plugin.ts +9 -4
- package/src/server/better-auth-instance.ts +5 -1
- package/src/shared/mcp-embed-headers.ts +1 -1
- package/src/vite/client.ts +17 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
1
2
|
import { Button } from "@agent-native/toolkit/ui/button";
|
|
2
3
|
import {
|
|
3
4
|
Collapsible,
|
|
@@ -15,12 +16,39 @@ import type {
|
|
|
15
16
|
import {
|
|
16
17
|
IconChevronDown,
|
|
17
18
|
IconChevronRight,
|
|
19
|
+
IconDatabase,
|
|
20
|
+
IconDots,
|
|
18
21
|
IconFileText,
|
|
19
22
|
IconLoader2,
|
|
23
|
+
IconPlus,
|
|
24
|
+
IconStar,
|
|
25
|
+
IconTrash,
|
|
20
26
|
} from "@tabler/icons-react";
|
|
21
|
-
import { useEffect, useState, type MouseEvent } from "react";
|
|
27
|
+
import { useEffect, useState, type MouseEvent, type ReactNode } from "react";
|
|
22
28
|
import { Link } from "react-router";
|
|
23
29
|
|
|
30
|
+
import {
|
|
31
|
+
AlertDialog,
|
|
32
|
+
AlertDialogAction,
|
|
33
|
+
AlertDialogCancel,
|
|
34
|
+
AlertDialogContent,
|
|
35
|
+
AlertDialogDescription,
|
|
36
|
+
AlertDialogFooter,
|
|
37
|
+
AlertDialogHeader,
|
|
38
|
+
AlertDialogTitle,
|
|
39
|
+
} from "@/components/ui/alert-dialog";
|
|
40
|
+
import {
|
|
41
|
+
DropdownMenu,
|
|
42
|
+
DropdownMenuContent,
|
|
43
|
+
DropdownMenuItem,
|
|
44
|
+
DropdownMenuSeparator,
|
|
45
|
+
DropdownMenuTrigger,
|
|
46
|
+
} from "@/components/ui/dropdown-menu";
|
|
47
|
+
import {
|
|
48
|
+
Tooltip,
|
|
49
|
+
TooltipContent,
|
|
50
|
+
TooltipTrigger,
|
|
51
|
+
} from "@/components/ui/tooltip";
|
|
24
52
|
import { cn } from "@/lib/utils";
|
|
25
53
|
|
|
26
54
|
import { applyDatabaseView } from "./filter-sort";
|
|
@@ -68,13 +96,33 @@ export function ContentFilesSidebarView({
|
|
|
68
96
|
data,
|
|
69
97
|
overrides,
|
|
70
98
|
isLoading,
|
|
99
|
+
activeDocumentId,
|
|
71
100
|
labels,
|
|
72
101
|
onSelectView,
|
|
102
|
+
onOpenItem,
|
|
103
|
+
onCreateChildPage,
|
|
104
|
+
onCreateChildDatabase,
|
|
105
|
+
onDeleteItem,
|
|
106
|
+
onToggleFavorite,
|
|
107
|
+
expandedDocumentIds,
|
|
108
|
+
onDocumentExpandedChange,
|
|
109
|
+
renderItem,
|
|
110
|
+
scroll = true,
|
|
73
111
|
}: {
|
|
74
112
|
data: ContentDatabaseResponse | undefined;
|
|
75
113
|
overrides: ContentDatabasePersonalViewOverrides | null | undefined;
|
|
76
114
|
isLoading: boolean;
|
|
115
|
+
activeDocumentId?: string | null;
|
|
77
116
|
onSelectView?: (viewId: string) => void;
|
|
117
|
+
onOpenItem?: (item: ContentDatabaseItem) => boolean;
|
|
118
|
+
onCreateChildPage?: (item: ContentDatabaseItem) => void;
|
|
119
|
+
onCreateChildDatabase?: (item: ContentDatabaseItem) => void;
|
|
120
|
+
onDeleteItem?: (item: ContentDatabaseItem) => void;
|
|
121
|
+
onToggleFavorite?: (item: ContentDatabaseItem) => void;
|
|
122
|
+
expandedDocumentIds?: ReadonlySet<string>;
|
|
123
|
+
onDocumentExpandedChange?: (documentId: string, expanded: boolean) => void;
|
|
124
|
+
renderItem?: (item: ContentDatabaseItem) => ReactNode;
|
|
125
|
+
scroll?: boolean;
|
|
78
126
|
labels: Omit<
|
|
79
127
|
Parameters<typeof DatabaseSidebarView>[0],
|
|
80
128
|
| "groups"
|
|
@@ -84,6 +132,8 @@ export function ContentFilesSidebarView({
|
|
|
84
132
|
| "openPagesIn"
|
|
85
133
|
| "onClearResultConstraints"
|
|
86
134
|
| "onPreview"
|
|
135
|
+
| "renderItem"
|
|
136
|
+
| "scroll"
|
|
87
137
|
>;
|
|
88
138
|
}) {
|
|
89
139
|
const viewConfig = applyPersonalSidebarViewOverrides(
|
|
@@ -99,12 +149,17 @@ export function ContentFilesSidebarView({
|
|
|
99
149
|
const activeView =
|
|
100
150
|
viewConfig.views.find((view) => view.id === selectedViewId) ??
|
|
101
151
|
activeDatabaseView(viewConfig);
|
|
152
|
+
const [constraintsCleared, setConstraintsCleared] = useState(false);
|
|
153
|
+
const activeFilterKey = JSON.stringify(activeView.filters);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
setConstraintsCleared(false);
|
|
156
|
+
}, [activeFilterKey, activeView.id]);
|
|
102
157
|
const items = data
|
|
103
158
|
? applyDatabaseView(
|
|
104
159
|
data.items,
|
|
105
160
|
data.properties,
|
|
106
161
|
"",
|
|
107
|
-
activeView.filters,
|
|
162
|
+
constraintsCleared ? [] : activeView.filters,
|
|
108
163
|
activeView.sorts,
|
|
109
164
|
activeView.filterMode ?? "and",
|
|
110
165
|
)
|
|
@@ -117,6 +172,11 @@ export function ContentFilesSidebarView({
|
|
|
117
172
|
),
|
|
118
173
|
activeView.hideEmptyGroups === true,
|
|
119
174
|
);
|
|
175
|
+
const hierarchyItems = data?.properties.some(
|
|
176
|
+
(property) => property.definition.systemRole === "files_parent",
|
|
177
|
+
)
|
|
178
|
+
? items
|
|
179
|
+
: undefined;
|
|
120
180
|
return (
|
|
121
181
|
<div className="min-w-0">
|
|
122
182
|
{viewConfig.views.length > 1 && (
|
|
@@ -147,10 +207,23 @@ export function ContentFilesSidebarView({
|
|
|
147
207
|
!!databaseViewGroupingProperty(activeView, data?.properties ?? [])
|
|
148
208
|
}
|
|
149
209
|
isLoading={isLoading}
|
|
150
|
-
hasActiveConstraints={
|
|
210
|
+
hasActiveConstraints={
|
|
211
|
+
!constraintsCleared && activeView.filters.length > 0
|
|
212
|
+
}
|
|
151
213
|
openPagesIn="full_page"
|
|
152
|
-
onClearResultConstraints={() =>
|
|
214
|
+
onClearResultConstraints={() => setConstraintsCleared(true)}
|
|
153
215
|
onPreview={() => {}}
|
|
216
|
+
onOpenItem={onOpenItem}
|
|
217
|
+
activeDocumentId={activeDocumentId}
|
|
218
|
+
onCreateChildPage={onCreateChildPage}
|
|
219
|
+
onCreateChildDatabase={onCreateChildDatabase}
|
|
220
|
+
onDeleteItem={onDeleteItem}
|
|
221
|
+
onToggleFavorite={onToggleFavorite}
|
|
222
|
+
expandedDocumentIds={expandedDocumentIds}
|
|
223
|
+
onDocumentExpandedChange={onDocumentExpandedChange}
|
|
224
|
+
renderItem={renderItem}
|
|
225
|
+
hierarchyItems={hierarchyItems}
|
|
226
|
+
scroll={scroll}
|
|
154
227
|
/>
|
|
155
228
|
</div>
|
|
156
229
|
);
|
|
@@ -164,6 +237,17 @@ export function DatabaseSidebarView({
|
|
|
164
237
|
openPagesIn,
|
|
165
238
|
onClearResultConstraints,
|
|
166
239
|
onPreview,
|
|
240
|
+
onOpenItem,
|
|
241
|
+
activeDocumentId,
|
|
242
|
+
onCreateChildPage,
|
|
243
|
+
onCreateChildDatabase,
|
|
244
|
+
onDeleteItem,
|
|
245
|
+
onToggleFavorite,
|
|
246
|
+
expandedDocumentIds,
|
|
247
|
+
onDocumentExpandedChange,
|
|
248
|
+
renderItem,
|
|
249
|
+
hierarchyItems,
|
|
250
|
+
scroll = true,
|
|
167
251
|
loadingLabel,
|
|
168
252
|
noMatchesLabel,
|
|
169
253
|
clearLabel,
|
|
@@ -177,6 +261,17 @@ export function DatabaseSidebarView({
|
|
|
177
261
|
openPagesIn: ContentDatabaseOpenPagesIn;
|
|
178
262
|
onClearResultConstraints: () => void;
|
|
179
263
|
onPreview: (item: ContentDatabaseItem) => void;
|
|
264
|
+
onOpenItem?: (item: ContentDatabaseItem) => boolean;
|
|
265
|
+
activeDocumentId?: string | null;
|
|
266
|
+
onCreateChildPage?: (item: ContentDatabaseItem) => void;
|
|
267
|
+
onCreateChildDatabase?: (item: ContentDatabaseItem) => void;
|
|
268
|
+
onDeleteItem?: (item: ContentDatabaseItem) => void;
|
|
269
|
+
onToggleFavorite?: (item: ContentDatabaseItem) => void;
|
|
270
|
+
expandedDocumentIds?: ReadonlySet<string>;
|
|
271
|
+
onDocumentExpandedChange?: (documentId: string, expanded: boolean) => void;
|
|
272
|
+
renderItem?: (item: ContentDatabaseItem) => ReactNode;
|
|
273
|
+
hierarchyItems?: ContentDatabaseItem[];
|
|
274
|
+
scroll?: boolean;
|
|
180
275
|
loadingLabel: string;
|
|
181
276
|
noMatchesLabel: string;
|
|
182
277
|
clearLabel: string;
|
|
@@ -186,7 +281,14 @@ export function DatabaseSidebarView({
|
|
|
186
281
|
const [collapsedGroupIds, setCollapsedGroupIds] = useState<Set<string>>(
|
|
187
282
|
() => new Set(),
|
|
188
283
|
);
|
|
284
|
+
const [localExpandedDocumentIds, setLocalExpandedDocumentIds] = useState<
|
|
285
|
+
Set<string>
|
|
286
|
+
>(() => new Set());
|
|
189
287
|
const items = groups.flatMap((group) => group.items);
|
|
288
|
+
const itemTree =
|
|
289
|
+
!grouped && hierarchyItems
|
|
290
|
+
? databaseSidebarItemTree(items, hierarchyItems)
|
|
291
|
+
: null;
|
|
190
292
|
|
|
191
293
|
function setGroupOpen(groupId: string, open: boolean) {
|
|
192
294
|
setCollapsedGroupIds((current) => {
|
|
@@ -197,6 +299,52 @@ export function DatabaseSidebarView({
|
|
|
197
299
|
});
|
|
198
300
|
}
|
|
199
301
|
|
|
302
|
+
function setDocumentOpen(documentId: string, open: boolean) {
|
|
303
|
+
if (onDocumentExpandedChange) {
|
|
304
|
+
onDocumentExpandedChange(documentId, open);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
setLocalExpandedDocumentIds((current) => {
|
|
308
|
+
const next = new Set(current);
|
|
309
|
+
if (open) next.add(documentId);
|
|
310
|
+
else next.delete(documentId);
|
|
311
|
+
return next;
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function renderTreeNode(node: DatabaseSidebarItemTreeNode, depth: number) {
|
|
316
|
+
const open = (expandedDocumentIds ?? localExpandedDocumentIds).has(
|
|
317
|
+
node.item.document.id,
|
|
318
|
+
);
|
|
319
|
+
return (
|
|
320
|
+
<div key={node.item.id} className="min-w-0">
|
|
321
|
+
<DatabaseSidebarRow
|
|
322
|
+
item={node.item}
|
|
323
|
+
openPagesIn={openPagesIn}
|
|
324
|
+
onPreview={onPreview}
|
|
325
|
+
onOpenItem={onOpenItem}
|
|
326
|
+
active={node.item.document.id === activeDocumentId}
|
|
327
|
+
onCreateChildPage={onCreateChildPage}
|
|
328
|
+
onCreateChildDatabase={onCreateChildDatabase}
|
|
329
|
+
onDeleteItem={onDeleteItem}
|
|
330
|
+
onToggleFavorite={onToggleFavorite}
|
|
331
|
+
untitledLabel={untitledLabel}
|
|
332
|
+
depth={depth}
|
|
333
|
+
hasChildren={node.children.length > 0}
|
|
334
|
+
expanded={open}
|
|
335
|
+
onToggleExpanded={(nextOpen) =>
|
|
336
|
+
setDocumentOpen(node.item.document.id, nextOpen)
|
|
337
|
+
}
|
|
338
|
+
/>
|
|
339
|
+
{open && node.children.length > 0 ? (
|
|
340
|
+
<div>
|
|
341
|
+
{node.children.map((child) => renderTreeNode(child, depth + 1))}
|
|
342
|
+
</div>
|
|
343
|
+
) : null}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
200
348
|
if (isLoading) {
|
|
201
349
|
return (
|
|
202
350
|
<div className="flex h-16 items-center gap-2 px-2 text-sm text-muted-foreground">
|
|
@@ -222,56 +370,86 @@ export function DatabaseSidebarView({
|
|
|
222
370
|
);
|
|
223
371
|
}
|
|
224
372
|
|
|
225
|
-
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
373
|
+
const navigation = (
|
|
374
|
+
<nav
|
|
375
|
+
aria-label={navigationLabel}
|
|
376
|
+
className="grid min-w-0 gap-1 overflow-x-hidden p-1"
|
|
377
|
+
>
|
|
378
|
+
{grouped
|
|
379
|
+
? groups.map((group) => {
|
|
380
|
+
const open = !collapsedGroupIds.has(group.id);
|
|
381
|
+
return (
|
|
382
|
+
<Collapsible
|
|
383
|
+
key={group.id}
|
|
384
|
+
open={open}
|
|
385
|
+
onOpenChange={(nextOpen) => setGroupOpen(group.id, nextOpen)}
|
|
386
|
+
>
|
|
387
|
+
<CollapsibleTrigger className="group flex h-7 w-full items-center gap-1 rounded px-1.5 text-left text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
388
|
+
{open ? (
|
|
389
|
+
<IconChevronDown className="size-3.5 shrink-0" />
|
|
390
|
+
) : (
|
|
391
|
+
<IconChevronRight className="size-3.5 shrink-0" />
|
|
392
|
+
)}
|
|
393
|
+
<span className="min-w-0 flex-1 truncate">{group.label}</span>
|
|
394
|
+
<span className="text-[11px] font-normal text-muted-foreground/75">
|
|
395
|
+
{group.items.length}
|
|
396
|
+
</span>
|
|
397
|
+
</CollapsibleTrigger>
|
|
398
|
+
<CollapsibleContent className="grid gap-0.5 pl-2">
|
|
399
|
+
{group.items.map((item) =>
|
|
400
|
+
renderItem ? (
|
|
401
|
+
<div key={item.id} className="min-w-0">
|
|
402
|
+
{renderItem(item)}
|
|
403
|
+
</div>
|
|
240
404
|
) : (
|
|
241
|
-
<IconChevronRight className="size-3.5 shrink-0" />
|
|
242
|
-
)}
|
|
243
|
-
<span className="min-w-0 flex-1 truncate">
|
|
244
|
-
{group.label}
|
|
245
|
-
</span>
|
|
246
|
-
<span className="text-[11px] font-normal text-muted-foreground/75">
|
|
247
|
-
{group.items.length}
|
|
248
|
-
</span>
|
|
249
|
-
</CollapsibleTrigger>
|
|
250
|
-
<CollapsibleContent className="grid gap-0.5 pl-2">
|
|
251
|
-
{group.items.map((item) => (
|
|
252
405
|
<DatabaseSidebarRow
|
|
253
406
|
key={item.id}
|
|
254
407
|
item={item}
|
|
255
408
|
openPagesIn={openPagesIn}
|
|
256
409
|
onPreview={onPreview}
|
|
410
|
+
onOpenItem={onOpenItem}
|
|
411
|
+
active={item.document.id === activeDocumentId}
|
|
412
|
+
onCreateChildPage={onCreateChildPage}
|
|
413
|
+
onCreateChildDatabase={onCreateChildDatabase}
|
|
414
|
+
onDeleteItem={onDeleteItem}
|
|
415
|
+
onToggleFavorite={onToggleFavorite}
|
|
257
416
|
untitledLabel={untitledLabel}
|
|
258
417
|
/>
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
</
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
418
|
+
),
|
|
419
|
+
)}
|
|
420
|
+
</CollapsibleContent>
|
|
421
|
+
</Collapsible>
|
|
422
|
+
);
|
|
423
|
+
})
|
|
424
|
+
: itemTree
|
|
425
|
+
? itemTree.map((node) => renderTreeNode(node, 0))
|
|
426
|
+
: items.map((item) =>
|
|
427
|
+
renderItem ? (
|
|
428
|
+
<div key={item.id} className="min-w-0">
|
|
429
|
+
{renderItem(item)}
|
|
430
|
+
</div>
|
|
431
|
+
) : (
|
|
432
|
+
<DatabaseSidebarRow
|
|
433
|
+
key={item.id}
|
|
434
|
+
item={item}
|
|
435
|
+
openPagesIn={openPagesIn}
|
|
436
|
+
onPreview={onPreview}
|
|
437
|
+
onOpenItem={onOpenItem}
|
|
438
|
+
active={item.document.id === activeDocumentId}
|
|
439
|
+
onCreateChildPage={onCreateChildPage}
|
|
440
|
+
onCreateChildDatabase={onCreateChildDatabase}
|
|
441
|
+
onDeleteItem={onDeleteItem}
|
|
442
|
+
onToggleFavorite={onToggleFavorite}
|
|
443
|
+
untitledLabel={untitledLabel}
|
|
444
|
+
/>
|
|
445
|
+
),
|
|
446
|
+
)}
|
|
447
|
+
</nav>
|
|
448
|
+
);
|
|
449
|
+
return scroll ? (
|
|
450
|
+
<ScrollArea className="max-h-[32rem] w-full">{navigation}</ScrollArea>
|
|
451
|
+
) : (
|
|
452
|
+
navigation
|
|
275
453
|
);
|
|
276
454
|
}
|
|
277
455
|
|
|
@@ -279,16 +457,46 @@ function DatabaseSidebarRow({
|
|
|
279
457
|
item,
|
|
280
458
|
openPagesIn,
|
|
281
459
|
onPreview,
|
|
460
|
+
onOpenItem,
|
|
461
|
+
active,
|
|
462
|
+
onCreateChildPage,
|
|
463
|
+
onCreateChildDatabase,
|
|
464
|
+
onDeleteItem,
|
|
465
|
+
onToggleFavorite,
|
|
282
466
|
untitledLabel,
|
|
467
|
+
depth = 0,
|
|
468
|
+
hasChildren = false,
|
|
469
|
+
expanded = false,
|
|
470
|
+
onToggleExpanded,
|
|
283
471
|
}: {
|
|
284
472
|
item: ContentDatabaseItem;
|
|
285
473
|
openPagesIn: ContentDatabaseOpenPagesIn;
|
|
286
474
|
onPreview: (item: ContentDatabaseItem) => void;
|
|
475
|
+
onOpenItem?: (item: ContentDatabaseItem) => boolean;
|
|
476
|
+
active: boolean;
|
|
477
|
+
onCreateChildPage?: (item: ContentDatabaseItem) => void;
|
|
478
|
+
onCreateChildDatabase?: (item: ContentDatabaseItem) => void;
|
|
479
|
+
onDeleteItem?: (item: ContentDatabaseItem) => void;
|
|
480
|
+
onToggleFavorite?: (item: ContentDatabaseItem) => void;
|
|
287
481
|
untitledLabel: string;
|
|
482
|
+
depth?: number;
|
|
483
|
+
hasChildren?: boolean;
|
|
484
|
+
expanded?: boolean;
|
|
485
|
+
onToggleExpanded?: (open: boolean) => void;
|
|
288
486
|
}) {
|
|
487
|
+
const t = useT();
|
|
488
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
489
|
+
const canEdit = item.document.canEdit !== false;
|
|
490
|
+
const canManage =
|
|
491
|
+
item.document.canManage === true ||
|
|
492
|
+
item.document.accessRole === "owner" ||
|
|
493
|
+
item.document.accessRole === "admin";
|
|
494
|
+
const canCreateChild = canEdit && Boolean(onCreateChildPage);
|
|
495
|
+
const hasMenuActions =
|
|
496
|
+
(canEdit && Boolean(onToggleFavorite)) ||
|
|
497
|
+
(canManage && Boolean(onDeleteItem));
|
|
289
498
|
function handleClick(event: MouseEvent<HTMLAnchorElement>) {
|
|
290
499
|
if (
|
|
291
|
-
openPagesIn !== "preview" ||
|
|
292
500
|
event.defaultPrevented ||
|
|
293
501
|
event.button !== 0 ||
|
|
294
502
|
event.metaKey ||
|
|
@@ -298,33 +506,226 @@ function DatabaseSidebarRow({
|
|
|
298
506
|
) {
|
|
299
507
|
return;
|
|
300
508
|
}
|
|
509
|
+
if (onOpenItem?.(item)) {
|
|
510
|
+
event.preventDefault();
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (openPagesIn !== "preview") return;
|
|
301
514
|
event.preventDefault();
|
|
302
515
|
onPreview(item);
|
|
303
516
|
}
|
|
304
517
|
|
|
518
|
+
const title = item.document.title || untitledLabel;
|
|
519
|
+
|
|
305
520
|
return (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
521
|
+
<>
|
|
522
|
+
<div className="group relative min-w-0">
|
|
523
|
+
{hasChildren ? (
|
|
524
|
+
<button
|
|
525
|
+
type="button"
|
|
526
|
+
className="pointer-events-none absolute top-0 z-10 flex size-7 items-center justify-center rounded text-muted-foreground opacity-0 hover:bg-muted hover:text-foreground group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
527
|
+
style={{
|
|
528
|
+
insetInlineStart: `${databaseSidebarRowIndent(depth, hasChildren)}px`,
|
|
529
|
+
}}
|
|
530
|
+
aria-label={`${expanded ? "Collapse" : "Expand"} ${title}`}
|
|
531
|
+
aria-expanded={expanded}
|
|
532
|
+
onPointerUp={(event) => event.currentTarget.blur()}
|
|
533
|
+
onClick={() => onToggleExpanded?.(!expanded)}
|
|
534
|
+
>
|
|
535
|
+
<IconChevronRight
|
|
536
|
+
className={cn(
|
|
537
|
+
"size-3.5 transition-transform",
|
|
538
|
+
expanded && "rotate-90",
|
|
539
|
+
)}
|
|
540
|
+
/>
|
|
541
|
+
</button>
|
|
542
|
+
) : null}
|
|
543
|
+
<Link
|
|
544
|
+
to={`/page/${item.document.id}`}
|
|
545
|
+
className={cn(
|
|
546
|
+
"flex h-7 min-w-0 items-center gap-1.5 rounded pe-1.5 text-sm text-foreground/85 hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
547
|
+
active && "font-semibold text-foreground",
|
|
548
|
+
)}
|
|
549
|
+
style={{
|
|
550
|
+
paddingInlineStart: `${databaseSidebarRowIndent(depth, hasChildren)}px`,
|
|
551
|
+
}}
|
|
552
|
+
onClick={handleClick}
|
|
553
|
+
onPointerUp={(event) => event.currentTarget.blur()}
|
|
554
|
+
aria-current={active ? "page" : undefined}
|
|
555
|
+
>
|
|
556
|
+
<span
|
|
557
|
+
className={cn(
|
|
558
|
+
"flex size-7 shrink-0 items-center justify-center",
|
|
559
|
+
hasChildren &&
|
|
560
|
+
"group-hover:opacity-0 group-focus-within:opacity-0",
|
|
561
|
+
)}
|
|
562
|
+
aria-hidden="true"
|
|
563
|
+
>
|
|
564
|
+
{item.document.icon ? (
|
|
565
|
+
<span className="text-sm leading-none">{item.document.icon}</span>
|
|
566
|
+
) : (
|
|
567
|
+
<IconFileText className="size-3.5 text-muted-foreground" />
|
|
568
|
+
)}
|
|
569
|
+
</span>
|
|
570
|
+
<span
|
|
571
|
+
className={cn(
|
|
572
|
+
"min-w-0 flex-1 truncate",
|
|
573
|
+
(hasMenuActions || canCreateChild) && "pe-12",
|
|
574
|
+
)}
|
|
575
|
+
>
|
|
576
|
+
{title}
|
|
577
|
+
</span>
|
|
578
|
+
</Link>
|
|
579
|
+
|
|
580
|
+
{(hasMenuActions || canCreateChild) && (
|
|
581
|
+
<div className="pointer-events-none absolute end-1 top-1/2 z-10 flex -translate-y-1/2 items-center gap-0.5 rounded bg-sidebar px-0.5 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100">
|
|
582
|
+
{hasMenuActions && (
|
|
583
|
+
<DropdownMenu>
|
|
584
|
+
<DropdownMenuTrigger asChild>
|
|
585
|
+
<button
|
|
586
|
+
type="button"
|
|
587
|
+
className="flex size-6 items-center justify-center rounded text-foreground hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
588
|
+
aria-label={`More actions for ${title}`}
|
|
589
|
+
>
|
|
590
|
+
<IconDots size={14} />
|
|
591
|
+
</button>
|
|
592
|
+
</DropdownMenuTrigger>
|
|
593
|
+
<DropdownMenuContent align="start" className="w-48">
|
|
594
|
+
{canEdit && onToggleFavorite ? (
|
|
595
|
+
<DropdownMenuItem onSelect={() => onToggleFavorite(item)}>
|
|
596
|
+
<IconStar
|
|
597
|
+
className={cn(
|
|
598
|
+
"me-2 size-4",
|
|
599
|
+
item.document.isFavorite && "fill-current",
|
|
600
|
+
)}
|
|
601
|
+
/>
|
|
602
|
+
{item.document.isFavorite
|
|
603
|
+
? "Remove from favorites"
|
|
604
|
+
: "Add to favorites"}
|
|
605
|
+
</DropdownMenuItem>
|
|
606
|
+
) : null}
|
|
607
|
+
{canEdit && onToggleFavorite && canManage && onDeleteItem ? (
|
|
608
|
+
<DropdownMenuSeparator />
|
|
609
|
+
) : null}
|
|
610
|
+
{canManage && onDeleteItem ? (
|
|
611
|
+
<DropdownMenuItem
|
|
612
|
+
className="text-destructive focus:text-destructive"
|
|
613
|
+
onSelect={() => setDeleteDialogOpen(true)}
|
|
614
|
+
>
|
|
615
|
+
<IconTrash className="me-2 size-4" />
|
|
616
|
+
{t("database.delete")}
|
|
617
|
+
</DropdownMenuItem>
|
|
618
|
+
) : null}
|
|
619
|
+
</DropdownMenuContent>
|
|
620
|
+
</DropdownMenu>
|
|
621
|
+
)}
|
|
622
|
+
|
|
623
|
+
{canCreateChild && (
|
|
624
|
+
<DropdownMenu>
|
|
625
|
+
<Tooltip>
|
|
626
|
+
<TooltipTrigger asChild>
|
|
627
|
+
<DropdownMenuTrigger asChild>
|
|
628
|
+
<button
|
|
629
|
+
type="button"
|
|
630
|
+
className="flex size-6 items-center justify-center rounded text-foreground hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
631
|
+
aria-label={t("sidebar.addChildTo", { title })}
|
|
632
|
+
>
|
|
633
|
+
<IconPlus size={14} />
|
|
634
|
+
</button>
|
|
635
|
+
</DropdownMenuTrigger>
|
|
636
|
+
</TooltipTrigger>
|
|
637
|
+
<TooltipContent>{t("sidebar.addChild")}</TooltipContent>
|
|
638
|
+
</Tooltip>
|
|
639
|
+
<DropdownMenuContent align="start" className="w-44">
|
|
640
|
+
<DropdownMenuItem onSelect={() => onCreateChildPage?.(item)}>
|
|
641
|
+
<IconFileText className="me-2 size-4" />
|
|
642
|
+
{t("sidebar.page")}
|
|
643
|
+
</DropdownMenuItem>
|
|
644
|
+
{onCreateChildDatabase ? (
|
|
645
|
+
<DropdownMenuItem
|
|
646
|
+
onSelect={() => onCreateChildDatabase(item)}
|
|
647
|
+
>
|
|
648
|
+
<IconDatabase className="me-2 size-4" />
|
|
649
|
+
{t("sidebar.database")}
|
|
650
|
+
</DropdownMenuItem>
|
|
651
|
+
) : null}
|
|
652
|
+
</DropdownMenuContent>
|
|
653
|
+
</DropdownMenu>
|
|
654
|
+
)}
|
|
655
|
+
</div>
|
|
656
|
+
)}
|
|
657
|
+
</div>
|
|
658
|
+
|
|
659
|
+
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
660
|
+
<AlertDialogContent>
|
|
661
|
+
<AlertDialogHeader>
|
|
662
|
+
<AlertDialogTitle>
|
|
663
|
+
{t("sidebar.deletePageQuestion")}
|
|
664
|
+
</AlertDialogTitle>
|
|
665
|
+
<AlertDialogDescription>
|
|
666
|
+
{t("sidebar.deletePageDescription", { title })}
|
|
667
|
+
</AlertDialogDescription>
|
|
668
|
+
</AlertDialogHeader>
|
|
669
|
+
<AlertDialogFooter>
|
|
670
|
+
<AlertDialogCancel>{t("comments.cancel")}</AlertDialogCancel>
|
|
671
|
+
<AlertDialogAction
|
|
672
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
673
|
+
onClick={() => onDeleteItem?.(item)}
|
|
674
|
+
>
|
|
675
|
+
{t("database.delete")}
|
|
676
|
+
</AlertDialogAction>
|
|
677
|
+
</AlertDialogFooter>
|
|
678
|
+
</AlertDialogContent>
|
|
679
|
+
</AlertDialog>
|
|
680
|
+
</>
|
|
325
681
|
);
|
|
326
682
|
}
|
|
327
683
|
|
|
328
684
|
export function databaseSidebarRows(groups: DatabaseBoardGroup[]) {
|
|
329
685
|
return groups.flatMap((group) => group.items);
|
|
330
686
|
}
|
|
687
|
+
|
|
688
|
+
export interface DatabaseSidebarItemTreeNode {
|
|
689
|
+
item: ContentDatabaseItem;
|
|
690
|
+
children: DatabaseSidebarItemTreeNode[];
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export function databaseSidebarRowIndent(depth: number, _hasChildren: boolean) {
|
|
694
|
+
return depth * 18;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export function databaseSidebarItemTree(
|
|
698
|
+
rootItems: ContentDatabaseItem[],
|
|
699
|
+
allItems: ContentDatabaseItem[],
|
|
700
|
+
): DatabaseSidebarItemTreeNode[] {
|
|
701
|
+
const childrenByParentId = new Map<string, ContentDatabaseItem[]>();
|
|
702
|
+
for (const item of allItems) {
|
|
703
|
+
const parentId = item.document.parentId;
|
|
704
|
+
if (!parentId) continue;
|
|
705
|
+
childrenByParentId.set(parentId, [
|
|
706
|
+
...(childrenByParentId.get(parentId) ?? []),
|
|
707
|
+
item,
|
|
708
|
+
]);
|
|
709
|
+
}
|
|
710
|
+
const emitted = new Set<string>();
|
|
711
|
+
const visit = (
|
|
712
|
+
item: ContentDatabaseItem,
|
|
713
|
+
ancestors: Set<string>,
|
|
714
|
+
): DatabaseSidebarItemTreeNode | null => {
|
|
715
|
+
const documentId = item.document.id;
|
|
716
|
+
if (emitted.has(documentId) || ancestors.has(documentId)) return null;
|
|
717
|
+
emitted.add(documentId);
|
|
718
|
+
const nextAncestors = new Set(ancestors).add(documentId);
|
|
719
|
+
return {
|
|
720
|
+
item,
|
|
721
|
+
children: (childrenByParentId.get(documentId) ?? []).flatMap((child) => {
|
|
722
|
+
const node = visit(child, nextAncestors);
|
|
723
|
+
return node ? [node] : [];
|
|
724
|
+
}),
|
|
725
|
+
};
|
|
726
|
+
};
|
|
727
|
+
return rootItems.flatMap((item) => {
|
|
728
|
+
const node = visit(item, new Set());
|
|
729
|
+
return node ? [node] : [];
|
|
730
|
+
});
|
|
731
|
+
}
|