@agent-native/core 0.102.2 → 0.103.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.
Files changed (205) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/local-file-mode.mdx +99 -489
  4. package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  5. package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
  6. package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  7. package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
  8. package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  9. package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
  10. package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  11. package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
  12. package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  13. package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
  14. package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  15. package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
  16. package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  17. package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
  18. package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  19. package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
  20. package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  21. package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
  22. package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  23. package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
  24. package/corpus/core/docs/content/template-content.mdx +26 -42
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/content-local.ts +86 -18
  27. package/corpus/core/src/cli/index.ts +2 -2
  28. package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
  29. package/corpus/core/src/cli/skills.ts +47 -13
  30. package/corpus/core/src/local-artifacts/index.ts +65 -0
  31. package/corpus/core/src/vite/client.ts +2 -0
  32. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  33. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  34. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  35. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  36. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  37. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  38. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  39. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
  40. package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
  41. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  42. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  43. package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
  44. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
  45. package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
  46. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
  47. package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
  48. package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
  49. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
  50. package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
  51. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
  52. package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
  53. package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
  54. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
  55. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  56. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  57. package/corpus/templates/content/AGENTS.md +25 -30
  58. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  59. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  60. package/corpus/templates/content/actions/_content-files.ts +338 -0
  61. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  62. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  63. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  64. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  65. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  66. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  67. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  68. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  69. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  70. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  71. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  72. package/corpus/templates/content/actions/create-document.ts +65 -55
  73. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  74. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  75. package/corpus/templates/content/actions/delete-document.ts +7 -11
  76. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  77. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  78. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  79. package/corpus/templates/content/actions/edit-document.ts +2 -28
  80. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  81. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  82. package/corpus/templates/content/actions/get-document.ts +0 -14
  83. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  84. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  85. package/corpus/templates/content/actions/list-documents.ts +12 -9
  86. package/corpus/templates/content/actions/move-document.ts +3 -14
  87. package/corpus/templates/content/actions/pull-document.ts +0 -23
  88. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  89. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  90. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  91. package/corpus/templates/content/actions/search-documents.ts +0 -34
  92. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  93. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  94. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  95. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  96. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  97. package/corpus/templates/content/actions/update-document.ts +0 -15
  98. package/corpus/templates/content/actions/view-screen.ts +2 -50
  99. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  100. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  101. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  102. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  103. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  104. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  105. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  106. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  107. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  109. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  111. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  112. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  113. package/corpus/templates/content/app/i18n-data.ts +75 -0
  114. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  115. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +2 -1
  119. package/corpus/templates/content/parity/matrix.ts +37 -1
  120. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  121. package/corpus/templates/content/server/db/schema.ts +113 -40
  122. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  123. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  124. package/corpus/templates/content/server/plugins/db.ts +153 -0
  125. package/corpus/templates/content/shared/api.ts +17 -3
  126. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  127. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  128. package/corpus/templates/design/AGENTS.md +6 -1
  129. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  130. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  131. package/corpus/templates/design/actions/navigate.ts +2 -2
  132. package/corpus/templates/design/actions/view-screen.ts +15 -2
  133. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  134. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  135. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  136. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  137. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  138. package/corpus/templates/design/app/i18n-data.ts +21 -2
  139. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  140. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  141. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  142. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  143. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  144. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  145. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  146. package/corpus/templates/mail/AGENTS.md +23 -18
  147. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  148. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  149. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  150. package/corpus/templates/mail/actions/move-email.ts +30 -24
  151. package/corpus/templates/mail/actions/send-email.ts +73 -68
  152. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  153. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  154. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  155. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  156. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  157. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  158. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  159. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  160. package/dist/cli/content-local.d.ts +1 -0
  161. package/dist/cli/content-local.d.ts.map +1 -1
  162. package/dist/cli/content-local.js +75 -17
  163. package/dist/cli/content-local.js.map +1 -1
  164. package/dist/cli/index.js +2 -2
  165. package/dist/cli/index.js.map +1 -1
  166. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  167. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  168. package/dist/cli/skills-content/content-skill.js +36 -25
  169. package/dist/cli/skills-content/content-skill.js.map +1 -1
  170. package/dist/cli/skills.d.ts.map +1 -1
  171. package/dist/cli/skills.js +47 -14
  172. package/dist/cli/skills.js.map +1 -1
  173. package/dist/collab/struct-routes.d.ts +1 -1
  174. package/dist/local-artifacts/index.d.ts +10 -0
  175. package/dist/local-artifacts/index.d.ts.map +1 -1
  176. package/dist/local-artifacts/index.js +39 -0
  177. package/dist/local-artifacts/index.js.map +1 -1
  178. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  179. package/dist/vite/client.d.ts.map +1 -1
  180. package/dist/vite/client.js +2 -0
  181. package/dist/vite/client.js.map +1 -1
  182. package/docs/content/local-file-mode.mdx +99 -489
  183. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  184. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  185. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  186. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  187. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  188. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  189. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  190. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  191. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  192. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  193. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  194. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  195. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  196. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  197. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  198. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  199. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  200. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  201. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  202. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  203. package/docs/content/template-content.mdx +26 -42
  204. package/package.json +1 -1
  205. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -0,0 +1,330 @@
