@agent-native/core 0.164.11 → 0.164.15

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 (106) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +84 -0
  3. package/corpus/templates/calendar/AGENTS.md +5 -0
  4. package/corpus/templates/calendar/actions/delete-event.ts +1 -1
  5. package/corpus/templates/calendar/actions/delete-events.ts +508 -0
  6. package/corpus/templates/calendar/actions/event-action-helpers.ts +1 -1
  7. package/corpus/templates/calendar/actions/list-events.ts +53 -0
  8. package/corpus/templates/calendar/changelog/2026-08-18-the-agent-can-now-remove-many-meetings-at-once-such-as-every.md +6 -0
  9. package/corpus/templates/calendar/server/lib/event-weekday.ts +112 -0
  10. package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
  11. package/corpus/templates/content/actions/_database-utils.ts +2 -0
  12. package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
  13. package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
  14. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
  15. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
  16. package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
  17. package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
  18. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
  19. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
  20. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +78 -1
  21. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
  22. package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
  23. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
  24. package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
  25. package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
  26. package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
  27. package/corpus/templates/content/app/i18n-data.ts +79 -0
  28. package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
  29. package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
  30. package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
  31. package/corpus/templates/content/app/root.tsx +2 -1
  32. package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
  33. package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
  34. package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
  35. package/corpus/templates/content/docs/product/roadmap.md +2 -2
  36. package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
  37. package/corpus/templates/content/shared/api.ts +25 -0
  38. package/corpus/templates/factory/AGENTS.md +2 -1
  39. package/corpus/templates/factory/actions/get-factory-graph-version.ts +69 -0
  40. package/corpus/templates/factory/actions/list-factory-graph-versions.ts +82 -0
  41. package/corpus/templates/factory/actions/restore-factory-graph-version.ts +163 -0
  42. package/corpus/templates/factory/actions/save-factory-graph.ts +93 -60
  43. package/corpus/templates/factory/app/components/factory/FactoryHistoryView.tsx +709 -0
  44. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +62 -2
  45. package/corpus/templates/factory/app/components/ui/alert-dialog.tsx +1 -0
  46. package/corpus/templates/factory/app/i18n/en-US.ts +43 -0
  47. package/corpus/templates/factory/app/routes/factory.tsx +115 -15
  48. package/corpus/templates/factory/server/plugins/agent-chat.ts +3 -1
  49. package/dist/app-config/auth.d.ts +4 -0
  50. package/dist/app-config/auth.js +7 -0
  51. package/dist/app-config/schema.d.ts +3 -0
  52. package/dist/app-config/schema.js +2 -0
  53. package/dist/cli/code-agent-executor.js +6 -1
  54. package/dist/cli/code-agent-mcp-config.d.ts +2 -2
  55. package/dist/cli/code-agent-mcp-config.js +34 -9
  56. package/dist/client/org/OrgSwitcher.js +8 -4
  57. package/dist/collab/awareness.d.ts +2 -2
  58. package/dist/collab/routes.d.ts +1 -1
  59. package/dist/collab/struct-routes.d.ts +1 -1
  60. package/dist/mcp-client/config.d.ts +11 -0
  61. package/dist/mcp-client/config.js +33 -26
  62. package/dist/notifications/routes.d.ts +3 -3
  63. package/dist/observability/routes.d.ts +3 -3
  64. package/dist/progress/routes.d.ts +1 -1
  65. package/dist/resources/handlers.d.ts +2 -2
  66. package/dist/secrets/routes.d.ts +9 -9
  67. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  68. package/dist/server/auth.js +15 -1
  69. package/dist/server/realtime-token.d.ts +1 -1
  70. package/docs/content/actions-access-control.mdx +136 -2
  71. package/docs/content/actions-agent-tools.mdx +101 -0
  72. package/docs/content/actions-defining.mdx +2 -2
  73. package/docs/content/audit-log.mdx +1 -1
  74. package/docs/content/authentication.mdx +1 -1
  75. package/docs/content/cloudflare.mdx +43 -0
  76. package/docs/content/creating-templates.mdx +1 -1
  77. package/docs/content/deploy-an-app.mdx +109 -0
  78. package/docs/content/deployment-environment-variables.mdx +101 -0
  79. package/docs/content/deployment.mdx +125 -792
  80. package/docs/content/docs-components.mdx +2 -2
  81. package/docs/content/environment-variables.mdx +41 -41
  82. package/docs/content/file-uploads.mdx +74 -40
  83. package/docs/content/harness-agents.mdx +2 -2
  84. package/docs/content/internationalization.mdx +61 -65
  85. package/docs/content/key-concepts.mdx +3 -3
  86. package/docs/content/multi-app-workspace.mdx +2 -2
  87. package/docs/content/netlify.mdx +47 -0
  88. package/docs/content/node-docker.mdx +138 -0
  89. package/docs/content/organizations-teams-permissions.mdx +5 -5
  90. package/docs/content/other-platforms.mdx +27 -0
  91. package/docs/content/ssr-caching.mdx +45 -0
  92. package/docs/content/template-assets-developers.mdx +2 -2
  93. package/docs/content/template-chat-developers.mdx +2 -2
  94. package/docs/content/template-content-developers.mdx +2 -2
  95. package/docs/content/{local-file-mode.mdx → template-content-local-files.mdx} +2 -1
  96. package/docs/content/template-content-sync.mdx +2 -2
  97. package/docs/content/toolkit-agent-ux.mdx +1 -1
  98. package/docs/content/toolkit-resources.mdx +2 -2
  99. package/docs/content/updating-ui-in-production.mdx +35 -0
  100. package/docs/content/vercel.mdx +38 -0
  101. package/docs/content/what-is-agent-native.mdx +1 -1
  102. package/docs/content/workspace-deployment.mdx +350 -0
  103. package/package.json +1 -1
  104. package/docs/content/agents.mdx +0 -9
  105. package/docs/content/database.mdx +0 -317
  106. package/docs/content/human-approval.mdx +0 -156
