@agent-native/core 0.102.2 → 0.103.0

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 (206) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -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/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  32. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  33. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  34. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  35. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  36. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  37. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  38. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
  39. package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
  40. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  41. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  42. package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
  43. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
  44. package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
  45. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
  46. package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
  47. package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
  48. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
  49. package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
  50. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
  51. package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
  52. package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
  53. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
  54. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  55. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  56. package/corpus/templates/content/AGENTS.md +25 -30
  57. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  58. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  59. package/corpus/templates/content/actions/_content-files.ts +338 -0
  60. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  61. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  62. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  63. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  64. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  65. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  66. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  67. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  68. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  69. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  70. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  71. package/corpus/templates/content/actions/create-document.ts +65 -55
  72. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  73. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  74. package/corpus/templates/content/actions/delete-document.ts +7 -11
  75. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  76. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  77. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  78. package/corpus/templates/content/actions/edit-document.ts +2 -28
  79. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  80. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  81. package/corpus/templates/content/actions/get-document.ts +0 -14
  82. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  83. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  84. package/corpus/templates/content/actions/list-documents.ts +12 -9
  85. package/corpus/templates/content/actions/move-document.ts +3 -14
  86. package/corpus/templates/content/actions/pull-document.ts +0 -23
  87. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  88. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  89. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  90. package/corpus/templates/content/actions/search-documents.ts +0 -34
  91. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  92. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  93. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  94. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  95. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  96. package/corpus/templates/content/actions/update-document.ts +0 -15
  97. package/corpus/templates/content/actions/view-screen.ts +2 -50
  98. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  99. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  100. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  101. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  102. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  103. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  104. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  105. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  106. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  107. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  108. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  109. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  110. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  111. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  112. package/corpus/templates/content/app/i18n-data.ts +75 -0
  113. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  114. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  115. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  117. package/corpus/templates/content/parity/matrix.md +2 -1
  118. package/corpus/templates/content/parity/matrix.ts +37 -1
  119. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  120. package/corpus/templates/content/server/db/schema.ts +113 -40
  121. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  122. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  123. package/corpus/templates/content/server/plugins/db.ts +153 -0
  124. package/corpus/templates/content/shared/api.ts +17 -3
  125. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  126. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  127. package/corpus/templates/design/AGENTS.md +6 -1
  128. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  129. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  130. package/corpus/templates/design/actions/navigate.ts +2 -2
  131. package/corpus/templates/design/actions/view-screen.ts +15 -2
  132. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  133. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  134. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  135. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  136. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  137. package/corpus/templates/design/app/i18n-data.ts +21 -2
  138. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  139. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  140. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  141. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  142. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  143. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  144. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  145. package/corpus/templates/mail/AGENTS.md +23 -18
  146. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  147. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  148. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  149. package/corpus/templates/mail/actions/move-email.ts +30 -24
  150. package/corpus/templates/mail/actions/send-email.ts +73 -68
  151. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  152. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  153. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  154. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  155. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  156. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  157. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  158. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  159. package/dist/cli/content-local.d.ts +1 -0
  160. package/dist/cli/content-local.d.ts.map +1 -1
  161. package/dist/cli/content-local.js +75 -17
  162. package/dist/cli/content-local.js.map +1 -1
  163. package/dist/cli/index.js +2 -2
  164. package/dist/cli/index.js.map +1 -1
  165. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  166. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  167. package/dist/cli/skills-content/content-skill.js +36 -25
  168. package/dist/cli/skills-content/content-skill.js.map +1 -1
  169. package/dist/cli/skills.d.ts.map +1 -1
  170. package/dist/cli/skills.js +47 -14
  171. package/dist/cli/skills.js.map +1 -1
  172. package/dist/collab/awareness.d.ts +2 -2
  173. package/dist/collab/awareness.d.ts.map +1 -1
  174. package/dist/collab/struct-routes.d.ts +1 -1
  175. package/dist/local-artifacts/index.d.ts +10 -0
  176. package/dist/local-artifacts/index.d.ts.map +1 -1
  177. package/dist/local-artifacts/index.js +39 -0
  178. package/dist/local-artifacts/index.js.map +1 -1
  179. package/dist/notifications/routes.d.ts +1 -1
  180. package/dist/observability/routes.d.ts +5 -5
  181. package/dist/resources/handlers.d.ts +1 -1
  182. package/dist/secrets/routes.d.ts +9 -9
  183. package/docs/content/local-file-mode.mdx +99 -489
  184. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  185. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  186. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  187. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  188. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  189. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  190. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  191. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  192. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  193. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  194. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  195. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  196. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  197. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  198. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  199. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  200. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  201. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  202. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  203. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  204. package/docs/content/template-content.mdx +26 -42
  205. package/package.json +1 -1
  206. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -0,0 +1,123 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ getLocalArtifactApp,