1
+ import { Button } from "@agent-native/toolkit/ui/button";
2
+ import {
3
+ Collapsible,
4
+ CollapsibleContent,
5
+ CollapsibleTrigger,
6
+ } from "@agent-native/toolkit/ui/collapsible";
7
+ import { ScrollArea } from "@agent-native/toolkit/ui/scroll-area";
8
+ import type {
9
+ ContentDatabaseItem,
10
+ ContentDatabaseOpenPagesIn,
11
+ ContentDatabasePersonalViewOverrides,
12
+ ContentDatabaseResponse,
13
+ ContentDatabaseViewConfig,
14
+ } from "@shared/api";
15
+ import {
16
+ IconChevronDown,
17
+ IconChevronRight,
18
+ IconFileText,
19
+ IconLoader2,
20
+ } from "@tabler/icons-react";
21
+ import { useEffect, useState, type MouseEvent } from "react";
22
+ import { Link } from "react-router";
23
+
24
+ import { cn } from "@/lib/utils";
25
+
26
+ import { applyDatabaseView } from "./filter-sort";
27
+ import {
28
+ databaseViewGroupingProperty,
29
+ databaseViewItemGroups,
30
+ databaseVisibleGroups,
31
+ } from "./grouping";
32
+ import type { DatabaseBoardGroup } from "./types";
33
+ import {
34
+ activeDatabaseView,
35
+ defaultDatabaseViewConfig,
36
+ normalizeClientDatabaseViewConfig,
37
+ } from "./view-config";
38
+
39
+ function applyPersonalSidebarViewOverrides(
40
+ savedViewConfig: ContentDatabaseViewConfig,
41
+ overrides: ContentDatabasePersonalViewOverrides | null | undefined,
42
+ ) {
43
+ const saved = normalizeClientDatabaseViewConfig(savedViewConfig);
44
+ if (!overrides) return saved;
45
+ const overridesByViewId = new Map(
46
+ overrides.views.map((view) => [view.id, view]),
47
+ );
48
+ return normalizeClientDatabaseViewConfig({
49
+ ...saved,
50
+ activeViewId: saved.views.some((view) => view.id === overrides.activeViewId)
51
+ ? overrides.activeViewId
52
+ : saved.activeViewId,
53
+ views: saved.views.map((view) => {
54
+ const override = overridesByViewId.get(view.id);
55
+ return override
56
+ ? {
57
+ ...view,
58
+ sorts: override.sorts,
59
+ filters: override.filters,
60
+ filterMode: override.filterMode,
61
+ }
62
+ : view;
63
+ }),
64
+ });
65
+ }
66
+
67
+ export function ContentFilesSidebarView({
68
+ data,
69
+ overrides,
70
+ isLoading,
71
+ labels,
72
+ onSelectView,
73
+ }: {
74
+ data: ContentDatabaseResponse | undefined;
75
+ overrides: ContentDatabasePersonalViewOverrides | null | undefined;
76
+ isLoading: boolean;
77
+ onSelectView?: (viewId: string) => void;
78
+ labels: Omit<
79
+ Parameters<typeof DatabaseSidebarView>[0],
80
+ | "groups"
81
+ | "grouped"
82
+ | "isLoading"
83
+ | "hasActiveConstraints"
84
+ | "openPagesIn"
85
+ | "onClearResultConstraints"
86
+ | "onPreview"
87
+ >;
88
+ }) {
89
+ const viewConfig = applyPersonalSidebarViewOverrides(
90
+ data?.database.viewConfig ?? defaultDatabaseViewConfig(),
91
+ overrides,
92
+ );
93
+ const [selectedViewId, setSelectedViewId] = useState(
94
+ () => viewConfig.activeViewId,
95
+ );
96
+ useEffect(() => {
97
+ setSelectedViewId(viewConfig.activeViewId);
98
+ }, [viewConfig.activeViewId]);
99
+ const activeView =
100
+ viewConfig.views.find((view) => view.id === selectedViewId) ??
101
+ activeDatabaseView(viewConfig);
102
+ const items = data
103
+ ? applyDatabaseView(
104
+ data.items,
105
+ data.properties,
106
+ "",
107
+ activeView.filters,
108
+ activeView.sorts,
109
+ activeView.filterMode ?? "and",
110
+ )
111
+ : [];
112
+ const groups = databaseVisibleGroups(
113
+ databaseViewItemGroups(
114
+ items,
115
+ data?.properties ?? [],
116
+ activeView.groupByPropertyId,
117
+ ),
118
+ activeView.hideEmptyGroups === true,
119
+ );
120
+ return (
121
+ <div className="min-w-0">
122
+ {viewConfig.views.length > 1 && (
123
+ <div className="flex min-w-0 gap-1 overflow-x-auto px-1 pb-1">
124
+ {viewConfig.views.map((view) => (
125
+ <button
126
+ key={view.id}
127
+ type="button"
128
+ className={cn(
129
+ "shrink-0 rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
130
+ activeView.id === view.id &&
131
+ "bg-muted font-medium text-foreground",
132
+ )}
133
+ onClick={() => {
134
+ setSelectedViewId(view.id);
135
+ onSelectView?.(view.id);
136
+ }}
137
+ >
138
+ {view.name}
139
+ </button>
140
+ ))}
141
+ </div>
142
+ )}
143
+ <DatabaseSidebarView
144
+ {...labels}
145
+ groups={groups}
146
+ grouped={
147
+ !!databaseViewGroupingProperty(activeView, data?.properties ?? [])
148
+ }
149
+ isLoading={isLoading}
150
+ hasActiveConstraints={false}
151
+ openPagesIn="full_page"
152
+ onClearResultConstraints={() => {}}
153
+ onPreview={() => {}}
154
+ />
155
+ </div>
156
+ );
157
+ }
158
+
159
+ export function DatabaseSidebarView({
160
+ groups,
161
+ grouped,
162
+ isLoading,
163
+ hasActiveConstraints,
164
+ openPagesIn,
165
+ onClearResultConstraints,
166
+ onPreview,
167
+ loadingLabel,
168
+ noMatchesLabel,
169
+ clearLabel,
170
+ navigationLabel,
171
+ untitledLabel,
172
+ }: {
173
+ groups: DatabaseBoardGroup[];
174
+ grouped: boolean;
175
+ isLoading: boolean;
176
+ hasActiveConstraints: boolean;
177
+ openPagesIn: ContentDatabaseOpenPagesIn;
178
+ onClearResultConstraints: () => void;
179
+ onPreview: (item: ContentDatabaseItem) => void;
180
+ loadingLabel: string;
181
+ noMatchesLabel: string;
182
+ clearLabel: string;
183
+ navigationLabel: string;
184
+ untitledLabel: string;
185
+ }) {
186
+ const [collapsedGroupIds, setCollapsedGroupIds] = useState<Set<string>>(
187
+ () => new Set(),
188
+ );
189
+ const items = groups.flatMap((group) => group.items);
190
+
191
+ function setGroupOpen(groupId: string, open: boolean) {
192
+ setCollapsedGroupIds((current) => {
193
+ const next = new Set(current);
194
+ if (open) next.delete(groupId);
195
+ else next.add(groupId);
196
+ return next;
197
+ });
198
+ }
199
+
200
+ if (isLoading) {
201
+ return (
202
+ <div className="flex h-16 items-center gap-2 px-2 text-sm text-muted-foreground">
203
+ <IconLoader2 className="size-4 animate-spin" />
204
+ {loadingLabel}
205
+ </div>
206
+ );
207
+ }
208
+
209
+ if (items.length === 0 && hasActiveConstraints) {
210
+ return (
211
+ <div className="flex min-h-16 flex-wrap items-center justify-between gap-2 px-2 py-3 text-sm text-muted-foreground">
212
+ <span>{noMatchesLabel}</span>
213
+ <Button
214
+ type="button"
215
+ size="sm"
216
+ variant="ghost"
217
+ onClick={onClearResultConstraints}
218
+ >
219
+ {clearLabel}
220
+ </Button>
221
+ </div>
222
+ );
223
+ }
224
+
225
+ return (
226
+ <ScrollArea className="max-h-[32rem] w-full">
227
+ <nav aria-label={navigationLabel} className="grid gap-1 p-1">
228
+ {grouped
229
+ ? groups.map((group) => {
230
+ const open = !collapsedGroupIds.has(group.id);
231
+ return (
232
+ <Collapsible
233
+ key={group.id}
234
+ open={open}
235
+ onOpenChange={(nextOpen) => setGroupOpen(group.id, nextOpen)}
236
+ >
237
+ <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">
238
+ {open ? (
239
+ <IconChevronDown className="size-3.5 shrink-0" />
240
+ ) : (
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
+ <DatabaseSidebarRow
253
+ key={item.id}
254
+ item={item}
255
+ openPagesIn={openPagesIn}
256
+ onPreview={onPreview}
257
+ untitledLabel={untitledLabel}
258
+ />
259
+ ))}
260
+ </CollapsibleContent>
261
+ </Collapsible>
262
+ );
263
+ })
264
+ : items.map((item) => (
265
+ <DatabaseSidebarRow
266
+ key={item.id}
267
+ item={item}
268
+ openPagesIn={openPagesIn}
269
+ onPreview={onPreview}
270
+ untitledLabel={untitledLabel}
271
+ />
272
+ ))}
273
+ </nav>
274
+ </ScrollArea>
275
+ );
276
+ }
277
+
278
+ function DatabaseSidebarRow({
279
+ item,
280
+ openPagesIn,
281
+ onPreview,
282
+ untitledLabel,
283
+ }: {
284
+ item: ContentDatabaseItem;
285
+ openPagesIn: ContentDatabaseOpenPagesIn;
286
+ onPreview: (item: ContentDatabaseItem) => void;
287
+ untitledLabel: string;
288
+ }) {
289
+ function handleClick(event: MouseEvent<HTMLAnchorElement>) {
290
+ if (
291
+ openPagesIn !== "preview" ||
292
+ event.defaultPrevented ||
293
+ event.button !== 0 ||
294
+ event.metaKey ||
295
+ event.altKey ||
296
+ event.ctrlKey ||
297
+ event.shiftKey
298
+ ) {
299
+ return;
300
+ }
301
+ event.preventDefault();
302
+ onPreview(item);
303
+ }
304
+
305
+ return (
306
+ <Link
307
+ to={`/page/${item.document.id}`}
308
+ className={cn(
309
+ "flex h-7 min-w-0 items-center gap-1.5 rounded px-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",
310
+ item.document.icon ? "pl-1" : "pl-1.5",
311
+ )}
312
+ onClick={handleClick}
313
+ >
314
+ {item.document.icon ? (
315
+ <span aria-hidden="true" className="shrink-0 text-sm leading-none">
316
+ {item.document.icon}
317
+ </span>
318
+ ) : (
319
+ <IconFileText className="size-3.5 shrink-0 text-muted-foreground" />
320
+ )}
321
+ <span className="min-w-0 flex-1 truncate">
322
+ {item.document.title || untitledLabel}
323
+ </span>
324
+ </Link>
325
+ );
326
+ }
327
+
328
+ export function databaseSidebarRows(groups: DatabaseBoardGroup[]) {
329
+ return groups.flatMap((group) => group.items);
330
+ }
@@ -42,6 +42,7 @@ export const DATABASE_VIEW_TYPES: ContentDatabaseViewType[] = [
42
42
  "list",
43
43
  "timeline",
44
44
  "calendar",
45
+ "sidebar",
45
46
  ];