@@ -0,0 +1,189 @@
1
+ import type { ContentDatabaseItem, DocumentProperty } from "@shared/api";
2
+
3
+ const LOCAL_SOURCE_DIRECTORY_PREFIX = "local-source-directory:";
4
+
5
+ function normalizedSourcePath(path: string | undefined) {
6
+ const normalized = (path ?? "").replace(/\\/g, "/").replace(/^\/+/, "");
7
+ if (
8
+ !normalized ||
9
+ normalized.includes("\0") ||
10
+ normalized.split("/").some((part) => !part || part === "." || part === "..")
11
+ ) {
12
+ return null;
13
+ }
14
+ return normalized;
15
+ }
16
+
17
+ function parentPath(path: string) {
18
+ const separator = path.lastIndexOf("/");
19
+ return separator === -1 ? "" : path.slice(0, separator);
20
+ }
21
+
22
+ function sourceIds(item: ContentDatabaseItem) {
23
+ const value = item.properties.find(
24
+ (property) => property.definition.systemRole === "files_source",
25
+ )?.value;
26
+ return Array.isArray(value)
27
+ ? value.filter(
28
+ (candidate): candidate is string =>
29
+ typeof candidate === "string" && candidate !== "local",
30
+ )
31
+ : [];
32
+ }
33
+
34
+ export function localSourceItemIdentity(item: ContentDatabaseItem) {
35
+ return sourceIds(item)[0] ?? item.document.source?.rootPath ?? null;
36
+ }
37
+
38
+ function directoryId(sourceIdentity: string, path: string) {
39
+ return `${LOCAL_SOURCE_DIRECTORY_PREFIX}${encodeURIComponent(sourceIdentity)}:${encodeURIComponent(path)}`;
40
+ }
41
+
42
+ function systemProperties(
43
+ item: ContentDatabaseItem,
44
+ sourceIdentity: string,
45
+ parentId: string | null,
46
+ ): DocumentProperty[] {
47
+ return item.properties
48
+ .filter((property) => property.definition.systemRole)
49
+ .map((property) => {
50
+ if (property.definition.systemRole === "files_parent") {
51
+ return { ...property, value: parentId, editable: false };
52
+ }
53
+ if (property.definition.systemRole === "files_source") {
54
+ return { ...property, value: [sourceIdentity], editable: false };
55
+ }
56
+ return { ...property, editable: false };
57
+ });
58
+ }
59
+
60
+ function directoryItem(
61
+ template: ContentDatabaseItem,
62
+ sourceIdentity: string,
63
+ path: string,
64
+ position: number,
65
+ ): ContentDatabaseItem {
66
+ const parent = parentPath(path);
67
+ const parentId = parent ? directoryId(sourceIdentity, parent) : null;
68
+ const id = directoryId(sourceIdentity, path);
69
+ return {
70
+ id,
71
+ databaseId: template.databaseId,
72
+ document: {
73
+ ...template.document,
74
+ id,
75
+ parentId,
76
+ title: path.split("/").pop()?.replace(/[-_]+/g, " ") || path,
77
+ content: "",
78
+ description: "",
79
+ icon: null,
80
+ position,
81
+ isFavorite: false,
82
+ hideFromSearch: true,
83
+ accessRole: "viewer",
84
+ canView: true,
85
+ canComment: false,
86
+ canEdit: false,
87
+ canManage: false,
88
+ databaseMembership: undefined,
89
+ source: {
90
+ mode: "local-files",
91
+ kind: "folder",
92
+ path,
93
+ rootPath: template.document.source?.rootPath,
94
+ },
95
+ },
96
+ position,
97
+ properties: systemProperties(template, sourceIdentity, parentId),
98
+ };
99
+ }
100
+
101
+ export interface LocalSourceHierarchySelection {
102
+ sourceId?: string | null;
103
+ rootPath?: string | null;
104
+ }
105
+
106
+ export function firstLocalSourceDocumentId(items: ContentDatabaseItem[]) {
107
+ return items.find(
108
+ (item) =>
109
+ item.document.source?.mode === "local-files" &&
110
+ item.document.source.kind !== "folder",
111
+ )?.document.id;
112
+ }
113
+
114
+ export function projectLocalSourceHierarchy(
115
+ items: ContentDatabaseItem[],
116
+ selection: LocalSourceHierarchySelection = {},
117
+ ) {
118
+ const localFiles = items.filter((item) => {
119
+ if (
120
+ item.document.source?.mode !== "local-files" ||
121
+ item.document.source.kind === "folder"
122
+ ) {
123
+ return false;
124
+ }
125
+ if (selection.sourceId) {
126
+ return sourceIds(item).includes(selection.sourceId);
127
+ }
128
+ if (selection.rootPath) {
129
+ return item.document.source.rootPath === selection.rootPath;
130
+ }
131
+ return true;
132
+ });
133
+ const retained = items.filter(
134
+ (item) => item.document.source?.mode !== "local-files",
135
+ );
136
+ const directories = new Map<
137
+ string,
138
+ { template: ContentDatabaseItem; sourceIdentity: string; path: string }
139
+ >();
140
+ const projectedFiles: ContentDatabaseItem[] = [];
141
+
142
+ for (const item of localFiles) {
143
+ const path = normalizedSourcePath(item.document.source?.path);
144
+ const sourceIdentity =
145
+ selection.sourceId && sourceIds(item).includes(selection.sourceId)
146
+ ? selection.sourceId
147
+ : localSourceItemIdentity(item);
148
+ if (!path || !sourceIdentity) continue;
149
+ const folder = parentPath(path);
150
+ if (folder) {
151
+ const parts = folder.split("/");
152
+ for (let index = 1; index <= parts.length; index += 1) {
153
+ const directoryPath = parts.slice(0, index).join("/");
154
+ directories.set(`${sourceIdentity}\0${directoryPath}`, {
155
+ template: item,
156
+ sourceIdentity,
157
+ path: directoryPath,
158
+ });
159
+ }
160
+ }
161
+ const parentId = folder ? directoryId(sourceIdentity, folder) : null;
162
+ projectedFiles.push({
163
+ ...item,
164
+ document: { ...item.document, parentId },
165
+ properties: item.properties.map((property) =>
166
+ property.definition.systemRole === "files_parent"
167
+ ? { ...property, value: parentId }
168
+ : property,
169
+ ),
170
+ });
171
+ }
172
+
173
+ const projectedDirectories = [...directories.values()]
174
+ .sort((left, right) =>
175
+ left.sourceIdentity === right.sourceIdentity
176
+ ? left.path.localeCompare(right.path)
177
+ : left.sourceIdentity.localeCompare(right.sourceIdentity),
178
+ )
179
+ .map((directory, index) =>
180
+ directoryItem(
181
+ directory.template,
182
+ directory.sourceIdentity,
183
+ directory.path,
184
+ index,
185
+ ),
186
+ );
187
+
188
+ return [...retained, ...projectedDirectories, ...projectedFiles];
189
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Document } from "@shared/api";
2
2
  import { useQueryClient } from "@tanstack/react-query";