4
+ listConfiguredLocalArtifactFiles,
5
+ readConfiguredLocalArtifactFile,
6
+ type LocalArtifactOptions,
7
+ } from "@agent-native/core/local-artifacts";
8
+ import { z } from "zod";
9
+
10
+ import { parseContentSourceFile } from "../shared/content-source.js";
11
+ import connectLocalFolderSource from "./connect-local-folder-source.js";
12
+ import syncLocalFolderSource from "./sync-local-folder-source.js";
13
+
14
+ const CONTENT_LOCAL_DEFAULTS: LocalArtifactOptions["defaults"] = {
15
+ roots: [],
16
+ components: "components",
17
+ hide: ["**/_*.md", "**/_*.mdx"],
18
+ };
19
+
20
+ function localOptions(): LocalArtifactOptions {
21
+ return { appId: "content", defaults: CONTENT_LOCAL_DEFAULTS };
22
+ }
23
+
24
+ export default defineAction({
25
+ description:
26
+ "Bootstrap a manifest-declared local-folder source through the trusted local server bridge into normal SQL-backed Content.",
27
+ schema: z.object({
28
+ connectionId: z.string().min(1).max(300),
29
+ file: z.string().optional(),
30
+ spaceId: z.string().optional(),
31
+ dryRun: z.boolean().optional().default(false),
32
+ }),
33
+ run: async ({ connectionId, file, spaceId, dryRun }) => {
34
+ const options = localOptions();
35
+ const app = await getLocalArtifactApp(options);
36
+ const root = app.roots.find(
37
+ (candidate) =>
38
+ candidate.source?.type === "local-folder" &&
39
+ candidate.source.connectionId === connectionId,
40
+ );
41
+ if (!root) {
42
+ throw new Error(
43
+ `Local folder connection "${connectionId}" is not declared in agent-native.json`,
44
+ );
45
+ }
46
+ const source = root.source;
47
+ if (!source || source.type !== "local-folder") {
48
+ throw new Error(`Local folder connection "${connectionId}" is invalid`);
49
+ }
50
+ const metadata = (await listConfiguredLocalArtifactFiles(options)).filter(
51
+ (candidate) => candidate.rootPath === root.path,
52
+ );
53
+ const files: Record<string, string> = {};
54
+ for (const candidate of metadata) {
55
+ const loaded = await readConfiguredLocalArtifactFile({
56
+ ...options,
57
+ path: candidate.path,
58
+ });
59
+ if (loaded) files[candidate.path] = loaded.content;
60
+ }
61
+ if (file && !Object.prototype.hasOwnProperty.call(files, file)) {
62
+ throw new Error(`Local file "${file}" is not in the configured folder`);
63
+ }
64
+ const connection = await connectLocalFolderSource.run({
65
+ connectionId,
66
+ label: root.name,
67
+ spaceId,
68
+ createSourceBackedSpace: !spaceId,
69
+ truthPolicy: source.truthPolicy ?? "source_primary",
70
+ dryRun,
71
+ });
72
+ if (!connection.connected || !connection.sourceId) {
73
+ const parsed = Object.entries(files).map(([path, content]) =>
74
+ parseContentSourceFile(path, content),
75
+ );
76
+ const valid = parsed.filter((candidate) => !candidate.errors?.length);
77
+ return {
78
+ sourceId: null,
79
+ spaceId: null,
80
+ filesDatabaseId: null,
81
+ truthPolicy: source.truthPolicy ?? "source_primary",
82
+ dryRun: true,
83
+ filesSeen: parsed.length,
84
+ created: valid.map((candidate) => ({
85
+ id: candidate.id ?? "",
86
+ path: candidate.path,
87
+ title: candidate.title,
88
+ })),
89
+ updated: [],
90
+ unchanged: [],
91
+ conflicts: [],
92
+ outbound: [],
93
+ skipped: parsed.flatMap((candidate) =>
94
+ candidate.errors?.length
95
+ ? [{ path: candidate.path, reason: candidate.errors.join(" ") }]
96
+ : [],
97
+ ),
98
+ errors: parsed.flatMap((candidate) =>
99
+ candidate.errors?.length
100
+ ? [{ path: candidate.path, reason: candidate.errors.join(" ") }]
101
+ : [],
102
+ ),
103
+ idByPath: Object.fromEntries(
104
+ valid.map((candidate) => [candidate.path, candidate.id ?? ""]),
105
+ ),
106
+ connectionId,
107
+ requestedFile: file ?? null,
108
+ requestedDocumentId: null,
109
+ };
110
+ }
111
+ const result = await syncLocalFolderSource.run({
112
+ sourceId: connection.sourceId,
113
+ files,
114
+ dryRun,
115
+ });
116
+ return {
117
+ ...result,
118
+ connectionId,
119
+ requestedFile: file ?? null,
120
+ requestedDocumentId: file ? (result.idByPath[file] ?? null) : null,
121
+ };
122
+ },
123
+ });
@@ -66,7 +66,16 @@ const viewSchema = z.object({
66
66
  id: z.string(),
67
67
  name: z.string(),
68
68
  type: z
69
- .enum(["table", "board", "list", "gallery", "calendar", "timeline", "form"])
69
+ .enum([
70
+ "table",
71
+ "board",
72
+ "list",
73
+ "gallery",
74
+ "calendar",
75
+ "timeline",
76
+ "form",
77
+ "sidebar",
78
+ ])
70
79
  .default("table"),
71
80
  sorts: z.array(sortSchema).default([]),
72
81
  filters: z.array(filterSchema).default([]),
@@ -14,11 +14,6 @@ import type { DocumentUpdateResponse } from "../shared/api.js";
14
14
  import { BUILDER_CMS_BODY_CONTENT_KEY } from "./_builder-cms-source-adapter.js";
15
15
  import { reconcileInlineDatabasesForDocument } from "./_content-database-lifecycle.js";
16
16
  import { serializeDocumentSource } from "./_document-source.js";
17
- import {
18
- isLocalDocumentId,
19
- isContentLocalFileMode,
20
- updateLocalFileDocument,
21
- } from "./_local-file-documents.js";
22
17
 
23
18
  // Not (yet) part of the shared API surface — kept local to avoid touching
24
19
  // shared/api.ts, which another workstream owns concurrently. Structural
@@ -198,16 +193,6 @@ export default defineAction({
198
193
  const isAgentCaller =
199
194
  ctx?.caller === "tool" || ctx?.caller === "mcp" || ctx?.caller === "a2a";
200
195
 
201
- if ((await isContentLocalFileMode()) && isLocalDocumentId(id)) {
202
- const doc = await updateLocalFileDocument(id, args);
203
- await writeAppState("refresh-signal", { ts: Date.now() });
204
- return {
205
- ...doc,
206
- urlPath: `/page/${doc.id}`,
207
- softDeletedDatabaseIds: [],
208
- };
209
- }
210
-
211
196
  const access = await assertAccess("document", id, "editor");
212
197
  const existing = access.resource;
213
198
  const ownerEmail = existing.ownerEmail as string;
@@ -37,14 +37,6 @@ import {
37
37
  getDocumentContextPath,
38
38
  serializeDatabaseMembership,
39
39
  } from "./_database-utils.js";
40
- import { serializeDocumentSource } from "./_document-source.js";
41
- import {
42
- getLocalFileDocument,
43
- getLocalDocumentContextPath,
44
- isContentLocalFileMode,
45
- isLocalDocumentId,
46
- localContentViewScreenSummary,
47
- } from "./_local-file-documents.js";
48
40
  import {
49
41
  listPropertiesForDocument,
50
42
  serializeDatabase,
@@ -659,53 +651,13 @@ export default defineAction({
659
651
  run: async () => {
660
652
  const navigation = await readAppStateForCurrentTab("navigation");
661
653
  const localFilesState = await readAppState("local-files");
654
+ const contentSpaceState = await readAppState("content-space");
662
655
 
663
656
  const screen: Record<string, unknown> = {};
664
657
  if (navigation) screen.navigation = navigation;
658
+ if (contentSpaceState) screen.contentSpace = contentSpaceState;
665
659
 
666
660
  const nav = navigation as NavigationState | null;
667
- if (await isContentLocalFileMode()) {
668
- screen.localFiles = {
669
- ...(await localContentViewScreenSummary()),
670
- actions: [
671
- "list-documents",
672
- "get-document",
673
- "create-document",
674
- "update-document",
675
- "delete-document",
676
- "share-local-file-document",
677
- ],
678
- };
679
- if (nav?.documentId && isLocalDocumentId(nav.documentId)) {
680
- screen.document = {
681
- ...(await getLocalFileDocument(nav.documentId)),
682
- contextPath: await getLocalDocumentContextPath(nav.documentId),
683
- };
684
- } else if (nav?.documentId) {
685
- const access = await resolveAccess("document", nav.documentId);
686
- if (access) {
687
- const doc = access.resource;
688
- screen.document = {
689
- id: doc.id,
690
- parentId: doc.parentId,
691
- title: doc.title,
692
- content: doc.content,
693
- description: doc.description,
694
- icon: doc.icon,
695
- position: doc.position,
696
- isFavorite: parseDocumentFavorite(doc.isFavorite),
697
- hideFromSearch: parseDocumentHideFromSearch(doc.hideFromSearch),
698
- visibility: doc.visibility,
699
- source: serializeDocumentSource(doc),
700
- createdAt: doc.createdAt,
701
- updatedAt: doc.updatedAt,
702
- contextPath: await getDocumentContextPath(doc),
703
- };
704
- }
705
- }
706
- return screen;
707
- }
708
-
709
661
  const db = getDb();
710
662
 
711
663
  if (nav?.view === "local-files") {
@@ -10,6 +10,8 @@ import {
10
10
  IconH2,
11
11
  IconH3,
12
12
  IconH4,
13
+ IconH5,
14
+ IconH6,
13
15
  IconList,
14
16
  IconListNumbers,
15
17
  IconSquareCheck,
@@ -77,7 +79,7 @@ interface EquationDraft {
77
79
  position: EditorMenuPosition;
78
80
  }
79
81
 
80
- interface CommandItem {
82
+ export interface CommandItem {
81
83
  title: string;
82
84
  description: string;
83
85
  searchText?: string;
@@ -114,11 +116,81 @@ function waitForEditorUpdateFrame() {
114
116
  });
115
117
  }
116
118
 
117
- interface CommandTemplate extends Omit<CommandItem, "title" | "description"> {
119
+ export interface CommandTemplate extends Omit<
120
+ CommandItem,
121
+ "title" | "description"
122
+ > {
118
123
  titleKey: string;
119
124
  descriptionKey: string;
120
125
  }
121
126
 
127
+ export const CONTENT_HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;
128
+
129
+ const headingCommandMetadata = [
130
+ {
131
+ level: 1,
132
+ titleKey: "editor.heading1",
133
+ descriptionKey: "editor.slash.heading1Description",
134
+ shortcut: "#",
135
+ icon: IconH1,
136
+ },
137
+ {
138
+ level: 2,
139
+ titleKey: "editor.heading2",
140
+ descriptionKey: "editor.slash.heading2Description",
141
+ shortcut: "##",
142
+ icon: IconH2,
143
+ },
144
+ {
145
+ level: 3,
146
+ titleKey: "editor.heading3",
147
+ descriptionKey: "editor.slash.heading3Description",
148
+ shortcut: "###",
149
+ icon: IconH3,
150
+ },
151
+ {
152
+ level: 4,
153
+ titleKey: "editor.heading4",
154
+ descriptionKey: "editor.slash.heading4Description",
155
+ shortcut: "####",
156
+ icon: IconH4,
157
+ },
158
+ {
159
+ level: 5,
160
+ titleKey: "editor.heading5",
161
+ descriptionKey: "editor.slash.heading5Description",
162
+ shortcut: "#####",
163
+ icon: IconH5,
164
+ },
165
+ {
166
+ level: 6,
167
+ titleKey: "editor.heading6",
168
+ descriptionKey: "editor.slash.heading6Description",
169
+ shortcut: "######",
170
+ icon: IconH6,
171
+ },
172
+ ] as const satisfies ReadonlyArray<{
173
+ level: (typeof CONTENT_HEADING_LEVELS)[number];
174
+ titleKey: string;
175
+ descriptionKey: string;
176
+ shortcut: string;
177
+ icon: React.ElementType;
178
+ }>;
179
+
180
+ export function buildHeadingCommands(
181
+ behavior: "toggle" | "set",
182
+ ): CommandTemplate[] {
183
+ return headingCommandMetadata.map((heading) => ({
184
+ ...heading,
185
+ action: (editor) => {
186
+ const chain = editor.chain().focus();
187
+ return behavior === "toggle"
188
+ ? chain.toggleHeading({ level: heading.level }).run()
189
+ : chain.setHeading({ level: heading.level }).run();
190
+ },
191
+ }));
192
+ }
193
+
122
194
  export function setPlainTextBlock(editor: Editor) {
123
195
  const chain = editor.chain().focus();
124
196
  if (typeof (chain as any).setParagraph === "function") {
@@ -239,38 +311,7 @@ const commands: CommandTemplate[] = [
239
311
  icon: IconTypography,
240
312
  action: setPlainTextBlock,
241
313
  },
242
- {
243
- titleKey: "editor.heading1",
244
- descriptionKey: "editor.slash.heading1Description",
245
- shortcut: "#",
246
- icon: IconH1,
247
- action: (editor) =>
248
- editor.chain().focus().toggleHeading({ level: 1 }).run(),
249
- },
250
- {
251
- titleKey: "editor.heading2",
252
- descriptionKey: "editor.slash.heading2Description",
253
- shortcut: "##",
254
- icon: IconH2,
255
- action: (editor) =>
256
- editor.chain().focus().toggleHeading({ level: 2 }).run(),
257
- },
258
- {
259
- titleKey: "editor.heading3",
260
- descriptionKey: "editor.slash.heading3Description",
261
- shortcut: "###",
262
- icon: IconH3,
263
- action: (editor) =>
264
- editor.chain().focus().toggleHeading({ level: 3 }).run(),
265
- },
266
- {
267
- titleKey: "editor.heading4",
268
- descriptionKey: "editor.slash.heading4Description",
269
- shortcut: "####",
270
- icon: IconH4,
271
- action: (editor) =>
272
- editor.chain().focus().toggleHeading({ level: 4 }).run(),
273
- },
314
+ ...buildHeadingCommands("toggle"),
274
315
  {
275
316
  titleKey: "editor.slash.bulletedList",
276
317
  descriptionKey: "editor.slash.bulletedListDescription",
@@ -367,34 +408,7 @@ const turnIntoCommands: CommandTemplate[] = [
367
408
  icon: IconTypography,
368
409
  action: setPlainTextBlock,
369
410
  },
370
- {
371
- titleKey: "editor.heading1",
372
- descriptionKey: "editor.slash.heading1Description",
373
- shortcut: "#",
374
- icon: IconH1,
375
- action: (editor) => editor.chain().focus().setHeading({ level: 1 }).run(),
376
- },
377
- {
378
- titleKey: "editor.heading2",
379
- descriptionKey: "editor.slash.heading2Description",
380
- shortcut: "##",
381
- icon: IconH2,
382
- action: (editor) => editor.chain().focus().setHeading({ level: 2 }).run(),
383
- },
384
- {
385
- titleKey: "editor.heading3",
386
- descriptionKey: "editor.slash.heading3Description",
387
- shortcut: "###",
388
- icon: IconH3,
389
- action: (editor) => editor.chain().focus().setHeading({ level: 3 }).run(),
390
- },
391
- {
392
- titleKey: "editor.heading4",
393
- descriptionKey: "editor.slash.heading4Description",
394
- shortcut: "####",
395
- icon: IconH4,
396
- action: (editor) => editor.chain().focus().setHeading({ level: 4 }).run(),
397
- },
411
+ ...buildHeadingCommands("set"),
398
412
  {
399
413
  titleKey: "editor.slash.bulletedList",
400
414
  descriptionKey: "editor.slash.bulletedListDescription",
@@ -116,6 +116,7 @@ import {
116
116
  IconGripVertical,
117
117
  IconLayoutKanban,
118
118
  IconLayoutGrid,
119
+ IconLayoutSidebar,
119
120
  IconList,
120
121
  IconLock,
121
122
  IconMinus,
@@ -248,6 +249,7 @@ import { VisualEditor } from "../VisualEditor";
248
249
  import { DatabaseFormView } from "./FormView";
249
250
  import { DatabaseGalleryView } from "./GalleryView";
250
251
  import { DatabaseListView } from "./ListView";
252
+ import { DatabaseSidebarView } from "./sidebar";
251
253
  import { DatabaseTimelineView } from "./TimelineView";
252
254
 
253
255
  export interface DatabaseViewProps {
@@ -307,6 +309,7 @@ const DATABASE_VIEW_TYPES: ContentDatabaseViewType[] = [
307
309
  "timeline",
308
310
  "calendar",
309
311
  "form",
312
+ "sidebar",
310
313
  ];
311
314
  const DATABASE_OPEN_PAGES_IN: ContentDatabaseOpenPagesIn[] = [
312
315
  "preview",
@@ -2010,6 +2013,27 @@ function DatabaseTable({
2010
2013
  orderedProperties,
2011
2014
  visibleItems,
2012
2015
  ]);
2016
+ const sidebarGroups = useMemo(
2017
+ () =>
2018
+ databaseVisibleGroups(
2019
+ databaseViewItemGroups(
2020
+ visibleItems,
2021
+ orderedProperties,
2022
+ activeView.groupByPropertyId,
2023
+ ),
2024
+ activeView.hideEmptyGroups === true,
2025
+ ),
2026
+ [
2027
+ activeView.groupByPropertyId,
2028
+ activeView.hideEmptyGroups,
2029
+ orderedProperties,
2030
+ visibleItems,
2031
+ ],
2032
+ );
2033
+ const sidebarIsGrouped = useMemo(
2034
+ () => !!databaseViewGroupingProperty(activeView, orderedProperties),
2035
+ [activeView, orderedProperties],
2036
+ );
2013
2037
 
2014
2038
  useEffect(() => {
2015
2039
  if (!data?.database.id) return;
@@ -2433,6 +2457,22 @@ function DatabaseTable({
2433
2457
  onDeletedPreviewItem={handleDeletedPreviewItem}
2434
2458
  onOpenPage={openItemPage}
2435
2459
  />
2460
+ ) : activeView.type === "sidebar" ? (
2461
+ <DatabaseSidebarView
2462
+ key={activeView.id}
2463
+ groups={sidebarGroups}
2464
+ grouped={sidebarIsGrouped}
2465
+ isLoading={isDatabaseViewLoading}
2466
+ hasActiveConstraints={hasResultConstraints}
2467
+ openPagesIn={activeView.openPagesIn ?? "preview"}
2468
+ loadingLabel={dbText("loadingList")}
2469
+ noMatchesLabel={dbText("noRowsMatchThisView")}
2470
+ clearLabel={dbText("clearSearchAndFilters")}
2471
+ navigationLabel={dbText("databasePagePreview")}
2472
+ untitledLabel={dbText("untitled")}
2473
+ onClearResultConstraints={clearSearchAndFilters}
2474
+ onPreview={previewItemPage}
2475
+ />
2436
2476
  ) : activeView.type === "list" ? (
2437
2477
  <DatabaseListView
2438
2478
  properties={tableProperties}
@@ -7625,20 +7665,19 @@ function DatabaseSettingsSourcePanel({
7625
7665
  source={secondary}
7626
7666
  canEdit={canEdit}
7627
7667
  pending={sourceActionPending}
7628
- onAddDetails={() =>
7629
- secondary
7630
- ? onNavPush({
7631
- kind: "keyConfirm",
7632
- candidate: {
7633
- sourceType: secondary.sourceType,
7634
- sourceName: secondary.sourceName,
7635
- sourceTable: secondary.sourceTable,
7636
- displayName: secondary.sourceName,
7637
- existingSourceId: secondary.id,
7638
- },
7639
- })
7640
- : undefined
7641
- }
7668
+ onAddDetails={() => {
7669
+ if (!secondary || secondary.sourceType === "local-folder") return;
7670
+ onNavPush({
7671
+ kind: "keyConfirm",
7672
+ candidate: {
7673
+ sourceType: secondary.sourceType,
7674
+ sourceName: secondary.sourceName,
7675
+ sourceTable: secondary.sourceTable,
7676
+ displayName: secondary.sourceName,
7677
+ existingSourceId: secondary.id,
7678
+ },
7679
+ });
7680
+ }}
7642
7681
  onAddItems={async () => {
7643
7682
  if (!secondary) return;
7644
7683
  await onChangeSourceRole(secondary.id, "items");
@@ -8151,12 +8190,17 @@ function DatabaseSettingsSourcePanel({
8151
8190
 
8152
8191
  <SourceRoleCard
8153
8192
  source={selectedSource}
8154
- canAddDetails={sources.some(
8155
- (item) => item.id !== selectedSource.id && !sourceAddsDetails(item),
8156
- )}
8193
+ canAddDetails={
8194
+ selectedSource.sourceType !== "local-folder" &&
8195
+ sources.some(
8196
+ (item) =>
8197
+ item.id !== selectedSource.id && !sourceAddsDetails(item),
8198
+ )
8199
+ }
8157
8200
  canEdit={canEdit}
8158
8201
  pending={selectedSourceRoleControlPending}
8159
- onAddDetails={() =>
8202
+ onAddDetails={() => {
8203
+ if (selectedSource.sourceType === "local-folder") return;
8160
8204
  onNavPush({
8161
8205
  kind: "keyConfirm",
8162
8206
  candidate: {
@@ -8166,8 +8210,8 @@ function DatabaseSettingsSourcePanel({
8166
8210
  displayName: selectedSource.sourceName,
8167
8211
  existingSourceId: selectedSource.id,
8168
8212
  },
8169
- })
8170
- }
8213
+ });
8214
+ }}
8171
8215
  onAddItems={async () => {
8172
8216
  await onChangeSourceRole(selectedSource.id, "items");
8173
8217
  onNavReplace([]);
@@ -12988,7 +13032,8 @@ function normalizeClientDatabaseView(
12988
13032
  value.type === "gallery" ||
12989
13033
  value.type === "calendar" ||
12990
13034
  value.type === "timeline" ||
12991
- value.type === "form"
13035
+ value.type === "form" ||
13036
+ value.type === "sidebar"
12992
13037
  ? value.type
12993
13038
  : "table";
12994
13039
  return createDatabaseView(
@@ -13524,6 +13569,7 @@ function databaseViewIcon(type: ContentDatabaseViewType) {
13524
13569
  if (type === "calendar") return IconCalendar;
13525
13570
  if (type === "timeline") return IconTimeline;
13526
13571
  if (type === "form") return IconForms;
13572
+ if (type === "sidebar") return IconLayoutSidebar;
13527
13573
  return IconTable;
13528
13574
  }
13529
13575
 
@@ -13534,6 +13580,7 @@ function databaseViewDefaultName(type: ContentDatabaseViewType) {
13534
13580
  if (type === "calendar") return "Calendar";
13535
13581
  if (type === "timeline") return "Timeline";
13536
13582
  if (type === "form") return "Form";
13583
+ if (type === "sidebar") return "Sidebar";
13537
13584
  return "Table";
13538
13585
  }
13539
13586
 
@@ -13584,7 +13631,8 @@ export function databaseViewGroupingProperty(
13584
13631
  if (
13585
13632
  view.type !== "table" &&
13586
13633
  view.type !== "list" &&
13587
- view.type !== "gallery"
13634
+ view.type !== "gallery" &&
13635
+ view.type !== "sidebar"
13588
13636
  ) {
13589
13637
  return null;
13590
13638
  }
@@ -73,7 +73,8 @@ export function databaseViewGroupingProperty(
73
73
  if (
74
74
  view.type !== "table" &&
75
75
  view.type !== "list" &&
76
- view.type !== "gallery"
76
+ view.type !== "gallery" &&
77
+ view.type !== "sidebar"
77
78
  ) {
78
79
  return null;
79
80
  }
@@ -768,20 +768,19 @@ function DatabaseSettingsSourcePanel({
768
768
  source={secondary}
769
769
  canEdit={canEdit}
770
770
  pending={sourceActionPending}
771
- onAddDetails={() =>
772
- secondary
773
- ? onNavPush({
774
- kind: "keyConfirm",
775
- candidate: {
776
- sourceType: secondary.sourceType,
777
- sourceName: secondary.sourceName,
778
- sourceTable: secondary.sourceTable,
779
- displayName: secondary.sourceName,
780
- existingSourceId: secondary.id,
781
- },
782
- })
783
- : undefined
784
- }
771
+ onAddDetails={() => {
772
+ if (!secondary || secondary.sourceType === "local-folder") return;
773
+ onNavPush({
774
+ kind: "keyConfirm",
775
+ candidate: {
776
+ sourceType: secondary.sourceType,
777
+ sourceName: secondary.sourceName,
778
+ sourceTable: secondary.sourceTable,
779
+ displayName: secondary.sourceName,
780
+ existingSourceId: secondary.id,
781
+ },
782
+ });
783
+ }}
785
784
  onAddItems={async () => {
786
785
  if (!secondary) return;
787
786
  await onChangeSourceRole(secondary.id, "items");
@@ -1142,12 +1141,16 @@ function DatabaseSettingsSourcePanel({
1142
1141
 
1143
1142
  <SourceRoleCard
1144
1143
  source={source}
1145
- canAddDetails={sources.some(
1146
- (item) => item.id !== source.id && !sourceAddsDetails(item),
1147
- )}
1144
+ canAddDetails={
1145
+ source.sourceType !== "local-folder" &&
1146
+ sources.some(
1147
+ (item) => item.id !== source.id && !sourceAddsDetails(item),
1148
+ )
1149
+ }
1148
1150
  canEdit={canEdit}
1149
1151
  pending={sourceActionPending}
1150
- onAddDetails={() =>
1152
+ onAddDetails={() => {
1153
+ if (source.sourceType === "local-folder") return;
1151
1154
  onNavPush({
1152
1155
  kind: "keyConfirm",
1153
1156
  candidate: {
@@ -1157,8 +1160,8 @@ function DatabaseSettingsSourcePanel({
1157
1160
  displayName: source.sourceName,
1158
1161
  existingSourceId: source.id,
1159
1162
  },
1160
- })
1161
- }
1163
+ });
1164
+ }}
1162
1165
  onAddItems={async () => {
1163
1166
  await onChangeSourceRole(source.id, "items");
1164
1167
  onNavReplace([]);
@@ -18,6 +18,7 @@ import {
18
18
  IconFilter,
19
19
  IconLayoutGrid,
20
20
  IconLayoutKanban,
21
+ IconLayoutSidebar,
21
22
  IconList,
22
23
  IconMinus,
23
24
  IconSearch,
@@ -71,6 +72,7 @@ export function databaseViewIcon(type: ContentDatabaseViewType) {
71
72
  if (type === "calendar") return IconCalendar;
72
73
  if (type === "timeline") return IconTimeline;
73
74
  if (type === "form") return IconForms;
75
+ if (type === "sidebar") return IconLayoutSidebar;
74
76
  return IconTable;
75
77
  }
76
78