@lapis-notes/api 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +206 -0
- package/README.md +21 -2
- package/dist/__tests__/fixtures/indexed-mutations.json +6862 -0
- package/dist/__tests__/fixtures/memory-storage.json +309 -0
- package/dist/__tests__/fixtures/native-facade-contract.json +3224 -0
- package/dist/__tests__/fixtures/projection-query.json +195 -0
- package/dist/__tests__/fixtures/projection-values.json +60 -0
- package/dist/__tests__/fixtures/search-preparation.json +1513 -0
- package/dist/__tests__/fixtures/search-retrieval.json +15409 -0
- package/dist/__tests__/fixtures/search-semantic-model.json +3691 -0
- package/dist/__tests__/fixtures/search-semantic-retrieval.json +6603 -0
- package/dist/__tests__/fixtures/search-storage.json +409 -0
- package/dist/__tests__/fixtures/task-projection.json +428 -0
- package/dist/__tests__/indexed-mutation-support.js +262 -0
- package/dist/__tests__/projection-query-support.js +40 -0
- package/dist/__tests__/search-retrieval-support.js +47 -0
- package/dist/app-database-host.d.ts +1 -0
- package/dist/app-shell-plugin-persistence.d.ts +3 -2
- package/dist/app-shell-plugin-persistence.js +37 -3
- package/dist/cache.svelte.d.ts +8 -1
- package/dist/cache.svelte.js +103 -5
- package/dist/command.svelte.js +29 -2
- package/dist/components/editor/editor.css +4 -0
- package/dist/components/editor/editor.svelte +21 -9
- package/dist/components/editor/embedded-editor-surface.js +3 -1
- package/dist/components/editor/inline-title-alignment.d.ts +3 -0
- package/dist/components/editor/inline-title-alignment.js +57 -0
- package/dist/configuration.svelte.d.ts +270 -206
- package/dist/configuration.svelte.js +161 -86
- package/dist/context.svelte.d.ts +21 -3
- package/dist/context.svelte.js +63 -20
- package/dist/diagnostics.d.ts +8 -0
- package/dist/diagnostics.js +6 -0
- package/dist/editor-save-context.d.ts +12 -1
- package/dist/editor-save-context.js +32 -1
- package/dist/editor-view-registry.d.ts +10 -0
- package/dist/editor-view-registry.js +44 -4
- package/dist/editor.svelte.js +128 -37
- package/dist/embed-registry.d.ts +10 -0
- package/dist/embed-registry.js +15 -0
- package/dist/file-editing-failures.d.ts +2 -0
- package/dist/file-editing-failures.js +29 -0
- package/dist/file-editing-native-issues.d.ts +19 -0
- package/dist/file-editing-native-issues.js +65 -0
- package/dist/file-editing-sessions.d.ts +56 -8
- package/dist/file-editing-sessions.js +185 -15
- package/dist/file-manager.d.ts +8 -1
- package/dist/file-manager.js +37 -3
- package/dist/file-state-store.d.ts +5 -0
- package/dist/file-state-store.js +34 -0
- package/dist/file-text-conflicts.d.ts +25 -0
- package/dist/file-text-conflicts.js +35 -0
- package/dist/file-text-mutation.d.ts +13 -0
- package/dist/file-text-mutation.js +100 -0
- package/dist/file-text-persistence.d.ts +36 -0
- package/dist/file-text-persistence.js +234 -0
- package/dist/file-text-state-writer.d.ts +34 -0
- package/dist/file-text-state-writer.js +183 -0
- package/dist/files-configuration.d.ts +20 -0
- package/dist/files-configuration.js +47 -0
- package/dist/generated/plugin-host-modules.schema.generated.d.ts +87 -37
- package/dist/generated/plugin-host-modules.schema.generated.js +102 -37
- package/dist/glob.d.ts +3 -0
- package/dist/glob.js +2 -1
- package/dist/icons/changes.d.ts +3 -0
- package/dist/icons/changes.js +16 -0
- package/dist/icons/index.d.ts +5 -1
- package/dist/icons/index.js +44 -3
- package/dist/icons/registry.d.ts +35 -0
- package/dist/icons/registry.js +154 -0
- package/dist/icons/safe-svg.d.ts +2 -0
- package/dist/icons/safe-svg.js +44 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/lapis-extension.d.ts +6 -2
- package/dist/lapis-extension.js +2 -0
- package/dist/metadata.svelte.js +45 -2
- package/dist/native-background-indexing.d.ts +48 -0
- package/dist/native-background-indexing.js +208 -0
- package/dist/native-database-protocol.d.ts +29 -0
- package/dist/native-database-protocol.js +1 -0
- package/dist/native-editing-intent-lease.d.ts +17 -0
- package/dist/native-editing-intent-lease.js +188 -0
- package/dist/native-editing-intents-protocol.d.ts +70 -0
- package/dist/native-editing-intents-protocol.js +1 -0
- package/dist/native-editing-intents.d.ts +8 -0
- package/dist/native-editing-intents.js +1 -0
- package/dist/native-editing-problems.d.ts +3 -0
- package/dist/native-editing-problems.js +180 -0
- package/dist/native-editing-recovery.d.ts +24 -0
- package/dist/native-editing-recovery.js +198 -0
- package/dist/native-editing-session.d.ts +51 -0
- package/dist/native-editing-session.js +251 -0
- package/dist/native-history-index-protocol.d.ts +80 -0
- package/dist/native-history-index-protocol.js +1 -0
- package/dist/native-history-index.d.ts +6 -0
- package/dist/native-history-index.js +1 -0
- package/dist/native-indexing-protocol.d.ts +63 -0
- package/dist/native-indexing-protocol.js +1 -0
- package/dist/native-participant-protocol.d.ts +67 -0
- package/dist/native-participant-protocol.js +3 -0
- package/dist/native-queue-protocol.d.ts +73 -0
- package/dist/native-queue-protocol.js +3 -0
- package/dist/native-tasks-protocol.d.ts +264 -0
- package/dist/native-tasks-protocol.js +1 -0
- package/dist/native-tasks.d.ts +35 -0
- package/dist/native-tasks.js +79 -0
- package/dist/native-turn-protocol.d.ts +81 -0
- package/dist/native-turn-protocol.js +1 -0
- package/dist/native-turns.d.ts +29 -0
- package/dist/native-turns.js +2 -0
- package/dist/plugin-asset-server.js +3 -2
- package/dist/plugin-dependency-resolver.d.ts +1 -1
- package/dist/plugin-distribution/compatibility.d.ts +1 -1
- package/dist/plugin-distribution/compatibility.js +10 -5
- package/dist/plugin-distribution/installed-plugin-state.js +2 -1
- package/dist/plugin-distribution/installer.d.ts +6 -0
- package/dist/plugin-distribution/installer.js +90 -37
- package/dist/plugin-distribution/manager.d.ts +4 -1
- package/dist/plugin-distribution/manager.js +12 -5
- package/dist/plugin-distribution/registry-cache.js +2 -1
- package/dist/plugin-distribution/registry-source-state.js +2 -1
- package/dist/plugin-distribution/schemas.d.ts +26 -8
- package/dist/plugin-distribution/schemas.js +2 -1
- package/dist/plugin-distribution/types.d.ts +4 -0
- package/dist/plugin-manager.d.ts +20 -4
- package/dist/plugin-manager.js +228 -64
- package/dist/plugin-runtime-entry.d.ts +1 -1
- package/dist/plugin-runtime-entry.js +16 -0
- package/dist/plugin.d.ts +18 -2
- package/dist/plugin.js +93 -39
- package/dist/registry-schema/catalog-v1.d.ts +60 -9
- package/dist/registry-schema/catalog-v1.js +2 -1
- package/dist/registry-schema/catalog-v2.d.ts +60 -9
- package/dist/registry-schema/catalog-v2.js +1 -1
- package/dist/search-result-provider.d.ts +95 -0
- package/dist/search-result-provider.js +42 -0
- package/dist/status-bar.svelte.d.ts +26 -20
- package/dist/status-bar.svelte.js +1 -0
- package/dist/storage/app-database.d.ts +6 -2
- package/dist/storage/app-database.js +41 -34
- package/dist/storage/desktop-native.d.ts +5 -3
- package/dist/storage/desktop-native.js +59 -6
- package/dist/storage/directory-watcher.d.ts +27 -0
- package/dist/storage/directory-watcher.js +201 -0
- package/dist/storage/fs.d.ts +14 -27
- package/dist/storage/fs.js +2 -168
- package/dist/storage/index-projection.js +33 -15
- package/dist/storage/memory-vault-adapter.d.ts +2 -0
- package/dist/storage/memory-vault-adapter.js +14 -1
- package/dist/storage/path.d.ts +6 -2
- package/dist/storage/path.js +8 -2
- package/dist/storage/projection-query-candidates.d.ts +3 -0
- package/dist/storage/projection-query-candidates.js +69 -0
- package/dist/storage/remote-app-database.d.ts +2 -2
- package/dist/storage/remote-app-database.js +1 -1
- package/dist/storage/runtime.d.ts +3 -0
- package/dist/storage/runtime.js +3 -0
- package/dist/storage/search-embedding-record.d.ts +3 -0
- package/dist/storage/search-embedding-record.js +18 -0
- package/dist/storage/turso-app-database.d.ts +2 -0
- package/dist/storage/turso-app-database.js +67 -30
- package/dist/storage/vault-listing.d.ts +3 -0
- package/dist/storage/vault-listing.js +36 -0
- package/dist/storage/vault-reconciliation.d.ts +19 -0
- package/dist/storage/vault-reconciliation.js +137 -0
- package/dist/storage/vault-session.d.ts +8 -0
- package/dist/storage/vault-session.js +10 -2
- package/dist/storage/vault.svelte.d.ts +53 -4
- package/dist/storage/vault.svelte.js +223 -87
- package/dist/sync-host.d.ts +12 -3
- package/dist/sync-host.js +5 -1
- package/dist/vault-change-views.d.ts +4 -0
- package/dist/vault-change-views.js +43 -0
- package/dist/vault-changes.d.ts +37 -0
- package/dist/vault-changes.js +228 -0
- package/dist/view.svelte.d.ts +182 -149
- package/dist/view.svelte.js +11 -2
- package/dist/workspace-close-guard.d.ts +3 -0
- package/dist/workspace-close-guard.js +35 -0
- package/dist/workspace-host-internal.js +10 -2
- package/dist/workspace-layout-normalizer.d.ts +4 -2
- package/dist/workspace-layout-normalizer.js +5 -5
- package/dist/workspace-view-document.d.ts +3 -0
- package/dist/workspace-view-document.js +12 -0
- package/dist/workspace.svelte.d.ts +13 -2
- package/dist/workspace.svelte.js +371 -82
- package/package.json +45 -8
|
@@ -3,7 +3,10 @@ export interface EditorSaveSnapshot {
|
|
|
3
3
|
sequence: number;
|
|
4
4
|
documentGeneration: number;
|
|
5
5
|
}
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* One document's queued saves and failures; the editor supplies shared
|
|
8
|
+
* serialization.
|
|
9
|
+
*/
|
|
7
10
|
export declare class EditorSaveContext {
|
|
8
11
|
#private;
|
|
9
12
|
private readonly options;
|
|
@@ -15,6 +18,14 @@ export declare class EditorSaveContext {
|
|
|
15
18
|
released(): void;
|
|
16
19
|
});
|
|
17
20
|
get dirty(): boolean;
|
|
21
|
+
get latest(): EditorSaveSnapshot | undefined;
|
|
22
|
+
/** A protected explicit recovery may acknowledge the latest retained snapshot. */
|
|
23
|
+
accept(snapshot: EditorSaveSnapshot): void;
|
|
24
|
+
/**
|
|
25
|
+
* Discard only after the recovery owner has revalidated the external
|
|
26
|
+
* snapshot.
|
|
27
|
+
*/
|
|
28
|
+
discard(): void;
|
|
18
29
|
queue(snapshot: EditorSaveSnapshot, explicit?: boolean): Promise<void> | undefined;
|
|
19
30
|
flushSave(): Promise<void> | undefined;
|
|
20
31
|
cancelSave(): void;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import debounce from "lodash-es/debounce";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* One document's queued saves and failures; the editor supplies shared
|
|
4
|
+
* serialization.
|
|
5
|
+
*/
|
|
3
6
|
export class EditorSaveContext {
|
|
4
7
|
options;
|
|
5
8
|
#latest;
|
|
@@ -28,6 +31,34 @@ export class EditorSaveContext {
|
|
|
28
31
|
!!this.#failure ||
|
|
29
32
|
this.#unverified);
|
|
30
33
|
}
|
|
34
|
+
get latest() {
|
|
35
|
+
return this.#latest;
|
|
36
|
+
}
|
|
37
|
+
/** A protected explicit recovery may acknowledge the latest retained snapshot. */
|
|
38
|
+
accept(snapshot) {
|
|
39
|
+
this.#reset();
|
|
40
|
+
this.#completed = Math.max(this.#completed, snapshot.sequence);
|
|
41
|
+
this.options.saved(snapshot);
|
|
42
|
+
this.#releaseIfClean();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Discard only after the recovery owner has revalidated the external
|
|
46
|
+
* snapshot.
|
|
47
|
+
*/
|
|
48
|
+
discard() {
|
|
49
|
+
this.#reset();
|
|
50
|
+
this.#completed = Math.max(this.#completed, this.#latest?.sequence ?? 0);
|
|
51
|
+
this.#latest = undefined;
|
|
52
|
+
this.#releaseIfClean();
|
|
53
|
+
}
|
|
54
|
+
#reset() {
|
|
55
|
+
if (this.#pending.size)
|
|
56
|
+
throw new Error("Editor recovery requires settled saves");
|
|
57
|
+
this.cancelChange();
|
|
58
|
+
this.cancelSave();
|
|
59
|
+
this.#failure = undefined;
|
|
60
|
+
this.#unverified = false;
|
|
61
|
+
}
|
|
31
62
|
queue(snapshot, explicit = false) {
|
|
32
63
|
this.#latest = snapshot;
|
|
33
64
|
if (this.options.unownedExternal)
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { EventDispatcher } from "./events";
|
|
2
|
+
export type ExtensionRegistrationOptions = {
|
|
3
|
+
fileIcon?: string;
|
|
4
|
+
};
|
|
2
5
|
export type EditorViewPriority = "default" | "option" | "exclusive";
|
|
3
6
|
export type EditorViewContribution = {
|
|
4
7
|
id: string;
|
|
5
8
|
viewType?: string;
|
|
6
9
|
label: string;
|
|
7
10
|
description?: string;
|
|
11
|
+
/** Registered API icon ID used for matching file rows. */
|
|
12
|
+
fileIcon?: string;
|
|
8
13
|
filenamePatterns?: string[];
|
|
9
14
|
priority?: EditorViewPriority;
|
|
10
15
|
pluginId?: string;
|
|
@@ -23,8 +28,13 @@ export type EditorViewRegistryChange = {
|
|
|
23
28
|
export declare class EditorViewRegistry extends EventDispatcher<{
|
|
24
29
|
changed: [change: EditorViewRegistryChange];
|
|
25
30
|
}> {
|
|
31
|
+
private readonly layers;
|
|
26
32
|
private readonly views;
|
|
27
33
|
register(contribution: EditorViewContribution): () => void;
|
|
34
|
+
/** Overlay runtime metadata on the same plugin's admitted manifest entry. */
|
|
35
|
+
overlay(contribution: EditorViewContribution): () => void;
|
|
36
|
+
private removeLayer;
|
|
37
|
+
private setVisible;
|
|
28
38
|
upsert(contribution: EditorViewContribution): () => void;
|
|
29
39
|
update(id: string, contribution: Partial<EditorViewContribution>): boolean;
|
|
30
40
|
removeFilenamePatterns(id: string, patterns: readonly string[]): boolean;
|
|
@@ -19,6 +19,7 @@ function normalizeContribution(contribution) {
|
|
|
19
19
|
id,
|
|
20
20
|
viewType: normalizeString(contribution.viewType ?? id),
|
|
21
21
|
label,
|
|
22
|
+
fileIcon: contribution.fileIcon?.trim() || undefined,
|
|
22
23
|
filenamePatterns: uniqueStrings(contribution.filenamePatterns),
|
|
23
24
|
priority: contribution.priority ?? "option",
|
|
24
25
|
};
|
|
@@ -35,6 +36,7 @@ function mergeContribution(current, next) {
|
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
export class EditorViewRegistry extends EventDispatcher {
|
|
39
|
+
layers = new Map();
|
|
38
40
|
views = new Map();
|
|
39
41
|
register(contribution) {
|
|
40
42
|
const normalized = normalizeContribution(contribution);
|
|
@@ -42,12 +44,48 @@ export class EditorViewRegistry extends EventDispatcher {
|
|
|
42
44
|
console.warn(`Editor view ${normalized.id} is already registered.`);
|
|
43
45
|
return () => { };
|
|
44
46
|
}
|
|
47
|
+
const owner = {};
|
|
48
|
+
this.layers.set(normalized.id, [{ owner, contribution: normalized }]);
|
|
45
49
|
this.views.set(normalized.id, normalized);
|
|
46
50
|
this.emit("changed", { id: normalized.id, action: "registered" });
|
|
47
51
|
return () => {
|
|
48
|
-
this.
|
|
52
|
+
this.removeLayer(normalized.id, owner);
|
|
49
53
|
};
|
|
50
54
|
}
|
|
55
|
+
/** Overlay runtime metadata on the same plugin's admitted manifest entry. */
|
|
56
|
+
overlay(contribution) {
|
|
57
|
+
const current = this.views.get(contribution.id);
|
|
58
|
+
if (!current)
|
|
59
|
+
return this.register(contribution);
|
|
60
|
+
const normalized = mergeContribution(current, contribution);
|
|
61
|
+
const owner = {};
|
|
62
|
+
this.layers.get(normalized.id).push({ owner, contribution: normalized });
|
|
63
|
+
this.views.set(normalized.id, normalized);
|
|
64
|
+
this.emit("changed", { id: normalized.id, action: "updated" });
|
|
65
|
+
return () => this.removeLayer(normalized.id, owner);
|
|
66
|
+
}
|
|
67
|
+
removeLayer(id, owner) {
|
|
68
|
+
const entries = this.layers.get(id);
|
|
69
|
+
const index = entries?.findIndex((entry) => entry.owner === owner) ?? -1;
|
|
70
|
+
if (!entries || index < 0)
|
|
71
|
+
return;
|
|
72
|
+
const visible = index === entries.length - 1;
|
|
73
|
+
entries.splice(index, 1);
|
|
74
|
+
if (!entries.length) {
|
|
75
|
+
this.unregister(id);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (visible) {
|
|
79
|
+
this.views.set(id, entries.at(-1).contribution);
|
|
80
|
+
this.emit("changed", { id, action: "updated" });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
setVisible(id, contribution) {
|
|
84
|
+
this.views.set(id, contribution);
|
|
85
|
+
const top = this.layers.get(id)?.at(-1);
|
|
86
|
+
if (top)
|
|
87
|
+
top.contribution = contribution;
|
|
88
|
+
}
|
|
51
89
|
upsert(contribution) {
|
|
52
90
|
const normalized = normalizeContribution(contribution);
|
|
53
91
|
const existing = this.views.get(normalized.id);
|
|
@@ -55,7 +93,7 @@ export class EditorViewRegistry extends EventDispatcher {
|
|
|
55
93
|
return this.register(normalized);
|
|
56
94
|
}
|
|
57
95
|
const merged = mergeContribution(existing, normalized);
|
|
58
|
-
this.
|
|
96
|
+
this.setVisible(merged.id, merged);
|
|
59
97
|
this.emit("changed", { id: merged.id, action: "updated" });
|
|
60
98
|
return () => { };
|
|
61
99
|
}
|
|
@@ -70,7 +108,7 @@ export class EditorViewRegistry extends EventDispatcher {
|
|
|
70
108
|
id: existing.id,
|
|
71
109
|
label: contribution.label ?? existing.label,
|
|
72
110
|
});
|
|
73
|
-
this.
|
|
111
|
+
this.setVisible(existing.id, merged);
|
|
74
112
|
this.emit("changed", { id: existing.id, action: "updated" });
|
|
75
113
|
return true;
|
|
76
114
|
}
|
|
@@ -86,14 +124,16 @@ export class EditorViewRegistry extends EventDispatcher {
|
|
|
86
124
|
}
|
|
87
125
|
if (filenamePatterns.length === 0 && existing.source === "compat") {
|
|
88
126
|
this.views.delete(id);
|
|
127
|
+
this.layers.delete(id);
|
|
89
128
|
this.emit("changed", { id, action: "unregistered" });
|
|
90
129
|
return true;
|
|
91
130
|
}
|
|
92
|
-
this.
|
|
131
|
+
this.setVisible(id, { ...existing, filenamePatterns });
|
|
93
132
|
this.emit("changed", { id, action: "updated" });
|
|
94
133
|
return true;
|
|
95
134
|
}
|
|
96
135
|
unregister(id) {
|
|
136
|
+
this.layers.delete(id);
|
|
97
137
|
const deleted = this.views.delete(id);
|
|
98
138
|
if (deleted) {
|
|
99
139
|
this.emit("changed", { id, action: "unregistered" });
|
package/dist/editor.svelte.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Annotation, Compartment, EditorState, Transaction, StateField, EditorSelection as CodeMirrorSelection, } from "@codemirror/state";
|
|
1
|
+
import { Annotation, Compartment, EditorState, StateEffect, Transaction, StateField, EditorSelection as CodeMirrorSelection, } from "@codemirror/state";
|
|
2
2
|
import { EditorView } from "@codemirror/view";
|
|
3
3
|
import { redo as redoCommand, undo as undoCommand } from "@codemirror/commands";
|
|
4
4
|
import { EventDispatcher } from "./events";
|
|
5
5
|
import { tick } from "svelte";
|
|
6
6
|
import { EditorSaveContext, } from "./editor-save-context";
|
|
7
7
|
import { resolveApplication } from "./application-compatibility";
|
|
8
|
+
import { FileTextPersistence } from "./file-text-persistence";
|
|
8
9
|
export const editorViewField = StateField.define({
|
|
9
10
|
create() {
|
|
10
11
|
return {
|
|
@@ -50,32 +51,35 @@ export function createEditorState(editor, doc, extensions = [], selection) {
|
|
|
50
51
|
return EditorState.create({
|
|
51
52
|
doc,
|
|
52
53
|
selection,
|
|
53
|
-
extensions:
|
|
54
|
-
editorViewField.init(() => ({
|
|
55
|
-
get app() {
|
|
56
|
-
return editor.app;
|
|
57
|
-
},
|
|
58
|
-
editor,
|
|
59
|
-
get file() {
|
|
60
|
-
return editor.file;
|
|
61
|
-
},
|
|
62
|
-
})),
|
|
63
|
-
editor.protectionExtensions(),
|
|
64
|
-
EditorState.transactionFilter.of((transaction) => transaction.docChanged &&
|
|
65
|
-
editor.editingProtected &&
|
|
66
|
-
!transaction.annotation(syncAnnotation)
|
|
67
|
-
? []
|
|
68
|
-
: transaction),
|
|
69
|
-
EditorView.updateListener.of((v) => {
|
|
70
|
-
if (v.docChanged &&
|
|
71
|
-
!v.transactions.some((it) => it.annotation(syncAnnotation))) {
|
|
72
|
-
editor.queueChange(v.state.doc.toString());
|
|
73
|
-
}
|
|
74
|
-
}),
|
|
75
|
-
...extensions,
|
|
76
|
-
],
|
|
54
|
+
extensions: editorStateExtensions(editor, extensions),
|
|
77
55
|
});
|
|
78
56
|
}
|
|
57
|
+
function editorStateExtensions(editor, extensions) {
|
|
58
|
+
return [
|
|
59
|
+
editorViewField.init(() => ({
|
|
60
|
+
get app() {
|
|
61
|
+
return editor.app;
|
|
62
|
+
},
|
|
63
|
+
editor,
|
|
64
|
+
get file() {
|
|
65
|
+
return editor.file;
|
|
66
|
+
},
|
|
67
|
+
})),
|
|
68
|
+
editor.protectionExtensions(),
|
|
69
|
+
EditorState.transactionFilter.of((transaction) => transaction.docChanged &&
|
|
70
|
+
editor.editingProtected &&
|
|
71
|
+
!transaction.annotation(syncAnnotation)
|
|
72
|
+
? []
|
|
73
|
+
: transaction),
|
|
74
|
+
EditorView.updateListener.of((v) => {
|
|
75
|
+
if (v.docChanged &&
|
|
76
|
+
!v.transactions.some((it) => it.annotation(syncAnnotation))) {
|
|
77
|
+
editor.queueChange(v.state.doc.toString());
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
...extensions,
|
|
81
|
+
];
|
|
82
|
+
}
|
|
79
83
|
export function createEditor(editor, doc, ...extensions) {
|
|
80
84
|
return new EditorView({
|
|
81
85
|
doc,
|
|
@@ -169,7 +173,13 @@ export class Editor extends EventDispatcher {
|
|
|
169
173
|
#retireDocument() {
|
|
170
174
|
this.#document?.saves.retire();
|
|
171
175
|
}
|
|
176
|
+
#serializeWrite(run) {
|
|
177
|
+
const result = this.#writeTail.then(run);
|
|
178
|
+
this.#writeTail = result.catch(() => undefined);
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
172
181
|
#createDocument(application, file, persistence, writer) {
|
|
182
|
+
const initialText = this.data;
|
|
173
183
|
const document = {
|
|
174
184
|
application,
|
|
175
185
|
file,
|
|
@@ -179,15 +189,15 @@ export class Editor extends EventDispatcher {
|
|
|
179
189
|
};
|
|
180
190
|
document.saves = new EditorSaveContext({
|
|
181
191
|
unownedExternal: persistence === "external" && !writer,
|
|
182
|
-
serialize: (run) =>
|
|
183
|
-
const result = this.#writeTail.then(run);
|
|
184
|
-
this.#writeTail = result.catch(() => undefined);
|
|
185
|
-
return result;
|
|
186
|
-
},
|
|
192
|
+
serialize: (run) => this.#serializeWrite(run),
|
|
187
193
|
persist: async (snapshot) => {
|
|
194
|
+
if (document.unregister?.native)
|
|
195
|
+
await document.unregister.ready();
|
|
188
196
|
if (file?.path !== document.path)
|
|
189
197
|
throw new Error("Editor persistence target changed");
|
|
190
|
-
if (
|
|
198
|
+
if (document.text)
|
|
199
|
+
await document.text.persist(snapshot.content);
|
|
200
|
+
else if (persistence === "vault" && file)
|
|
191
201
|
await application.vault.modify(file, snapshot.content);
|
|
192
202
|
else if (persistence === "external" && writer)
|
|
193
203
|
await writer({ application, file, content: snapshot.content });
|
|
@@ -209,10 +219,69 @@ export class Editor extends EventDispatcher {
|
|
|
209
219
|
this.#documents.delete(document);
|
|
210
220
|
},
|
|
211
221
|
});
|
|
222
|
+
if (persistence === "vault" &&
|
|
223
|
+
file &&
|
|
224
|
+
application.vault.supportsConditionalTextWrites) {
|
|
225
|
+
document.text = new FileTextPersistence({
|
|
226
|
+
vault: application.vault,
|
|
227
|
+
file,
|
|
228
|
+
baseline: this.data,
|
|
229
|
+
conflicts: application.fileEditingSessions.textConflicts,
|
|
230
|
+
local: () => document.saves.latest?.content ?? this.data,
|
|
231
|
+
protect: () => ({
|
|
232
|
+
release: !this.destroyed && this.#document === document
|
|
233
|
+
? this.#holdEditing()
|
|
234
|
+
: () => undefined,
|
|
235
|
+
saved: () => {
|
|
236
|
+
const snapshot = document.saves.latest;
|
|
237
|
+
if (!snapshot)
|
|
238
|
+
throw new Error("No retained editor snapshot");
|
|
239
|
+
document.saves.accept(snapshot);
|
|
240
|
+
},
|
|
241
|
+
reset: (contents) => {
|
|
242
|
+
if (!this.destroyed && this.#document === document) {
|
|
243
|
+
this.#replaceContent(contents);
|
|
244
|
+
this.onChange(contents);
|
|
245
|
+
}
|
|
246
|
+
document.saves.discard();
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
settle: async () => {
|
|
250
|
+
await document.loading;
|
|
251
|
+
try {
|
|
252
|
+
await document.saves.flush(false);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (!document.text?.hasConflict)
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
serialize: (run) => this.#serializeWrite(run),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
212
262
|
if (file) {
|
|
213
263
|
if (!application.fileEditingSessions)
|
|
214
264
|
throw new Error("App has no file editing-session registry");
|
|
215
265
|
document.unregister = application.fileEditingSessions.register(file.path, {
|
|
266
|
+
native: {
|
|
267
|
+
hold: () => this.#holdEditing(),
|
|
268
|
+
savedText: () => document.text
|
|
269
|
+
? document.text.baseline
|
|
270
|
+
: (document.saves.latest?.content ?? initialText),
|
|
271
|
+
refreshSavedText: (contents) => {
|
|
272
|
+
if (document.loading || document.saves.dirty || !document.text)
|
|
273
|
+
throw new Error("Native editor refresh requires clean conditional text authority");
|
|
274
|
+
document.text.acceptBaseline(contents);
|
|
275
|
+
if (!this.destroyed &&
|
|
276
|
+
this.#document === document &&
|
|
277
|
+
contents !== null) {
|
|
278
|
+
if (this.getValue() !== contents)
|
|
279
|
+
this.#replaceContent(contents);
|
|
280
|
+
this.data = contents;
|
|
281
|
+
this.onChange(contents);
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
},
|
|
216
285
|
get dirty() {
|
|
217
286
|
return !!document.loading || document.saves.dirty;
|
|
218
287
|
},
|
|
@@ -294,7 +363,10 @@ export class Editor extends EventDispatcher {
|
|
|
294
363
|
return Promise.reject(new Error("Editor is destroyed"));
|
|
295
364
|
return this.#protectDocument(this.#currentDocument());
|
|
296
365
|
}
|
|
297
|
-
/**
|
|
366
|
+
/**
|
|
367
|
+
* Register and protect a target, then publish its file and contents
|
|
368
|
+
* atomically.
|
|
369
|
+
*/
|
|
298
370
|
async loadDocument(file, read, publish) {
|
|
299
371
|
if (this.destroyed)
|
|
300
372
|
throw new Error("Editor is destroyed");
|
|
@@ -331,6 +403,14 @@ export class Editor extends EventDispatcher {
|
|
|
331
403
|
this.#loading = load;
|
|
332
404
|
let published = false;
|
|
333
405
|
try {
|
|
406
|
+
if (document.unregister?.native) {
|
|
407
|
+
const admission = await Promise.race([
|
|
408
|
+
document.unregister.ready().then(() => ({ ready: true })),
|
|
409
|
+
cancellation,
|
|
410
|
+
]);
|
|
411
|
+
if ("canceled" in admission || canceled)
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
334
414
|
const drained = await Promise.race([
|
|
335
415
|
this.flushChanges().then(() => ({ drained: true })),
|
|
336
416
|
cancellation,
|
|
@@ -427,7 +507,9 @@ export class Editor extends EventDispatcher {
|
|
|
427
507
|
}
|
|
428
508
|
save = Object.assign(() => {
|
|
429
509
|
this.#assertWritable();
|
|
430
|
-
|
|
510
|
+
const document = this.#currentDocument();
|
|
511
|
+
document.unregister?.changed();
|
|
512
|
+
return document.saves.queue(this.captureChange(this.getValue()), true);
|
|
431
513
|
}, {
|
|
432
514
|
flush: () => this.#document?.saves.flushSave(),
|
|
433
515
|
cancel: () => {
|
|
@@ -444,7 +526,10 @@ export class Editor extends EventDispatcher {
|
|
|
444
526
|
/** Queue the latest editor contents for persistence/change notification. */
|
|
445
527
|
queueChange(data) {
|
|
446
528
|
this.#assertWritable();
|
|
447
|
-
this.#currentDocument()
|
|
529
|
+
const document = this.#currentDocument();
|
|
530
|
+
document.unregister?.changed();
|
|
531
|
+
document.saves.queue(this.captureChange(data));
|
|
532
|
+
document.text?.updateLocal(data);
|
|
448
533
|
}
|
|
449
534
|
/** Drain every captured document, including failed or retired saves. */
|
|
450
535
|
async flushChanges() {
|
|
@@ -534,6 +619,8 @@ export class Editor extends EventDispatcher {
|
|
|
534
619
|
this.#refreshError = undefined;
|
|
535
620
|
if (this.getValue() === contents) {
|
|
536
621
|
this.data = contents;
|
|
622
|
+
if (!this.#document?.saves.dirty)
|
|
623
|
+
this.#document?.text?.acceptBaseline(contents);
|
|
537
624
|
return;
|
|
538
625
|
}
|
|
539
626
|
if (this.getValue() !== this.data ||
|
|
@@ -575,10 +662,10 @@ export class Editor extends EventDispatcher {
|
|
|
575
662
|
}
|
|
576
663
|
span.setAttribute("editor.extension_count", editorExtensions.length);
|
|
577
664
|
span.setAttribute("file.extension", ext ?? "");
|
|
578
|
-
const content = this.getValue();
|
|
579
|
-
const selection = this.view.state.selection;
|
|
580
665
|
this.extensions = editorExtensions;
|
|
581
|
-
this.view.
|
|
666
|
+
this.view.dispatch({
|
|
667
|
+
effects: StateEffect.reconfigure.of(editorStateExtensions(this, this.extensions)),
|
|
668
|
+
});
|
|
582
669
|
return this;
|
|
583
670
|
}, {
|
|
584
671
|
attributes: { "file.extension": this.file?.extension ?? "" },
|
|
@@ -592,6 +679,8 @@ export class Editor extends EventDispatcher {
|
|
|
592
679
|
throw new Error("Editor is destroyed");
|
|
593
680
|
this.#documentGeneration++;
|
|
594
681
|
this.#refreshError = undefined;
|
|
682
|
+
if (!this.#document?.saves.dirty)
|
|
683
|
+
this.#document?.text?.acceptBaseline(content);
|
|
595
684
|
this.app.telemetry.measure("editor.set_value", (span) => {
|
|
596
685
|
span.setAttribute("editor.content_length", content.length);
|
|
597
686
|
span.setAttribute("file.extension", this.file?.extension ?? "");
|
|
@@ -609,6 +698,8 @@ export class Editor extends EventDispatcher {
|
|
|
609
698
|
#replaceContent(content, options = {}) {
|
|
610
699
|
this.#documentGeneration++;
|
|
611
700
|
this.#refreshError = undefined;
|
|
701
|
+
if (!this.#document?.saves.dirty)
|
|
702
|
+
this.#document?.text?.acceptBaseline(content);
|
|
612
703
|
this.data = content;
|
|
613
704
|
const annotations = [syncAnnotation.of(true)];
|
|
614
705
|
this.view.dispatch({
|
package/dist/embed-registry.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { App } from "./context.svelte";
|
|
2
|
+
import type { TFile } from "./storage/fs";
|
|
2
3
|
export type EmbedViewHandle = {
|
|
3
4
|
destroy?: () => void | Promise<void>;
|
|
4
5
|
};
|
|
@@ -7,9 +8,18 @@ export type EmbedView = (props: {
|
|
|
7
8
|
containerEl: HTMLElement;
|
|
8
9
|
state: Record<string, any>;
|
|
9
10
|
}) => void | EmbedViewHandle;
|
|
11
|
+
export type MarkdownEmbedContext = {
|
|
12
|
+
app: App;
|
|
13
|
+
containerEl: HTMLElement;
|
|
14
|
+
file: TFile;
|
|
15
|
+
};
|
|
16
|
+
export type MarkdownEmbedder = (props: MarkdownEmbedContext) => false | EmbedViewHandle;
|
|
10
17
|
export declare class EmbedRegistry {
|
|
18
|
+
#private;
|
|
11
19
|
readonly embedByExtension: Record<string, EmbedView>;
|
|
12
20
|
constructor();
|
|
21
|
+
registerMarkdownEmbedder(embedder: MarkdownEmbedder): () => void;
|
|
22
|
+
renderMarkdownEmbed(props: MarkdownEmbedContext): false | EmbedViewHandle;
|
|
13
23
|
register(extension: string, view: EmbedView): () => void;
|
|
14
24
|
get(extension: string): EmbedView | null;
|
|
15
25
|
}
|
package/dist/embed-registry.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
export class EmbedRegistry {
|
|
2
2
|
embedByExtension = {};
|
|
3
|
+
#markdownEmbedders = [];
|
|
3
4
|
constructor() { }
|
|
5
|
+
registerMarkdownEmbedder(embedder) {
|
|
6
|
+
this.#markdownEmbedders = [...this.#markdownEmbedders, embedder];
|
|
7
|
+
return () => {
|
|
8
|
+
this.#markdownEmbedders = this.#markdownEmbedders.filter((candidate) => candidate !== embedder);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
renderMarkdownEmbed(props) {
|
|
12
|
+
for (const embedder of this.#markdownEmbedders) {
|
|
13
|
+
const handle = embedder(props);
|
|
14
|
+
if (handle !== false)
|
|
15
|
+
return handle;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
4
19
|
register(extension, view) {
|
|
5
20
|
const normalizedExtension = extension.toLowerCase();
|
|
6
21
|
this.embedByExtension[normalizedExtension] = view;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Keep primary failures intact; cleanup may only re-report their object refs. */
|
|
2
|
+
export function unreportedCleanup(cleanup, primary) {
|
|
3
|
+
const reported = new Set();
|
|
4
|
+
const remember = (error) => {
|
|
5
|
+
if (error === null || typeof error !== "object" || reported.has(error))
|
|
6
|
+
return;
|
|
7
|
+
reported.add(error);
|
|
8
|
+
if (error instanceof AggregateError)
|
|
9
|
+
error.errors.forEach(remember);
|
|
10
|
+
};
|
|
11
|
+
primary.forEach(remember);
|
|
12
|
+
const retained = (error, ancestors) => {
|
|
13
|
+
if (reported.has(error))
|
|
14
|
+
return [];
|
|
15
|
+
if (!(error instanceof AggregateError) || ancestors.has(error))
|
|
16
|
+
return [error];
|
|
17
|
+
const next = new Set(ancestors).add(error);
|
|
18
|
+
const errors = error.errors.flatMap((child) => retained(child, next));
|
|
19
|
+
if (errors.length === error.errors.length &&
|
|
20
|
+
errors.every((child, index) => child === error.errors[index]))
|
|
21
|
+
return [error];
|
|
22
|
+
return errors.length
|
|
23
|
+
? [
|
|
24
|
+
new AggregateError(errors, error.message, "cause" in error ? { cause: error.cause } : undefined),
|
|
25
|
+
]
|
|
26
|
+
: [];
|
|
27
|
+
};
|
|
28
|
+
return cleanup.flatMap((error) => retained(error, new Set()));
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NativeEditingIssue, NativeRegisteredEditingSession } from "./native-editing-session";
|
|
2
|
+
export interface NativeEditingProblemIssue extends NativeEditingIssue {
|
|
3
|
+
readonly registration: string;
|
|
4
|
+
}
|
|
5
|
+
type Entry = {
|
|
6
|
+
path: string;
|
|
7
|
+
session: NativeRegisteredEditingSession;
|
|
8
|
+
retry(check: () => void): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
/** App-local failures remain visible even when native admission never succeeded. */
|
|
11
|
+
export declare class FileEditingNativeIssues {
|
|
12
|
+
#private;
|
|
13
|
+
get entries(): readonly NativeEditingProblemIssue[];
|
|
14
|
+
subscribe(listener: () => void): () => void;
|
|
15
|
+
/** @internal The editing registry owns registration and its guarded retry. */
|
|
16
|
+
register(path: string, session: NativeRegisteredEditingSession, retry: Entry["retry"]): () => void;
|
|
17
|
+
retry(captured: NativeEditingProblemIssue): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** App-local failures remain visible even when native admission never succeeded. */
|
|
2
|
+
export class FileEditingNativeIssues {
|
|
3
|
+
#entries = new Map();
|
|
4
|
+
#listeners = new Set();
|
|
5
|
+
get entries() {
|
|
6
|
+
return [...this.#entries].flatMap(([registration, entry]) => {
|
|
7
|
+
const issue = entry.session.issue;
|
|
8
|
+
return issue ? [Object.freeze({ ...issue, registration })] : [];
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
subscribe(listener) {
|
|
12
|
+
this.#listeners.add(listener);
|
|
13
|
+
listener();
|
|
14
|
+
return () => {
|
|
15
|
+
this.#listeners.delete(listener);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** @internal The editing registry owns registration and its guarded retry. */
|
|
19
|
+
register(path, session, retry) {
|
|
20
|
+
const registration = crypto.randomUUID();
|
|
21
|
+
this.#entries.set(registration, { path, session, retry });
|
|
22
|
+
const stop = session.subscribe(() => this.#notify());
|
|
23
|
+
this.#notify();
|
|
24
|
+
return () => {
|
|
25
|
+
stop();
|
|
26
|
+
if (this.#entries.delete(registration))
|
|
27
|
+
this.#notify();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
retry(captured) {
|
|
31
|
+
try {
|
|
32
|
+
const entry = this.#require(captured);
|
|
33
|
+
return entry.retry(() => {
|
|
34
|
+
this.#require(captured);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return Promise.reject(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
#require(captured) {
|
|
42
|
+
const entry = this.#entries.get(captured.registration);
|
|
43
|
+
const issue = entry?.session.issue;
|
|
44
|
+
if (!entry ||
|
|
45
|
+
entry.path !== captured.path ||
|
|
46
|
+
!issue ||
|
|
47
|
+
issue.id !== captured.id ||
|
|
48
|
+
issue.path !== captured.path ||
|
|
49
|
+
issue.phase !== captured.phase ||
|
|
50
|
+
captured.status !== "failed" ||
|
|
51
|
+
issue.status !== "failed")
|
|
52
|
+
throw new Error("Editing admission changed; refresh Problems");
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
#notify() {
|
|
56
|
+
for (const listener of this.#listeners) {
|
|
57
|
+
try {
|
|
58
|
+
listener();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error("Editing admission subscriber failed", error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|