3
3
  import { useCallback } from "react";
4
- import { useNavigate } from "react-router";
4
+ import { useLocation, useNavigate } from "react-router";
5
5
  import { toast } from "sonner";
6
6
 
7
7
  import {
@@ -10,7 +10,10 @@ import {
10
10
  SELECTED_CONTENT_SPACE_STORAGE_KEY,
11
11
  } from "@/components/sidebar/select-content-space";
12
12
  import { useContentSpaces } from "@/hooks/use-content-spaces";
13
- import { useCreateDocument } from "@/hooks/use-documents";
13
+ import {
14
+ rollbackOptimisticCreatedDocument,
15
+ useCreateDocument,
16
+ } from "@/hooks/use-documents";
14
17
  import { useLocalStorage } from "@/hooks/use-local-storage";
15
18
  import { documentQueryFilter } from "@/lib/document-query";
16
19
  import { markDocumentCreationPending } from "@/lib/optimistic-document";
@@ -34,6 +37,7 @@ export function useCreatePage(opts?: {
34
37
  awaitPersist?: boolean;
35
38
  }) {
36
39
  const navigate = useNavigate();
40
+ const location = useLocation();
37
41
  const queryClient = useQueryClient();
38
42
  const createDocument = useCreateDocument();
39
43
  const contentSpacesQuery = useContentSpaces();
@@ -78,11 +82,19 @@ export function useCreatePage(opts?: {
78
82
  createdAt: now,
79
83
  updatedAt: now,
80
84
  });
85
+ const previousDocuments = queryClient.getQueryData(
86
+ LIST_DOCUMENTS_QUERY_KEY,
87
+ );
88
+ const previousPath = `${location.pathname}${location.search}${location.hash}`;
81
89
 
82
90
  queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, (old: any) => {
83
91
  const docs: Document[] =
84
92
  old?.documents ?? (Array.isArray(old) ? old : []);
85
- return { documents: [...docs, tempDoc] };
93
+ if (Array.isArray(old)) return [...docs, tempDoc];
94
+ return {
95
+ ...(old && typeof old === "object" ? old : {}),
96
+ documents: [...docs, tempDoc],
97
+ };
86
98
  });
87
99
  queryClient.setQueryData(["action", "get-document", { id }], tempDoc);
88
100
 
@@ -111,11 +123,18 @@ export function useCreatePage(opts?: {
111
123
  };
112
124
 
113
125
  const onPersistError = (err: unknown) => {
126
+ rollbackOptimisticCreatedDocument(
127
+ queryClient,
128
+ id,
129
+ previousDocuments !== undefined,
130
+ );
114
131
  queryClient.invalidateQueries({
115
132
  queryKey: ["action", "list-documents"],
116
133
  });
117
134
  queryClient.removeQueries(documentQueryFilter(id));
118
- if (shouldNavigate) navigate("/");
135
+ if (shouldNavigate) {
136
+ navigate(previousPath, { replace: true, flushSync: true });
137
+ }
119
138
  toast.error("Failed to create page", {
120
139
  description:
121
140
  err instanceof Error ? err.message : "Something went wrong",
@@ -137,6 +156,9 @@ export function useCreatePage(opts?: {
137
156
  },
138
157
  [
139
158
  createDocument,
159
+ location.hash,
160
+ location.pathname,
161
+ location.search,
140
162
  navigate,
141
163
  onAfterNavigate,
142
164
  queryClient,
@@ -66,6 +66,89 @@ export const LIST_DOCUMENTS_QUERY_KEY = [
66
66
  undefined,
67
67
  ] as const;
68
68
 
69
+ export function restoreListDocumentsSnapshot(
70
+ queryClient: Pick<QueryClient, "removeQueries" | "setQueryData">,
71
+ snapshot: unknown,
72
+ ) {
73
+ if (snapshot === undefined) {
74
+ queryClient.removeQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY });
75
+ return;
76
+ }
77
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, snapshot);
78
+ }
79
+
80
+ export function rollbackOptimisticCreatedDocument(
81
+ queryClient: Pick<
82
+ QueryClient,
83
+ "getQueryData" | "removeQueries" | "setQueryData"
84
+ >,
85
+ documentId: string,
86
+ hadListSnapshot: boolean,
87
+ ) {
88
+ const current = queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY);
89
+ const documents: Document[] = Array.isArray(current)
90
+ ? current
91
+ : ((current as DocumentListResponse | undefined)?.documents ?? []);
92
+ const remaining = documents.filter((document) => document.id !== documentId);
93
+
94
+ if (!hadListSnapshot && remaining.length === 0) {
95
+ queryClient.removeQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY });
96
+ return;
97
+ }
98
+
99
+ if (Array.isArray(current)) {
100
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, remaining);
101
+ return;
102
+ }
103
+
104
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, {
105
+ ...(current && typeof current === "object" ? current : {}),
106
+ documents: remaining,
107
+ });
108
+ }
109
+
110
+ export function restoreDeletedDocumentSnapshots(
111
+ queryClient: Pick<QueryClient, "getQueryData" | "setQueryData">,
112
+ listSnapshot: unknown,
113
+ documentSnapshots: Array<[readonly unknown[], unknown]>,
114
+ deletedDocumentIds: Iterable<string>,
115
+ ) {
116
+ const deletedIds = new Set(deletedDocumentIds);
117
+ const snapshotDocuments: Document[] = Array.isArray(listSnapshot)
118
+ ? listSnapshot
119
+ : ((listSnapshot as DocumentListResponse | undefined)?.documents ?? []);
120
+ const current = queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY);
121
+ const currentDocuments: Document[] = Array.isArray(current)
122
+ ? current
123
+ : ((current as DocumentListResponse | undefined)?.documents ?? []);
124
+ const currentDocumentIds = new Set(
125
+ currentDocuments.map((document) => document.id),
126
+ );
127
+ const restoredDocuments = snapshotDocuments.filter(
128
+ (document) =>
129
+ deletedIds.has(document.id) && !currentDocumentIds.has(document.id),
130
+ );
131
+ const documents = [...currentDocuments, ...restoredDocuments];
132
+
133
+ if (Array.isArray(current)) {
134
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, documents);
135
+ } else {
136
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, {
137
+ ...(current && typeof current === "object"
138
+ ? current
139
+ : listSnapshot && typeof listSnapshot === "object"
140
+ ? listSnapshot
141
+ : {}),
142
+ documents,
143
+ });
144
+ }
145
+ for (const [queryKey, data] of documentSnapshots) {
146
+ if (queryClient.getQueryData(queryKey) === undefined) {
147
+ queryClient.setQueryData(queryKey, data);
148
+ }
149
+ }
150
+ }
151
+
69
152
  const DOCUMENT_LIST_PAGE_SIZE = 200;
