@lapis-notes/api 0.1.3 → 0.2.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 (199) hide show
  1. package/CHANGELOG.md +304 -13
  2. package/README.md +19 -0
  3. package/dist/__tests__/fixtures/indexed-mutations.json +6862 -0
  4. package/dist/__tests__/fixtures/memory-storage.json +309 -0
  5. package/dist/__tests__/fixtures/native-facade-contract.json +3224 -0
  6. package/dist/__tests__/fixtures/projection-query.json +195 -0
  7. package/dist/__tests__/fixtures/projection-values.json +60 -0
  8. package/dist/__tests__/fixtures/search-preparation.json +1513 -0
  9. package/dist/__tests__/fixtures/search-retrieval.json +15409 -0
  10. package/dist/__tests__/fixtures/search-semantic-model.json +3691 -0
  11. package/dist/__tests__/fixtures/search-semantic-retrieval.json +6603 -0
  12. package/dist/__tests__/fixtures/search-storage.json +409 -0
  13. package/dist/__tests__/fixtures/task-projection.json +428 -0
  14. package/dist/__tests__/indexed-mutation-support.js +262 -0
  15. package/dist/__tests__/projection-query-support.js +40 -0
  16. package/dist/__tests__/search-retrieval-support.js +47 -0
  17. package/dist/app-database-host.d.ts +1 -0
  18. package/dist/app-shell-plugin-persistence.d.ts +3 -2
  19. package/dist/app-shell-plugin-persistence.js +37 -3
  20. package/dist/cache.svelte.d.ts +8 -1
  21. package/dist/cache.svelte.js +103 -5
  22. package/dist/command.svelte.js +29 -2
  23. package/dist/components/editor/editor.css +4 -0
  24. package/dist/components/editor/editor.svelte +24 -11
  25. package/dist/components/editor/embedded-editor-surface.js +3 -1
  26. package/dist/components/editor/inline-title-alignment.d.ts +3 -0
  27. package/dist/components/editor/inline-title-alignment.js +57 -0
  28. package/dist/configuration.svelte.d.ts +270 -206
  29. package/dist/configuration.svelte.js +161 -86
  30. package/dist/context.svelte.d.ts +23 -3
  31. package/dist/context.svelte.js +68 -30
  32. package/dist/diagnostics.d.ts +8 -0
  33. package/dist/diagnostics.js +6 -0
  34. package/dist/editor-save-context.d.ts +35 -0
  35. package/dist/editor-save-context.js +132 -0
  36. package/dist/editor-view-registry.d.ts +10 -0
  37. package/dist/editor-view-registry.js +44 -4
  38. package/dist/editor.svelte.d.ts +19 -1
  39. package/dist/editor.svelte.js +528 -55
  40. package/dist/embed-registry.d.ts +10 -0
  41. package/dist/embed-registry.js +15 -0
  42. package/dist/file-editing-failures.d.ts +2 -0
  43. package/dist/file-editing-failures.js +29 -0
  44. package/dist/file-editing-native-issues.d.ts +19 -0
  45. package/dist/file-editing-native-issues.js +65 -0
  46. package/dist/file-editing-sessions.d.ts +76 -0
  47. package/dist/file-editing-sessions.js +367 -0
  48. package/dist/file-manager.d.ts +9 -1
  49. package/dist/file-manager.js +45 -3
  50. package/dist/file-state-store.d.ts +5 -0
  51. package/dist/file-state-store.js +34 -0
  52. package/dist/file-text-conflicts.d.ts +25 -0
  53. package/dist/file-text-conflicts.js +35 -0
  54. package/dist/file-text-mutation.d.ts +13 -0
  55. package/dist/file-text-mutation.js +100 -0
  56. package/dist/file-text-persistence.d.ts +36 -0
  57. package/dist/file-text-persistence.js +234 -0
  58. package/dist/file-text-state-writer.d.ts +34 -0
  59. package/dist/file-text-state-writer.js +183 -0
  60. package/dist/files-configuration.d.ts +20 -0
  61. package/dist/files-configuration.js +47 -0
  62. package/dist/generated/plugin-host-modules.schema.generated.d.ts +114 -34
  63. package/dist/generated/plugin-host-modules.schema.generated.js +132 -34
  64. package/dist/glob.d.ts +3 -0
  65. package/dist/glob.js +2 -1
  66. package/dist/icons/changes.d.ts +3 -0
  67. package/dist/icons/changes.js +16 -0
  68. package/dist/icons/index.d.ts +5 -1
  69. package/dist/icons/index.js +44 -3
  70. package/dist/icons/registry.d.ts +35 -0
  71. package/dist/icons/registry.js +154 -0
  72. package/dist/icons/safe-svg.d.ts +2 -0
  73. package/dist/icons/safe-svg.js +44 -0
  74. package/dist/index.d.ts +6 -1
  75. package/dist/index.js +4 -1
  76. package/dist/lapis-extension.d.ts +6 -2
  77. package/dist/lapis-extension.js +2 -0
  78. package/dist/metadata-store.d.ts +23 -0
  79. package/dist/metadata-store.js +1 -0
  80. package/dist/metadata.svelte.js +45 -2
  81. package/dist/native-background-indexing.d.ts +48 -0
  82. package/dist/native-background-indexing.js +208 -0
  83. package/dist/native-database-protocol.d.ts +29 -0
  84. package/dist/native-database-protocol.js +1 -0
  85. package/dist/native-editing-intent-lease.d.ts +17 -0
  86. package/dist/native-editing-intent-lease.js +188 -0
  87. package/dist/native-editing-intents-protocol.d.ts +70 -0
  88. package/dist/native-editing-intents-protocol.js +1 -0
  89. package/dist/native-editing-intents.d.ts +8 -0
  90. package/dist/native-editing-intents.js +1 -0
  91. package/dist/native-editing-problems.d.ts +3 -0
  92. package/dist/native-editing-problems.js +180 -0
  93. package/dist/native-editing-recovery.d.ts +24 -0
  94. package/dist/native-editing-recovery.js +198 -0
  95. package/dist/native-editing-session.d.ts +51 -0
  96. package/dist/native-editing-session.js +251 -0
  97. package/dist/native-history-index-protocol.d.ts +80 -0
  98. package/dist/native-history-index-protocol.js +1 -0
  99. package/dist/native-history-index.d.ts +6 -0
  100. package/dist/native-history-index.js +1 -0
  101. package/dist/native-indexing-protocol.d.ts +63 -0
  102. package/dist/native-indexing-protocol.js +1 -0
  103. package/dist/native-participant-protocol.d.ts +67 -0
  104. package/dist/native-participant-protocol.js +3 -0
  105. package/dist/native-queue-protocol.d.ts +73 -0
  106. package/dist/native-queue-protocol.js +3 -0
  107. package/dist/native-tasks-protocol.d.ts +264 -0
  108. package/dist/native-tasks-protocol.js +1 -0
  109. package/dist/native-tasks.d.ts +35 -0
  110. package/dist/native-tasks.js +79 -0
  111. package/dist/native-turn-protocol.d.ts +81 -0
  112. package/dist/native-turn-protocol.js +1 -0
  113. package/dist/native-turns.d.ts +29 -0
  114. package/dist/native-turns.js +2 -0
  115. package/dist/plugin-asset-server.js +3 -2
  116. package/dist/plugin-dependency-resolver.d.ts +1 -1
  117. package/dist/plugin-distribution/compatibility.d.ts +1 -1
  118. package/dist/plugin-distribution/compatibility.js +10 -5
  119. package/dist/plugin-distribution/installed-plugin-state.js +2 -1
  120. package/dist/plugin-distribution/installer.d.ts +6 -0
  121. package/dist/plugin-distribution/installer.js +90 -37
  122. package/dist/plugin-distribution/manager.d.ts +4 -1
  123. package/dist/plugin-distribution/manager.js +12 -5
  124. package/dist/plugin-distribution/registry-cache.js +2 -1
  125. package/dist/plugin-distribution/registry-source-state.js +2 -1
  126. package/dist/plugin-distribution/schemas.d.ts +26 -8
  127. package/dist/plugin-distribution/schemas.js +2 -1
  128. package/dist/plugin-distribution/types.d.ts +4 -0
  129. package/dist/plugin-manager.d.ts +21 -5
  130. package/dist/plugin-manager.js +228 -64
  131. package/dist/plugin-runtime-entry.d.ts +1 -1
  132. package/dist/plugin-runtime-entry.js +16 -0
  133. package/dist/plugin.d.ts +19 -3
  134. package/dist/plugin.js +110 -44
  135. package/dist/registry-schema/catalog-v1.d.ts +60 -9
  136. package/dist/registry-schema/catalog-v1.js +2 -1
  137. package/dist/registry-schema/catalog-v2.d.ts +60 -9
  138. package/dist/registry-schema/catalog-v2.js +1 -1
  139. package/dist/search-result-provider.d.ts +95 -0
  140. package/dist/search-result-provider.js +42 -0
  141. package/dist/status-bar.svelte.d.ts +26 -20
  142. package/dist/status-bar.svelte.js +1 -0
  143. package/dist/storage/app-database.d.ts +9 -23
  144. package/dist/storage/app-database.js +41 -34
  145. package/dist/storage/desktop-native.d.ts +5 -3
  146. package/dist/storage/desktop-native.js +59 -6
  147. package/dist/storage/directory-watcher.d.ts +27 -0
  148. package/dist/storage/directory-watcher.js +201 -0
  149. package/dist/storage/fs.d.ts +14 -27
  150. package/dist/storage/fs.js +2 -168
  151. package/dist/storage/index-projection.js +33 -15
  152. package/dist/storage/memory-vault-adapter.d.ts +2 -0
  153. package/dist/storage/memory-vault-adapter.js +14 -1
  154. package/dist/storage/path.d.ts +6 -2
  155. package/dist/storage/path.js +8 -2
  156. package/dist/storage/projection-query-candidates.d.ts +3 -0
  157. package/dist/storage/projection-query-candidates.js +69 -0
  158. package/dist/storage/remote-app-database.d.ts +2 -2
  159. package/dist/storage/remote-app-database.js +1 -1
  160. package/dist/storage/runtime.d.ts +3 -0
  161. package/dist/storage/runtime.js +3 -0
  162. package/dist/storage/search-embedding-record.d.ts +3 -0
  163. package/dist/storage/search-embedding-record.js +18 -0
  164. package/dist/storage/turso-app-database.d.ts +2 -0
  165. package/dist/storage/turso-app-database.js +67 -30
  166. package/dist/storage/vault-listing.d.ts +3 -0
  167. package/dist/storage/vault-listing.js +36 -0
  168. package/dist/storage/vault-reconciliation.d.ts +19 -0
  169. package/dist/storage/vault-reconciliation.js +137 -0
  170. package/dist/storage/vault-session.d.ts +8 -0
  171. package/dist/storage/vault-session.js +10 -2
  172. package/dist/storage/vault.svelte.d.ts +53 -4
  173. package/dist/storage/vault.svelte.js +223 -87
  174. package/dist/sync-editor-flush.d.ts +6 -0
  175. package/dist/sync-editor-flush.js +22 -0
  176. package/dist/sync-host-local.d.ts +15 -0
  177. package/dist/sync-host-local.js +228 -0
  178. package/dist/sync-host-security.d.ts +24 -9
  179. package/dist/sync-host-security.js +47 -14
  180. package/dist/sync-host.d.ts +33 -4
  181. package/dist/sync-host.js +160 -10
  182. package/dist/sync-repository.d.ts +106 -0
  183. package/dist/sync-repository.js +18 -0
  184. package/dist/vault-change-views.d.ts +4 -0
  185. package/dist/vault-change-views.js +43 -0
  186. package/dist/vault-changes.d.ts +37 -0
  187. package/dist/vault-changes.js +228 -0
  188. package/dist/view.svelte.d.ts +182 -149
  189. package/dist/view.svelte.js +39 -21
  190. package/dist/workspace-close-guard.d.ts +3 -0
  191. package/dist/workspace-close-guard.js +35 -0
  192. package/dist/workspace-host-internal.js +10 -2
  193. package/dist/workspace-layout-normalizer.d.ts +4 -2
  194. package/dist/workspace-layout-normalizer.js +5 -5
  195. package/dist/workspace-view-document.d.ts +3 -0
  196. package/dist/workspace-view-document.js +12 -0
  197. package/dist/workspace.svelte.d.ts +13 -2
  198. package/dist/workspace.svelte.js +371 -82
  199. package/package.json +61 -8
