@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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { convertParsedSVG, getIconData, iconToHTML, iconToSVG, parseSVGContent, replaceIDs, } from "@iconify/utils";
|
|
2
|
+
import { resolveRegisteredIcon } from "./index";
|
|
3
|
+
import { iconRevision, onIconsChanged } from "./changes";
|
|
4
|
+
import { assertSafeIconSvg } from "./safe-svg";
|
|
5
|
+
function loadPack(input) {
|
|
6
|
+
try {
|
|
7
|
+
return Promise.resolve("icons" in input ? input.icons : input.loader());
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
return Promise.reject(error);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function namespace(owner) {
|
|
14
|
+
if (!owner.trim())
|
|
15
|
+
throw new Error("Icon owner must not be empty.");
|
|
16
|
+
return ("plugin-" +
|
|
17
|
+
[...new TextEncoder().encode(owner)]
|
|
18
|
+
.map((x) => x.toString(16).padStart(2, "0"))
|
|
19
|
+
.join(""));
|
|
20
|
+
}
|
|
21
|
+
function localName(id) {
|
|
22
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(id))
|
|
23
|
+
throw new Error("Use a lowercase icon name with hyphens.");
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
function render(data) {
|
|
27
|
+
const rendered = iconToSVG(data);
|
|
28
|
+
const svg = iconToHTML(replaceIDs(rendered.body), rendered.attributes);
|
|
29
|
+
assertSafeIconSvg(svg);
|
|
30
|
+
return svg;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* App-owned artwork with an explicit compatibility fallback to legacy global
|
|
34
|
+
* icons.
|
|
35
|
+
*/
|
|
36
|
+
export class IconRegistry {
|
|
37
|
+
icons = new Map();
|
|
38
|
+
packs = new Map();
|
|
39
|
+
listeners = new Set();
|
|
40
|
+
cache = new Map();
|
|
41
|
+
revision = 0;
|
|
42
|
+
legacyRevision = iconRevision();
|
|
43
|
+
stopLegacy;
|
|
44
|
+
disposed = false;
|
|
45
|
+
subscribe = (listener) => {
|
|
46
|
+
if (this.disposed)
|
|
47
|
+
return () => { };
|
|
48
|
+
if (!this.stopLegacy)
|
|
49
|
+
this.stopLegacy = onIconsChanged(() => this.changed());
|
|
50
|
+
this.listeners.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
this.listeners.delete(listener);
|
|
53
|
+
if (!this.listeners.size) {
|
|
54
|
+
this.stopLegacy?.();
|
|
55
|
+
this.stopLegacy = undefined;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
changed() {
|
|
60
|
+
this.revision++;
|
|
61
|
+
this.cache.clear();
|
|
62
|
+
this.legacyRevision = iconRevision();
|
|
63
|
+
for (const listener of [...this.listeners])
|
|
64
|
+
listener();
|
|
65
|
+
}
|
|
66
|
+
registerIcon(owner, id, svg) {
|
|
67
|
+
if (this.disposed)
|
|
68
|
+
throw new Error("Icon registry is disposed.");
|
|
69
|
+
assertSafeIconSvg(svg);
|
|
70
|
+
const parsed = parseSVGContent(svg);
|
|
71
|
+
const data = parsed && convertParsedSVG(parsed);
|
|
72
|
+
if (!data)
|
|
73
|
+
throw new Error("Invalid icon SVG.");
|
|
74
|
+
const name = `${namespace(owner)}:${localName(id)}`;
|
|
75
|
+
const entry = { svg: render(data) };
|
|
76
|
+
this.icons.set(name, entry);
|
|
77
|
+
this.changed();
|
|
78
|
+
return {
|
|
79
|
+
id: name,
|
|
80
|
+
dispose: () => {
|
|
81
|
+
if (this.icons.get(name) === entry) {
|
|
82
|
+
this.icons.delete(name);
|
|
83
|
+
this.changed();
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
registerIconPack(owner, input) {
|
|
89
|
+
if (this.disposed)
|
|
90
|
+
throw new Error("Icon registry is disposed.");
|
|
91
|
+
const prefix = `${namespace(owner)}-${localName(input.name)}`;
|
|
92
|
+
const entry = { input };
|
|
93
|
+
this.packs.set(prefix, entry);
|
|
94
|
+
this.changed();
|
|
95
|
+
return {
|
|
96
|
+
prefix,
|
|
97
|
+
dispose: () => {
|
|
98
|
+
if (this.packs.get(prefix) === entry) {
|
|
99
|
+
this.packs.delete(prefix);
|
|
100
|
+
this.changed();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
resolveIcon(name) {
|
|
106
|
+
if (this.disposed)
|
|
107
|
+
return Promise.resolve({ status: "missing" });
|
|
108
|
+
if (this.legacyRevision !== iconRevision())
|
|
109
|
+
this.changed();
|
|
110
|
+
const cached = this.cache.get(name);
|
|
111
|
+
if (cached)
|
|
112
|
+
return cached;
|
|
113
|
+
const revision = this.revision;
|
|
114
|
+
const pending = this.lookup(name)
|
|
115
|
+
.then((result) => this.disposed || revision !== this.revision
|
|
116
|
+
? { status: "missing" }
|
|
117
|
+
: result)
|
|
118
|
+
.catch(() => ({ status: "failed" }));
|
|
119
|
+
this.cache.set(name, pending);
|
|
120
|
+
return pending;
|
|
121
|
+
}
|
|
122
|
+
resolve = async (name) => {
|
|
123
|
+
const result = await this.resolveIcon(name);
|
|
124
|
+
return result.status === "found" ? result.svg : null;
|
|
125
|
+
};
|
|
126
|
+
async lookup(name) {
|
|
127
|
+
const icon = this.icons.get(name);
|
|
128
|
+
if (icon)
|
|
129
|
+
return { status: "found", svg: icon.svg };
|
|
130
|
+
const [prefix, id, ...extra] = name.split(":");
|
|
131
|
+
const pack = this.packs.get(prefix);
|
|
132
|
+
if (pack && id && extra.length === 0) {
|
|
133
|
+
pack.promise ??= loadPack(pack.input);
|
|
134
|
+
const data = getIconData(await pack.promise, id);
|
|
135
|
+
return data
|
|
136
|
+
? { status: "found", svg: render(data) }
|
|
137
|
+
: { status: "missing" };
|
|
138
|
+
}
|
|
139
|
+
// Owned IDs never resolve through a different App's compatibility registry.
|
|
140
|
+
if (/^plugin-(?:[a-f0-9]{2})+(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?:/u.test(name))
|
|
141
|
+
return { status: "missing" };
|
|
142
|
+
return resolveRegisteredIcon(name);
|
|
143
|
+
}
|
|
144
|
+
dispose() {
|
|
145
|
+
this.disposed = true;
|
|
146
|
+
this.stopLegacy?.();
|
|
147
|
+
this.stopLegacy = undefined;
|
|
148
|
+
this.icons.clear();
|
|
149
|
+
this.packs.clear();
|
|
150
|
+
this.cache.clear();
|
|
151
|
+
this.listeners.clear();
|
|
152
|
+
this.revision++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Reject executable/external content before a host displays plugin SVG markup. */
|
|
2
|
+
export function assertSafeIconSvg(svg) {
|
|
3
|
+
const tags = new Set([
|
|
4
|
+
"svg",
|
|
5
|
+
"g",
|
|
6
|
+
"path",
|
|
7
|
+
"rect",
|
|
8
|
+
"circle",
|
|
9
|
+
"ellipse",
|
|
10
|
+
"line",
|
|
11
|
+
"polyline",
|
|
12
|
+
"polygon",
|
|
13
|
+
"defs",
|
|
14
|
+
"lineargradient",
|
|
15
|
+
"radialgradient",
|
|
16
|
+
"stop",
|
|
17
|
+
"clippath",
|
|
18
|
+
"mask",
|
|
19
|
+
"pattern",
|
|
20
|
+
"symbol",
|
|
21
|
+
"use",
|
|
22
|
+
"title",
|
|
23
|
+
"desc",
|
|
24
|
+
]);
|
|
25
|
+
if (/xml:base\s*=|<!|<\?|&#|\\|javascript\s*:|data\s*:/iu.test(svg))
|
|
26
|
+
throw new Error("Unsafe icon SVG.");
|
|
27
|
+
for (const match of svg.matchAll(/<\/?\s*([\w:-]+)/gu)) {
|
|
28
|
+
if (!tags.has(match[1].toLowerCase()))
|
|
29
|
+
throw new Error("Unsupported icon SVG element.");
|
|
30
|
+
}
|
|
31
|
+
if (/\s(?:on[\w:-]+)\s*=/iu.test(svg) ||
|
|
32
|
+
/@import|expression\s*\(/iu.test(svg))
|
|
33
|
+
throw new Error("Executable icon SVG.");
|
|
34
|
+
for (const match of svg.matchAll(/(?:\b(?:xlink:)?href\s*=\s*)(["'])(.*?)\1/giu)) {
|
|
35
|
+
if (!/^#[\w:.-]+$/u.test(match[2]))
|
|
36
|
+
throw new Error("External icon SVG reference.");
|
|
37
|
+
}
|
|
38
|
+
if (/\b(?:xlink:)?href\s*=\s*[^\s"']/iu.test(svg))
|
|
39
|
+
throw new Error("Unquoted icon SVG reference.");
|
|
40
|
+
for (const match of svg.matchAll(/url\s*\((.*?)\)/giu)) {
|
|
41
|
+
if (!/^\s*["']?#[\w:.-]+["']?\s*$/u.test(match[1]))
|
|
42
|
+
throw new Error("External icon SVG URL.");
|
|
43
|
+
}
|
|
44
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import "./enhance";
|
|
2
2
|
export * from "./editor.svelte";
|
|
3
3
|
export * from "./file-editing-sessions";
|
|
4
|
+
export { bindNativeEditingProblems } from "./native-editing-problems";
|
|
5
|
+
export type { NativeEditingIntents } from "./native-editing-intents";
|
|
4
6
|
export * from "./events";
|
|
5
7
|
export * from "./components/editor/extensions/autocomplete";
|
|
6
8
|
export * from "./components/editor/extensions/lint";
|
|
7
9
|
export * from "./components/editor/language-service";
|
|
8
10
|
export * from "./menu.svelte";
|
|
9
11
|
export * from "./cache.svelte";
|
|
12
|
+
export type { NativeBackgroundIndexing, NativeBackgroundIndexingObserver, NativeIndexingProgressPresentation, } from "./native-background-indexing";
|
|
10
13
|
export * from "./plugin";
|
|
11
14
|
export * from "./plugin-capability-facade";
|
|
12
15
|
export * from "./sync-host";
|
|
@@ -54,7 +57,7 @@ export * from "./configuration-option-source-registry";
|
|
|
54
57
|
export * from "./configuration-option-source-providers";
|
|
55
58
|
export * from "./plugin-settings";
|
|
56
59
|
export * from "./editor-view-registry";
|
|
57
|
-
export
|
|
60
|
+
export { normalizeGlobPath, normalizeEditorAssociationGlob, validateEditorAssociationGlob, matchesEditorAssociationGlob, hasEditorAssociationGlobMagic, compareEditorAssociationPatternSpecificity, type EditorAssociationGlobOptions, type EditorAssociationGlobValidation, } from "./glob";
|
|
58
61
|
export * from "./platform";
|
|
59
62
|
export * from "./suggest";
|
|
60
63
|
export * from "./markdown";
|
|
@@ -83,6 +86,7 @@ export * from "./promise-worker";
|
|
|
83
86
|
export * from "./search";
|
|
84
87
|
export * from "./search-query";
|
|
85
88
|
export * from "./search-document-provider";
|
|
89
|
+
export * from "./search-result-provider";
|
|
86
90
|
export * from "./daily-document-provider";
|
|
87
91
|
export * from "./loaders";
|
|
88
92
|
export * from "./bases";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "./enhance";
|
|
2
2
|
export * from "./editor.svelte";
|
|
3
3
|
export * from "./file-editing-sessions";
|
|
4
|
+
export { bindNativeEditingProblems } from "./native-editing-problems";
|
|
4
5
|
export * from "./events";
|
|
5
6
|
export * from "./components/editor/extensions/autocomplete";
|
|
6
7
|
export * from "./components/editor/extensions/lint";
|
|
@@ -54,7 +55,7 @@ export * from "./configuration-option-source-registry";
|
|
|
54
55
|
export * from "./configuration-option-source-providers";
|
|
55
56
|
export * from "./plugin-settings";
|
|
56
57
|
export * from "./editor-view-registry";
|
|
57
|
-
export
|
|
58
|
+
export { normalizeGlobPath, normalizeEditorAssociationGlob, validateEditorAssociationGlob, matchesEditorAssociationGlob, hasEditorAssociationGlobMagic, compareEditorAssociationPatternSpecificity, } from "./glob";
|
|
58
59
|
export * from "./platform";
|
|
59
60
|
export * from "./suggest";
|
|
60
61
|
export * from "./markdown";
|
|
@@ -83,6 +84,7 @@ export * from "./promise-worker";
|
|
|
83
84
|
export * from "./search";
|
|
84
85
|
export * from "./search-query";
|
|
85
86
|
export * from "./search-document-provider";
|
|
87
|
+
export * from "./search-result-provider";
|
|
86
88
|
export * from "./daily-document-provider";
|
|
87
89
|
export * from "./loaders";
|
|
88
90
|
export * from "./bases";
|
|
@@ -54,6 +54,7 @@ export interface LapisEditorViewContribution {
|
|
|
54
54
|
viewType?: string;
|
|
55
55
|
label: string;
|
|
56
56
|
description?: string;
|
|
57
|
+
fileIcon?: string;
|
|
57
58
|
filenamePatterns: string[];
|
|
58
59
|
priority?: "default" | "option" | "exclusive";
|
|
59
60
|
pluginId?: string;
|
|
@@ -86,7 +87,7 @@ export interface LapisStatusBarItemContribution {
|
|
|
86
87
|
id: string;
|
|
87
88
|
text?: string;
|
|
88
89
|
icon?: string;
|
|
89
|
-
alignment?: "left" | "right";
|
|
90
|
+
alignment?: "left" | "center" | "right";
|
|
90
91
|
priority?: number;
|
|
91
92
|
tooltip?: string;
|
|
92
93
|
command?: string;
|
|
@@ -130,7 +131,10 @@ export interface LapisExtensionManifest {
|
|
|
130
131
|
};
|
|
131
132
|
};
|
|
132
133
|
database?: {
|
|
133
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Query-native access avoids the one-release full MetadataCache snapshot
|
|
136
|
+
* lease.
|
|
137
|
+
*/
|
|
134
138
|
metadataAccess?: "queries" | "snapshot";
|
|
135
139
|
};
|
|
136
140
|
permissions?: LapisExtensionPermission[];
|
package/dist/lapis-extension.js
CHANGED
|
@@ -566,6 +566,7 @@ function isEditorViewContribution(value) {
|
|
|
566
566
|
Array.isArray(value.filenamePatterns) &&
|
|
567
567
|
value.filenamePatterns.length > 0 &&
|
|
568
568
|
value.filenamePatterns.every((entry) => typeof entry === "string") &&
|
|
569
|
+
(value.fileIcon === undefined || typeof value.fileIcon === "string") &&
|
|
569
570
|
(value.viewType === undefined || typeof value.viewType === "string") &&
|
|
570
571
|
(value.description === undefined ||
|
|
571
572
|
typeof value.description === "string") &&
|
|
@@ -595,6 +596,7 @@ function isStatusBarItemContribution(value) {
|
|
|
595
596
|
(typeof value.text === "string" || typeof value.icon === "string") &&
|
|
596
597
|
(value.alignment === undefined ||
|
|
597
598
|
value.alignment === "left" ||
|
|
599
|
+
value.alignment === "center" ||
|
|
598
600
|
value.alignment === "right") &&
|
|
599
601
|
(value.priority === undefined || typeof value.priority === "number") &&
|
|
600
602
|
(value.tooltip === undefined || typeof value.tooltip === "string") &&
|
package/dist/metadata.svelte.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { FileTextStateWriter } from "./file-text-state-writer";
|
|
2
|
+
import { TFile as VaultFile } from "./storage/fs";
|
|
1
3
|
import { inferMetadataPropertyType, inferMetadataType, normalizeMetadataValue, } from "./metadata-value";
|
|
2
|
-
import { joinPath, } from "./storage";
|
|
4
|
+
import { joinPath, normalizePath, } from "./storage";
|
|
3
5
|
import { debounce, get, has, set, unset } from "lodash-es";
|
|
4
6
|
import { onMount } from "svelte";
|
|
5
7
|
function notifyMetadataTypeError(message) {
|
|
@@ -21,6 +23,8 @@ export class MetadataTypeManager {
|
|
|
21
23
|
propertiesLoading = $state(false);
|
|
22
24
|
queryError = $state(null);
|
|
23
25
|
propertiesGeneration = 0;
|
|
26
|
+
typesBaseline;
|
|
27
|
+
typesWriter;
|
|
24
28
|
valueGenerations = new Map();
|
|
25
29
|
schedulePropertiesRefresh = debounce(() => void this.updateProperties(), 50);
|
|
26
30
|
constructor(app) {
|
|
@@ -40,6 +44,7 @@ export class MetadataTypeManager {
|
|
|
40
44
|
this.app.metadataCache.offref(metadataHandler);
|
|
41
45
|
this.app.metadataCache.offref(indexHandler);
|
|
42
46
|
this.schedulePropertiesRefresh.cancel();
|
|
47
|
+
this.typesWriter?.dispose();
|
|
43
48
|
};
|
|
44
49
|
});
|
|
45
50
|
this.load();
|
|
@@ -112,8 +117,13 @@ export class MetadataTypeManager {
|
|
|
112
117
|
load() {
|
|
113
118
|
const promises = [];
|
|
114
119
|
const file = this.app.vault.getFileByPath(joinPath(this.app.props.configPath, "../", "types.json"));
|
|
120
|
+
if (!file && !this.typesWriter)
|
|
121
|
+
this.typesBaseline = null;
|
|
115
122
|
if (file) {
|
|
116
123
|
promises.push(this.app.vault.read(file).then((data) => {
|
|
124
|
+
if (this.typesWriter)
|
|
125
|
+
return;
|
|
126
|
+
this.typesBaseline = data;
|
|
117
127
|
try {
|
|
118
128
|
const typeData = JSON.parse(data);
|
|
119
129
|
if ("types" in typeData) {
|
|
@@ -166,12 +176,43 @@ export class MetadataTypeManager {
|
|
|
166
176
|
return "unknown";
|
|
167
177
|
return type === "checkbox" ? "checkbox" : "unknown";
|
|
168
178
|
}
|
|
169
|
-
|
|
179
|
+
saveLegacy = debounce(() => {
|
|
170
180
|
const file = this.app.vault.getFileByPath(joinPath(this.app.props.configPath, "../", "types.json"));
|
|
171
181
|
if (file) {
|
|
172
182
|
return this.app.vault.modify(file, JSON.stringify(this.toJSON(), null, 2));
|
|
173
183
|
}
|
|
174
184
|
}, 500);
|
|
185
|
+
save = () => {
|
|
186
|
+
if (!this.app.vault.supportsConditionalTextWrites) {
|
|
187
|
+
this.saveLegacy();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
void this.getTypesWriter().set(JSON.stringify(this.toJSON(), null, 2))
|
|
191
|
+
.catch((error) => notifyMetadataTypeError(`Failed to save types.json: ${error}`));
|
|
192
|
+
};
|
|
193
|
+
getTypesWriter() {
|
|
194
|
+
if (this.typesWriter)
|
|
195
|
+
return this.typesWriter;
|
|
196
|
+
const path = normalizePath(joinPath(this.app.props.configPath, "../", "types.json"));
|
|
197
|
+
const file = this.app.vault.getFileByPath(path) ??
|
|
198
|
+
new VaultFile(path, { ctime: 0, mtime: 0, size: 0 }, null);
|
|
199
|
+
this.typesWriter ??= new FileTextStateWriter(this.app, file, this.typesBaseline ?? null, (text) => {
|
|
200
|
+
for (const key of Object.keys(this.types))
|
|
201
|
+
delete this.types[key];
|
|
202
|
+
try {
|
|
203
|
+
const value = JSON.parse(text || '{"types":{}}');
|
|
204
|
+
for (const [name, type] of Object.entries(value.types ?? {})) {
|
|
205
|
+
if (typeof type === "string")
|
|
206
|
+
this.types[name] = { name, type: type };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
notifyMetadataTypeError(`Failed to load types.json: ${error}`);
|
|
211
|
+
}
|
|
212
|
+
void this.updateProperties();
|
|
213
|
+
});
|
|
214
|
+
return this.typesWriter;
|
|
215
|
+
}
|
|
175
216
|
toJSON() {
|
|
176
217
|
const types = {};
|
|
177
218
|
Object.values(this.types).forEach((t) => {
|
|
@@ -182,6 +223,8 @@ export class MetadataTypeManager {
|
|
|
182
223
|
};
|
|
183
224
|
}
|
|
184
225
|
setType(field, type) {
|
|
226
|
+
if (this.app.vault.supportsConditionalTextWrites)
|
|
227
|
+
this.getTypesWriter().assertEditable();
|
|
185
228
|
this.types[field] = { name: field, type };
|
|
186
229
|
this.properties[field] ||= {
|
|
187
230
|
name: field,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { App } from "./context.svelte";
|
|
2
|
+
import type { IndexProjectionRegistration } from "./storage/index-projection";
|
|
3
|
+
import type { ContributionStatus, IndexingCommand, IndexingStatus, IndexingValue, SearchIndexingSelection } from "./native-indexing-protocol";
|
|
4
|
+
export type * from "./native-indexing-protocol";
|
|
5
|
+
/** An explicitly supplied, already admitted native vault transport. */
|
|
6
|
+
export interface NativeBackgroundIndexing {
|
|
7
|
+
call(command: IndexingCommand): Promise<IndexingValue>;
|
|
8
|
+
}
|
|
9
|
+
export interface NativeIndexingProgressPresentation {
|
|
10
|
+
title: string;
|
|
11
|
+
source: string;
|
|
12
|
+
readyMessage: string;
|
|
13
|
+
}
|
|
14
|
+
/** One App-owned observation boundary; native work outlives renderer detach. */
|
|
15
|
+
export interface NativeBackgroundIndexingObserver {
|
|
16
|
+
readonly detached: boolean;
|
|
17
|
+
selectSearch(selection: SearchIndexingSelection): Promise<ContributionStatus>;
|
|
18
|
+
disableSearch(): Promise<void>;
|
|
19
|
+
getStatus(): Promise<IndexingStatus | null>;
|
|
20
|
+
refresh(rebuild: boolean, presentation?: NativeIndexingProgressPresentation): Promise<IndexingStatus>;
|
|
21
|
+
}
|
|
22
|
+
/** App-owned observer and declaration adapter; never an indexing writer. */
|
|
23
|
+
export declare class NativeBackgroundIndexingSession implements NativeBackgroundIndexingObserver {
|
|
24
|
+
private readonly app;
|
|
25
|
+
private readonly port;
|
|
26
|
+
private isDetached;
|
|
27
|
+
private searchDeclared;
|
|
28
|
+
private searchVersion;
|
|
29
|
+
private searchControl;
|
|
30
|
+
private readonly pending;
|
|
31
|
+
private readonly contributions;
|
|
32
|
+
private readonly observers;
|
|
33
|
+
constructor(app: App, port: NativeBackgroundIndexing);
|
|
34
|
+
get detached(): boolean;
|
|
35
|
+
selectSearch(selection: SearchIndexingSelection): Promise<ContributionStatus>;
|
|
36
|
+
disableSearch(): Promise<void>;
|
|
37
|
+
getStatus(): Promise<IndexingStatus | null>;
|
|
38
|
+
registerProcessor(owner: string, extension: string): void;
|
|
39
|
+
registerProjection(owner: string, id: string, registration: IndexProjectionRegistration): void;
|
|
40
|
+
private register;
|
|
41
|
+
private callSearch;
|
|
42
|
+
ready(owner?: string): Promise<void>;
|
|
43
|
+
disable(owner: string): Promise<void>;
|
|
44
|
+
getContributionStatuses(): ContributionStatus[];
|
|
45
|
+
load(): Promise<void>;
|
|
46
|
+
refresh(rebuild: boolean, presentation?: NativeIndexingProgressPresentation): Promise<IndexingStatus>;
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/** App-owned observer and declaration adapter; never an indexing writer. */
|
|
2
|
+
export class NativeBackgroundIndexingSession {
|
|
3
|
+
app;
|
|
4
|
+
port;
|
|
5
|
+
isDetached = false;
|
|
6
|
+
searchDeclared = false;
|
|
7
|
+
searchVersion = 0;
|
|
8
|
+
searchControl = Promise.resolve();
|
|
9
|
+
pending = new Map();
|
|
10
|
+
contributions = new Map();
|
|
11
|
+
observers = new Set();
|
|
12
|
+
constructor(app, port) {
|
|
13
|
+
this.app = app;
|
|
14
|
+
this.port = port;
|
|
15
|
+
}
|
|
16
|
+
get detached() {
|
|
17
|
+
return this.isDetached;
|
|
18
|
+
}
|
|
19
|
+
async selectSearch(selection) {
|
|
20
|
+
if (this.detached)
|
|
21
|
+
throw new Error("Native indexing observer is detached");
|
|
22
|
+
this.searchVersion += 1;
|
|
23
|
+
this.searchDeclared = true;
|
|
24
|
+
const contribution = await this.register("search", {
|
|
25
|
+
kind: "select_search",
|
|
26
|
+
selection,
|
|
27
|
+
});
|
|
28
|
+
if (!contribution || contribution.projection_id !== "search")
|
|
29
|
+
throw new Error("Native Search selection is unavailable");
|
|
30
|
+
return contribution;
|
|
31
|
+
}
|
|
32
|
+
async disableSearch() {
|
|
33
|
+
if (this.detached)
|
|
34
|
+
return;
|
|
35
|
+
// Controls share one order even when selection's response is uncertain.
|
|
36
|
+
const version = ++this.searchVersion;
|
|
37
|
+
await this.callSearch({ kind: "disable_search" });
|
|
38
|
+
if (version !== this.searchVersion)
|
|
39
|
+
return;
|
|
40
|
+
this.searchDeclared = false;
|
|
41
|
+
this.contributions.delete("search");
|
|
42
|
+
this.pending.delete("search");
|
|
43
|
+
}
|
|
44
|
+
async getStatus() {
|
|
45
|
+
return this.detached
|
|
46
|
+
? null
|
|
47
|
+
: (await this.port.call({ kind: "status" })).status;
|
|
48
|
+
}
|
|
49
|
+
registerProcessor(owner, extension) {
|
|
50
|
+
this.register(owner, {
|
|
51
|
+
kind: "register_processor",
|
|
52
|
+
owner_plugin_id: owner,
|
|
53
|
+
extension,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
registerProjection(owner, id, registration) {
|
|
57
|
+
this.register(owner, {
|
|
58
|
+
kind: "register_projection",
|
|
59
|
+
contribution: {
|
|
60
|
+
projection_id: id,
|
|
61
|
+
owner_plugin_id: owner,
|
|
62
|
+
schema_version: registration.version,
|
|
63
|
+
configuration_hash: registration.configurationHash ?? "",
|
|
64
|
+
path_prefix: registration.source?.pathPrefix ?? null,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
register(owner, command) {
|
|
69
|
+
const request = owner === "search" ? this.callSearch(command) : this.port.call(command);
|
|
70
|
+
const operation = request.then(({ contribution }) => {
|
|
71
|
+
if (!contribution || this.detached)
|
|
72
|
+
return contribution;
|
|
73
|
+
this.contributions.set(contribution.projection_id, {
|
|
74
|
+
owner,
|
|
75
|
+
status: contribution,
|
|
76
|
+
});
|
|
77
|
+
if (contribution.placement === "client_dependent") {
|
|
78
|
+
this.app.notifications.notify({
|
|
79
|
+
title: "Indexing unavailable",
|
|
80
|
+
source: owner,
|
|
81
|
+
severity: "warning",
|
|
82
|
+
message: `${contribution.projection_id}: ${contribution.message ?? "Requires a client processor"}`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return contribution;
|
|
86
|
+
});
|
|
87
|
+
this.pending.set(owner, [...(this.pending.get(owner) ?? []), operation]);
|
|
88
|
+
// Activation/load observes failures; do not create an unhandled rejection
|
|
89
|
+
// while a synchronous plugin declaration is still returning its query handle.
|
|
90
|
+
void operation.catch(() => { });
|
|
91
|
+
return operation;
|
|
92
|
+
}
|
|
93
|
+
callSearch(command) {
|
|
94
|
+
const operation = this.searchControl.then(() => {
|
|
95
|
+
if (this.detached)
|
|
96
|
+
throw new Error("Native indexing observer is detached");
|
|
97
|
+
return this.port.call(command);
|
|
98
|
+
});
|
|
99
|
+
this.searchControl = operation.then(() => undefined, () => undefined);
|
|
100
|
+
return operation;
|
|
101
|
+
}
|
|
102
|
+
async ready(owner) {
|
|
103
|
+
const operations = owner
|
|
104
|
+
? (this.pending.get(owner) ?? [])
|
|
105
|
+
: [...this.pending.values()].flat();
|
|
106
|
+
await Promise.all(operations);
|
|
107
|
+
}
|
|
108
|
+
async disable(owner) {
|
|
109
|
+
if (this.detached)
|
|
110
|
+
return;
|
|
111
|
+
if (owner === "search" && this.searchDeclared)
|
|
112
|
+
await this.disableSearch();
|
|
113
|
+
await this.ready(owner);
|
|
114
|
+
for (const [id, contribution] of this.contributions) {
|
|
115
|
+
if (contribution.owner !== owner)
|
|
116
|
+
continue;
|
|
117
|
+
if (id === "search") {
|
|
118
|
+
await this.disableSearch();
|
|
119
|
+
}
|
|
120
|
+
else if (contribution.status.placement === "native" &&
|
|
121
|
+
!id.startsWith("processor:")) {
|
|
122
|
+
await this.port.call({ kind: "disable_projection", projection_id: id });
|
|
123
|
+
}
|
|
124
|
+
this.contributions.delete(id);
|
|
125
|
+
}
|
|
126
|
+
this.pending.delete(owner);
|
|
127
|
+
}
|
|
128
|
+
getContributionStatuses() {
|
|
129
|
+
return [...this.contributions.values()].map(({ status }) => ({
|
|
130
|
+
...status,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
async load() {
|
|
134
|
+
const selected = await this.port.call({ kind: "select_metadata" });
|
|
135
|
+
if (!selected.status)
|
|
136
|
+
throw new Error("Native metadata owner was not selected");
|
|
137
|
+
await this.ready();
|
|
138
|
+
// Search declares after metadata-loaded. Its owning plugin can already be
|
|
139
|
+
// enabled; a replacement App without that plugin must retire the old owner.
|
|
140
|
+
if (!this.searchDeclared &&
|
|
141
|
+
!this.app.plugins.plugins.get("search")?.enabled)
|
|
142
|
+
await this.disableSearch();
|
|
143
|
+
await this.ready();
|
|
144
|
+
await this.refresh(false);
|
|
145
|
+
}
|
|
146
|
+
async refresh(rebuild, presentation) {
|
|
147
|
+
if (this.detached)
|
|
148
|
+
throw new Error("Native indexing observer is detached");
|
|
149
|
+
return this.app.notifications.withProgress({
|
|
150
|
+
title: presentation?.title ??
|
|
151
|
+
(rebuild ? "Rebuilding metadata cache" : "Loading metadata cache"),
|
|
152
|
+
source: presentation?.source ?? "Metadata",
|
|
153
|
+
location: "status",
|
|
154
|
+
persistOnError: true,
|
|
155
|
+
}, async (progress) => {
|
|
156
|
+
let observing = false;
|
|
157
|
+
const report = (status) => {
|
|
158
|
+
if (!status || this.detached)
|
|
159
|
+
return;
|
|
160
|
+
progress.report({
|
|
161
|
+
current: status.processed,
|
|
162
|
+
total: status.total,
|
|
163
|
+
message: status.error ??
|
|
164
|
+
(status.active
|
|
165
|
+
? `Indexing ${status.phase}`
|
|
166
|
+
: status.pending > 0
|
|
167
|
+
? "Indexing pending changes"
|
|
168
|
+
: (presentation?.readyMessage ?? "Metadata index ready")),
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
const observe = async () => {
|
|
172
|
+
if (observing || this.detached)
|
|
173
|
+
return;
|
|
174
|
+
observing = true;
|
|
175
|
+
try {
|
|
176
|
+
report((await this.port.call({ kind: "status" })).status);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
/* The accepted refresh owns its definitive outcome. */
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
observing = false;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const timer = setInterval(() => void observe(), 100);
|
|
186
|
+
this.observers.add(timer);
|
|
187
|
+
try {
|
|
188
|
+
const result = await this.port.call({ kind: "refresh", rebuild });
|
|
189
|
+
if (!result.status)
|
|
190
|
+
throw new Error("Native metadata owner is unavailable");
|
|
191
|
+
report(result.status);
|
|
192
|
+
if (result.status.error)
|
|
193
|
+
throw new Error(result.status.error);
|
|
194
|
+
return result.status;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
clearInterval(timer);
|
|
198
|
+
this.observers.delete(timer);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
dispose() {
|
|
203
|
+
this.isDetached = true;
|
|
204
|
+
for (const timer of this.observers)
|
|
205
|
+
clearInterval(timer);
|
|
206
|
+
this.observers.clear();
|
|
207
|
+
}
|
|
208
|
+
}
|