@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
@@ -1,8 +1,10 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v3";
2
2
  import { SettingTab } from "./settings.svelte";
3
3
  import { cloneDeep, isEqual } from "lodash-es";
4
4
  import { EventDispatcher } from "./events";
5
- import { dirname } from "./storage";
5
+ import { dirname, normalizePath } from "./storage";
6
+ import { TFile } from "./storage/fs";
7
+ import { FileTextStateWriter } from "./file-text-state-writer";
6
8
  function isRecoverableConfigurationReadError(error) {
7
9
  let current = error;
8
10
  while (current) {
@@ -626,6 +628,9 @@ export class Configuration extends EventDispatcher {
626
628
  basePath;
627
629
  values = $state({});
628
630
  loaded = false;
631
+ stateBaseline = null;
632
+ stateWriter;
633
+ updateTail = Promise.resolve();
629
634
  schema = new ConfigurationSchema();
630
635
  constructor(app, basePath) {
631
636
  super();
@@ -647,49 +652,60 @@ export class Configuration extends EventDispatcher {
647
652
  return cloneDeep(pluginData[pluginId]);
648
653
  }
649
654
  async updatePluginData(pluginId, value, options = {}) {
650
- const prev = this.getPluginData(pluginId);
651
- const pluginData = this.getPluginDataStore(this.values);
652
- const nextPluginData = {
653
- ...pluginData,
654
- [pluginId]: cloneDeep(value),
655
- };
656
- const next = { ...this.values, pluginData: nextPluginData };
657
- await this.persist(next);
658
- this.emit("plugin-data-updated", {
659
- pluginId,
660
- value: this.getPluginData(pluginId),
661
- prev,
662
- origin: options.origin ?? "configuration-update",
655
+ const captured = cloneDeep(value);
656
+ return this.change(() => {
657
+ const prev = this.getPluginData(pluginId);
658
+ const pluginData = {
659
+ ...this.getPluginDataStore(this.values),
660
+ [pluginId]: captured,
661
+ };
662
+ return {
663
+ data: { ...this.values, pluginData },
664
+ notify: () => this.emit("plugin-data-updated", {
665
+ pluginId,
666
+ value: this.getPluginData(pluginId),
667
+ prev,
668
+ origin: options.origin ?? "configuration-update",
669
+ }),
670
+ };
663
671
  });
664
672
  }
665
673
  async removePluginData(pluginId, options = {}) {
666
- const pluginData = this.getPluginDataStore(this.values);
667
- if (!Object.hasOwn(pluginData, pluginId)) {
668
- return;
669
- }
670
- const prev = cloneDeep(pluginData[pluginId]);
671
- const nextPluginData = { ...pluginData };
672
- delete nextPluginData[pluginId];
673
- const next = { ...this.values };
674
- if (Object.keys(nextPluginData).length > 0) {
675
- next.pluginData = nextPluginData;
676
- }
677
- else {
678
- delete next.pluginData;
679
- }
680
- await this.persist(next);
681
- this.emit("plugin-data-updated", {
682
- pluginId,
683
- value: undefined,
684
- prev,
685
- origin: options.origin ?? "configuration-update",
674
+ return this.change(() => {
675
+ const pluginData = this.getPluginDataStore(this.values);
676
+ if (!Object.hasOwn(pluginData, pluginId))
677
+ return;
678
+ const prev = cloneDeep(pluginData[pluginId]);
679
+ const nextPluginData = { ...pluginData };
680
+ delete nextPluginData[pluginId];
681
+ const next = { ...this.values };
682
+ if (Object.keys(nextPluginData).length)
683
+ next.pluginData = nextPluginData;
684
+ else
685
+ delete next.pluginData;
686
+ return {
687
+ data: next,
688
+ notify: () => this.emit("plugin-data-updated", {
689
+ pluginId,
690
+ value: undefined,
691
+ prev,
692
+ origin: options.origin ?? "configuration-update",
693
+ }),
694
+ };
686
695
  });
687
696
  }
688
697
  async reloadFromDisk(options = {}) {
698
+ if (this.stateWriter?.dirty)
699
+ return;
689
700
  const previousValues = cloneDeep(this.values);
690
- this.values = await this.readValuesFromDisk();
701
+ const loaded = await this.readValuesFromDisk();
702
+ if (this.stateWriter &&
703
+ !this.stateWriter.acceptBaseline(this.stateBaseline))
704
+ return;
705
+ this.values = loaded;
691
706
  this.loaded = true;
692
- await this.materializeSchemaDefaults();
707
+ if (options.materializeDefaults !== false)
708
+ await this.materializeSchemaDefaults();
693
709
  if (options.emitPluginDataEvents !== false) {
694
710
  this.emitPluginDataDiffs(previousValues, this.values, options.origin ?? "external-reload");
695
711
  }
@@ -747,38 +763,98 @@ export class Configuration extends EventDispatcher {
747
763
  async updateConfigurationOptions(changes) {
748
764
  const validated = Object.entries(changes).map(([key, value]) => ({
749
765
  key,
750
- value: this.schema.validateConfigurationOption(key, value),
766
+ value: cloneDeep(this.schema.validateConfigurationOption(key, value)),
751
767
  }));
752
- const changed = validated
753
- .map(({ key, value }) => ({
754
- key,
755
- value,
756
- prev: this.values[key],
757
- }))
758
- .filter(({ value, prev }) => !isEqual(value, prev));
759
- if (changed.length === 0)
760
- return;
761
- const next = { ...this.values };
762
- for (const { key, value } of changed) {
763
- next[key] = cloneDeep(value);
764
- }
765
- await this.persist(next);
766
- for (const { key, value, prev } of changed) {
767
- this.emit("updated", {
768
+ return this.change(() => {
769
+ const changed = validated
770
+ .map(({ key, value }) => ({
768
771
  key,
769
- value: cloneDeep(value),
770
- prev: cloneDeep(prev),
771
- });
772
- }
772
+ value,
773
+ prev: this.values[key],
774
+ }))
775
+ .filter(({ value, prev }) => !isEqual(value, prev));
776
+ if (changed.length === 0)
777
+ return;
778
+ const next = { ...this.values };
779
+ for (const { key, value } of changed) {
780
+ next[key] = cloneDeep(value);
781
+ }
782
+ return {
783
+ data: next,
784
+ notify: () => {
785
+ for (const { key, value, prev } of changed)
786
+ this.emit("updated", {
787
+ key,
788
+ value: cloneDeep(value),
789
+ prev: cloneDeep(prev),
790
+ });
791
+ },
792
+ };
793
+ });
773
794
  }
774
795
  async removeConfigurationOption(key) {
775
- const value = this.values[key];
776
- if (value !== undefined) {
796
+ return this.change(() => {
797
+ const value = this.values[key];
798
+ if (value === undefined)
799
+ return;
777
800
  const next = { ...this.values };
778
801
  delete next[key];
779
- await this.persist(next);
780
- this.emit("updated", { key, prev: value, value: undefined });
802
+ return {
803
+ data: next,
804
+ notify: () => this.emit("updated", { key, prev: value, value: undefined }),
805
+ };
806
+ });
807
+ }
808
+ getStateWriter() {
809
+ if (this.stateWriter)
810
+ return this.stateWriter;
811
+ const file = this.app.vault.getFileByPath(this.basePath) ??
812
+ new TFile(normalizePath(this.basePath), { ctime: 0, mtime: 0, size: 0 }, null);
813
+ this.stateWriter = new FileTextStateWriter(this.app, file, this.stateBaseline, (text) => {
814
+ const next = JSON.parse(text || "{}");
815
+ if (!next || typeof next !== "object" || Array.isArray(next))
816
+ throw new Error("Invalid app configuration record");
817
+ const previous = this.values;
818
+ this.values = next;
819
+ this.emitPluginDataDiffs(previous, next, "external-reload");
820
+ for (const key of new Set([
821
+ ...Object.keys(previous),
822
+ ...Object.keys(next),
823
+ ])) {
824
+ if (!isEqual(previous[key], next[key]))
825
+ this.emit("updated", {
826
+ key,
827
+ prev: previous[key],
828
+ value: next[key],
829
+ });
830
+ }
831
+ });
832
+ return this.stateWriter;
833
+ }
834
+ change(calculate) {
835
+ if (this.app.vault.supportsConditionalTextWrites) {
836
+ return this.getStateWriter().update(() => {
837
+ const change = calculate();
838
+ if (!change)
839
+ return;
840
+ return {
841
+ contents: JSON.stringify(change.data, null, 2),
842
+ onSaved: () => {
843
+ this.values = change.data;
844
+ change.notify?.();
845
+ },
846
+ };
847
+ });
781
848
  }
849
+ const run = this.updateTail.then(async () => {
850
+ const change = calculate();
851
+ if (!change)
852
+ return;
853
+ await this.persist(change.data);
854
+ change.notify?.();
855
+ });
856
+ this.updateTail = run.catch(() => { });
857
+ return run;
782
858
  }
783
859
  async persist(data) {
784
860
  const file = this.app.vault.getFileByPath(this.basePath);
@@ -803,10 +879,12 @@ export class Configuration extends EventDispatcher {
803
879
  async readValuesFromDisk() {
804
880
  const file = this.app.vault.getFileByPath(this.basePath);
805
881
  if (!file) {
882
+ this.stateBaseline = null;
806
883
  return {};
807
884
  }
808
885
  try {
809
886
  const contents = await this.app.vault.read(file);
887
+ this.stateBaseline = contents;
810
888
  const data = JSON.parse(contents);
811
889
  if (typeof data === "object" && data !== null && !Array.isArray(data)) {
812
890
  return data;
@@ -856,32 +934,29 @@ export class Configuration extends EventDispatcher {
856
934
  if (!this.loaded) {
857
935
  return;
858
936
  }
859
- const next = { ...this.values };
860
- let changed = false;
861
- for (const [key, defaultValue] of this.schema.getDefaultEntries()) {
862
- if (!(key in next) || next[key] === undefined) {
863
- next[key] = cloneDeep(defaultValue);
864
- changed = true;
865
- continue;
866
- }
867
- try {
868
- const validated = this.schema.validateConfigurationOption(key, next[key]);
869
- if (!isEqual(validated, next[key])) {
870
- next[key] = validated;
937
+ return this.change(() => {
938
+ const next = { ...this.values };
939
+ let changed = false;
940
+ for (const [key, defaultValue] of this.schema.getDefaultEntries()) {
941
+ if (!(key in next) || next[key] === undefined) {
942
+ next[key] = cloneDeep(defaultValue);
943
+ changed = true;
944
+ continue;
945
+ }
946
+ try {
947
+ const validated = this.schema.validateConfigurationOption(key, next[key]);
948
+ if (!isEqual(validated, next[key])) {
949
+ next[key] = validated;
950
+ changed = true;
951
+ }
952
+ }
953
+ catch (error) {
954
+ console.warn(`Invalid configuration value for ${key}; restoring default.`, error);
955
+ next[key] = cloneDeep(defaultValue);
871
956
  changed = true;
872
957
  }
873
958
  }
874
- catch (error) {
875
- console.warn(`Invalid configuration value for ${key}; restoring default.`, error);
876
- next[key] = cloneDeep(defaultValue);
877
- changed = true;
878
- }
879
- }
880
- if (changed) {
881
- await this.persist(next);
882
- }
883
- else {
884
- this.values = next;
885
- }
959
+ return changed ? { data: next } : undefined;
960
+ });
886
961
  }
887
962
  }
@@ -1,3 +1,5 @@
1
+ import type { IconRegistry } from "./icons/registry";
2
+ import type { NativeTasks, NativeTasksSession } from "./native-tasks";
1
3
  import { Workspace } from "./workspace.svelte";
2
4
  import { TFile, Vault, type AppDatabase, type DataAdapter, type VaultSession } from "./storage";
3
5
  import { AppSettings } from "./settings.svelte";
@@ -6,12 +8,16 @@ import { PluginManager } from "./plugin-manager";
6
8
  import { Configuration } from "./configuration.svelte";
7
9
  import { CommandManager, Keymap, Scope, type UserEvent } from "./command.svelte";
8
10
  import { MetadataCache } from "./cache.svelte";
11
+ import type { NativeBackgroundIndexingObserver } from "./native-background-indexing";
12
+ import type { NativeHistoryIndex } from "./native-history-index";
13
+ import type { NativeEditingIntents } from "./native-editing-intents";
9
14
  import type { MarkdownDirectiveRenderer, MarkdownPostProcessor, MarkdownViewMenuItemProvider } from "./markdown";
10
15
  import { type Logger } from "./logging";
11
16
  import type { Component } from "./view.svelte";
12
17
  import { MetadataTypeManager } from "./metadata.svelte";
13
18
  import type { EditorSuggest } from "./suggest";
14
19
  import { FileManager } from "./file-manager";
20
+ import { VaultChangeService } from "./vault-changes";
15
21
  import { EmbedRegistry } from "./embed-registry";
16
22
  import type { OpenViewState } from "./workspace.svelte";
17
23
  import { LocalizationManager } from "./localization-manager.svelte";
@@ -26,10 +32,12 @@ import { DefaultPluginDistributionManager, type DefaultPluginDistributionManager
26
32
  import type { PluginDependencyResolverFactory } from "./plugin-dependency-resolver";
27
33
  import type { PluginAssetServer } from "./plugin-asset-server";
28
34
  import { SearchDocumentProviderRegistry } from "./search-document-provider";
35
+ import { SearchResultProviderRegistry } from "./search-result-provider";
29
36
  import { DailyDocumentProviderRegistry } from "./daily-document-provider";
30
37
  import { AppToolRegistry } from "./agent-tools";
31
38
  import { MarkdownExtensionRegistry } from "./markdown-extension-registry";
32
39
  import { MarkdownFileSurfaceRegistry } from "./markdown-file-surface-registry";
40
+ import { FileEditingSessionRegistry } from "./file-editing-sessions";
33
41
  import { NostrSignerHost, type NostrSignerBroker } from "./nostr-signer";
34
42
  import { SyncHostService, type SyncHostServiceOptions } from "./sync-host";
35
43
  export interface AppWorkspaceShellApplicationProperties {
@@ -54,14 +62,19 @@ export interface AppWorkspaceShellProperties {
54
62
  */
55
63
  export type AppStateProperties = {
56
64
  version: string;
57
- configPath: string;
65
+ configPath?: string;
58
66
  adapter?: DataAdapter;
59
67
  session?: VaultSession;
68
+ nativeHistoryIndex?: NativeHistoryIndex;
69
+ nativeEditingIntents?: NativeEditingIntents;
70
+ nativeTasks?: NativeTasks;
60
71
  appDatabase?: AppDatabase;
61
72
  safeMode?: AppSafeModeState;
62
73
  createCommunityPluginDependencyResolver?: PluginDependencyResolverFactory;
63
74
  pluginAssetServer?: PluginAssetServer;
64
75
  pluginDistributionOptions?: AppPluginDistributionOptions;
76
+ /** Host restrictions apply to discovery, installation and external loading. */
77
+ unavailablePlugins?: DefaultPluginDistributionManagerOptions["unavailablePlugins"];
65
78
  /** Host-owned Nostr account, keychain, and remote-signer implementation. */
66
79
  nostrSignerBroker?: NostrSignerBroker;
67
80
  syncHost?: SyncHostServiceOptions;
@@ -69,8 +82,8 @@ export type AppStateProperties = {
69
82
  markdownRenderer: (markdown: string, el: HTMLElement, sourcePath: string, component: Component) => Promise<void>;
70
83
  };
71
84
  /** Renderer-safe application properties exposed to plugins and views. */
72
- export type AppPublicProperties = Pick<AppStateProperties, "version" | "configPath" | "workspaceShell" | "markdownRenderer">;
73
- export type AppPluginDistributionOptions = Omit<DefaultPluginDistributionManagerOptions, "adapter" | "appVersion" | "platform" | "workspaceTrusted" | "pluginManager">;
85
+ export type AppPublicProperties = Pick<AppStateProperties, "version" | "configPath" | "workspaceShell" | "markdownRenderer"> & { configPath: string };
86
+ export type AppPluginDistributionOptions = Omit<DefaultPluginDistributionManagerOptions, "adapter" | "appVersion" | "platform" | "workspaceTrusted" | "pluginManager" | "unavailablePlugins">;
74
87
  export type ExtType = Extension | ((context: Record<string, any>) => Extension);
75
88
  /**
76
89
  * Details about the last startup failure that caused safe mode to activate.
@@ -155,10 +168,13 @@ export declare class SecretStorage {
155
168
  export declare class App {
156
169
  readonly props: AppPublicProperties;
157
170
  readonly i18n: LocalizationManager;
171
+ readonly icons: IconRegistry;
158
172
  readonly workspace: Workspace;
159
173
  readonly vault: Vault;
160
174
  readonly appDatabase: AppDatabase;
161
175
  readonly session?: VaultSession;
176
+ readonly nativeHistoryIndex?: NativeHistoryIndex;
177
+ readonly nativeTasks?: NativeTasksSession;
162
178
  readonly safeMode: AppSafeModeState;
163
179
  plugins: PluginManager;
164
180
  scope: Scope;
@@ -168,6 +184,7 @@ export declare class App {
168
184
  readonly editorSuggest: Map<string, Set<EditorSuggest<any>>>;
169
185
  readonly configuration: Configuration;
170
186
  readonly fileManager: FileManager;
187
+ readonly vaultChanges: VaultChangeService;
171
188
  readonly contextKeys: ContextKeyService;
172
189
  readonly statusBar: StatusBarManager;
173
190
  readonly urls: AppUrlService;
@@ -183,13 +200,16 @@ export declare class App {
183
200
  settingsOpen: boolean;
184
201
  settingsTabId: string | null;
185
202
  metadataCache: MetadataCache;
203
+ get backgroundIndexing(): NativeBackgroundIndexingObserver | undefined;
186
204
  embedRegistry: EmbedRegistry;
187
205
  readonly searchDocumentProviders: SearchDocumentProviderRegistry;
206
+ readonly searchResultProviders: SearchResultProviderRegistry;
188
207
  readonly dailyDocumentProviders: DailyDocumentProviderRegistry;
189
208
  readonly indexProjections: import("./storage/index-projection").IndexProjectionRegistry;
190
209
  readonly agentTools: AppToolRegistry;
191
210
  readonly markdownExtensions: MarkdownExtensionRegistry;
192
211
  readonly markdownFileSurfaces: MarkdownFileSurfaceRegistry;
212
+ readonly fileEditingSessions: FileEditingSessionRegistry;
193
213
  readonly agentSkills: import("./agent-skills").AppSkillRegistry;
194
214
  readonly agentSlashCommands: import("./agent-skills").AppSlashCommandRegistry;
195
215
  readonly agentResultViews: import("./agent-result-views").AppResultViewRegistry;
@@ -1,15 +1,20 @@
1
+ import { IconRegistry } from "./icons/registry";
2
+ import { FILES_CONFIGURATION_SCHEMA } from "./files-configuration";
3
+ import { DEFAULT_VAULT_EXCLUDES } from "./storage/vault-listing";
1
4
  import { Workspace } from "./workspace.svelte";
2
- import { getAdapterVaultId, TFile, Vault, } from "./storage";
5
+ import { getAdapterVaultId, isNativeDesktopRuntime, appStatePath, TFile, Vault, } from "./storage";
3
6
  import { IndexProjectionRegistry } from "./storage/index-projection";
4
7
  import { AppSettings } from "./settings.svelte";
5
8
  import { NativeDesktopCommunityPluginExecutionHost, PluginManager, } from "./plugin-manager";
6
9
  import { hasNativeDesktopCapability } from "./storage/desktop-native";
7
10
  import { Configuration } from "./configuration.svelte";
8
11
  import { CommandManager, Keymap, Scope, } from "./command.svelte";
12
+ import { NativeTasksSession } from "./native-tasks";
9
13
  import { MetadataCache } from "./cache.svelte";
10
14
  import { logging } from "./logging";
11
15
  import { MetadataTypeManager } from "./metadata.svelte";
12
16
  import { FileManager } from "./file-manager";
17
+ import { VaultChangeService } from "./vault-changes";
13
18
  import { EmbedRegistry } from "./embed-registry";
14
19
  import { findOpenFileLeaf, leafFilePath, applyOpenViewStateToLeaf, } from "./open-file";
15
20
  import { LocalizationManager, localeManager, } from "./localization-manager.svelte";
@@ -26,14 +31,17 @@ import { WorkspaceTrustService } from "./workspace-trust";
26
31
  import { DefaultPluginDistributionManager, } from "./plugin-distribution";
27
32
  import { ConfigurationOptionSourceRegistry } from "./configuration-option-source-registry";
28
33
  import { resolveMetadataFieldValuesAsync } from "./configuration-option-source-providers";
34
+ import { flushWorkspaceEditors } from "./sync-editor-flush";
35
+ import { FileEditingSessionRegistry } from "./file-editing-sessions";
29
36
  import { SearchDocumentProviderRegistry } from "./search-document-provider";
37
+ import { SearchResultProviderRegistry } from "./search-result-provider";
30
38
  import { DailyDocumentProviderRegistry } from "./daily-document-provider";
31
39
  import { AppToolRegistry } from "./agent-tools";
32
- import { AppSkillRegistry, AppSlashCommandRegistry, } from "./agent-skills";
40
+ import { AppSkillRegistry, AppSlashCommandRegistry } from "./agent-skills";
33
41
  import { AppResultViewRegistry } from "./agent-result-views";
34
42
  import { MarkdownExtensionRegistry } from "./markdown-extension-registry";
35
43
  import { MarkdownFileSurfaceRegistry } from "./markdown-file-surface-registry";
36
- import { SyncHostService, } from "./sync-host";
44
+ import { SyncHostService } from "./sync-host";
37
45
  import { installApplicationCompatibility, resolveApplication, } from "./application-compatibility";
38
46
  import { provideApplicationState, useApplicationState, } from "./application-state.svelte";
39
47
  const hostSyncServices = new WeakMap();
@@ -127,10 +135,13 @@ export class SecretStorage {
127
135
  export class App {
128
136
  props;
129
137
  i18n = localeManager;
138
+ icons = new IconRegistry();
130
139
  workspace;
131
140
  vault = $state();
132
141
  appDatabase;
133
142
  session;
143
+ nativeHistoryIndex;
144
+ nativeTasks;
134
145
  safeMode;
135
146
  plugins = $state();
136
147
  scope = $state(new Scope(undefined, this));
@@ -140,6 +151,7 @@ export class App {
140
151
  editorSuggest = new Map();
141
152
  configuration;
142
153
  fileManager = new FileManager(this);
154
+ vaultChanges = new VaultChangeService(this);
143
155
  contextKeys = new ContextKeyService();
144
156
  statusBar = new StatusBarManager();
145
157
  urls = new AppUrlService(this);
@@ -156,9 +168,13 @@ export class App {
156
168
  settingsOpen = $state(false);
157
169
  settingsTabId = $state(null);
158
170
  metadataCache = $state();
171
+ get backgroundIndexing() {
172
+ return this.metadataCache.backgroundIndexing;
173
+ }
159
174
  embedRegistry = new EmbedRegistry();
160
175
  configurationOptionSources = new ConfigurationOptionSourceRegistry();
161
176
  searchDocumentProviders = new SearchDocumentProviderRegistry();
177
+ searchResultProviders = new SearchResultProviderRegistry();
162
178
  dailyDocumentProviders = new DailyDocumentProviderRegistry();
163
179
  indexProjections = new IndexProjectionRegistry();
164
180
  agentTools = new AppToolRegistry();
@@ -167,6 +183,7 @@ export class App {
167
183
  agentResultViews = new AppResultViewRegistry();
168
184
  markdownExtensions = new MarkdownExtensionRegistry();
169
185
  markdownFileSurfaces = new MarkdownFileSurfaceRegistry();
186
+ fileEditingSessions;
170
187
  lastEvent = $state(null);
171
188
  renderContext = new RenderContext();
172
189
  secretStorage = new SecretStorage();
@@ -192,7 +209,7 @@ export class App {
192
209
  constructor(props) {
193
210
  this.props = Object.freeze({
194
211
  version: props.version,
195
- configPath: props.configPath,
212
+ configPath: props.configPath ?? appStatePath("app.json"),
196
213
  workspaceShell: props.workspaceShell,
197
214
  markdownRenderer: props.markdownRenderer,
198
215
  });
@@ -202,6 +219,9 @@ export class App {
202
219
  throw new Error("App requires a vault adapter or vault session");
203
220
  }
204
221
  this.session = props.session;
222
+ this.fileEditingSessions = new FileEditingSessionRegistry(props.nativeEditingIntents ?? props.session?.nativeEditingIntents);
223
+ this.nativeHistoryIndex =
224
+ props.nativeHistoryIndex ?? props.session?.nativeHistoryIndex;
205
225
  this.safeMode = props.safeMode ?? createDefaultAppSafeModeState();
206
226
  const appDatabase = props.session?.appDatabase ?? props.appDatabase;
207
227
  if (!appDatabase) {
@@ -226,31 +246,40 @@ export class App {
226
246
  this.settings.addGroup((options) => options.setId("options").setTitle("Options"));
227
247
  this.settings.addGroup((options) => options.setId("core-plugins").setTitle("Core plugins"));
228
248
  this.settings.addGroup((options) => options.setId("community-plugins").setTitle("Community plugins"));
229
- this.configuration = new Configuration(this, props.configPath);
249
+ this.configuration = new Configuration(this, this.props.configPath);
250
+ this.configuration.schema.register(FILES_CONFIGURATION_SCHEMA);
230
251
  this.workspace.bindConfiguration();
231
- this.vault = new Vault(adapter);
252
+ this.vault = new Vault(adapter, this.fileEditingSessions, {
253
+ listingExclusions: () => this.configuration
254
+ .getConfiguration()
255
+ .get("files.exclude", DEFAULT_VAULT_EXCLUDES),
256
+ });
257
+ this.configuration.on("updated", ({ key }) => {
258
+ if (key === "files.exclude")
259
+ this.vault.trigger("load");
260
+ });
232
261
  this.#syncHost = new SyncHostService(this.appDatabase, this.vault, Date.now, {
233
262
  ...props.syncHost,
234
- flushOpenEditor: async (path) => {
235
- const active = this.workspace.activeEditor;
236
- if (!active?.editor || !active.file)
237
- return { flushed: false, path };
238
- if (path && active.file.path !== path)
239
- return { flushed: false, path };
240
- await active.editor.flushChanges();
241
- return { flushed: true, path: active.file.path };
242
- },
263
+ flushOpenEditor: (path) => flushWorkspaceEditors(this.workspace, path),
243
264
  });
244
265
  hostSyncServices.set(this, this.#syncHost);
245
266
  this.workspaceTrust = new WorkspaceTrustService(adapter);
246
267
  this.metadataCache = new MetadataCache(this);
268
+ const nativeTasks = props.session?.nativeTasks ?? props.nativeTasks;
269
+ this.nativeTasks = nativeTasks
270
+ ? new NativeTasksSession(nativeTasks, () => {
271
+ const state = this.plugins?.plugins.get("tasks")?.state;
272
+ return state === "enabled" || state === "enabling";
273
+ })
274
+ : undefined;
247
275
  installVaultMaintenanceCommands(this);
248
- this.plugins = new PluginManager(this, "/.obsidian/plugins", adapter, {
276
+ this.plugins = new PluginManager(this, "/" + appStatePath("plugins"), adapter, {
249
277
  communityPluginHost: hasNativeDesktopCapability("plugin-sidecar")
250
278
  ? new NativeDesktopCommunityPluginExecutionHost(adapter, undefined, props.createCommunityPluginDependencyResolver)
251
279
  : undefined,
252
280
  createCommunityPluginDependencyResolver: props.createCommunityPluginDependencyResolver,
253
281
  pluginAssetServer: props.pluginAssetServer,
282
+ unavailablePlugins: props.unavailablePlugins,
254
283
  createSyncHostCapabilities: (pluginId, grants) => this.#syncHost.forPlugin(pluginId, grants),
255
284
  revokeSyncHostCapabilities: (pluginId) => this.#syncHost.revokePlugin(pluginId),
256
285
  });
@@ -268,7 +297,10 @@ export class App {
268
297
  ...props.pluginDistributionOptions,
269
298
  adapter,
270
299
  appVersion: this.version,
271
- platform: props.session?.runtime === "deno-desktop" ? "desktop" : "web",
300
+ unavailablePlugins: props.unavailablePlugins,
301
+ platform: isNativeDesktopRuntime(props.session?.runtime)
302
+ ? "desktop"
303
+ : "web",
272
304
  workspaceTrusted: async () => (await this.workspaceTrust.ready()).trusted,
273
305
  pluginManager: this.plugins,
274
306
  });
@@ -324,17 +356,21 @@ export class App {
324
356
  ? view.editor
325
357
  : undefined;
326
358
  });
327
- openEditor?.view.dispatch({
328
- changes: [...edits]
329
- .sort((left, right) => left.from - right.from)
330
- .map((edit) => ({
331
- from: edit.from,
332
- to: edit.to,
333
- insert: edit.insert,
334
- })),
335
- userEvent: "input.complete",
336
- });
337
- await this.vault.modify(file, value);
359
+ if (openEditor) {
360
+ openEditor.view.dispatch({
361
+ changes: [...edits]
362
+ .sort((left, right) => left.from - right.from)
363
+ .map((edit) => ({
364
+ from: edit.from,
365
+ to: edit.to,
366
+ insert: edit.insert,
367
+ })),
368
+ userEvent: "input.complete",
369
+ });
370
+ await openEditor.flushChanges();
371
+ }
372
+ else
373
+ await this.vault.modify(file, value);
338
374
  }
339
375
  }
340
376
  }
@@ -408,16 +444,18 @@ export class App {
408
444
  const existingLeaf = findOpenFileLeaf(this.workspace, file);
409
445
  if (existingLeaf) {
410
446
  span.setAttribute("workspace.file_already_open", true);
411
- await applyOpenViewStateToLeaf(existingLeaf, openState);
412
447
  this.workspace.activateLeaf(existingLeaf, {
413
448
  operation: "open-existing-file",
414
449
  });
450
+ await this.workspace.revealLeaf(existingLeaf);
451
+ await applyOpenViewStateToLeaf(existingLeaf, openState);
415
452
  return;
416
453
  }
417
454
  const reusedRootLeaf = Boolean(this.workspace.activeRootLeaf);
418
455
  const leaf = this.workspace.getLeaf();
419
456
  span.setAttribute("workspace.reused_active_leaf", reusedRootLeaf);
420
- this.workspace.activeLeaf = leaf;
457
+ this.workspace.activateLeaf(leaf, { operation: "open-file" });
458
+ await this.workspace.revealLeaf(leaf);
421
459
  if (leafFilePath(leaf) === file.path) {
422
460
  span.setAttribute("workspace.file_already_open", true);
423
461
  if (openState?.state) {
@@ -41,6 +41,13 @@ export interface WorkspaceDiagnosticEntry {
41
41
  resource: DiagnosticResource | null;
42
42
  diagnostic: WorkspaceDiagnostic;
43
43
  }
44
+ /** Application-owned navigation target for a serializable diagnostic resource. */
45
+ export interface DiagnosticLocation {
46
+ resource: DiagnosticResource;
47
+ range?: DiagnosticRange;
48
+ diagnostic: WorkspaceDiagnostic;
49
+ related?: DiagnosticRelatedInformation;
50
+ }
44
51
  export interface DiagnosticCollectionOptions {
45
52
  label?: string;
46
53
  buildItemMenu?: (menu: Menu, entry: WorkspaceDiagnosticEntry) => void;
@@ -81,6 +88,7 @@ export declare class DiagnosticsManager {
81
88
  }
82
89
  export declare function bindRuntimePluginDiagnostics(plugins: {
83
90
  on(name: "plugin-error", listener: (id: string, message: string) => void): unknown;
91
+ on(name: "plugin-unloaded", listener: (id: string) => void): unknown;
84
92
  on(name: "plugin-enabled", listener: (plugin: {
85
93
  manifest: {
86
94
  id: string;