70
153
 
71
154
  export async function fetchCompleteDocumentList(
@@ -2876,6 +2876,7 @@ const editorToolbarMessages = {
2876
2876
  pushFailed: "Push failed.",
2877
2877
  pushLocal: "Push local",
2878
2878
  pushToNotion: "Push to Notion",
2879
+ redo: "Redo",
2879
2880
  revealInFinder: "Reveal in Finder",
2880
2881
  revealedLocalFile: "Revealed local file",
2881
2882
  searchNotionPages: "Search Notion pages...",
@@ -2896,6 +2897,7 @@ const editorToolbarMessages = {
2896
2897
  disconnectedNotionWorkspace: "Disconnected Notion workspace.",
2897
2898
  unlinkFailed: "Unlink failed.",
2898
2899
  unlinkedFromNotion: "Unlinked from Notion.",
2900
+ undo: "Undo",
2899
2901
  versionHistory: "Version history",
2900
2902
  };
2901
2903
 
@@ -3153,6 +3155,13 @@ const enUS = {
3153
3155
  "This Blocks field changed elsewhere. Your edit wasn't saved; the latest version is now shown.",
3154
3156
  couldNotReadLocalSourceFile: "Could not read local source file",
3155
3157
  couldNotSaveLocalFile: "Could not save local file",
3158
+ localFileChangedWithUnsavedEdits:
3159
+ "The file changed on disk while this page had unsaved edits.",
3160
+ localFileReadOnlySnapshot:
3161
+ "Read-only snapshot · Last synced from {{device}} at {{date}}",
3162
+ copyUnsavedText: "Copy my unsaved text",
3163
+ unsavedTextCopied: "Unsaved text copied",
3164
+ useDiskVersion: "Use disk version",
3156
3165
  collabConnectingReadOnly:
3157
3166
  "Connecting live editor. Showing a read-only snapshot.",
3158
3167
  liveDocumentSaveBeforeSyncFailed:
@@ -3497,10 +3506,17 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
3497
3506
  editor: {
3498
3507
  couldNotReadLocalSourceFile: "无法读取本地源文件",
3499
3508
  couldNotSaveLocalFile: "无法保存本地文件",
3509
+ localFileChangedWithUnsavedEdits:
3510
+ "此页面有未保存的编辑时,磁盘上的文件已更改。",
3511
+ copyUnsavedText: "复制我未保存的文本",
3512
+ unsavedTextCopied: "已复制未保存的文本",
3513
+ useDiskVersion: "使用磁盘版本",
3500
3514
  documentTitle: "文档标题",
3501
3515
  localFileSavedHistoryNotUpdated: "本地文件已保存,但历史记录未更新",
3502
3516
  title: "标题",
3503
3517
  toolbar: {
3518
+ undo: "撤销",
3519
+ redo: "重做",
3504
3520
  conflict: "冲突",
3505
3521
  conflictResolutionFailed: "冲突解决失败。",
3506
3522
  conflictResolvedFromLocalDocument: "已从本本文档解决冲突。",
@@ -3725,6 +3741,11 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
3725
3741
  editor: {
3726
3742
  couldNotReadLocalSourceFile: "No se pudo leer el archivo local de origen",
3727
3743
  couldNotSaveLocalFile: "No se pudo guardar el archivo local",
3744
+ localFileChangedWithUnsavedEdits:
3745
+ "El archivo del disco cambió mientras esta página tenía ediciones sin guardar.",
3746
+ copyUnsavedText: "Copiar mi texto sin guardar",
3747
+ unsavedTextCopied: "Texto sin guardar copiado",
3748
+ useDiskVersion: "Usar la versión del disco",
3728
3749
  documentTitle: "Título del documento",
3729
3750
  localFileSavedHistoryNotUpdated:
3730
3751
  "El archivo local se guardó, pero el historial no se actualizó",
@@ -3791,6 +3812,8 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
3791
3812
  "Transcribe este video y agrega la transcripción debajo.",
3792
3813
  },
3793
3814
  toolbar: {
3815
+ undo: "Deshacer",
3816
+ redo: "Rehacer",
3794
3817
  conflict: "Conflicto",
3795
3818
  conflictResolutionFailed: "Error al resolver el conflicto.",
3796
3819
  conflictResolvedFromLocalDocument:
@@ -3929,6 +3952,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
3929
3952
  },
3930
3953
  },
3931
3954
  "fr-FR": {
3955
+ editor: {
3956
+ localFileChangedWithUnsavedEdits:
3957
+ "Le fichier sur le disque a changé alors que cette page contenait des modifications non enregistrées.",
3958
+ copyUnsavedText: "Copier mon texte non enregistré",
3959
+ unsavedTextCopied: "Texte non enregistré copié",
3960
+ useDiskVersion: "Utiliser la version du disque",
3961
+ toolbar: { undo: "Annuler", redo: "Rétablir" },
3962
+ },
3932
3963
  sidebar: {
3933
3964
  addChild: "Ajouter un enfant",
3934
3965
  addChildTo: "Ajouter un enfant à {{title}}",
@@ -3965,6 +3996,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
3965
3996
  },
3966
3997
  },
3967
3998
  "de-DE": {
3999
+ editor: {
4000
+ localFileChangedWithUnsavedEdits:
4001
+ "Die Datei auf dem Datenträger wurde geändert, während diese Seite ungespeicherte Änderungen enthielt.",
4002
+ copyUnsavedText: "Meinen ungespeicherten Text kopieren",
4003
+ unsavedTextCopied: "Ungespeicherter Text kopiert",
4004
+ useDiskVersion: "Version vom Datenträger verwenden",
4005
+ toolbar: { undo: "Rückgängig", redo: "Wiederholen" },
4006
+ },
3968
4007
  sidebar: {
3969
4008
  addChild: "Unterelement hinzufügen",
3970
4009
  addChildTo: "Unterelement zu {{title}} hinzufügen",
@@ -4000,6 +4039,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
4000
4039
  },
4001
4040
  },
4002
4041
  "ja-JP": {
4042
+ editor: {
4043
+ localFileChangedWithUnsavedEdits:
4044
+ "このページに未保存の編集がある間に、ディスク上のファイルが変更されました。",
4045
+ copyUnsavedText: "未保存のテキストをコピー",
4046
+ unsavedTextCopied: "未保存のテキストをコピーしました",
4047
+ useDiskVersion: "ディスク上の版を使用",
4048
+ toolbar: { undo: "元に戻す", redo: "やり直す" },
4049
+ },
4003
4050
  sidebar: {
4004
4051
  addChild: "子項目を追加",
4005
4052
  addChildTo: "{{title}} に子項目を追加",
@@ -4033,6 +4080,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
4033
4080
  },
4034
4081
  },
4035
4082
  "ko-KR": {
4083
+ editor: {
4084
+ localFileChangedWithUnsavedEdits:
4085
+ "이 페이지에 저장하지 않은 편집 내용이 있는 동안 디스크의 파일이 변경되었습니다.",
4086
+ copyUnsavedText: "저장하지 않은 텍스트 복사",
4087
+ unsavedTextCopied: "저장하지 않은 텍스트를 복사했습니다",
4088
+ useDiskVersion: "디스크 버전 사용",
4089
+ toolbar: { undo: "실행 취소", redo: "다시 실행" },
4090
+ },
4036
4091
  sidebar: {
4037
4092
  addChild: "하위 항목 추가",
4038
4093
  addChildTo: "{{title}}에 하위 항목 추가",
@@ -4065,6 +4120,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
4065
4120
  },
4066
4121
  },
4067
4122
  "pt-BR": {
4123
+ editor: {
4124
+ localFileChangedWithUnsavedEdits:
4125
+ "O arquivo no disco foi alterado enquanto esta página tinha edições não salvas.",
4126
+ copyUnsavedText: "Copiar meu texto não salvo",
4127
+ unsavedTextCopied: "Texto não salvo copiado",
4128
+ useDiskVersion: "Usar versão do disco",
4129
+ toolbar: { undo: "Desfazer", redo: "Refazer" },
4130
+ },
4068
4131
  sidebar: {
4069
4132
  addChild: "Adicionar filho",
4070
4133
  addChildTo: "Adicionar filho a {{title}}",
@@ -4099,6 +4162,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
4099
4162
  },
4100
4163
  },
4101
4164
  "hi-IN": {
4165
+ editor: {
4166
+ localFileChangedWithUnsavedEdits:
4167
+ "इस पेज में सहेजे नहीं गए बदलाव होने के दौरान डिस्क की फ़ाइल बदल गई।",
4168
+ copyUnsavedText: "मेरा सहेजा नहीं गया टेक्स्ट कॉपी करें",
4169
+ unsavedTextCopied: "सहेजा नहीं गया टेक्स्ट कॉपी किया गया",
4170
+ useDiskVersion: "डिस्क वाला संस्करण उपयोग करें",
4171
+ toolbar: { undo: "पूर्ववत करें", redo: "फिर से करें" },
4172
+ },
4102
4173
  sidebar: {
4103
4174
  addChild: "चाइल्ड जोड़ें",
4104
4175
  addChildTo: "{{title}} में चाइल्ड जोड़ें",
@@ -4131,6 +4202,14 @@ const rawLiteralLocaleMessages: Partial<Record<LocaleCode, PartialMessages>> = {
4131
4202
  },
4132
4203
  },
4133
4204
  "ar-SA": {
4205
+ editor: {
4206
+ localFileChangedWithUnsavedEdits:
4207
+ "تغيّر الملف على القرص أثناء وجود تعديلات غير محفوظة في هذه الصفحة.",
4208
+ copyUnsavedText: "نسخ النص غير المحفوظ",
4209
+ unsavedTextCopied: "تم نسخ النص غير المحفوظ",
4210
+ useDiskVersion: "استخدام نسخة القرص",
4211
+ toolbar: { undo: "تراجع", redo: "إعادة" },
4212
+ },
4134
4213
  sidebar: {
4135
4214
  addChild: "إضافة عنصر فرعي",
4136
4215
  addChildTo: "إضافة عنصر فرعي إلى {{title}}",
@@ -1,7 +1,17 @@
1
1
  export interface DesktopContentFilesFolder {
2
2
  id?: string;
3
3
  name: string;
4
- path?: string;
4
+ kind?: "persistent" | "temporary";
5
+ repository?: {
6
+ localId: string;
7
+ branch?: string;
8
+ commit?: string;
9
+ detached?: boolean;
10
+ };
11
+ contentSource?: {
12
+ sourceId: string;
13
+ databaseId?: string;
14
+ };
5
15
  sourcePrefix?: string;
6
16
  updatedAt?: string;
7
17
  }
@@ -10,6 +20,27 @@ export interface DesktopContentFilesFolderRequest {
10
20
  folderId?: string;
11
21
  }
12
22
 
23
+ /** Opaque content fingerprint returned by the trusted local bridge. */
24
+ export type DesktopContentFileRevision = string;
25
+
26
+ export interface DesktopContentFileConflict {
27
+ path: string;
28
+ expectedRevision?: string | null;
29
+ actualRevision?: string;
30
+ }
31
+
32
+ export type DesktopContentFilesChange = {
33
+ folderId: string;
34
+ revision: string;
35
+ changedAt: string;
36
+ missing?: boolean;
37
+ reason?: "attached" | "changed" | "missing";
38
+ };
39
+
40
+ export type DesktopContentFilesWatchResult =
41
+ | { ok: true; unsubscribe(): void }
42
+ | { ok: false; error: string; unavailable?: boolean };
43
+
13
44
  export type DesktopContentFilesResult =
14
45
  | {
15
46
  ok: true;
@@ -17,6 +48,8 @@ export type DesktopContentFilesResult =
17
48
  folders?: DesktopContentFilesFolder[];
18
49
  files?: string[];
19
50
  sources?: Record<string, string>;
51
+ revisions?: Record<string, DesktopContentFileRevision>;
52
+ identities?: Record<string, string>;
20
53
  controlResources?: Record<string, string>;
21
54
  }
22
55
  | {
@@ -25,6 +58,8 @@ export type DesktopContentFilesResult =
25
58
  canceled?: boolean;
26
59
  folder?: DesktopContentFilesFolder;
27
60
  folders?: DesktopContentFilesFolder[];
61
+ code?: "conflict" | "unavailable" | "invalid-request";
62
+ conflict?: DesktopContentFileConflict;
28
63
  };
29
64
 
30
65
  export interface DesktopContentFilesApi {
@@ -32,18 +67,27 @@ export interface DesktopContentFilesApi {
32
67
  request?: DesktopContentFilesFolderRequest,
33
68
  ): Promise<DesktopContentFilesResult>;
34
69
  chooseFolder(): Promise<DesktopContentFilesResult>;
70
+ associateSource?(request: {
71
+ folderId: string;
72
+ sourceId: string;
73
+ databaseId?: string;
74
+ }): Promise<DesktopContentFilesResult>;
35
75
  writeFiles(request: {
36
76
  folderId?: string;
37
77
  files: Record<string, string>;
78
+ expectedRevisions: Record<string, string | null>;
38
79
  }): Promise<DesktopContentFilesResult>;
39
80
  writeFile(request: {
40
81
  folderId?: string;
41
82
  path: string;
42
83
  content: string;
84
+ /** The revision observed by the editor; a mismatch must not overwrite. */
85
+ expectedRevision: string | null;
43
86
  }): Promise<DesktopContentFilesResult>;
44
87
  deleteFile?(request: {
45
88
  folderId?: string;
46
89
  path: string;
90
+ expectedRevision: string;
47
91
  }): Promise<DesktopContentFilesResult>;
48
92
  readFiles(
49
93
  request?: DesktopContentFilesFolderRequest,
@@ -55,6 +99,18 @@ export interface DesktopContentFilesApi {
55
99
  clearFolder(
56
100
  request?: DesktopContentFilesFolderRequest,
57
101
  ): Promise<DesktopContentFilesResult>;
102
+ subscribeChanges?(
103
+ request: DesktopContentFilesFolderRequest,
104
+ ): Promise<DesktopContentFilesResult>;
105
+ unsubscribeChanges?(
106
+ request: DesktopContentFilesFolderRequest,
107
+ ): Promise<DesktopContentFilesResult>;
108
+ onChange?(callback: (change: DesktopContentFilesChange) => void): () => void;
109
+ /** Optional until the installed Desktop bridge supports live folder events. */
110
+ watchFiles?(
111
+ request: DesktopContentFilesFolderRequest,
112
+ onChange: (change: DesktopContentFilesChange) => void,
113
+ ): Promise<DesktopContentFilesWatchResult>;
58
114
  }
59
115
 
60
116
  type WindowWithAgentNativeDesktop = Window & {