@@ -0,0 +1,29 @@
1
+ export type DatabaseMethod = "migrate" | "beginSearchIndexingBatch" | "endSearchIndexingBatch" | "configureSearchEmbeddingProvider" | "getSearchEmbeddingProvider" | "getSearchEmbeddingRuntimeStatus" | "getSearchIndexStats" | "getMeta" | "setMeta" | "scanMeta" | "transactMeta" | "getNotebookState" | "setNotebookState" | "deleteNotebookState" | "loadMetadataSnapshot" | "saveMetadataSnapshot" | "getFileHistory" | "storeFileHistoryRevision" | "listNotifications" | "upsertNotification" | "markNotificationRead" | "clearNotification" | "clearAllNotifications" | "getMemorySourceState" | "listMemorySourceStates" | "upsertMemorySourceState" | "deleteMemorySourceState" | "upsertMemoryCandidate" | "queryMemoryCandidates" | "deleteMemoryCandidatesByConversation" | "recordMemoryRecallSignal" | "listMemoryRecallSignals" | "claimMemoryJob" | "updateMemoryJob" | "listMemoryJobs" | "clearMemoryDerivedState" | "getChangeRevision" | "upsertIndexedFile" | "getIndexedFile" | "listIndexedFileManifest" | "queryIndexedMetadata" | "queryIndexedMetadataPage" | "queryMetadataFacets" | "queryMetadataLinks" | "deleteIndexedFile" | "renameIndexedFile" | "upsertSearchDocument" | "deleteSearchDocument" | "getSearchDocument" | "listSearchDocumentManifest" | "listSearchDocuments" | "rebuildSearchIndex" | "searchDocuments" | "searchDocumentPaths" | "upsertTaskProjection" | "deleteTaskProjection" | "queryTasks" | "getTaskRow" | "listChildLinks" | "listTaskDescendants" | "registerProjectionDefinition" | "unregisterProjectionDefinition" | "replaceProjectionSource" | "markProjectionSourceError" | "deleteProjectionSource" | "queryProjection" | "getProjectionRow" | "queryRelated";
2
+ export type DatabaseValue = {
3
+ kind: "undefined";
4
+ } | {
5
+ kind: "value";
6
+ value: unknown;
7
+ };
8
+ export type DatabaseCall = {
9
+ method: DatabaseMethod;
10
+ args?: Array<DatabaseValue>;
11
+ };
12
+ export type ChangeDomain = "metadata" | "search" | "history" | "notification" | "notebook" | "task" | "projection" | "memory" | "meta";
13
+ export type ChangedPath = {
14
+ oldPath: string;
15
+ newPath: string;
16
+ };
17
+ export type ChangeSet = {
18
+ revision: number;
19
+ domains: Array<ChangeDomain>;
20
+ paths: Array<string>;
21
+ renamed?: Array<ChangedPath>;
22
+ reset?: boolean;
23
+ committedAt: number;
24
+ };
25
+ export type SearchChunkingSettings = {
26
+ targetChars: number;
27
+ breakpointWindowChars: number;
28
+ breakpointDecay: number;
29
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { NativeEditingIntents } from "./native-editing-intents";
2
+ /** One document's native fence; this class never writes document content. */
3
+ export declare class NativeEditingIntentLease {
4
+ #private;
5
+ private readonly port;
6
+ private readonly path;
7
+ private readonly savedText;
8
+ readonly id: `${string}-${string}-${string}-${string}-${string}`;
9
+ constructor(port: NativeEditingIntents, path: string, savedText: () => string | null | undefined);
10
+ get retired(): boolean;
11
+ get yielded(): boolean;
12
+ ready(): Promise<void>;
13
+ changed(): Promise<void>;
14
+ yield(): Promise<void>;
15
+ resume(): Promise<void>;
16
+ retire(): Promise<void>;
17
+ }
@@ -0,0 +1,188 @@
1
+ /** One document's native fence; this class never writes document content. */
2
+ export class NativeEditingIntentLease {
3
+ port;
4
+ path;
5
+ savedText;
6
+ id = crypto.randomUUID();
7
+ #generation = 1;
8
+ #revision = 0;
9
+ #tail = Promise.resolve();
10
+ #admission;
11
+ #changes = new Map();
12
+ #resuming;
13
+ #retiring = false;
14
+ #state = "unknown";
15
+ constructor(port, path, savedText) {
16
+ this.port = port;
17
+ this.path = path;
18
+ this.savedText = savedText;
19
+ }
20
+ get retired() {
21
+ return this.#state === "retired";
22
+ }
23
+ get yielded() {
24
+ return this.#state === "yielded";
25
+ }
26
+ #valid(value) {
27
+ if (value.kind !== "state" ||
28
+ value.state.editor_id !== this.id ||
29
+ value.state.path !== this.path ||
30
+ value.state.detached)
31
+ throw new Error(`Native editor intent is unavailable: ${this.path}`);
32
+ return value.state;
33
+ }
34
+ async #transition(command, accepts) {
35
+ try {
36
+ const value = await this.port.call(command);
37
+ if (!accepts(value))
38
+ throw new Error("Native editor intent reply changed");
39
+ return value;
40
+ }
41
+ catch (error) {
42
+ // Status can prove an applied transition. It never retries file writes.
43
+ try {
44
+ const observed = await this.port.call({
45
+ kind: "inspect",
46
+ editor_id: this.id,
47
+ });
48
+ if (accepts(observed))
49
+ return observed;
50
+ }
51
+ catch {
52
+ /* Preserve the definitive transition error and local protection. */
53
+ }
54
+ this.#state = "unknown";
55
+ throw error;
56
+ }
57
+ }
58
+ ready() {
59
+ if (this.#state === "retired")
60
+ return Promise.reject(new Error("Native editor intent is retired"));
61
+ if (this.#admission)
62
+ return this.#admission;
63
+ this.#admission = this.#transition({ kind: "acquire", editor_id: this.id, path: this.path }, (value) => {
64
+ const state = this.#valid(value);
65
+ return (state.generation === 1 &&
66
+ state.revision === 0 &&
67
+ state.active &&
68
+ !state.dirty);
69
+ }).then(() => {
70
+ this.#state = "active";
71
+ });
72
+ void this.#admission.catch(() => {
73
+ this.#admission = undefined;
74
+ });
75
+ return this.#admission;
76
+ }
77
+ changed() {
78
+ const revision = ++this.#revision;
79
+ this.#changes.set(revision, {
80
+ kind: "changed",
81
+ editor_id: this.id,
82
+ generation: this.#generation,
83
+ revision,
84
+ });
85
+ const changed = this.#tail
86
+ .catch(() => { })
87
+ .then(() => this.#settleChanges());
88
+ this.#tail = changed;
89
+ // Native failures are observed by ready/flush/guard, including debounce callers.
90
+ void changed.catch(() => { });
91
+ return changed;
92
+ }
93
+ async #settleChanges() {
94
+ await this.ready();
95
+ for (const [revision, command] of this.#changes) {
96
+ await this.#transition(command, (value) => {
97
+ const state = this.#valid(value);
98
+ return (state.generation === this.#generation &&
99
+ state.revision === revision &&
100
+ state.active &&
101
+ state.dirty);
102
+ });
103
+ this.#changes.delete(revision);
104
+ }
105
+ }
106
+ async #reconcile() {
107
+ const value = await this.port.call({ kind: "inspect", editor_id: this.id });
108
+ if (value.kind === "retired" && this.#retiring) {
109
+ this.#state = "retired";
110
+ return;
111
+ }
112
+ const state = this.#valid(value);
113
+ if (state.revision !== this.#revision ||
114
+ (state.generation !== this.#generation &&
115
+ !(state.active && state.generation === this.#resuming)))
116
+ throw new Error(`Native editor intent requires recovery: ${this.path}`);
117
+ this.#generation = state.generation;
118
+ this.#state = state.active ? "active" : "yielded";
119
+ this.#resuming = undefined;
120
+ }
121
+ yield() {
122
+ return this.#yield(true);
123
+ }
124
+ async #yield(requireSavedMatch) {
125
+ await this.ready();
126
+ await this.#tail.catch(() => { });
127
+ await this.#settleChanges();
128
+ if (this.#state === "unknown")
129
+ await this.#reconcile();
130
+ if (this.#state === "yielded")
131
+ return;
132
+ const saved = this.savedText();
133
+ const current = await this.port.read(this.path);
134
+ if (requireSavedMatch &&
135
+ saved !== undefined &&
136
+ saved !== (current?.text ?? null))
137
+ throw Object.assign(new Error(`Saved editor content changed: ${this.path}`), { code: "ECONFLICT" });
138
+ const expected = current
139
+ ? { kind: "version", version: current.version }
140
+ : { kind: "missing" };
141
+ await this.#transition({
142
+ kind: "yield",
143
+ editor_id: this.id,
144
+ generation: this.#generation,
145
+ revision: this.#revision,
146
+ expected,
147
+ }, (value) => {
148
+ const state = this.#valid(value);
149
+ return (state.generation === this.#generation &&
150
+ state.revision === this.#revision &&
151
+ !state.active &&
152
+ !state.dirty);
153
+ });
154
+ this.#state = "yielded";
155
+ }
156
+ async resume() {
157
+ await this.ready();
158
+ await this.#tail.catch(() => { });
159
+ await this.#settleChanges();
160
+ if (this.#state === "unknown")
161
+ await this.#reconcile();
162
+ if (this.#state === "active")
163
+ return;
164
+ const generation = this.#generation + 1;
165
+ this.#resuming = generation;
166
+ await this.#transition({ kind: "resume", editor_id: this.id, generation }, (value) => {
167
+ const state = this.#valid(value);
168
+ return (state.generation === generation &&
169
+ state.revision === this.#revision &&
170
+ state.active);
171
+ });
172
+ this.#generation = generation;
173
+ this.#resuming = undefined;
174
+ this.#state = "active";
175
+ }
176
+ async retire() {
177
+ if (this.#state === "unknown" && this.#retiring)
178
+ await this.#reconcile();
179
+ if (this.retired)
180
+ return;
181
+ // The registry permits terminal retirement only after the producer is clean.
182
+ // External deletion/rename may change saved bytes; no mutation follows.
183
+ await this.#yield(false);
184
+ this.#retiring = true;
185
+ await this.#transition({ kind: "retire", editor_id: this.id, generation: this.#generation }, (value) => value.kind === "retired");
186
+ this.#state = "retired";
187
+ }
188
+ }
@@ -0,0 +1,70 @@
1
+ export type ExpectedVersion = {
2
+ kind: "missing";
3
+ } | {
4
+ kind: "version";
5
+ version: string;
6
+ };
7
+ export type TextSnapshot = {
8
+ path: string;
9
+ text: string;
10
+ version: string;
11
+ };
12
+ export type EditingIntentState = {
13
+ editor_id: string;
14
+ path: string;
15
+ generation: number;
16
+ revision: number;
17
+ dirty: boolean;
18
+ active: boolean;
19
+ detached: boolean;
20
+ };
21
+ export type EditingIntentCommand = {
22
+ kind: "acquire";
23
+ editor_id: string;
24
+ path: string;
25
+ } | {
26
+ kind: "changed";
27
+ editor_id: string;
28
+ generation: number;
29
+ revision: number;
30
+ } | {
31
+ kind: "yield";
32
+ editor_id: string;
33
+ generation: number;
34
+ revision: number;
35
+ expected: ExpectedVersion;
36
+ } | {
37
+ kind: "resume";
38
+ editor_id: string;
39
+ generation: number;
40
+ } | {
41
+ kind: "retire";
42
+ editor_id: string;
43
+ generation: number;
44
+ } | {
45
+ kind: "recover";
46
+ editor_id: string;
47
+ generation: number;
48
+ revision: number;
49
+ } | {
50
+ kind: "discard";
51
+ editor_id: string;
52
+ generation: number;
53
+ revision: number;
54
+ } | {
55
+ kind: "inspect";
56
+ editor_id: string;
57
+ } | {
58
+ kind: "list";
59
+ };
60
+ export type EditingIntentValue = {
61
+ kind: "state";
62
+ state: EditingIntentState;
63
+ } | {
64
+ kind: "states";
65
+ states: Array<EditingIntentState>;
66
+ } | {
67
+ kind: "retired";
68
+ } | {
69
+ kind: "missing";
70
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { EditingIntentCommand, EditingIntentValue, TextSnapshot } from "./native-editing-intents-protocol";
2
+ export type * from "./native-editing-intents-protocol";
3
+ /** An admitted native fence; persistence stays with existing App writers. */
4
+ export interface NativeEditingIntents {
5
+ call(command: EditingIntentCommand): Promise<EditingIntentValue>;
6
+ /** Expected absence is null; unavailable or unreadable files reject. */
7
+ read(path: string): Promise<TextSnapshot | null>;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { App } from "./context.svelte";
2
+ /** Share one disposable Problems collection across every shell of the same App. */
3
+ export declare function bindNativeEditingProblems(app: App): () => void;
@@ -0,0 +1,180 @@
1
+ const prefix = "lapis-editing-claim:";
2
+ const bindings = new WeakMap();
3
+ const claimCode = (state) => `claim:${state.editor_id}:${state.generation}:${state.revision}:${+state.active}${+state.dirty}${+state.detached}`;
4
+ const issueCode = (issue) => `admission:${issue.registration}:${issue.id}:${issue.status}`;
5
+ /** Share one disposable Problems collection across every shell of the same App. */
6
+ export function bindNativeEditingProblems(app) {
7
+ const recovery = app.fileEditingSessions.nativeRecovery;
8
+ if (!recovery)
9
+ return () => { };
10
+ let binding = bindings.get(app);
11
+ if (!binding) {
12
+ binding = createBinding(app);
13
+ bindings.set(app, binding);
14
+ }
15
+ const owned = binding;
16
+ owned.references++;
17
+ let held = true;
18
+ return () => {
19
+ if (!held)
20
+ return;
21
+ held = false;
22
+ if (--owned.references === 0) {
23
+ owned.dispose();
24
+ bindings.delete(app);
25
+ }
26
+ };
27
+ }
28
+ function createBinding(app) {
29
+ const registry = app.fileEditingSessions;
30
+ const recovery = registry.nativeRecovery;
31
+ let active = true;
32
+ let actions = new Map();
33
+ let resources = new Set();
34
+ const run = async (operation) => {
35
+ if (!active)
36
+ return;
37
+ try {
38
+ await operation();
39
+ }
40
+ catch (error) {
41
+ if (active)
42
+ app.notifications.notify({
43
+ severity: "error",
44
+ source: "editing",
45
+ message: error instanceof Error ? error.message : String(error),
46
+ });
47
+ }
48
+ };
49
+ const openSavedFile = (path) => registry.track(async () => {
50
+ const file = app.vault.getFileByPath(path);
51
+ if (!file)
52
+ throw new Error("The saved file is no longer available");
53
+ const leaf = app.workspace.getLeaf("tab");
54
+ await leaf.openFile(file, {
55
+ state: { type: leaf.view.getViewType(), state: { mode: "preview" } },
56
+ });
57
+ app.workspace.setActiveLeaf(leaf);
58
+ await app.workspace.revealLeaf(leaf);
59
+ });
60
+ const collection = app.workspace.diagnostics.createCollection("lapis:editing-claims", {
61
+ label: "Editing",
62
+ buildItemMenu(menu, entry) {
63
+ const code = entry.diagnostic.code;
64
+ const action = typeof code === "string" ? actions.get(code) : undefined;
65
+ if (!active)
66
+ return;
67
+ if (!action) {
68
+ if (code === "editing-observation-unavailable")
69
+ menu.addItem((item) => item
70
+ .setTitle("Refresh editing claims")
71
+ .setIcon("refresh-cw")
72
+ .onClick(() => run(() => registry.track(() => recovery.refresh()))));
73
+ return;
74
+ }
75
+ const path = action.kind === "claim" ? action.state.path : action.issue.path;
76
+ menu.addItem((item) => item
77
+ .setTitle("Review saved file")
78
+ .setIcon("file-text")
79
+ .setDisabled(!app.vault.getFileByPath(path))
80
+ .onClick(() => run(() => openSavedFile(path))));
81
+ if (action.kind === "claim") {
82
+ const captured = action.state;
83
+ menu.addItem((item) => item
84
+ .setTitle("Recover editing claim")
85
+ .setIcon("rotate-ccw")
86
+ .setDisabled(!captured.detached)
87
+ .onClick(() => run(() => recovery.recover(captured))));
88
+ menu.addItem((item) => item
89
+ .setTitle("Discard editing claim")
90
+ .setIcon("trash-2")
91
+ .onClick(() => run(() => recovery.discard(captured))));
92
+ }
93
+ else {
94
+ const captured = action.issue;
95
+ menu.addItem((item) => item
96
+ .setTitle("Retry editing admission")
97
+ .setIcon("refresh-cw")
98
+ .setDisabled(captured.status !== "failed")
99
+ .onClick(() => run(() => registry.nativeIssues.retry(captured))));
100
+ }
101
+ },
102
+ });
103
+ const publish = () => {
104
+ if (!active)
105
+ return;
106
+ const grouped = new Map();
107
+ const next = new Map();
108
+ const add = (path, code, message, action) => {
109
+ const group = grouped.get(path) ?? { path, diagnostics: [] };
110
+ group.diagnostics.push({
111
+ message,
112
+ severity: "warning",
113
+ source: "Editing",
114
+ code,
115
+ });
116
+ grouped.set(path, group);
117
+ next.set(code, action);
118
+ };
119
+ const snapshot = recovery.snapshot();
120
+ for (const state of snapshot.claims)
121
+ add(state.path, claimCode(state), "Unresolved editing claim. Review the saved file before recovering or discarding this claim; unsaved draft content is not stored here.", { kind: "claim", state });
122
+ for (const issue of registry.nativeIssues.entries)
123
+ add(issue.path, issueCode(issue), issue.status === "pending"
124
+ ? `Editing ${issue.phase} is pending. Editable input remains protected.`
125
+ : `Editing ${issue.phase} failed: ${issue.message}. Retry explicitly to restore editable access.`, { kind: "local", issue });
126
+ actions = next;
127
+ const current = new Set(grouped.keys());
128
+ for (const path of resources)
129
+ if (!current.has(path))
130
+ collection.delete({ uri: prefix + encodeURIComponent(path) });
131
+ const updates = [...grouped.values()].map(({ path, diagnostics }) => [
132
+ {
133
+ uri: prefix + encodeURIComponent(path),
134
+ label: path,
135
+ detail: path,
136
+ icon: "file-warning",
137
+ },
138
+ diagnostics,
139
+ ]);
140
+ updates.push([
141
+ null,
142
+ snapshot.error
143
+ ? [
144
+ {
145
+ severity: "error",
146
+ source: "Editing",
147
+ code: "editing-observation-unavailable",
148
+ message: `Editing claims could not be checked: ${snapshot.error}. Existing protection remains active.`,
149
+ },
150
+ ]
151
+ : undefined,
152
+ ]);
153
+ collection.set(updates);
154
+ resources = current;
155
+ };
156
+ const stopNavigation = app.workspace.registerDiagnosticOpener(async (location) => {
157
+ if (!location.resource.uri.startsWith(prefix))
158
+ return false;
159
+ if (!active)
160
+ return true;
161
+ const path = decodeURIComponent(location.resource.uri.slice(prefix.length));
162
+ if (resources.has(path) && app.vault.getFileByPath(path))
163
+ await run(() => openSavedFile(path));
164
+ return true;
165
+ });
166
+ const stopRecovery = recovery.subscribe(publish);
167
+ const stopIssues = registry.nativeIssues.subscribe(publish);
168
+ const stopObserver = recovery.observe();
169
+ return {
170
+ references: 0,
171
+ dispose() {
172
+ active = false;
173
+ stopObserver();
174
+ stopIssues();
175
+ stopRecovery();
176
+ stopNavigation();
177
+ collection.dispose();
178
+ },
179
+ };
180
+ }
@@ -0,0 +1,24 @@
1
+ import type { EditingIntentState, NativeEditingIntents } from "./native-editing-intents";
2
+ export interface NativeEditingRecoverySnapshot {
3
+ readonly claims: readonly Readonly<EditingIntentState>[];
4
+ readonly error?: string;
5
+ }
6
+ type Track = <T>(operation: () => Promise<T>) => Promise<T>;
7
+ type Listener = (snapshot: NativeEditingRecoverySnapshot) => void;
8
+ /** Passive recovery metadata. Native intents contain no document or draft bytes. */
9
+ export declare class NativeEditingRecovery {
10
+ #private;
11
+ private readonly port;
12
+ private readonly track;
13
+ constructor(port: NativeEditingIntents, track: Track);
14
+ snapshot(): NativeEditingRecoverySnapshot;
15
+ subscribe(listener: Listener): () => void;
16
+ /** Shell-owned read-only observation; multiple shells share one poll. */
17
+ observe(): () => void;
18
+ refresh(): Promise<void>;
19
+ /** Hosts call this before destructive App/registry teardown. */
20
+ assertClear(): Promise<void>;
21
+ recover(captured: EditingIntentState): Promise<void>;
22
+ discard(captured: EditingIntentState): Promise<void>;
23
+ }
24
+ export {};