46
47
 
47
48
  export const DATABASE_OPEN_PAGES_IN: ContentDatabaseOpenPagesIn[] = [
@@ -299,7 +299,8 @@ function normalizeClientDatabaseView(
299
299
  value.type === "gallery" ||
300
300
  value.type === "calendar" ||
301
301
  value.type === "timeline" ||
302
- value.type === "form"
302
+ value.type === "form" ||
303
+ value.type === "sidebar"
303
304
  ? value.type
304
305
  : "table";
305
306
  return createDatabaseView(
@@ -460,6 +461,7 @@ export function databaseViewDefaultName(type: ContentDatabaseViewType) {
460
461
  if (type === "calendar") return "Calendar";
461
462
  if (type === "timeline") return "Timeline";
462
463
  if (type === "form") return "Form";
464
+ if (type === "sidebar") return "Sidebar";
463
465
  return "Table";
464
466
  }
465
467
 
@@ -24,6 +24,7 @@ const SIDEBAR_WIDTH_KEY = "sidebar-width";
24
24
  const DEFAULT_SIDEBAR_WIDTH = 240;
25
25
  const MIN_SIDEBAR_WIDTH = 240;
26
26
  const MAX_SIDEBAR_WIDTH = 480;
27
+ const NARROW_DESKTOP_QUERY = "(max-width: 1099px)";
27
28
 
28
29
  // Routes whose page renders its own custom toolbar (with AgentToggleButton).
29
30
  // Layout still mounts Sidebar + AgentSidebar, but skips its own Header so
@@ -41,6 +42,22 @@ function loadSidebarWidth(): number {
41
42
  return DEFAULT_SIDEBAR_WIDTH;
42
43
  }
43
44
 
45
+ function useIsNarrowDesktop() {
46
+ const [isNarrow, setIsNarrow] = useState(
47
+ () =>
48
+ typeof window !== "undefined" &&
49
+ window.matchMedia(NARROW_DESKTOP_QUERY).matches,
50
+ );
51
+ useEffect(() => {
52
+ const media = window.matchMedia(NARROW_DESKTOP_QUERY);
53
+ const update = () => setIsNarrow(media.matches);
54
+ update();
55
+ media.addEventListener("change", update);
56
+ return () => media.removeEventListener("change", update);
57
+ }, []);
58
+ return isNarrow;
59
+ }
60
+
44
61
  export function documentPageIdFromPathname(pathname: string) {
45
62
  return pathname.match(/^\/page\/(.+)/)?.[1] ?? null;
46
63
  }
@@ -72,6 +89,7 @@ export function Layout({ children }: LayoutProps) {
72
89
  [activeDocumentId],
73
90
  );
74
91
  const isMobile = useIsMobile();
92
+ const isNarrowDesktop = useIsNarrowDesktop();
75
93
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
76
94
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
77
95
  const [sidebarWidth, setSidebarWidth] = useState(loadSidebarWidth);
@@ -105,6 +123,12 @@ export function Layout({ children }: LayoutProps) {
105
123
  return () => window.removeEventListener("keydown", onKeyDown);
106
124
  }, [createPage]);
107
125
 
126
+ useEffect(() => {
127
+ if (isNarrowDesktop) {
128
+ window.dispatchEvent(new Event("agent-panel:close"));
129
+ }
130
+ }, [isNarrowDesktop]);
131
+
108
132
  const mobileSidebarTrigger = isMobile ? (
109
133
  <button
110
134
  type="button"
@@ -164,7 +188,7 @@ export function Layout({ children }: LayoutProps) {
164
188
  )}
165
189
  <AgentSidebar
166
190
  position="right"
167
- defaultOpen={!isMobile}
191
+ defaultOpen={!isMobile && !isNarrowDesktop}
168
192
  agentPageHref="/agent"
169
193
  emptyStateText={t("chat.emptyState")}
170
194
  suggestions={[