obsidian-dev-utils 101.5.0 → 101.6.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 (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/integration-test-plugin/main.js +1714 -1622
  3. package/dist/lib/cjs/__merged.cjs +4 -1
  4. package/dist/lib/cjs/__merged.d.cts +1 -0
  5. package/dist/lib/cjs/generated-during-build.cjs +1 -1
  6. package/dist/lib/cjs/obsidian/components/index.cjs +4 -1
  7. package/dist/lib/cjs/obsidian/components/index.d.cts +1 -0
  8. package/dist/lib/cjs/obsidian/components/monkey-around-component.cjs +1 -1
  9. package/dist/lib/cjs/obsidian/components/settings-migration-component.cjs +232 -0
  10. package/dist/lib/cjs/obsidian/components/settings-migration-component.d.cts +120 -0
  11. package/dist/lib/cjs/obsidian/mobile-trusted-input.cjs +1 -1
  12. package/dist/lib/cjs/obsidian/mobile-trusted-input.d.cts +5 -3
  13. package/dist/lib/cjs/obsidian/plugin/index.cjs +6 -3
  14. package/dist/lib/cjs/obsidian/plugin/index.d.cts +1 -0
  15. package/dist/lib/cjs/obsidian/plugin/settings-migration-api.cjs +141 -0
  16. package/dist/lib/cjs/obsidian/plugin/settings-migration-api.d.cts +91 -0
  17. package/dist/lib/esm/__merged.d.mts +1 -0
  18. package/dist/lib/esm/__merged.mjs +3 -1
  19. package/dist/lib/esm/generated-during-build.mjs +1 -1
  20. package/dist/lib/esm/obsidian/components/index.d.mts +1 -0
  21. package/dist/lib/esm/obsidian/components/index.mjs +3 -1
  22. package/dist/lib/esm/obsidian/components/monkey-around-component.mjs +1 -1
  23. package/dist/lib/esm/obsidian/components/settings-migration-component.d.mts +120 -0
  24. package/dist/lib/esm/obsidian/components/settings-migration-component.mjs +127 -0
  25. package/dist/lib/esm/obsidian/mobile-trusted-input.d.mts +5 -3
  26. package/dist/lib/esm/obsidian/mobile-trusted-input.mjs +1 -1
  27. package/dist/lib/esm/obsidian/plugin/index.d.mts +1 -0
  28. package/dist/lib/esm/obsidian/plugin/index.mjs +4 -2
  29. package/dist/lib/esm/obsidian/plugin/settings-migration-api.d.mts +91 -0
  30. package/dist/lib/esm/obsidian/plugin/settings-migration-api.mjs +44 -0
  31. package/obsidian/components/settings-migration-component/package.json +6 -0
  32. package/obsidian/plugin/settings-migration-api/package.json +6 -0
  33. package/package.json +15 -15
@@ -10411,6 +10411,7 @@ __export(merged_exports, {
10411
10411
  SetTransformer: () => SetTransformer,
10412
10412
  SettingEx: () => SettingEx,
10413
10413
  SettingGroupEx: () => SettingGroupEx,
10414
+ SettingsMigrationComponent: () => SettingsMigrationComponent,
10414
10415
  SilentError: () => SilentError,
10415
10416
  SkipPrivatePropertyTransformer: () => SkipPrivatePropertyTransformer,
10416
10417
  SpellcheckMode: () => SpellcheckMode,
@@ -32232,195 +32233,660 @@ var RmdirGuardComponent = class extends MonkeyAroundComponent {
32232
32233
  }
32233
32234
  };
32234
32235
 
32235
- // src/obsidian/components/syntax-highlighting-component.ts
32236
- var syntax_highlighting_component_exports = {};
32237
- __export(syntax_highlighting_component_exports, {
32238
- SyntaxHighlightingComponent: () => SyntaxHighlightingComponent
32236
+ // src/obsidian/components/settings-migration-component.ts
32237
+ var settings_migration_component_exports = {};
32238
+ __export(settings_migration_component_exports, {
32239
+ SettingsMigrationComponent: () => SettingsMigrationComponent
32239
32240
  });
32240
- init_error();
32241
- init_component_ex();
32242
- var SyntaxHighlightingComponent = class extends ComponentEx {
32241
+ init_async();
32242
+ init_object_utils();
32243
+
32244
+ // src/obsidian/plugin/plugin-api.ts
32245
+ var plugin_api_exports = {};
32246
+ __export(plugin_api_exports, {
32247
+ PluginApiPayloadKind: () => PluginApiPayloadKind,
32248
+ PluginApiRevokedError: () => PluginApiRevokedError,
32249
+ PluginApiUnavailabilityReason: () => PluginApiUnavailabilityReason,
32250
+ PluginApiUnavailableError: () => PluginApiUnavailableError,
32251
+ PluginApiValidationError: () => PluginApiValidationError,
32252
+ publishPluginApi: () => publishPluginApi,
32253
+ watchPluginApi: () => watchPluginApi
32254
+ });
32255
+ var import_compare_versions3 = __toESM(require_umd(), 1);
32256
+ init_array();
32257
+ init_async_events();
32258
+ init_async();
32259
+ init_debug();
32260
+ init_object_utils();
32261
+ init_obsidian_dev_utils_state();
32262
+ var PLUGIN_API_DEBUGGER_NAMESPACE = "PluginApi";
32263
+ var PLUGIN_API_REGISTRY_STATE_KEY = "pluginApiRegistry";
32264
+ var DEFAULT_WHEN_AVAILABLE_TIMEOUT_IN_MILLISECONDS = 1e4;
32265
+ var PluginApiPayloadKind = /* @__PURE__ */ ((PluginApiPayloadKind2) => {
32266
+ PluginApiPayloadKind2["Input"] = "input";
32267
+ PluginApiPayloadKind2["Output"] = "output";
32268
+ return PluginApiPayloadKind2;
32269
+ })(PluginApiPayloadKind || {});
32270
+ var PluginApiUnavailabilityReason = /* @__PURE__ */ ((PluginApiUnavailabilityReason2) => {
32271
+ PluginApiUnavailabilityReason2["NotEnabled"] = "notEnabled";
32272
+ PluginApiUnavailabilityReason2["NotInstalled"] = "notInstalled";
32273
+ PluginApiUnavailabilityReason2["NotPublished"] = "notPublished";
32274
+ PluginApiUnavailabilityReason2["Revoked"] = "revoked";
32275
+ PluginApiUnavailabilityReason2["ShapeMismatch"] = "shapeMismatch";
32276
+ PluginApiUnavailabilityReason2["VersionMismatch"] = "versionMismatch";
32277
+ return PluginApiUnavailabilityReason2;
32278
+ })(PluginApiUnavailabilityReason || {});
32279
+ var PluginApiWatchBase = class {
32280
+ // A `protected` constructor is not assignable to `AbstractConstructor<object>`, which is what
32281
+ // `mixinAsyncEvents` accepts, so this stays public even though only the subclass below calls it.
32282
+ constructor(params) {
32283
+ this.params = params;
32284
+ }
32285
+ params;
32286
+ handle = null;
32287
+ record = null;
32243
32288
  /**
32244
- * Registers a fenced code block language, highlighting it in the editor and, optionally, in reading view.
32245
- *
32246
- * @param params - The parameters for registering the language.
32247
- * @returns A {@link Promise} that resolves when the language is registered.
32289
+ * Builds the error describing why the API is not available right now.
32248
32290
  *
32249
- * @example
32250
- * ```ts
32251
- * await component.registerCodeBlockLanguage({
32252
- * editorMode: 'text/typescript',
32253
- * language: 'my-language',
32254
- * prismGrammar: 'typescript'
32255
- * });
32256
- * ```
32291
+ * @returns The classified error.
32257
32292
  */
32258
- async registerCodeBlockLanguage(params) {
32259
- const {
32260
- editorMode,
32261
- language,
32262
- prismGrammar
32263
- } = params;
32264
- this.ensureLoaded();
32265
- if (prismGrammar !== void 0) {
32266
- await this.registerPrismLanguage({
32267
- grammar: prismGrammar,
32268
- language
32293
+ createUnavailableError() {
32294
+ return new PluginApiUnavailableError({
32295
+ pluginId: this.params.pluginId,
32296
+ reason: resolveUnavailabilityReason(this.params)
32297
+ });
32298
+ }
32299
+ };
32300
+ var PluginApiRefImpl = class extends mixinAsyncEvents()(PluginApiWatchBase) {
32301
+ get value() {
32302
+ return castTo(this.handle);
32303
+ }
32304
+ constructor(params) {
32305
+ super(params);
32306
+ }
32307
+ /**
32308
+ * Recomputes the current value from the registry, firing `change` only when it actually changed.
32309
+ */
32310
+ refresh() {
32311
+ const record = selectRecord(this.params);
32312
+ if (record === this.record) {
32313
+ return;
32314
+ }
32315
+ this.record = record;
32316
+ this.handle = record === null ? null : createRevocableHandle(record, this.params.contract);
32317
+ this.trigger("change");
32318
+ }
32319
+ async whenAvailable(options) {
32320
+ const currentValue = this.value;
32321
+ if (currentValue !== null) {
32322
+ return currentValue;
32323
+ }
32324
+ try {
32325
+ return await runWithTimeout({
32326
+ operationFunction: async (abortSignal) => await this.waitForValue(abortSignal),
32327
+ operationName: `watchPluginApi("${this.params.pluginId}").whenAvailable`,
32328
+ timeoutInMilliseconds: options?.timeoutInMilliseconds ?? DEFAULT_WHEN_AVAILABLE_TIMEOUT_IN_MILLISECONDS
32269
32329
  });
32330
+ } catch {
32331
+ throw this.createUnavailableError();
32270
32332
  }
32271
- this.ensureLoaded();
32272
- const previousModeFactory = window.CodeMirror.modes[language];
32273
- window.CodeMirror.defineMode(language, (config) => window.CodeMirror.getMode(config, editorMode));
32274
- this.register(() => {
32275
- if (previousModeFactory !== void 0) {
32276
- window.CodeMirror.modes[language] = previousModeFactory;
32277
- return;
32333
+ }
32334
+ async waitForValue(abortSignal) {
32335
+ return await new Promise((resolve2, reject) => {
32336
+ const eventRef = this.on("change", () => {
32337
+ handleChange();
32338
+ });
32339
+ function cleanUp() {
32340
+ eventRef.asyncEventSource.offref(eventRef);
32341
+ abortSignal.removeEventListener("abort", handleAbort);
32278
32342
  }
32279
- delete window.CodeMirror.modes[language];
32343
+ function handleAbort() {
32344
+ cleanUp();
32345
+ reject(abortSignal.reason);
32346
+ }
32347
+ const handleChange = () => {
32348
+ const value = this.value;
32349
+ if (value !== null) {
32350
+ cleanUp();
32351
+ resolve2(value);
32352
+ }
32353
+ };
32354
+ abortSignal.addEventListener("abort", handleAbort);
32355
+ handleChange();
32280
32356
  });
32281
32357
  }
32358
+ };
32359
+ var PluginApiUnavailableError = class extends Error {
32282
32360
  /**
32283
- * Registers a Prism language, highlighting it everywhere Prism renders code — reading view and the
32284
- * {@link CodeHighlighterComponent} settings field.
32285
- *
32286
- * @param params - The parameters for registering the language.
32287
- * @returns A {@link Promise} that resolves when the language is registered.
32361
+ * The `manifest.id` of the plugin whose API was requested.
32362
+ */
32363
+ pluginId;
32364
+ /**
32365
+ * Which of the five failures occurred.
32366
+ */
32367
+ reason;
32368
+ /**
32369
+ * Creates a {@link PluginApiUnavailableError}.
32288
32370
  *
32289
- * @example
32290
- * ```ts
32291
- * await component.registerPrismLanguage({
32292
- * grammar: {
32293
- * keyword: /\bfoo\b/
32294
- * },
32295
- * language: 'my-language'
32296
- * });
32297
- * ```
32371
+ * @param params - The parameters for the error.
32298
32372
  */
32299
- async registerPrismLanguage(params) {
32300
- const {
32301
- grammar,
32302
- language
32303
- } = params;
32304
- this.ensureLoaded();
32305
- const prism = await loadPrism();
32306
- this.ensureLoaded();
32307
- const previousGrammar = prism.languages[language];
32308
- prism.languages[language] = this.resolveGrammar(prism, grammar);
32309
- this.register(() => {
32310
- if (previousGrammar !== void 0) {
32311
- prism.languages[language] = previousGrammar;
32312
- return;
32313
- }
32314
- delete prism.languages[language];
32315
- });
32373
+ constructor(params) {
32374
+ super(params.message ?? `The API of the plugin "${params.pluginId}" is unavailable: ${params.reason}.`);
32375
+ this.name = "PluginApiUnavailableError";
32376
+ this.pluginId = params.pluginId;
32377
+ this.reason = params.reason;
32316
32378
  }
32379
+ };
32380
+ var PluginApiRevokedError = class extends PluginApiUnavailableError {
32317
32381
  /**
32318
- * Returns the grammar of an already registered Prism language.
32382
+ * Creates a {@link PluginApiRevokedError}.
32319
32383
  *
32320
- * @param prism - The loaded Prism module.
32321
- * @param language - The language to look up.
32322
- * @returns The grammar of the language.
32323
- * @throws An {@link Error} if the language is not registered.
32384
+ * @param pluginId - The `manifest.id` of the plugin whose API was revoked.
32324
32385
  */
32325
- requirePrismLanguage(prism, language) {
32326
- return prism.languages[language] ?? throwExpression(new Error(`Prism language "${language}" is not registered.`));
32386
+ constructor(pluginId) {
32387
+ super({
32388
+ message: `The API of the plugin "${pluginId}" has been revoked, because the plugin was unloaded.`,
32389
+ pluginId,
32390
+ reason: "revoked" /* Revoked */
32391
+ });
32392
+ this.name = "PluginApiRevokedError";
32327
32393
  }
32394
+ };
32395
+ var PluginApiValidationError = class extends Error {
32328
32396
  /**
32329
- * Resolves a grammar source into the grammar to register.
32397
+ * The issues the schema reported.
32398
+ */
32399
+ issues;
32400
+ /**
32401
+ * The name of the method whose payload failed validation.
32402
+ */
32403
+ methodName;
32404
+ /**
32405
+ * The `manifest.id` of the providing plugin.
32406
+ */
32407
+ pluginId;
32408
+ /**
32409
+ * Creates a {@link PluginApiValidationError}.
32330
32410
  *
32331
- * @param prism - The loaded Prism module.
32332
- * @param grammarSource - The grammar source to resolve.
32333
- * @returns The resolved grammar.
32411
+ * @param params - The parameters for the error.
32334
32412
  */
32335
- resolveGrammar(prism, grammarSource) {
32336
- if (typeof grammarSource === "string") {
32337
- return this.requirePrismLanguage(prism, grammarSource);
32338
- }
32339
- if (typeof grammarSource === "function") {
32340
- return grammarSource({
32341
- prism,
32342
- requirePrismLanguage: (language) => this.requirePrismLanguage(prism, language)
32343
- });
32344
- }
32345
- return grammarSource;
32413
+ constructor(params) {
32414
+ super(
32415
+ `The ${params.payloadKind} of "${params.pluginId}" API method "${params.methodName}" failed validation: ${formatIssues(params.issues)}`
32416
+ );
32417
+ this.name = "PluginApiValidationError";
32418
+ this.issues = params.issues;
32419
+ this.methodName = params.methodName;
32420
+ this.pluginId = params.pluginId;
32346
32421
  }
32347
32422
  };
32348
-
32349
- // src/obsidian/constructors/get-dom-events-handlers-constructor.ts
32350
- var get_dom_events_handlers_constructor_exports = {};
32351
- __export(get_dom_events_handlers_constructor_exports, {
32352
- getDomEventsHandlersConstructor: () => getDomEventsHandlersConstructor
32353
- });
32354
- var import_obsidian121 = require("obsidian");
32355
- init_async();
32356
- init_type_guards();
32357
- init_monkey_around_component();
32358
- var MarkdownPreviewRendererRegisterDomEventsPatchComponent = class extends MonkeyAroundComponent {
32359
- domEventsHandlersConstructor = null;
32360
- onload() {
32361
- this.registerMethodPatch({
32362
- $object: import_obsidian121.MarkdownPreviewRenderer,
32363
- methodName: "registerDomEvents",
32364
- patchHandler: ({
32365
- fallback,
32366
- originalArguments: [, handlers]
32367
- }) => {
32368
- this.domEventsHandlersConstructor = handlers.constructor;
32369
- fallback();
32370
- }
32371
- });
32372
- }
32373
- };
32374
- async function getDomEventsHandlersConstructor(app) {
32375
- let mdFile = app.vault.getMarkdownFiles()[0];
32376
- let shouldDelete = false;
32377
- if (!mdFile) {
32378
- mdFile = await app.vault.create("__temp.md", "");
32379
- shouldDelete = true;
32423
+ function publishPluginApi(params) {
32424
+ const pluginId = params.plugin.manifest.id;
32425
+ const registry = getRegistry();
32426
+ const records = registry.records[pluginId] ?? [];
32427
+ registry.records[pluginId] = records;
32428
+ if (records.some((record2) => record2.apiVersion === params.apiVersion)) {
32429
+ throw new Error(`The plugin "${pluginId}" has already published the API version "${params.apiVersion}".`);
32380
32430
  }
32381
- try {
32382
- var _stack = [];
32383
- try {
32384
- const patch = __using(_stack, new MarkdownPreviewRendererRegisterDomEventsPatchComponent());
32385
- patch.load();
32386
- const leaf = app.workspace.getLeaf(true);
32387
- await leaf.openFile(mdFile, {
32388
- active: true,
32389
- state: { mode: "preview" }
32431
+ const record = {
32432
+ api: params.api,
32433
+ apiVersion: params.apiVersion,
32434
+ contract: params.contract ?? {},
32435
+ isRevoked: false,
32436
+ pluginId
32437
+ };
32438
+ records.push(record);
32439
+ notifySubscribers();
32440
+ params.plugin.register(() => {
32441
+ revokeRecord(record);
32442
+ });
32443
+ }
32444
+ function watchPluginApi(params) {
32445
+ const ref = new PluginApiRefImpl(params);
32446
+ const unsubscribe = subscribe(() => {
32447
+ ref.refresh();
32448
+ });
32449
+ params.component.register(unsubscribe);
32450
+ ref.refresh();
32451
+ return ref;
32452
+ }
32453
+ function checkIsPromiseLike(value) {
32454
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
32455
+ }
32456
+ function checkMatchesContractShape(record, consumerContract) {
32457
+ const contract = consumerContract ?? record.contract;
32458
+ return Object.keys(contract).every((methodName) => typeof getMember(record.api, methodName) === "function");
32459
+ }
32460
+ function createMethodWrapper(params) {
32461
+ return function pluginApiMethod(...$arguments) {
32462
+ const method = castTo(getMember(params.target, params.propertyKey));
32463
+ const methodContract = params.methodContract;
32464
+ if (methodContract === void 0 || !getPluginApiDebugger().enabled) {
32465
+ return method.apply(params.target, $arguments);
32466
+ }
32467
+ if (methodContract.input) {
32468
+ validatePayload({
32469
+ methodName: params.methodName,
32470
+ payloadKind: "input" /* Input */,
32471
+ pluginId: params.pluginId,
32472
+ schema: methodContract.input,
32473
+ value: $arguments
32390
32474
  });
32391
- try {
32392
- await retryWithTimeout({
32393
- operationFunction: () => patch.domEventsHandlersConstructor !== null,
32394
- operationName: "getDomEventsHandlersConstructor"
32475
+ }
32476
+ const returnValue = method.apply(params.target, $arguments);
32477
+ const outputSchema = methodContract.output;
32478
+ if (!outputSchema) {
32479
+ return returnValue;
32480
+ }
32481
+ if (checkIsPromiseLike(returnValue)) {
32482
+ return returnValue.then((resolvedValue) => {
32483
+ validatePayload({
32484
+ methodName: params.methodName,
32485
+ payloadKind: "output" /* Output */,
32486
+ pluginId: params.pluginId,
32487
+ schema: outputSchema,
32488
+ value: resolvedValue
32395
32489
  });
32396
- } finally {
32397
- leaf.detach();
32490
+ return resolvedValue;
32491
+ });
32492
+ }
32493
+ validatePayload({
32494
+ methodName: params.methodName,
32495
+ payloadKind: "output" /* Output */,
32496
+ pluginId: params.pluginId,
32497
+ schema: outputSchema,
32498
+ value: returnValue
32499
+ });
32500
+ return returnValue;
32501
+ };
32502
+ }
32503
+ function createRevocableHandle(record, consumerContract) {
32504
+ const contract = consumerContract ?? record.contract;
32505
+ const methodCache = /* @__PURE__ */ new Map();
32506
+ return new Proxy(record.api, {
32507
+ get(target, propertyKey) {
32508
+ if (record.isRevoked) {
32509
+ throw new PluginApiRevokedError(record.pluginId);
32398
32510
  }
32399
- assertNonNullable(patch.domEventsHandlersConstructor, "Failed to get register dom events handlers constructor");
32400
- return patch.domEventsHandlersConstructor;
32401
- } catch (_) {
32402
- var _error = _, _hasError = true;
32403
- } finally {
32404
- __callDispose(_stack, _error, _hasError);
32511
+ const value = getMember(target, propertyKey);
32512
+ if (typeof value !== "function") {
32513
+ return value;
32514
+ }
32515
+ let method = methodCache.get(propertyKey);
32516
+ if (method === void 0) {
32517
+ method = createMethodWrapper({
32518
+ methodContract: typeof propertyKey === "string" ? contract[propertyKey] : void 0,
32519
+ methodName: String(propertyKey),
32520
+ pluginId: record.pluginId,
32521
+ propertyKey,
32522
+ target
32523
+ });
32524
+ methodCache.set(propertyKey, method);
32525
+ }
32526
+ return method;
32405
32527
  }
32406
- } finally {
32407
- if (shouldDelete) {
32408
- await trashSafe(app, mdFile);
32528
+ });
32529
+ }
32530
+ function formatIssues(issues) {
32531
+ return issues.map((issue) => {
32532
+ const path = issue.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
32533
+ return path ? `${path}: ${issue.message}` : issue.message;
32534
+ }).join("; ");
32535
+ }
32536
+ function getLiveRecords(pluginId) {
32537
+ return (getRegistry().records[pluginId] ?? []).filter((record) => !record.isRevoked);
32538
+ }
32539
+ function getMember(api, methodName) {
32540
+ return Reflect.get(api, methodName);
32541
+ }
32542
+ function getPluginApiDebugger() {
32543
+ return getLibDebugger(PLUGIN_API_DEBUGGER_NAMESPACE);
32544
+ }
32545
+ function getRegistry() {
32546
+ const wrapper = getObsidianDevUtilsState(PLUGIN_API_REGISTRY_STATE_KEY, {});
32547
+ const registry = wrapper.value;
32548
+ registry.records ??= {};
32549
+ registry.subscribers ??= [];
32550
+ return castTo(registry);
32551
+ }
32552
+ function notifySubscribers() {
32553
+ for (const subscriber of snapshot(getRegistry().subscribers)) {
32554
+ subscriber();
32555
+ }
32556
+ }
32557
+ function resolveUnavailabilityReason(params) {
32558
+ if (!Object.hasOwn(params.app.plugins.manifests, params.pluginId)) {
32559
+ return "notInstalled" /* NotInstalled */;
32560
+ }
32561
+ if (!params.app.plugins.enabledPlugins.has(params.pluginId)) {
32562
+ return "notEnabled" /* NotEnabled */;
32563
+ }
32564
+ const records = getLiveRecords(params.pluginId);
32565
+ if (records.length === 0) {
32566
+ return "notPublished" /* NotPublished */;
32567
+ }
32568
+ if (records.every((record) => !(0, import_compare_versions3.satisfies)(record.apiVersion, params.apiVersionRange))) {
32569
+ return "versionMismatch" /* VersionMismatch */;
32570
+ }
32571
+ return "shapeMismatch" /* ShapeMismatch */;
32572
+ }
32573
+ function revokeRecord(record) {
32574
+ record.isRevoked = true;
32575
+ const registry = getRegistry();
32576
+ const records = registry.records[record.pluginId];
32577
+ if (records) {
32578
+ registry.records[record.pluginId] = records.filter((candidate) => candidate !== record);
32579
+ }
32580
+ notifySubscribers();
32581
+ }
32582
+ function selectRecord(params) {
32583
+ const candidates = getLiveRecords(params.pluginId).filter((record) => (0, import_compare_versions3.satisfies)(record.apiVersion, params.apiVersionRange)).filter((record) => checkMatchesContractShape(record, params.contract)).sort((a2, b) => (0, import_compare_versions3.compareVersions)(b.apiVersion, a2.apiVersion));
32584
+ return candidates[0] ?? null;
32585
+ }
32586
+ function subscribe(subscriber) {
32587
+ const registry = getRegistry();
32588
+ registry.subscribers.push(subscriber);
32589
+ return () => {
32590
+ const index2 = registry.subscribers.indexOf(subscriber);
32591
+ if (index2 !== -1) {
32592
+ registry.subscribers.splice(index2, 1);
32409
32593
  }
32594
+ };
32595
+ }
32596
+ function validatePayload(params) {
32597
+ const result = params.schema["~standard"].validate(params.value);
32598
+ if (checkIsPromiseLike(result)) {
32599
+ invokeAsyncSafely(async () => {
32600
+ const asyncResult = await result;
32601
+ if (asyncResult.issues) {
32602
+ getPluginApiDebugger()(
32603
+ 'Asynchronous validation of the %s of "%s" API method "%s" failed: %s',
32604
+ params.payloadKind,
32605
+ params.pluginId,
32606
+ params.methodName,
32607
+ formatIssues(asyncResult.issues)
32608
+ );
32609
+ }
32610
+ });
32611
+ return;
32612
+ }
32613
+ if (result.issues) {
32614
+ throw new PluginApiValidationError({
32615
+ issues: result.issues,
32616
+ methodName: params.methodName,
32617
+ payloadKind: params.payloadKind,
32618
+ pluginId: params.pluginId
32619
+ });
32410
32620
  }
32411
32621
  }
32412
32622
 
32413
- // src/__merged.ts
32414
- init_css_class();
32415
-
32416
- // src/obsidian/data-handler.ts
32417
- var data_handler_exports = {};
32418
- __export(data_handler_exports, {
32419
- PluginDataHandler: () => PluginDataHandler
32420
- });
32421
- var PluginDataHandler = class {
32623
+ // src/obsidian/components/settings-migration-component.ts
32624
+ init_component_ex();
32625
+ var SettingsMigrationComponent = class extends ComponentEx {
32626
+ apiVersionRange;
32627
+ app;
32628
+ contract;
32629
+ getProposedSettings;
32630
+ isProposing = false;
32631
+ pluginSettingsComponent;
32632
+ providerPluginId;
32633
+ retireProposedSettings;
32634
+ sourcePluginId;
32422
32635
  /**
32423
- * Creates a new plugin data handler.
32636
+ * Creates an instance of {@link SettingsMigrationComponent}.
32637
+ *
32638
+ * @param params - The constructor parameters.
32639
+ */
32640
+ constructor(params) {
32641
+ super();
32642
+ this.apiVersionRange = params.apiVersionRange;
32643
+ this.app = params.app;
32644
+ this.contract = params.contract;
32645
+ this.getProposedSettings = params.getProposedSettings;
32646
+ this.pluginSettingsComponent = params.pluginSettingsComponent;
32647
+ this.providerPluginId = params.providerPluginId;
32648
+ this.retireProposedSettings = params.retireProposedSettings;
32649
+ this.sourcePluginId = params.sourcePluginId;
32650
+ }
32651
+ /**
32652
+ * Loads the component, wiring both edges that can make an offer possible.
32653
+ */
32654
+ onload() {
32655
+ const ref = watchPluginApi(normalizeOptionalProperties({
32656
+ apiVersionRange: this.apiVersionRange,
32657
+ app: this.app,
32658
+ component: this,
32659
+ contract: this.contract,
32660
+ pluginId: this.providerPluginId
32661
+ }));
32662
+ registerAsyncEvent(
32663
+ this,
32664
+ ref.on("change", () => {
32665
+ this.handleChange(ref);
32666
+ })
32667
+ );
32668
+ registerAsyncEvent(
32669
+ this,
32670
+ this.pluginSettingsComponent.on("loadSettings", () => {
32671
+ this.handleChange(ref);
32672
+ })
32673
+ );
32674
+ this.handleChange(ref);
32675
+ }
32676
+ handleChange(ref) {
32677
+ invokeAsyncSafely(async () => {
32678
+ await this.propose(ref.value);
32679
+ });
32680
+ }
32681
+ async propose(api) {
32682
+ const proposedSettings = this.getProposedSettings();
32683
+ if (!api || this.isProposing || proposedSettings === null) {
32684
+ return;
32685
+ }
32686
+ this.isProposing = true;
32687
+ try {
32688
+ const result = await api.migrateSettings({
32689
+ proposedSettings,
32690
+ sourcePluginId: this.sourcePluginId
32691
+ });
32692
+ if (result.isApplied) {
32693
+ await this.retireProposedSettings();
32694
+ }
32695
+ } finally {
32696
+ this.isProposing = false;
32697
+ }
32698
+ }
32699
+ };
32700
+
32701
+ // src/obsidian/components/syntax-highlighting-component.ts
32702
+ var syntax_highlighting_component_exports = {};
32703
+ __export(syntax_highlighting_component_exports, {
32704
+ SyntaxHighlightingComponent: () => SyntaxHighlightingComponent
32705
+ });
32706
+ init_error();
32707
+ init_component_ex();
32708
+ var SyntaxHighlightingComponent = class extends ComponentEx {
32709
+ /**
32710
+ * Registers a fenced code block language, highlighting it in the editor and, optionally, in reading view.
32711
+ *
32712
+ * @param params - The parameters for registering the language.
32713
+ * @returns A {@link Promise} that resolves when the language is registered.
32714
+ *
32715
+ * @example
32716
+ * ```ts
32717
+ * await component.registerCodeBlockLanguage({
32718
+ * editorMode: 'text/typescript',
32719
+ * language: 'my-language',
32720
+ * prismGrammar: 'typescript'
32721
+ * });
32722
+ * ```
32723
+ */
32724
+ async registerCodeBlockLanguage(params) {
32725
+ const {
32726
+ editorMode,
32727
+ language,
32728
+ prismGrammar
32729
+ } = params;
32730
+ this.ensureLoaded();
32731
+ if (prismGrammar !== void 0) {
32732
+ await this.registerPrismLanguage({
32733
+ grammar: prismGrammar,
32734
+ language
32735
+ });
32736
+ }
32737
+ this.ensureLoaded();
32738
+ const previousModeFactory = window.CodeMirror.modes[language];
32739
+ window.CodeMirror.defineMode(language, (config) => window.CodeMirror.getMode(config, editorMode));
32740
+ this.register(() => {
32741
+ if (previousModeFactory !== void 0) {
32742
+ window.CodeMirror.modes[language] = previousModeFactory;
32743
+ return;
32744
+ }
32745
+ delete window.CodeMirror.modes[language];
32746
+ });
32747
+ }
32748
+ /**
32749
+ * Registers a Prism language, highlighting it everywhere Prism renders code — reading view and the
32750
+ * {@link CodeHighlighterComponent} settings field.
32751
+ *
32752
+ * @param params - The parameters for registering the language.
32753
+ * @returns A {@link Promise} that resolves when the language is registered.
32754
+ *
32755
+ * @example
32756
+ * ```ts
32757
+ * await component.registerPrismLanguage({
32758
+ * grammar: {
32759
+ * keyword: /\bfoo\b/
32760
+ * },
32761
+ * language: 'my-language'
32762
+ * });
32763
+ * ```
32764
+ */
32765
+ async registerPrismLanguage(params) {
32766
+ const {
32767
+ grammar,
32768
+ language
32769
+ } = params;
32770
+ this.ensureLoaded();
32771
+ const prism = await loadPrism();
32772
+ this.ensureLoaded();
32773
+ const previousGrammar = prism.languages[language];
32774
+ prism.languages[language] = this.resolveGrammar(prism, grammar);
32775
+ this.register(() => {
32776
+ if (previousGrammar !== void 0) {
32777
+ prism.languages[language] = previousGrammar;
32778
+ return;
32779
+ }
32780
+ delete prism.languages[language];
32781
+ });
32782
+ }
32783
+ /**
32784
+ * Returns the grammar of an already registered Prism language.
32785
+ *
32786
+ * @param prism - The loaded Prism module.
32787
+ * @param language - The language to look up.
32788
+ * @returns The grammar of the language.
32789
+ * @throws An {@link Error} if the language is not registered.
32790
+ */
32791
+ requirePrismLanguage(prism, language) {
32792
+ return prism.languages[language] ?? throwExpression(new Error(`Prism language "${language}" is not registered.`));
32793
+ }
32794
+ /**
32795
+ * Resolves a grammar source into the grammar to register.
32796
+ *
32797
+ * @param prism - The loaded Prism module.
32798
+ * @param grammarSource - The grammar source to resolve.
32799
+ * @returns The resolved grammar.
32800
+ */
32801
+ resolveGrammar(prism, grammarSource) {
32802
+ if (typeof grammarSource === "string") {
32803
+ return this.requirePrismLanguage(prism, grammarSource);
32804
+ }
32805
+ if (typeof grammarSource === "function") {
32806
+ return grammarSource({
32807
+ prism,
32808
+ requirePrismLanguage: (language) => this.requirePrismLanguage(prism, language)
32809
+ });
32810
+ }
32811
+ return grammarSource;
32812
+ }
32813
+ };
32814
+
32815
+ // src/obsidian/constructors/get-dom-events-handlers-constructor.ts
32816
+ var get_dom_events_handlers_constructor_exports = {};
32817
+ __export(get_dom_events_handlers_constructor_exports, {
32818
+ getDomEventsHandlersConstructor: () => getDomEventsHandlersConstructor
32819
+ });
32820
+ var import_obsidian121 = require("obsidian");
32821
+ init_async();
32822
+ init_type_guards();
32823
+ init_monkey_around_component();
32824
+ var MarkdownPreviewRendererRegisterDomEventsPatchComponent = class extends MonkeyAroundComponent {
32825
+ domEventsHandlersConstructor = null;
32826
+ onload() {
32827
+ this.registerMethodPatch({
32828
+ $object: import_obsidian121.MarkdownPreviewRenderer,
32829
+ methodName: "registerDomEvents",
32830
+ patchHandler: ({
32831
+ fallback,
32832
+ originalArguments: [, handlers]
32833
+ }) => {
32834
+ this.domEventsHandlersConstructor = handlers.constructor;
32835
+ fallback();
32836
+ }
32837
+ });
32838
+ }
32839
+ };
32840
+ async function getDomEventsHandlersConstructor(app) {
32841
+ let mdFile = app.vault.getMarkdownFiles()[0];
32842
+ let shouldDelete = false;
32843
+ if (!mdFile) {
32844
+ mdFile = await app.vault.create("__temp.md", "");
32845
+ shouldDelete = true;
32846
+ }
32847
+ try {
32848
+ var _stack = [];
32849
+ try {
32850
+ const patch = __using(_stack, new MarkdownPreviewRendererRegisterDomEventsPatchComponent());
32851
+ patch.load();
32852
+ const leaf = app.workspace.getLeaf(true);
32853
+ await leaf.openFile(mdFile, {
32854
+ active: true,
32855
+ state: { mode: "preview" }
32856
+ });
32857
+ try {
32858
+ await retryWithTimeout({
32859
+ operationFunction: () => patch.domEventsHandlersConstructor !== null,
32860
+ operationName: "getDomEventsHandlersConstructor"
32861
+ });
32862
+ } finally {
32863
+ leaf.detach();
32864
+ }
32865
+ assertNonNullable(patch.domEventsHandlersConstructor, "Failed to get register dom events handlers constructor");
32866
+ return patch.domEventsHandlersConstructor;
32867
+ } catch (_) {
32868
+ var _error = _, _hasError = true;
32869
+ } finally {
32870
+ __callDispose(_stack, _error, _hasError);
32871
+ }
32872
+ } finally {
32873
+ if (shouldDelete) {
32874
+ await trashSafe(app, mdFile);
32875
+ }
32876
+ }
32877
+ }
32878
+
32879
+ // src/__merged.ts
32880
+ init_css_class();
32881
+
32882
+ // src/obsidian/data-handler.ts
32883
+ var data_handler_exports = {};
32884
+ __export(data_handler_exports, {
32885
+ PluginDataHandler: () => PluginDataHandler
32886
+ });
32887
+ var PluginDataHandler = class {
32888
+ /**
32889
+ * Creates a new plugin data handler.
32424
32890
  *
32425
32891
  * @param plugin - The plugin instance.
32426
32892
  */
@@ -33592,1604 +34058,1225 @@ var MINIMIZE_ICON_ID = "minus";
33592
34058
  var RESTORE_ICON_ID = "maximize-2";
33593
34059
  var CANCEL_ICON_ID = "x";
33594
34060
  var NAVIGATION_KEYS = /* @__PURE__ */ new Set([
33595
- "ArrowDown",
33596
- "ArrowLeft",
33597
- "ArrowRight",
33598
- "ArrowUp",
33599
- "End",
33600
- "Home",
33601
- "PageDown",
33602
- "PageUp"
33603
- ]);
33604
- var MODIFIER_KEYS = /* @__PURE__ */ new Set([
33605
- "Alt",
33606
- "Control",
33607
- "Meta",
33608
- "Shift"
33609
- ]);
33610
- var peekLockEntries = /* @__PURE__ */ new Set();
33611
- var beeper = new Beeper();
33612
- var PeekLockComponent = class extends MonkeyAroundComponent {
33613
- /**
33614
- * Constructs a new instance.
33615
- *
33616
- * @param app - The app whose settings modal is guarded and whose windows suppress input while locked.
33617
- */
33618
- constructor(app) {
33619
- super();
33620
- this.app = app;
33621
- }
33622
- app;
33623
- /**
33624
- * Registers the `open` patches and the input suppressors.
33625
- */
33626
- onload() {
33627
- super.onload();
33628
- this.registerMethodPatch({
33629
- $object: import_obsidian132.Modal.prototype,
33630
- methodName: "open",
33631
- patchHandler: ({ fallback, originalThis }) => {
33632
- blockOrFallback(originalThis, fallback);
33633
- }
33634
- });
33635
- this.registerMethodPatch({
33636
- $object: this.app.setting,
33637
- methodName: "open",
33638
- patchHandler: ({ fallback, originalThis }) => {
33639
- blockOrFallback(originalThis, fallback);
33640
- }
33641
- });
33642
- const allWindowsEventComponent = this.addChild(new AllWindowsEventComponent(this.app));
33643
- allWindowsEventComponent.registerAllWindowsDomEvent({
33644
- callback: suppressKeydownWhilePeekLocked,
33645
- options: { capture: true },
33646
- type: "keydown"
33647
- });
33648
- allWindowsEventComponent.registerAllWindowsDomEvent({
33649
- callback: suppressWhilePeekLocked,
33650
- options: { capture: true },
33651
- type: "beforeinput"
33652
- });
33653
- allWindowsEventComponent.registerAllWindowsDomEvent({
33654
- callback: suppressWhilePeekLocked,
33655
- options: { capture: true },
33656
- type: "contextmenu"
33657
- });
33658
- }
33659
- };
33660
- var MinimizableModal = class {
33661
- /**
33662
- * The wrapped modal. Open, close, and populate it as usual (e.g. `minimizable.modal.open()`).
33663
- */
33664
- modal;
33665
- /**
33666
- * Whether the modal is currently minimized.
33667
- *
33668
- * @returns `true` if the modal is currently minimized, `false` otherwise.
33669
- */
33670
- get isMinimized() {
33671
- return this.isMinimizedValue;
33672
- }
33673
- /**
33674
- * Patches the wrapped modal's `onClose` to run cleanup. Persists for the wrapper's whole life (a
33675
- * modal can be reopened, so the patch must survive each close), hence a dedicated component that is
33676
- * never unloaded — distinct from `peekLockComponent`, whose patches live only while minimized.
33677
- * Exposed to subclasses so they can register additional lifetime-scoped patches on the wrapper.
33678
- */
33679
- closePatchComponent = new MonkeyAroundComponent();
33680
- isMinimizedValue = false;
33681
- minimizeButtonEl;
33682
- minimizedBarEl = null;
33683
- peekLockComponent = null;
33684
- peekLockEntry = null;
33685
- shouldMinimizeOnClickOutside;
33686
- shouldShowCancelButton;
33687
- /**
33688
- * Wraps the given modal, adding a minimize button and wiring up cleanup on close.
33689
- *
33690
- * @param modal - The modal instance to make minimizable.
33691
- * @param options - Options controlling the minimized bar. See {@link MinimizableModalConstructorOptions}.
33692
- */
33693
- constructor(modal, options = {}) {
33694
- this.modal = modal;
33695
- this.shouldMinimizeOnClickOutside = options.shouldMinimizeOnClickOutside ?? true;
33696
- this.shouldShowCancelButton = options.shouldShowCancelButton ?? true;
33697
- addPluginCssClasses(modal.containerEl, ["minimizable-modal" /* MinimizableModal */]);
33698
- this.minimizeButtonEl = this.createMinimizeButton();
33699
- if (this.shouldMinimizeOnClickOutside) {
33700
- modal.containerEl.addEventListener("click", ($event) => {
33701
- this.handleContainerClick($event);
33702
- }, { capture: true });
33703
- }
33704
- this.closePatchComponent.load();
33705
- this.patchOnClose(modal);
33706
- }
33707
- /**
33708
- * Minimizes the modal: hides the modal and its backdrop, shows a floating bar with a restore button,
33709
- * and puts the app into the peek-only lock. A no-op if the modal is already minimized.
33710
- */
33711
- minimize() {
33712
- if (this.isMinimizedValue) {
33713
- return;
33714
- }
33715
- this.isMinimizedValue = true;
33716
- this.minimizeButtonEl.hide();
33717
- this.modal.containerEl.hide();
33718
- this.modal.app.keymap.popScope(this.modal.scope);
33719
- this.minimizedBarEl = this.createMinimizedBar();
33720
- this.enablePeekLock(this.minimizedBarEl);
33721
- }
33722
- /**
33723
- * Restores the modal from the minimized state, removing the floating bar and lifting the peek-only
33724
- * lock. A no-op if the modal is not minimized.
33725
- */
33726
- restore() {
33727
- if (!this.isMinimizedValue) {
33728
- return;
33729
- }
33730
- this.isMinimizedValue = false;
33731
- this.disablePeekLock();
33732
- this.removeMinimizedBar();
33733
- this.modal.app.keymap.pushScope(this.modal.scope);
33734
- this.modal.containerEl.show();
33735
- this.minimizeButtonEl.show();
33736
- }
33737
- createMinimizeButton() {
33738
- const buttonEl = this.modal.modalEl.createEl("button", { cls: "minimize-button" /* MinimizeButton */ });
33739
- (0, import_obsidian132.setIcon)(buttonEl, MINIMIZE_ICON_ID);
33740
- buttonEl.addEventListener("click", () => {
33741
- this.minimize();
33742
- });
33743
- return buttonEl;
33744
- }
33745
- createMinimizedBar() {
33746
- const barEl = this.modal.containerEl.ownerDocument.body.createDiv();
33747
- addPluginCssClasses(barEl, ["minimized-modal-bar" /* MinimizedModalBar */]);
33748
- barEl.createSpan({
33749
- cls: "minimized-modal-bar-title" /* MinimizedModalBarTitle */,
33750
- text: this.modal.titleEl.textContent
33751
- });
33752
- const restoreButtonEl = barEl.createEl("button", { cls: "restore-button" /* RestoreButton */ });
33753
- (0, import_obsidian132.setIcon)(restoreButtonEl, RESTORE_ICON_ID);
33754
- if (this.shouldShowCancelButton) {
33755
- const cancelButtonEl = barEl.createEl("button", { cls: "cancel-button" /* CancelButton */ });
33756
- (0, import_obsidian132.setIcon)(cancelButtonEl, CANCEL_ICON_ID);
33757
- (0, import_obsidian132.setTooltip)(cancelButtonEl, "Cancel");
33758
- cancelButtonEl.addEventListener("click", ($event) => {
33759
- $event.stopPropagation();
33760
- this.modal.close();
33761
- });
33762
- }
33763
- barEl.addEventListener("click", () => {
33764
- this.restore();
33765
- });
33766
- barEl.addEventListener("animationend", () => {
33767
- barEl.removeClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
33768
- });
33769
- return barEl;
33770
- }
33771
- disablePeekLock() {
33772
- if (!this.peekLockEntry) {
33773
- return;
33774
- }
33775
- peekLockEntries.delete(this.peekLockEntry);
33776
- this.peekLockEntry = null;
33777
- this.peekLockComponent?.unload();
33778
- this.peekLockComponent = null;
33779
- }
33780
- enablePeekLock(barEl) {
33781
- this.peekLockEntry = {
33782
- barEl,
33783
- flash: () => {
33784
- this.flashBar(barEl);
33785
- },
33786
- innerModal: this.modal
33787
- };
33788
- peekLockEntries.add(this.peekLockEntry);
33789
- this.peekLockComponent = new PeekLockComponent(this.modal.app);
33790
- this.peekLockComponent.load();
33791
- }
33792
- flashBar(barEl) {
33793
- barEl.removeClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
33794
- barEl.getBoundingClientRect();
33795
- barEl.addClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
33796
- }
33797
- handleClose() {
33798
- this.disablePeekLock();
33799
- this.removeMinimizedBar();
33800
- this.isMinimizedValue = false;
33801
- }
33802
- handleContainerClick($event) {
33803
- if (this.isMinimizedValue) {
33804
- return;
33805
- }
33806
- if ($event.composedPath().includes(this.modal.modalEl)) {
33807
- return;
33808
- }
33809
- if ($event.target === this.modal.containerEl) {
33810
- return;
33811
- }
33812
- $event.preventDefault();
33813
- $event.stopImmediatePropagation();
33814
- this.minimize();
33815
- }
33816
- patchOnClose(modal) {
33817
- this.closePatchComponent.registerMethodPatch({
33818
- $object: modal,
33819
- methodName: "onClose",
33820
- patchHandler: ({ fallback }) => {
33821
- fallback();
33822
- this.handleClose();
33823
- }
33824
- });
33825
- }
33826
- removeMinimizedBar() {
33827
- this.minimizedBarEl?.remove();
33828
- this.minimizedBarEl = null;
33829
- }
33830
- };
33831
- function blockEvent($event) {
33832
- $event.preventDefault();
33833
- $event.stopImmediatePropagation();
33834
- signalBlockedAttempt();
33835
- }
33836
- function blockOrFallback(modal, fallback) {
33837
- if (shouldBlockOpen(modal)) {
33838
- signalBlockedAttempt();
33839
- return;
33840
- }
33841
- fallback();
33842
- }
33843
- function flashMinimizedBars() {
33844
- for (const entry of peekLockEntries) {
33845
- entry.flash();
33846
- }
33847
- }
33848
- function isMinimizedInnerModal(modal) {
33849
- for (const entry of peekLockEntries) {
33850
- if (entry.innerModal === modal) {
33851
- return true;
33852
- }
33853
- }
33854
- return false;
33855
- }
33856
- function isPeekAllowedKey($event) {
33857
- if (NAVIGATION_KEYS.has($event.key)) {
33858
- return !$event.shiftKey;
33859
- }
33860
- return MODIFIER_KEYS.has($event.key);
33861
- }
33862
- function isPeekLocked() {
33863
- for (const entry of peekLockEntries) {
33864
- if (!entry.barEl.isConnected) {
33865
- peekLockEntries.delete(entry);
33866
- }
33867
- }
33868
- return peekLockEntries.size > 0;
33869
- }
33870
- function shouldBlockOpen(modal) {
33871
- return isPeekLocked() && !isMinimizedInnerModal(modal);
33872
- }
33873
- function signalBlockedAttempt() {
33874
- flashMinimizedBars();
33875
- beeper.beep();
33876
- }
33877
- function suppressKeydownWhilePeekLocked($event) {
33878
- if (!isPeekLocked() || isPeekAllowedKey($event)) {
33879
- return;
33880
- }
33881
- blockEvent($event);
33882
- }
33883
- function suppressWhilePeekLocked($event) {
33884
- if (!isPeekLocked()) {
33885
- return;
33886
- }
33887
- blockEvent($event);
33888
- }
33889
-
33890
- // src/obsidian/modals/modal-command-builder.ts
33891
- var modal_command_builder_exports = {};
33892
- __export(modal_command_builder_exports, {
33893
- ModalCommandBuilder: () => ModalCommandBuilder,
33894
- ModalCommandsRenderMode: () => ModalCommandsRenderMode
33895
- });
33896
- var import_obsidian133 = require("obsidian");
33897
- init_css_class();
33898
- init_plugin_context();
33899
- var KEYS_MAP = /* @__PURE__ */ new Map([
33900
- ["Enter", "\u21B5"],
33901
- ["UpDown", "\u2191\u2193"]
33902
- ]);
33903
- var ModalCommandsRenderMode = /* @__PURE__ */ ((ModalCommandsRenderMode2) => {
33904
- ModalCommandsRenderMode2["Buttons"] = "buttons";
33905
- ModalCommandsRenderMode2["Instructions"] = "instructions";
33906
- return ModalCommandsRenderMode2;
33907
- })(ModalCommandsRenderMode || {});
33908
- var NOOP_MODAL_COMMANDS = {
33909
- refresh: () => {
33910
- }
33911
- };
33912
- var ModalCommandBuilder = class {
33913
- entries = [];
33914
- /**
33915
- * Adds an interactive checkbox to the control strip, bound to a modifier+key shortcut that toggles it.
33916
- *
33917
- * @param command - The checkbox command to add.
33918
- * @returns The builder instance for chaining.
33919
- */
33920
- addCheckbox(command) {
33921
- this.entries.push({
33922
- checkIsAvailable: command.checkIsAvailable ?? null,
33923
- commandText: this.buildCommandText(command),
33924
- initButton: (buttonEl, scope) => {
33925
- const checkboxEl = createEl("input", { type: "checkbox" });
33926
- initCheckbox(checkboxEl, scope);
33927
- buttonEl.addEventListener("click", () => {
33928
- toggleCheckbox(checkboxEl);
33929
- });
33930
- return {
33931
- checkIsOn: () => checkboxEl.checked,
33932
- setDisabled: (isDisabled) => {
33933
- checkboxEl.disabled = isDisabled;
33934
- buttonEl.disabled = isDisabled;
33935
- }
33936
- };
33937
- },
33938
- initInstruction: (purposeEl, scope) => {
33939
- const checkboxEl = purposeEl.createEl("input", { type: "checkbox" });
33940
- addPluginCssClasses(checkboxEl, "modal-command-control" /* ModalCommandControl */);
33941
- return initCheckbox(checkboxEl, scope);
33942
- },
33943
- isDropDown: false,
33944
- purpose: command.purpose,
33945
- registerScope: null
33946
- });
33947
- return this;
33948
- function initCheckbox(checkboxEl, scope) {
33949
- command.onInit(checkboxEl);
33950
- checkboxEl.addEventListener("change", () => {
33951
- command.onChange(checkboxEl.checked);
33952
- });
33953
- scope.register(command.modifiers ?? [], command.key, () => {
33954
- toggleCheckbox(checkboxEl);
33955
- });
33956
- return {
33957
- setDisabled: (isDisabled) => {
33958
- checkboxEl.disabled = isDisabled;
33959
- }
33960
- };
33961
- }
33962
- }
33963
- /**
33964
- * Adds a dropdown to the control strip, bound to a modifier+key shortcut that cycles through its
33965
- * options.
33966
- *
33967
- * @param command - The dropdown command to add.
33968
- * @returns The builder instance for chaining.
33969
- */
33970
- addDropDown(command) {
33971
- this.entries.push({
33972
- checkIsAvailable: command.checkIsAvailable ?? null,
33973
- commandText: this.buildCommandText(command),
33974
- initButton: null,
33975
- initInstruction: (purposeEl, scope) => {
33976
- const dropdownComponent = new import_obsidian133.DropdownComponent(purposeEl);
33977
- addPluginCssClasses(dropdownComponent.selectEl, "modal-command-control" /* ModalCommandControl */);
33978
- command.onInit(dropdownComponent);
33979
- dropdownComponent.onChange((value) => {
33980
- command.onChange(value);
33981
- });
33982
- scope.register(command.modifiers ?? [], command.key, () => {
33983
- if (dropdownComponent.disabled) {
33984
- return;
33985
- }
33986
- const selectEl = dropdownComponent.selectEl;
33987
- selectEl.selectedIndex = (selectEl.selectedIndex + 1) % selectEl.options.length;
33988
- selectEl.trigger("change");
33989
- });
33990
- return {
33991
- setDisabled: (isDisabled) => {
33992
- dropdownComponent.setDisabled(isDisabled);
33993
- }
33994
- };
33995
- },
33996
- isDropDown: true,
33997
- purpose: command.purpose,
33998
- registerScope: null
33999
- });
34000
- return this;
34001
- }
34002
- /**
34003
- * Adds a keyboard command to the control strip. When {@link KeyboardCommand.onKey} is provided, its
34004
- * handler is registered with the modal scope; otherwise the command is a hint only.
34005
- *
34006
- * @param command - The keyboard command to add.
34007
- * @returns The builder instance for chaining.
34008
- */
34009
- addKeyboardCommand(command) {
34010
- const checkIsOn = command.checkIsOn ?? (() => false);
34011
- const onActivate = command.onActivate ?? null;
34012
- const onKey = command.onKey?.bind(command) ?? null;
34013
- this.entries.push({
34014
- checkIsAvailable: command.checkIsAvailable ?? null,
34015
- commandText: this.buildCommandText(command),
34016
- initButton: onActivate ? (buttonEl) => {
34017
- buttonEl.addEventListener("click", ($event) => {
34018
- onActivate($event);
34019
- });
34020
- return {
34021
- checkIsOn,
34022
- setDisabled: (isDisabled) => {
34023
- buttonEl.disabled = isDisabled;
34024
- }
34025
- };
34026
- } : null,
34027
- initInstruction: null,
34028
- isDropDown: false,
34029
- purpose: command.purpose,
34030
- registerScope: onKey ? (scope) => {
34031
- scope.register(command.modifiers ?? [], command.key, onKey);
34032
- } : null
34033
- });
34034
- return this;
34035
- }
34036
- /**
34037
- * Applies the accumulated commands to a modal. Always registers the essential navigation/action key
34038
- * handlers; only renders the control strip and registers the option-toggle shortcuts when
34039
- * {@link ModalCommandBuilderBuildOptions.shouldShowInstructions} is `true`.
34040
- *
34041
- * @param target - The modal, or the bare element + scope, to apply the commands to.
34042
- * @param options - The build options.
34043
- * @returns A handle whose {@link ModalCommands.refresh} re-states every rendered control.
34044
- */
34045
- build(target, options = {}) {
34046
- const {
34047
- renderMode = "instructions" /* Instructions */,
34048
- shouldShowInstructions = true
34049
- } = options;
34050
- const scope = target.scope;
34051
- for (const entry of this.entries) {
34052
- entry.registerScope?.(scope);
34053
- }
34054
- if (!shouldShowInstructions) {
34055
- return NOOP_MODAL_COMMANDS;
34056
- }
34057
- const refreshers = renderMode === "buttons" /* Buttons */ ? this.renderButtons(target, scope) : this.renderInstructions(target, scope);
34058
- const modalCommands = {
34059
- refresh: () => {
34060
- for (const refreshControl of refreshers) {
34061
- refreshControl();
34062
- }
34063
- }
34064
- };
34065
- modalCommands.refresh();
34066
- return modalCommands;
34067
- }
34068
- buildCommandText(command) {
34069
- let commandText = KEYS_MAP.get(command.key) ?? command.key;
34070
- for (const modifier of command.modifiers ?? []) {
34071
- commandText = `${this.getModifierString(modifier)} ${commandText}`;
34072
- }
34073
- return commandText;
34074
- }
34075
- getModifierString(modifier) {
34076
- switch (modifier) {
34077
- case "Alt": {
34078
- return "alt";
34079
- }
34080
- case "Ctrl": {
34081
- return "ctrl";
34082
- }
34083
- case "Meta": {
34084
- return import_obsidian133.Platform.isMacOS ? "cmd" : "win";
34085
- }
34086
- case "Mod": {
34087
- return import_obsidian133.Platform.isMacOS ? "cmd" : "ctrl";
34088
- }
34089
- case "Shift": {
34090
- return "shift";
34091
- }
34092
- default: {
34093
- return modifier;
34094
- }
34095
- }
34096
- }
34061
+ "ArrowDown",
34062
+ "ArrowLeft",
34063
+ "ArrowRight",
34064
+ "ArrowUp",
34065
+ "End",
34066
+ "Home",
34067
+ "PageDown",
34068
+ "PageUp"
34069
+ ]);
34070
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set([
34071
+ "Alt",
34072
+ "Control",
34073
+ "Meta",
34074
+ "Shift"
34075
+ ]);
34076
+ var peekLockEntries = /* @__PURE__ */ new Set();
34077
+ var beeper = new Beeper();
34078
+ var PeekLockComponent = class extends MonkeyAroundComponent {
34097
34079
  /**
34098
- * Resolves the element a self-created strip is appended to.
34099
- *
34100
- * A {@link Modal} carries both `modalEl` and a `containerEl` that is the OUTER `.modal-container`, so
34101
- * `modalEl` has to win — otherwise the strip lands outside the modal frame. Discriminated with `in`
34102
- * rather than a property read, because tests hand `build` a `strictProxy` mock whose `get` trap throws
34103
- * on unmocked members.
34080
+ * Constructs a new instance.
34104
34081
  *
34105
- * @param target - The build target.
34106
- * @returns The element to append the strip to.
34082
+ * @param app - The app whose settings modal is guarded and whose windows suppress input while locked.
34107
34083
  */
34108
- getStripContainerEl(target) {
34109
- return "modalEl" in target ? target.modalEl : target.containerEl;
34110
- }
34111
- renderButtons(target, scope) {
34112
- const stripEl = this.getStripContainerEl(target).createDiv();
34113
- addPluginCssClasses(stripEl, "modal-commands" /* ModalCommands */);
34114
- const refreshers = [];
34115
- for (const entry of this.entries) {
34116
- if (entry.isDropDown) {
34117
- throw new Error(`Cannot render dropdown command "${entry.purpose}" as a button: a control that cycles has no button form.`);
34118
- }
34119
- if (!entry.initButton) {
34120
- continue;
34121
- }
34122
- const buttonEl = stripEl.createEl("button", { type: "button" });
34123
- addPluginCssClasses(buttonEl, "modal-command" /* ModalCommand */);
34124
- buttonEl.createSpan({ text: entry.purpose });
34125
- if (!import_obsidian133.Platform.isMobile) {
34126
- const hotkeyEl = buttonEl.createSpan({ text: entry.commandText });
34127
- addPluginCssClasses(hotkeyEl, "modal-command-hotkey" /* ModalCommandHotkey */);
34128
- }
34129
- buttonEl.addEventListener("mousedown", ($event) => {
34130
- $event.preventDefault();
34131
- });
34132
- const buttonControl = entry.initButton(buttonEl, scope);
34133
- const checkIsAvailable = entry.checkIsAvailable;
34134
- refreshers.push(() => {
34135
- if (checkIsAvailable) {
34136
- buttonControl.setDisabled(!checkIsAvailable());
34137
- }
34138
- const isOn = buttonControl.checkIsOn();
34139
- buttonEl.toggleClass("is-active" /* IsActive */, isOn);
34140
- buttonEl.setAttribute("aria-pressed", String(isOn));
34141
- });
34142
- }
34143
- return refreshers;
34084
+ constructor(app) {
34085
+ super();
34086
+ this.app = app;
34144
34087
  }
34145
- renderInstructions(target, scope) {
34146
- const instructions = this.entries.map((entry) => ({
34147
- command: entry.commandText,
34148
- entry,
34149
- purpose: entry.purpose
34150
- }));
34151
- const purposeEls = "setInstructions" in target ? this.renderNativeInstructions(target, instructions) : this.renderOwnInstructions(this.getStripContainerEl(target), instructions);
34152
- const refreshers = [];
34153
- for (const [index2, instruction] of instructions.entries()) {
34154
- const purposeEl = purposeEls[index2];
34155
- if (!purposeEl) {
34156
- continue;
34088
+ app;
34089
+ /**
34090
+ * Registers the `open` patches and the input suppressors.
34091
+ */
34092
+ onload() {
34093
+ super.onload();
34094
+ this.registerMethodPatch({
34095
+ $object: import_obsidian132.Modal.prototype,
34096
+ methodName: "open",
34097
+ patchHandler: ({ fallback, originalThis }) => {
34098
+ blockOrFallback(originalThis, fallback);
34157
34099
  }
34158
- const instructionControl = instruction.entry.initInstruction?.(purposeEl, scope);
34159
- const checkIsAvailable = instruction.entry.checkIsAvailable;
34160
- if (instructionControl && checkIsAvailable) {
34161
- refreshers.push(() => {
34162
- instructionControl.setDisabled(!checkIsAvailable());
34163
- });
34100
+ });
34101
+ this.registerMethodPatch({
34102
+ $object: this.app.setting,
34103
+ methodName: "open",
34104
+ patchHandler: ({ fallback, originalThis }) => {
34105
+ blockOrFallback(originalThis, fallback);
34164
34106
  }
34165
- }
34166
- return refreshers;
34107
+ });
34108
+ const allWindowsEventComponent = this.addChild(new AllWindowsEventComponent(this.app));
34109
+ allWindowsEventComponent.registerAllWindowsDomEvent({
34110
+ callback: suppressKeydownWhilePeekLocked,
34111
+ options: { capture: true },
34112
+ type: "keydown"
34113
+ });
34114
+ allWindowsEventComponent.registerAllWindowsDomEvent({
34115
+ callback: suppressWhilePeekLocked,
34116
+ options: { capture: true },
34117
+ type: "beforeinput"
34118
+ });
34119
+ allWindowsEventComponent.registerAllWindowsDomEvent({
34120
+ callback: suppressWhilePeekLocked,
34121
+ options: { capture: true },
34122
+ type: "contextmenu"
34123
+ });
34167
34124
  }
34125
+ };
34126
+ var MinimizableModal = class {
34168
34127
  /**
34169
- * Renders through Obsidian's own instruction bar, the way every {@link SuggestModal} consumer has always
34170
- * had it.
34128
+ * The wrapped modal. Open, close, and populate it as usual (e.g. `minimizable.modal.open()`).
34129
+ */
34130
+ modal;
34131
+ /**
34132
+ * Whether the modal is currently minimized.
34171
34133
  *
34172
- * @param modal - The suggest modal.
34173
- * @param instructions - The instructions to render.
34174
- * @returns The purpose element of each rendered instruction.
34134
+ * @returns `true` if the modal is currently minimized, `false` otherwise.
34175
34135
  */
34176
- renderNativeInstructions(modal, instructions) {
34177
- modal.setInstructions(instructions);
34178
- return [...modal.instructionsEl.findAll(".prompt-instruction > span:nth-child(2)")];
34136
+ get isMinimized() {
34137
+ return this.isMinimizedValue;
34179
34138
  }
34180
34139
  /**
34181
- * Renders an instruction bar of the builder's own, for a host that has none.
34182
- *
34183
- * The markup mirrors what `setInstructions` produces including
34184
- * {@link CssClass.PromptInstructionCommand} on the key hint, which is not decoration: Obsidian's rule
34185
- * for that class is what supplies the hint's bold weight and the `margin-inline-end` separating it
34186
- * from the purpose text. Every class here is unscoped in the app's stylesheet, so all of it reaches a
34187
- * plain {@link Modal} as readily as it reaches the quick switcher.
34140
+ * Patches the wrapped modal's `onClose` to run cleanup. Persists for the wrapper's whole life (a
34141
+ * modal can be reopened, so the patch must survive each close), hence a dedicated component that is
34142
+ * never unloaded distinct from `peekLockComponent`, whose patches live only while minimized.
34143
+ * Exposed to subclasses so they can register additional lifetime-scoped patches on the wrapper.
34144
+ */
34145
+ closePatchComponent = new MonkeyAroundComponent();
34146
+ isMinimizedValue = false;
34147
+ minimizeButtonEl;
34148
+ minimizedBarEl = null;
34149
+ peekLockComponent = null;
34150
+ peekLockEntry = null;
34151
+ shouldMinimizeOnClickOutside;
34152
+ shouldShowCancelButton;
34153
+ /**
34154
+ * Wraps the given modal, adding a minimize button and wiring up cleanup on close.
34188
34155
  *
34189
- * @param containerEl - The element to append the bar to.
34190
- * @param instructions - The instructions to render.
34191
- * @returns The purpose element of each rendered instruction.
34156
+ * @param modal - The modal instance to make minimizable.
34157
+ * @param options - Options controlling the minimized bar. See {@link MinimizableModalConstructorOptions}.
34192
34158
  */
34193
- renderOwnInstructions(containerEl, instructions) {
34194
- const instructionsEl = containerEl.createDiv("prompt-instructions" /* PromptInstructions */);
34195
- addPluginCssClasses(instructionsEl, "modal-commands" /* ModalCommands */);
34196
- return instructions.map((instruction) => {
34197
- const instructionEl = instructionsEl.createDiv("prompt-instruction" /* PromptInstruction */);
34198
- instructionEl.createSpan({ cls: "prompt-instruction-command" /* PromptInstructionCommand */, text: instruction.command });
34199
- return instructionEl.createSpan({ text: instruction.purpose });
34200
- });
34201
- }
34202
- };
34203
- function toggleCheckbox(checkboxEl) {
34204
- if (checkboxEl.disabled) {
34205
- return;
34159
+ constructor(modal, options = {}) {
34160
+ this.modal = modal;
34161
+ this.shouldMinimizeOnClickOutside = options.shouldMinimizeOnClickOutside ?? true;
34162
+ this.shouldShowCancelButton = options.shouldShowCancelButton ?? true;
34163
+ addPluginCssClasses(modal.containerEl, ["minimizable-modal" /* MinimizableModal */]);
34164
+ this.minimizeButtonEl = this.createMinimizeButton();
34165
+ if (this.shouldMinimizeOnClickOutside) {
34166
+ modal.containerEl.addEventListener("click", ($event) => {
34167
+ this.handleContainerClick($event);
34168
+ }, { capture: true });
34169
+ }
34170
+ this.closePatchComponent.load();
34171
+ this.patchOnClose(modal);
34206
34172
  }
34207
- checkboxEl.checked = !checkboxEl.checked;
34208
- checkboxEl.trigger("change");
34209
- }
34210
-
34211
- // src/__merged.ts
34212
- init_modal();
34213
-
34214
- // src/obsidian/modals/prompt.ts
34215
- var prompt_exports = {};
34216
- __export(prompt_exports, {
34217
- prompt: () => prompt
34218
- });
34219
- var import_obsidian134 = require("obsidian");
34220
- init_async();
34221
- init_function();
34222
- init_object_utils();
34223
- init_css_class();
34224
- init_i18n();
34225
- init_modal();
34226
- var PromptModal = class extends ModalBase {
34227
- cancelButtonText;
34228
- commandBuilder;
34229
- commandsRenderMode;
34230
- isOkClicked = false;
34231
- isTouched = false;
34232
- okButtonText;
34233
- placeholder;
34234
- spellcheckMode;
34235
- title;
34236
- value;
34237
- valueValidator;
34238
- constructor(params) {
34239
- super(params);
34240
- this.addCssClasses("prompt-modal" /* PromptModal */);
34241
- this.cancelButtonText = params.cancelButtonText ?? t2(($) => $.obsidianDevUtils.buttons.cancel);
34242
- this.commandBuilder = params.commandBuilder ?? null;
34243
- this.commandsRenderMode = params.commandsRenderMode;
34244
- this.okButtonText = params.okButtonText ?? t2(($) => $.obsidianDevUtils.buttons.ok);
34245
- this.placeholder = params.placeholder ?? "";
34246
- this.spellcheckMode = params.spellcheckMode ?? "FollowObsidianSetting" /* FollowObsidianSetting */;
34247
- this.title = params.title ?? "";
34248
- this.valueValidator = params.valueValidator ?? noop;
34249
- this.value = params.defaultValue ?? "";
34173
+ /**
34174
+ * Minimizes the modal: hides the modal and its backdrop, shows a floating bar with a restore button,
34175
+ * and puts the app into the peek-only lock. A no-op if the modal is already minimized.
34176
+ */
34177
+ minimize() {
34178
+ if (this.isMinimizedValue) {
34179
+ return;
34180
+ }
34181
+ this.isMinimizedValue = true;
34182
+ this.minimizeButtonEl.hide();
34183
+ this.modal.containerEl.hide();
34184
+ this.modal.app.keymap.popScope(this.modal.scope);
34185
+ this.minimizedBarEl = this.createMinimizedBar();
34186
+ this.enablePeekLock(this.minimizedBarEl);
34250
34187
  }
34251
- onClose() {
34252
- this.promiseResolve(this.isOkClicked ? this.value : null);
34188
+ /**
34189
+ * Restores the modal from the minimized state, removing the floating bar and lifting the peek-only
34190
+ * lock. A no-op if the modal is not minimized.
34191
+ */
34192
+ restore() {
34193
+ if (!this.isMinimizedValue) {
34194
+ return;
34195
+ }
34196
+ this.isMinimizedValue = false;
34197
+ this.disablePeekLock();
34198
+ this.removeMinimizedBar();
34199
+ this.modal.app.keymap.pushScope(this.modal.scope);
34200
+ this.modal.containerEl.show();
34201
+ this.minimizeButtonEl.show();
34253
34202
  }
34254
- onOpen() {
34255
- this.titleEl.setText(this.title);
34256
- const textComponent = new import_obsidian134.TextComponent(this.contentEl);
34257
- const inputEl = textComponent.inputEl;
34258
- applySpellcheckMode({
34259
- app: this.app,
34260
- element: inputEl,
34261
- spellcheckMode: this.spellcheckMode
34203
+ createMinimizeButton() {
34204
+ const buttonEl = this.modal.modalEl.createEl("button", { cls: "minimize-button" /* MinimizeButton */ });
34205
+ (0, import_obsidian132.setIcon)(buttonEl, MINIMIZE_ICON_ID);
34206
+ buttonEl.addEventListener("click", () => {
34207
+ this.minimize();
34262
34208
  });
34263
- const validate = async (shouldReport) => {
34264
- const errorMessage = await this.valueValidator(inputEl.value);
34265
- inputEl.setCustomValidity(errorMessage ?? "");
34266
- if (shouldReport) {
34267
- inputEl.reportValidity();
34268
- }
34269
- };
34270
- const handleInput = async () => {
34271
- this.markTouched(inputEl);
34272
- await validate(true);
34273
- };
34274
- const handleFocus = async () => {
34275
- await validate(this.isTouched);
34276
- };
34277
- textComponent.setValue(this.value);
34278
- textComponent.inputEl.select();
34279
- textComponent.setPlaceholder(this.placeholder);
34280
- inputEl.addClass("text-box" /* TextBox */);
34281
- inputEl.addClass("untouched" /* Untouched */);
34282
- textComponent.onChange((newValue) => {
34283
- this.value = newValue;
34209
+ return buttonEl;
34210
+ }
34211
+ createMinimizedBar() {
34212
+ const barEl = this.modal.containerEl.ownerDocument.body.createDiv();
34213
+ addPluginCssClasses(barEl, ["minimized-modal-bar" /* MinimizedModalBar */]);
34214
+ barEl.createSpan({
34215
+ cls: "minimized-modal-bar-title" /* MinimizedModalBarTitle */,
34216
+ text: this.modal.titleEl.textContent
34284
34217
  });
34285
- inputEl.addEventListener("keydown", (event) => {
34286
- if (event.key === "Enter") {
34287
- this.handleOk(event, textComponent);
34288
- } else if (event.key === "Escape") {
34289
- this.close();
34290
- }
34218
+ const restoreButtonEl = barEl.createEl("button", { cls: "restore-button" /* RestoreButton */ });
34219
+ (0, import_obsidian132.setIcon)(restoreButtonEl, RESTORE_ICON_ID);
34220
+ if (this.shouldShowCancelButton) {
34221
+ const cancelButtonEl = barEl.createEl("button", { cls: "cancel-button" /* CancelButton */ });
34222
+ (0, import_obsidian132.setIcon)(cancelButtonEl, CANCEL_ICON_ID);
34223
+ (0, import_obsidian132.setTooltip)(cancelButtonEl, "Cancel");
34224
+ cancelButtonEl.addEventListener("click", ($event) => {
34225
+ $event.stopPropagation();
34226
+ this.modal.close();
34227
+ });
34228
+ }
34229
+ barEl.addEventListener("click", () => {
34230
+ this.restore();
34291
34231
  });
34292
- inputEl.addEventListener("input", convertAsyncToSync(handleInput));
34293
- inputEl.addEventListener("focus", convertAsyncToSync(handleFocus));
34294
- invokeAsyncSafely(() => validate(false));
34295
- const okButton = new import_obsidian134.ButtonComponent(this.contentEl);
34296
- okButton.setButtonText(this.okButtonText);
34297
- okButton.setCta();
34298
- okButton.onClick((event) => {
34299
- this.handleOk(event, textComponent);
34232
+ barEl.addEventListener("animationend", () => {
34233
+ barEl.removeClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
34300
34234
  });
34301
- okButton.setClass("ok-button" /* OkButton */);
34302
- const cancelButton = new import_obsidian134.ButtonComponent(this.contentEl);
34303
- cancelButton.setButtonText(this.cancelButtonText);
34304
- cancelButton.onClick(this.close.bind(this));
34305
- cancelButton.setClass("cancel-button" /* CancelButton */);
34306
- this.commandBuilder?.build(this, normalizeOptionalProperties({ renderMode: this.commandsRenderMode }));
34235
+ return barEl;
34307
34236
  }
34308
- handleOk(event, textComponent) {
34309
- event.preventDefault();
34310
- if (!textComponent.inputEl.checkValidity()) {
34311
- this.markTouched(textComponent.inputEl);
34312
- textComponent.inputEl.reportValidity();
34237
+ disablePeekLock() {
34238
+ if (!this.peekLockEntry) {
34313
34239
  return;
34314
34240
  }
34315
- this.isOkClicked = true;
34316
- this.close();
34317
- }
34318
- markTouched(inputEl) {
34319
- this.isTouched = true;
34320
- inputEl.removeClass("untouched" /* Untouched */);
34321
- }
34322
- };
34323
- async function prompt(params) {
34324
- return await showModal(
34325
- (promiseResolve) => new PromptModal({
34326
- ...params,
34327
- promiseResolve
34328
- })
34329
- );
34330
- }
34331
-
34332
- // src/obsidian/modals/select-item.ts
34333
- var select_item_exports = {};
34334
- __export(select_item_exports, {
34335
- selectItem: () => selectItem
34336
- });
34337
- var import_obsidian135 = require("obsidian");
34338
- init_css_class();
34339
- init_plugin_context();
34340
- init_modal();
34341
- var ItemSelectModal = class extends import_obsidian135.FuzzySuggestModal {
34342
- isSelected = false;
34343
- items;
34344
- itemTextFunction;
34345
- placeholder;
34346
- promiseResolve;
34347
- spellcheckMode;
34348
- constructor(params) {
34349
- super(params.app);
34350
- this.items = params.items;
34351
- this.itemTextFunction = params.itemTextFunction;
34352
- this.placeholder = params.placeholder;
34353
- this.promiseResolve = params.promiseResolve;
34354
- this.spellcheckMode = params.spellcheckMode ?? "Off" /* Off */;
34355
- this.setPlaceholder(this.placeholder ?? "");
34356
- addPluginCssClasses(this.containerEl, params.cssClasses);
34357
- addPluginCssClasses(this.containerEl, "select-item-modal" /* SelectItemModal */);
34241
+ peekLockEntries.delete(this.peekLockEntry);
34242
+ this.peekLockEntry = null;
34243
+ this.peekLockComponent?.unload();
34244
+ this.peekLockComponent = null;
34358
34245
  }
34359
- getItems() {
34360
- return this.items;
34246
+ enablePeekLock(barEl) {
34247
+ this.peekLockEntry = {
34248
+ barEl,
34249
+ flash: () => {
34250
+ this.flashBar(barEl);
34251
+ },
34252
+ innerModal: this.modal
34253
+ };
34254
+ peekLockEntries.add(this.peekLockEntry);
34255
+ this.peekLockComponent = new PeekLockComponent(this.modal.app);
34256
+ this.peekLockComponent.load();
34361
34257
  }
34362
- getItemText(item) {
34363
- return this.itemTextFunction(item);
34258
+ flashBar(barEl) {
34259
+ barEl.removeClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
34260
+ barEl.getBoundingClientRect();
34261
+ barEl.addClass("minimized-modal-bar-attention" /* MinimizedModalBarAttention */);
34364
34262
  }
34365
- onChooseItem(item) {
34366
- this.promiseResolve(item);
34263
+ handleClose() {
34264
+ this.disablePeekLock();
34265
+ this.removeMinimizedBar();
34266
+ this.isMinimizedValue = false;
34367
34267
  }
34368
- onClose() {
34369
- if (!this.isSelected) {
34370
- this.promiseResolve(null);
34268
+ handleContainerClick($event) {
34269
+ if (this.isMinimizedValue) {
34270
+ return;
34271
+ }
34272
+ if ($event.composedPath().includes(this.modal.modalEl)) {
34273
+ return;
34274
+ }
34275
+ if ($event.target === this.modal.containerEl) {
34276
+ return;
34371
34277
  }
34278
+ $event.preventDefault();
34279
+ $event.stopImmediatePropagation();
34280
+ this.minimize();
34372
34281
  }
34373
- onOpen() {
34374
- super.onOpen();
34375
- applySpellcheckMode({
34376
- app: this.app,
34377
- element: this.inputEl,
34378
- spellcheckMode: this.spellcheckMode
34282
+ patchOnClose(modal) {
34283
+ this.closePatchComponent.registerMethodPatch({
34284
+ $object: modal,
34285
+ methodName: "onClose",
34286
+ patchHandler: ({ fallback }) => {
34287
+ fallback();
34288
+ this.handleClose();
34289
+ }
34379
34290
  });
34380
34291
  }
34381
- selectSuggestion(value, $event) {
34382
- this.isSelected = true;
34383
- super.selectSuggestion(value, $event);
34292
+ removeMinimizedBar() {
34293
+ this.minimizedBarEl?.remove();
34294
+ this.minimizedBarEl = null;
34384
34295
  }
34385
34296
  };
34386
- async function selectItem(params) {
34387
- return await showModal(
34388
- (promiseResolve) => new ItemSelectModal({
34389
- ...params,
34390
- promiseResolve
34391
- })
34392
- );
34393
- }
34394
-
34395
- // src/__merged.ts
34396
- init_select_option();
34397
-
34398
- // src/obsidian/modals/suggest-modal-command-builder.ts
34399
- var suggest_modal_command_builder_exports = {};
34400
- __export(suggest_modal_command_builder_exports, {
34401
- SuggestModalCommandBuilder: () => SuggestModalCommandBuilder
34402
- });
34403
- var SuggestModalCommandBuilder = ModalCommandBuilder;
34404
-
34405
- // src/obsidian/note-priority.ts
34406
- var note_priority_exports = {};
34407
- __export(note_priority_exports, {
34408
- NO_PRIORITY_MATCH: () => NO_PRIORITY_MATCH,
34409
- NoPriorityWinnerReason: () => NoPriorityWinnerReason,
34410
- filterHighestPriorityNotePaths: () => filterHighestPriorityNotePaths,
34411
- findNoPriorityWinnerReason: () => findNoPriorityWinnerReason,
34412
- findNotePriorityRank: () => findNotePriorityRank,
34413
- pickHighestPriorityNotePath: () => pickHighestPriorityNotePath
34414
- });
34415
- init_reg_exp();
34416
- var PROPERTY_PREFIX = "property:";
34417
- var REG_EXP_DELIMITER = "/";
34418
- var EXTENSION_PREFIX = ".";
34419
- var PROPERTY_VALUE_SEPARATOR = "=";
34420
- var NoPriorityWinnerReason = /* @__PURE__ */ ((NoPriorityWinnerReason2) => {
34421
- NoPriorityWinnerReason2["EmptyList"] = "EmptyList";
34422
- NoPriorityWinnerReason2["NoMatch"] = "NoMatch";
34423
- NoPriorityWinnerReason2["Tie"] = "Tie";
34424
- return NoPriorityWinnerReason2;
34425
- })(NoPriorityWinnerReason || {});
34426
- var NO_PRIORITY_MATCH = Infinity;
34427
- function filterHighestPriorityNotePaths(params) {
34428
- const rankedNotePaths = params.notePaths.map((notePath) => ({
34429
- notePath,
34430
- rank: params.rank(notePath)
34431
- }));
34432
- let bestRank = NO_PRIORITY_MATCH;
34433
- for (const ranked of rankedNotePaths) {
34434
- if (ranked.rank < bestRank) {
34435
- bestRank = ranked.rank;
34436
- }
34297
+ function blockEvent($event) {
34298
+ $event.preventDefault();
34299
+ $event.stopImmediatePropagation();
34300
+ signalBlockedAttempt();
34301
+ }
34302
+ function blockOrFallback(modal, fallback) {
34303
+ if (shouldBlockOpen(modal)) {
34304
+ signalBlockedAttempt();
34305
+ return;
34437
34306
  }
34438
- return rankedNotePaths.filter((ranked) => ranked.rank === bestRank).map((ranked) => ranked.notePath);
34307
+ fallback();
34439
34308
  }
34440
- function findNoPriorityWinnerReason(params) {
34441
- if (params.entries.length === 0) {
34442
- return "EmptyList" /* EmptyList */;
34309
+ function flashMinimizedBars() {
34310
+ for (const entry of peekLockEntries) {
34311
+ entry.flash();
34443
34312
  }
34444
- let bestRank = NO_PRIORITY_MATCH;
34445
- for (const notePath of params.notePaths) {
34446
- const currentRank = params.rank(notePath);
34447
- if (currentRank < bestRank) {
34448
- bestRank = currentRank;
34313
+ }
34314
+ function isMinimizedInnerModal(modal) {
34315
+ for (const entry of peekLockEntries) {
34316
+ if (entry.innerModal === modal) {
34317
+ return true;
34449
34318
  }
34450
34319
  }
34451
- return bestRank === NO_PRIORITY_MATCH ? "NoMatch" /* NoMatch */ : "Tie" /* Tie */;
34320
+ return false;
34452
34321
  }
34453
- function findNotePriorityRank(params) {
34454
- let bestRank = NO_PRIORITY_MATCH;
34455
- let bestLength = -1;
34456
- for (const [index2, entry] of params.entries.entries()) {
34457
- if (entry.length <= bestLength) {
34458
- continue;
34459
- }
34460
- if (!checkEntryMatches(entry, params)) {
34461
- continue;
34462
- }
34463
- bestRank = index2;
34464
- bestLength = entry.length;
34322
+ function isPeekAllowedKey($event) {
34323
+ if (NAVIGATION_KEYS.has($event.key)) {
34324
+ return !$event.shiftKey;
34465
34325
  }
34466
- return bestRank;
34326
+ return MODIFIER_KEYS.has($event.key);
34467
34327
  }
34468
- function pickHighestPriorityNotePath(params) {
34469
- let bestRank = NO_PRIORITY_MATCH;
34470
- let bestNotePath = null;
34471
- let bestCount = 0;
34472
- for (const notePath of params.notePaths) {
34473
- const currentRank = params.rank(notePath);
34474
- if (currentRank < bestRank) {
34475
- bestRank = currentRank;
34476
- bestNotePath = notePath;
34477
- bestCount = 1;
34478
- } else if (currentRank === bestRank) {
34479
- bestCount++;
34328
+ function isPeekLocked() {
34329
+ for (const entry of peekLockEntries) {
34330
+ if (!entry.barEl.isConnected) {
34331
+ peekLockEntries.delete(entry);
34480
34332
  }
34481
34333
  }
34482
- if (bestCount !== 1 || bestRank === NO_PRIORITY_MATCH) {
34483
- return null;
34484
- }
34485
- return bestNotePath;
34334
+ return peekLockEntries.size > 0;
34486
34335
  }
34487
- function checkEntryMatches(entry, params) {
34488
- if (!entry) {
34489
- return false;
34490
- }
34491
- if (entry.startsWith(PROPERTY_PREFIX)) {
34492
- return checkPropertyMatches(entry.slice(PROPERTY_PREFIX.length), params.frontmatter);
34493
- }
34494
- if (entry.length > 1 && entry.startsWith(REG_EXP_DELIMITER) && entry.endsWith(REG_EXP_DELIMITER)) {
34495
- const source = entry.slice(1, -1);
34496
- return isValidRegExp(source) && new RegExp(source).test(params.notePath);
34497
- }
34498
- if (entry.startsWith(EXTENSION_PREFIX)) {
34499
- return params.notePath.toLowerCase().endsWith(entry.toLowerCase());
34500
- }
34501
- const prefix = entry.endsWith("/") ? entry : `${entry}/`;
34502
- return params.notePath === entry || params.notePath.startsWith(prefix);
34336
+ function shouldBlockOpen(modal) {
34337
+ return isPeekLocked() && !isMinimizedInnerModal(modal);
34503
34338
  }
34504
- function checkPropertyMatches(specifier, frontmatter) {
34505
- if (!frontmatter) {
34506
- return false;
34507
- }
34508
- const separatorIndex = specifier.indexOf(PROPERTY_VALUE_SEPARATOR);
34509
- if (separatorIndex === -1) {
34510
- return Object.hasOwn(frontmatter, specifier);
34511
- }
34512
- const propertyName = specifier.slice(0, separatorIndex);
34513
- const expectedValue = specifier.slice(separatorIndex + 1);
34514
- if (!Object.hasOwn(frontmatter, propertyName)) {
34515
- return false;
34516
- }
34517
- const actualValue = frontmatter[propertyName];
34518
- if (Array.isArray(actualValue)) {
34519
- return actualValue.some((item) => String(item) === expectedValue);
34520
- }
34521
- return String(actualValue) === expectedValue;
34339
+ function signalBlockedAttempt() {
34340
+ flashMinimizedBars();
34341
+ beeper.beep();
34522
34342
  }
34523
-
34524
- // src/obsidian/obsidian-protocol-handler-registrar.ts
34525
- var obsidian_protocol_handler_registrar_exports = {};
34526
- __export(obsidian_protocol_handler_registrar_exports, {
34527
- PluginObsidianProtocolHandlerRegistrar: () => PluginObsidianProtocolHandlerRegistrar
34528
- });
34529
- init_async();
34530
- var PluginObsidianProtocolHandlerRegistrar = class {
34531
- /**
34532
- * The Obsidian plugin instance the protocol handlers are registered with.
34533
- */
34534
- plugin;
34535
- /**
34536
- * Creates a new instance of the {@link PluginObsidianProtocolHandlerRegistrar} class.
34537
- *
34538
- * @param plugin - The plugin to register the protocol handler with.
34539
- */
34540
- constructor(plugin) {
34541
- this.plugin = plugin;
34343
+ function suppressKeydownWhilePeekLocked($event) {
34344
+ if (!isPeekLocked() || isPeekAllowedKey($event)) {
34345
+ return;
34542
34346
  }
34543
- /**
34544
- * Registers an Obsidian protocol handler.
34545
- *
34546
- * @param params - The parameters for the Obsidian protocol handler registration.
34547
- */
34548
- registerObsidianProtocolHandler(params) {
34549
- this.plugin.registerObsidianProtocolHandler(params.action, (obsidianProtocolData) => normalizePromisable(params.handler(obsidianProtocolData)));
34347
+ blockEvent($event);
34348
+ }
34349
+ function suppressWhilePeekLocked($event) {
34350
+ if (!isPeekLocked()) {
34351
+ return;
34550
34352
  }
34551
- };
34353
+ blockEvent($event);
34354
+ }
34552
34355
 
34553
- // src/obsidian/path-settings.ts
34554
- var path_settings_exports = {};
34555
- __export(path_settings_exports, {
34556
- PathSettings: () => PathSettings,
34557
- pathsValidator: () => pathsValidator
34356
+ // src/obsidian/modals/modal-command-builder.ts
34357
+ var modal_command_builder_exports = {};
34358
+ __export(modal_command_builder_exports, {
34359
+ ModalCommandBuilder: () => ModalCommandBuilder,
34360
+ ModalCommandsRenderMode: () => ModalCommandsRenderMode
34558
34361
  });
34559
- init_reg_exp();
34560
- init_string();
34561
- init_type_guards();
34562
- init_i18n();
34563
- var PathSetting = class {
34564
- constructor(type) {
34565
- this.type = type;
34566
- this.defaultRegExp = getDefaultRegExp(type);
34567
- this.regExp = this.defaultRegExp;
34568
- }
34569
- type;
34570
- get array() {
34571
- return this._array;
34572
- }
34573
- set array(value) {
34574
- this._array = value.filter(Boolean);
34575
- this.regExp = makeRegExp(this._array, this.defaultRegExp) ?? this.defaultRegExp;
34576
- }
34577
- _array = [];
34578
- defaultRegExp;
34579
- regExp;
34580
- isPathIgnored(path) {
34581
- switch (this.type) {
34582
- /* v8 ignore stop */
34583
- case "Exclude" /* Exclude */: {
34584
- return this.regExp.test(path);
34585
- }
34586
- case "Include" /* Include */: {
34587
- return !this.regExp.test(path);
34588
- }
34589
- default: {
34590
- assertNever(this.type);
34591
- }
34592
- }
34362
+ var import_obsidian133 = require("obsidian");
34363
+ init_css_class();
34364
+ init_plugin_context();
34365
+ var KEYS_MAP = /* @__PURE__ */ new Map([
34366
+ ["Enter", "\u21B5"],
34367
+ ["UpDown", "\u2191\u2193"]
34368
+ ]);
34369
+ var ModalCommandsRenderMode = /* @__PURE__ */ ((ModalCommandsRenderMode2) => {
34370
+ ModalCommandsRenderMode2["Buttons"] = "buttons";
34371
+ ModalCommandsRenderMode2["Instructions"] = "instructions";
34372
+ return ModalCommandsRenderMode2;
34373
+ })(ModalCommandsRenderMode || {});
34374
+ var NOOP_MODAL_COMMANDS = {
34375
+ refresh: () => {
34593
34376
  }
34594
34377
  };
34595
- var PathSettings = class {
34596
- /**
34597
- * Gets the exclude paths.
34598
- *
34599
- * @returns The exclude paths.
34600
- */
34601
- get excludePaths() {
34602
- return this._excludePaths.array;
34603
- }
34378
+ var ModalCommandBuilder = class {
34379
+ entries = [];
34604
34380
  /**
34605
- * Sets the exclude paths.
34381
+ * Adds an interactive checkbox to the control strip, bound to a modifier+key shortcut that toggles it.
34606
34382
  *
34607
- * @param value - The exclude paths.
34383
+ * @param command - The checkbox command to add.
34384
+ * @returns The builder instance for chaining.
34608
34385
  */
34609
- set excludePaths(value) {
34610
- this._excludePaths.array = value;
34386
+ addCheckbox(command) {
34387
+ this.entries.push({
34388
+ checkIsAvailable: command.checkIsAvailable ?? null,
34389
+ commandText: this.buildCommandText(command),
34390
+ initButton: (buttonEl, scope) => {
34391
+ const checkboxEl = createEl("input", { type: "checkbox" });
34392
+ initCheckbox(checkboxEl, scope);
34393
+ buttonEl.addEventListener("click", () => {
34394
+ toggleCheckbox(checkboxEl);
34395
+ });
34396
+ return {
34397
+ checkIsOn: () => checkboxEl.checked,
34398
+ setDisabled: (isDisabled) => {
34399
+ checkboxEl.disabled = isDisabled;
34400
+ buttonEl.disabled = isDisabled;
34401
+ }
34402
+ };
34403
+ },
34404
+ initInstruction: (purposeEl, scope) => {
34405
+ const checkboxEl = purposeEl.createEl("input", { type: "checkbox" });
34406
+ addPluginCssClasses(checkboxEl, "modal-command-control" /* ModalCommandControl */);
34407
+ return initCheckbox(checkboxEl, scope);
34408
+ },
34409
+ isDropDown: false,
34410
+ purpose: command.purpose,
34411
+ registerScope: null
34412
+ });
34413
+ return this;
34414
+ function initCheckbox(checkboxEl, scope) {
34415
+ command.onInit(checkboxEl);
34416
+ checkboxEl.addEventListener("change", () => {
34417
+ command.onChange(checkboxEl.checked);
34418
+ });
34419
+ scope.register(command.modifiers ?? [], command.key, () => {
34420
+ toggleCheckbox(checkboxEl);
34421
+ });
34422
+ return {
34423
+ setDisabled: (isDisabled) => {
34424
+ checkboxEl.disabled = isDisabled;
34425
+ }
34426
+ };
34427
+ }
34611
34428
  }
34612
34429
  /**
34613
- * Gets the include paths.
34430
+ * Adds a dropdown to the control strip, bound to a modifier+key shortcut that cycles through its
34431
+ * options.
34614
34432
  *
34615
- * @returns The include paths.
34433
+ * @param command - The dropdown command to add.
34434
+ * @returns The builder instance for chaining.
34616
34435
  */
34617
- get includePaths() {
34618
- return this._includePaths.array;
34436
+ addDropDown(command) {
34437
+ this.entries.push({
34438
+ checkIsAvailable: command.checkIsAvailable ?? null,
34439
+ commandText: this.buildCommandText(command),
34440
+ initButton: null,
34441
+ initInstruction: (purposeEl, scope) => {
34442
+ const dropdownComponent = new import_obsidian133.DropdownComponent(purposeEl);
34443
+ addPluginCssClasses(dropdownComponent.selectEl, "modal-command-control" /* ModalCommandControl */);
34444
+ command.onInit(dropdownComponent);
34445
+ dropdownComponent.onChange((value) => {
34446
+ command.onChange(value);
34447
+ });
34448
+ scope.register(command.modifiers ?? [], command.key, () => {
34449
+ if (dropdownComponent.disabled) {
34450
+ return;
34451
+ }
34452
+ const selectEl = dropdownComponent.selectEl;
34453
+ selectEl.selectedIndex = (selectEl.selectedIndex + 1) % selectEl.options.length;
34454
+ selectEl.trigger("change");
34455
+ });
34456
+ return {
34457
+ setDisabled: (isDisabled) => {
34458
+ dropdownComponent.setDisabled(isDisabled);
34459
+ }
34460
+ };
34461
+ },
34462
+ isDropDown: true,
34463
+ purpose: command.purpose,
34464
+ registerScope: null
34465
+ });
34466
+ return this;
34619
34467
  }
34620
34468
  /**
34621
- * Sets the include paths.
34469
+ * Adds a keyboard command to the control strip. When {@link KeyboardCommand.onKey} is provided, its
34470
+ * handler is registered with the modal scope; otherwise the command is a hint only.
34622
34471
  *
34623
- * @param value - The include paths.
34472
+ * @param command - The keyboard command to add.
34473
+ * @returns The builder instance for chaining.
34624
34474
  */
34625
- set includePaths(value) {
34626
- this._includePaths.array = value;
34475
+ addKeyboardCommand(command) {
34476
+ const checkIsOn = command.checkIsOn ?? (() => false);
34477
+ const onActivate = command.onActivate ?? null;
34478
+ const onKey = command.onKey?.bind(command) ?? null;
34479
+ this.entries.push({
34480
+ checkIsAvailable: command.checkIsAvailable ?? null,
34481
+ commandText: this.buildCommandText(command),
34482
+ initButton: onActivate ? (buttonEl) => {
34483
+ buttonEl.addEventListener("click", ($event) => {
34484
+ onActivate($event);
34485
+ });
34486
+ return {
34487
+ checkIsOn,
34488
+ setDisabled: (isDisabled) => {
34489
+ buttonEl.disabled = isDisabled;
34490
+ }
34491
+ };
34492
+ } : null,
34493
+ initInstruction: null,
34494
+ isDropDown: false,
34495
+ purpose: command.purpose,
34496
+ registerScope: onKey ? (scope) => {
34497
+ scope.register(command.modifiers ?? [], command.key, onKey);
34498
+ } : null
34499
+ });
34500
+ return this;
34627
34501
  }
34628
- _excludePaths = new PathSetting("Exclude" /* Exclude */);
34629
- _includePaths = new PathSetting("Include" /* Include */);
34630
34502
  /**
34631
- * Checks if a path is ignored by the include/exclude path settings.
34503
+ * Applies the accumulated commands to a modal. Always registers the essential navigation/action key
34504
+ * handlers; only renders the control strip and registers the option-toggle shortcuts when
34505
+ * {@link ModalCommandBuilderBuildOptions.shouldShowInstructions} is `true`.
34632
34506
  *
34633
- * @param path - The path to check.
34634
- * @returns `true` if the path is ignored, `false` otherwise.
34507
+ * @param target - The modal, or the bare element + scope, to apply the commands to.
34508
+ * @param options - The build options.
34509
+ * @returns A handle whose {@link ModalCommands.refresh} re-states every rendered control.
34635
34510
  */
34636
- isPathIgnored(path) {
34637
- return this._includePaths.isPathIgnored(path) || this._excludePaths.isPathIgnored(path);
34638
- }
34639
- };
34640
- function pathsValidator(paths) {
34641
- for (const path of paths) {
34642
- if (path.startsWith("/") && path.endsWith("/") && !isValidRegExp(path.slice(1, -1))) {
34643
- return t2(($) => $.obsidianDevUtils.pathSettings.invalidRegularExpression, { regExp: path });
34511
+ build(target, options = {}) {
34512
+ const {
34513
+ renderMode = "instructions" /* Instructions */,
34514
+ shouldShowInstructions = true
34515
+ } = options;
34516
+ const scope = target.scope;
34517
+ for (const entry of this.entries) {
34518
+ entry.registerScope?.(scope);
34644
34519
  }
34645
- }
34646
- }
34647
- function getDefaultRegExp(type) {
34648
- switch (type) {
34649
- /* v8 ignore stop */
34650
- case "Exclude" /* Exclude */: {
34651
- return NEVER_MATCH_REG_EXP;
34520
+ if (!shouldShowInstructions) {
34521
+ return NOOP_MODAL_COMMANDS;
34652
34522
  }
34653
- case "Include" /* Include */: {
34654
- return ALWAYS_MATCH_REG_EXP;
34523
+ const refreshers = renderMode === "buttons" /* Buttons */ ? this.renderButtons(target, scope) : this.renderInstructions(target, scope);
34524
+ const modalCommands = {
34525
+ refresh: () => {
34526
+ for (const refreshControl of refreshers) {
34527
+ refreshControl();
34528
+ }
34529
+ }
34530
+ };
34531
+ modalCommands.refresh();
34532
+ return modalCommands;
34533
+ }
34534
+ buildCommandText(command) {
34535
+ let commandText = KEYS_MAP.get(command.key) ?? command.key;
34536
+ for (const modifier of command.modifiers ?? []) {
34537
+ commandText = `${this.getModifierString(modifier)} ${commandText}`;
34655
34538
  }
34656
- default: {
34657
- assertNever(type);
34539
+ return commandText;
34540
+ }
34541
+ getModifierString(modifier) {
34542
+ switch (modifier) {
34543
+ case "Alt": {
34544
+ return "alt";
34545
+ }
34546
+ case "Ctrl": {
34547
+ return "ctrl";
34548
+ }
34549
+ case "Meta": {
34550
+ return import_obsidian133.Platform.isMacOS ? "cmd" : "win";
34551
+ }
34552
+ case "Mod": {
34553
+ return import_obsidian133.Platform.isMacOS ? "cmd" : "ctrl";
34554
+ }
34555
+ case "Shift": {
34556
+ return "shift";
34557
+ }
34558
+ default: {
34559
+ return modifier;
34560
+ }
34658
34561
  }
34659
34562
  }
34660
- }
34661
- function makeRegExp(paths, defaultRegExp) {
34662
- if (paths.length === 0) {
34663
- return defaultRegExp;
34563
+ /**
34564
+ * Resolves the element a self-created strip is appended to.
34565
+ *
34566
+ * A {@link Modal} carries both `modalEl` and a `containerEl` that is the OUTER `.modal-container`, so
34567
+ * `modalEl` has to win — otherwise the strip lands outside the modal frame. Discriminated with `in`
34568
+ * rather than a property read, because tests hand `build` a `strictProxy` mock whose `get` trap throws
34569
+ * on unmocked members.
34570
+ *
34571
+ * @param target - The build target.
34572
+ * @returns The element to append the strip to.
34573
+ */
34574
+ getStripContainerEl(target) {
34575
+ return "modalEl" in target ? target.modalEl : target.containerEl;
34664
34576
  }
34665
- const regExpStringCombined = paths.map((path) => {
34666
- if (path === "/") {
34667
- return defaultRegExp.source;
34668
- }
34669
- if (path.startsWith("/") && path.endsWith("/")) {
34670
- return path.slice(1, -1);
34577
+ renderButtons(target, scope) {
34578
+ const stripEl = this.getStripContainerEl(target).createDiv();
34579
+ addPluginCssClasses(stripEl, "modal-commands" /* ModalCommands */);
34580
+ const refreshers = [];
34581
+ for (const entry of this.entries) {
34582
+ if (entry.isDropDown) {
34583
+ throw new Error(`Cannot render dropdown command "${entry.purpose}" as a button: a control that cycles has no button form.`);
34584
+ }
34585
+ if (!entry.initButton) {
34586
+ continue;
34587
+ }
34588
+ const buttonEl = stripEl.createEl("button", { type: "button" });
34589
+ addPluginCssClasses(buttonEl, "modal-command" /* ModalCommand */);
34590
+ buttonEl.createSpan({ text: entry.purpose });
34591
+ if (!import_obsidian133.Platform.isMobile) {
34592
+ const hotkeyEl = buttonEl.createSpan({ text: entry.commandText });
34593
+ addPluginCssClasses(hotkeyEl, "modal-command-hotkey" /* ModalCommandHotkey */);
34594
+ }
34595
+ buttonEl.addEventListener("mousedown", ($event) => {
34596
+ $event.preventDefault();
34597
+ });
34598
+ const buttonControl = entry.initButton(buttonEl, scope);
34599
+ const checkIsAvailable = entry.checkIsAvailable;
34600
+ refreshers.push(() => {
34601
+ if (checkIsAvailable) {
34602
+ buttonControl.setDisabled(!checkIsAvailable());
34603
+ }
34604
+ const isOn = buttonControl.checkIsOn();
34605
+ buttonEl.toggleClass("is-active" /* IsActive */, isOn);
34606
+ buttonEl.setAttribute("aria-pressed", String(isOn));
34607
+ });
34671
34608
  }
34672
- path = trimEnd({
34673
- $string: path,
34674
- suffix: "/"
34675
- });
34676
- return `^${escapeRegExp(path)}(/|$)`;
34677
- }).map((regExpString) => `(${regExpString})`).join("|");
34678
- try {
34679
- return new RegExp(regExpStringCombined);
34680
- } catch {
34681
- return null;
34682
- }
34683
- }
34684
-
34685
- // src/obsidian/pdf.ts
34686
- var pdf_exports = {};
34687
- __export(pdf_exports, {
34688
- printToPdf: () => printToPdf
34689
- });
34690
- var import_obsidian136 = require("obsidian");
34691
- var ELECTRON_PRINT_TO_PDF_CHANNEL = "print-to-pdf";
34692
- async function printToPdf(element, options) {
34693
- if (import_obsidian136.Platform.isMobile) {
34694
- throw new Error("Printing to PDF is not supported on mobile devices.");
34609
+ return refreshers;
34695
34610
  }
34696
- const printDiv = activeDocument.body.createDiv("print");
34697
- printDiv.append(element);
34698
- await ensureLoaded(printDiv);
34699
- const DEFAULT_SCALE_FACTOR = 100;
34700
- const DEFAULT_OPTIONS = {
34701
- filepath: "Untitled.pdf",
34702
- includeName: false,
34703
- landscape: false,
34704
- marginsType: 0,
34705
- open: true,
34706
- pageSize: "A4",
34707
- scale: 1,
34708
- scaleFactor: DEFAULT_SCALE_FACTOR
34709
- };
34710
- const fullOptions = { ...DEFAULT_OPTIONS, ...options };
34711
- try {
34712
- await new Promise((resolve2) => {
34713
- activeWindow.electron.ipcRenderer.once(ELECTRON_PRINT_TO_PDF_CHANNEL, resolve2);
34714
- activeWindow.electron.ipcRenderer.send(ELECTRON_PRINT_TO_PDF_CHANNEL, fullOptions);
34715
- });
34716
- } finally {
34717
- printDiv.remove();
34611
+ renderInstructions(target, scope) {
34612
+ const instructions = this.entries.map((entry) => ({
34613
+ command: entry.commandText,
34614
+ entry,
34615
+ purpose: entry.purpose
34616
+ }));
34617
+ const purposeEls = "setInstructions" in target ? this.renderNativeInstructions(target, instructions) : this.renderOwnInstructions(this.getStripContainerEl(target), instructions);
34618
+ const refreshers = [];
34619
+ for (const [index2, instruction] of instructions.entries()) {
34620
+ const purposeEl = purposeEls[index2];
34621
+ if (!purposeEl) {
34622
+ continue;
34623
+ }
34624
+ const instructionControl = instruction.entry.initInstruction?.(purposeEl, scope);
34625
+ const checkIsAvailable = instruction.entry.checkIsAvailable;
34626
+ if (instructionControl && checkIsAvailable) {
34627
+ refreshers.push(() => {
34628
+ instructionControl.setDisabled(!checkIsAvailable());
34629
+ });
34630
+ }
34631
+ }
34632
+ return refreshers;
34718
34633
  }
34719
- }
34720
-
34721
- // src/obsidian/plugin-cli-handler-registrar.ts
34722
- var plugin_cli_handler_registrar_exports = {};
34723
- __export(plugin_cli_handler_registrar_exports, {
34724
- PluginCliHandlerRegistrar: () => PluginCliHandlerRegistrar
34725
- });
34726
- var import_obsidian137 = require("obsidian");
34727
- init_async();
34728
- var PluginCliHandlerRegistrar = class {
34729
34634
  /**
34730
- * Creates a new instance of the {@link PluginCliHandlerRegistrar} class.
34635
+ * Renders through Obsidian's own instruction bar, the way every {@link SuggestModal} consumer has always
34636
+ * had it.
34731
34637
  *
34732
- * @param plugin - The Obsidian plugin instance.
34638
+ * @param modal - The suggest modal.
34639
+ * @param instructions - The instructions to render.
34640
+ * @returns The purpose element of each rendered instruction.
34733
34641
  */
34734
- constructor(plugin) {
34735
- this.plugin = plugin;
34642
+ renderNativeInstructions(modal, instructions) {
34643
+ modal.setInstructions(instructions);
34644
+ return [...modal.instructionsEl.findAll(".prompt-instruction > span:nth-child(2)")];
34736
34645
  }
34737
- plugin;
34738
34646
  /**
34739
- * Registers a CLI handler.
34647
+ * Renders an instruction bar of the builder's own, for a host that has none.
34740
34648
  *
34741
- * @param params - The parameters for the CLI handler registration.
34649
+ * The markup mirrors what `setInstructions` produces including
34650
+ * {@link CssClass.PromptInstructionCommand} on the key hint, which is not decoration: Obsidian's rule
34651
+ * for that class is what supplies the hint's bold weight and the `margin-inline-end` separating it
34652
+ * from the purpose text. Every class here is unscoped in the app's stylesheet, so all of it reaches a
34653
+ * plain {@link Modal} as readily as it reaches the quick switcher.
34654
+ *
34655
+ * @param containerEl - The element to append the bar to.
34656
+ * @param instructions - The instructions to render.
34657
+ * @returns The purpose element of each rendered instruction.
34742
34658
  */
34743
- registerCliHandler(params) {
34744
- this.plugin.registerCliHandler(params.command, params.description, params.flags, (cliData) => normalizePromisable(params.handler(cliData)));
34659
+ renderOwnInstructions(containerEl, instructions) {
34660
+ const instructionsEl = containerEl.createDiv("prompt-instructions" /* PromptInstructions */);
34661
+ addPluginCssClasses(instructionsEl, "modal-commands" /* ModalCommands */);
34662
+ return instructions.map((instruction) => {
34663
+ const instructionEl = instructionsEl.createDiv("prompt-instruction" /* PromptInstruction */);
34664
+ instructionEl.createSpan({ cls: "prompt-instruction-command" /* PromptInstructionCommand */, text: instruction.command });
34665
+ return instructionEl.createSpan({ text: instruction.purpose });
34666
+ });
34745
34667
  }
34746
34668
  };
34669
+ function toggleCheckbox(checkboxEl) {
34670
+ if (checkboxEl.disabled) {
34671
+ return;
34672
+ }
34673
+ checkboxEl.checked = !checkboxEl.checked;
34674
+ checkboxEl.trigger("change");
34675
+ }
34747
34676
 
34748
- // src/obsidian/plugin/obsidian-plugin-repo-paths.ts
34749
- var obsidian_plugin_repo_paths_exports = {};
34750
- __export(obsidian_plugin_repo_paths_exports, {
34751
- ObsidianPluginRepoPaths: () => ObsidianPluginRepoPaths
34752
- });
34753
- var ObsidianPluginRepoPaths = /* @__PURE__ */ ((ObsidianPluginRepoPaths2) => {
34754
- ObsidianPluginRepoPaths2["AnyAndroidCaptureIntegrationTestTs"] = "*.android-capture.integration.test.ts";
34755
- ObsidianPluginRepoPaths2["AnyAndroidIntegrationTestTs"] = "*.android.integration.test.ts";
34756
- ObsidianPluginRepoPaths2["AnyCjs"] = "*.cjs";
34757
- ObsidianPluginRepoPaths2["AnyDesktopTs"] = "desktop-*.ts";
34758
- ObsidianPluginRepoPaths2["AnyIntegrationTestTs"] = "*.integration.test.ts";
34759
- ObsidianPluginRepoPaths2["AnyJs"] = "*.js";
34760
- ObsidianPluginRepoPaths2["AnyMjs"] = "*.mjs";
34761
- ObsidianPluginRepoPaths2["AnyPath"] = "**";
34762
- ObsidianPluginRepoPaths2["AnyTestTs"] = "*.test.ts";
34763
- ObsidianPluginRepoPaths2["AnyTs"] = "*.ts";
34764
- ObsidianPluginRepoPaths2["AnyTsx"] = "*.tsx";
34765
- ObsidianPluginRepoPaths2["AppJson"] = "app.json";
34766
- ObsidianPluginRepoPaths2["ChangelogMd"] = "CHANGELOG.md";
34767
- ObsidianPluginRepoPaths2["CommitlintConfigTs"] = "commitlint.config.ts";
34768
- ObsidianPluginRepoPaths2["CurrentFolder"] = ".";
34769
- ObsidianPluginRepoPaths2["DemoVault"] = "demo-vault";
34770
- ObsidianPluginRepoPaths2["Dist"] = "dist";
34771
- ObsidianPluginRepoPaths2["DistBuild"] = "dist/build";
34772
- ObsidianPluginRepoPaths2["DistDev"] = "dist/dev";
34773
- ObsidianPluginRepoPaths2["DotObsidian"] = ".obsidian";
34774
- ObsidianPluginRepoPaths2["EslintConfigCjs"] = "eslint.config.cjs";
34775
- ObsidianPluginRepoPaths2["EslintConfigCts"] = "eslint.config.cts";
34776
- ObsidianPluginRepoPaths2["EslintConfigJs"] = "eslint.config.js";
34777
- ObsidianPluginRepoPaths2["EslintConfigMjs"] = "eslint.config.mjs";
34778
- ObsidianPluginRepoPaths2["EslintConfigMts"] = "eslint.config.mts";
34779
- ObsidianPluginRepoPaths2["EslintConfigTs"] = "eslint.config.ts";
34780
- ObsidianPluginRepoPaths2["HotReload"] = ".hotreload";
34781
- ObsidianPluginRepoPaths2["License"] = "LICENSE";
34782
- ObsidianPluginRepoPaths2["MainCss"] = "main.css";
34783
- ObsidianPluginRepoPaths2["MainJs"] = "main.js";
34784
- ObsidianPluginRepoPaths2["MainTs"] = "main.ts";
34785
- ObsidianPluginRepoPaths2["ManifestBetaJson"] = "manifest-beta.json";
34786
- ObsidianPluginRepoPaths2["ManifestJson"] = "manifest.json";
34787
- ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigCjs"] = ".markdownlint-cli2.cjs";
34788
- ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigJsonc"] = ".markdownlint-cli2.jsonc";
34789
- ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigMjs"] = ".markdownlint-cli2.mjs";
34790
- ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigYaml"] = ".markdownlint-cli2.yaml";
34791
- ObsidianPluginRepoPaths2["MarkdownlintConfigCjs"] = ".markdownlint.cjs";
34792
- ObsidianPluginRepoPaths2["MarkdownlintConfigJson"] = ".markdownlint.json";
34793
- ObsidianPluginRepoPaths2["MarkdownlintConfigJsonc"] = ".markdownlint.jsonc";
34794
- ObsidianPluginRepoPaths2["MarkdownlintConfigMjs"] = ".markdownlint.mjs";
34795
- ObsidianPluginRepoPaths2["MarkdownlintConfigYaml"] = ".markdownlint.yaml";
34796
- ObsidianPluginRepoPaths2["MarkdownlintConfigYml"] = ".markdownlint.yml";
34797
- ObsidianPluginRepoPaths2["Mocks"] = "src/test-helpers";
34798
- ObsidianPluginRepoPaths2["NodeModules"] = "node_modules";
34799
- ObsidianPluginRepoPaths2["NpmShrinkwrapJson"] = "npm-shrinkwrap.json";
34800
- ObsidianPluginRepoPaths2["PackageJson"] = "package.json";
34801
- ObsidianPluginRepoPaths2["PackageLockJson"] = "package-lock.json";
34802
- ObsidianPluginRepoPaths2["Plugins"] = "plugins";
34803
- ObsidianPluginRepoPaths2["Scripts"] = "scripts";
34804
- ObsidianPluginRepoPaths2["ScriptUtils"] = "src/script-utils";
34805
- ObsidianPluginRepoPaths2["Src"] = "src";
34806
- ObsidianPluginRepoPaths2["StylesCss"] = "styles.css";
34807
- ObsidianPluginRepoPaths2["Tests"] = "__tests__";
34808
- ObsidianPluginRepoPaths2["TsConfigJson"] = "tsconfig.json";
34809
- ObsidianPluginRepoPaths2["VersionsJson"] = "versions.json";
34810
- ObsidianPluginRepoPaths2["VitestConfigTs"] = "vitest.config.ts";
34811
- return ObsidianPluginRepoPaths2;
34812
- })(ObsidianPluginRepoPaths || {});
34677
+ // src/__merged.ts
34678
+ init_modal();
34813
34679
 
34814
- // src/obsidian/plugin/plugin-api.ts
34815
- var plugin_api_exports = {};
34816
- __export(plugin_api_exports, {
34817
- PluginApiPayloadKind: () => PluginApiPayloadKind,
34818
- PluginApiRevokedError: () => PluginApiRevokedError,
34819
- PluginApiUnavailabilityReason: () => PluginApiUnavailabilityReason,
34820
- PluginApiUnavailableError: () => PluginApiUnavailableError,
34821
- PluginApiValidationError: () => PluginApiValidationError,
34822
- publishPluginApi: () => publishPluginApi,
34823
- watchPluginApi: () => watchPluginApi
34680
+ // src/obsidian/modals/prompt.ts
34681
+ var prompt_exports = {};
34682
+ __export(prompt_exports, {
34683
+ prompt: () => prompt
34684
+ });
34685
+ var import_obsidian134 = require("obsidian");
34686
+ init_async();
34687
+ init_function();
34688
+ init_object_utils();
34689
+ init_css_class();
34690
+ init_i18n();
34691
+ init_modal();
34692
+ var PromptModal = class extends ModalBase {
34693
+ cancelButtonText;
34694
+ commandBuilder;
34695
+ commandsRenderMode;
34696
+ isOkClicked = false;
34697
+ isTouched = false;
34698
+ okButtonText;
34699
+ placeholder;
34700
+ spellcheckMode;
34701
+ title;
34702
+ value;
34703
+ valueValidator;
34704
+ constructor(params) {
34705
+ super(params);
34706
+ this.addCssClasses("prompt-modal" /* PromptModal */);
34707
+ this.cancelButtonText = params.cancelButtonText ?? t2(($) => $.obsidianDevUtils.buttons.cancel);
34708
+ this.commandBuilder = params.commandBuilder ?? null;
34709
+ this.commandsRenderMode = params.commandsRenderMode;
34710
+ this.okButtonText = params.okButtonText ?? t2(($) => $.obsidianDevUtils.buttons.ok);
34711
+ this.placeholder = params.placeholder ?? "";
34712
+ this.spellcheckMode = params.spellcheckMode ?? "FollowObsidianSetting" /* FollowObsidianSetting */;
34713
+ this.title = params.title ?? "";
34714
+ this.valueValidator = params.valueValidator ?? noop;
34715
+ this.value = params.defaultValue ?? "";
34716
+ }
34717
+ onClose() {
34718
+ this.promiseResolve(this.isOkClicked ? this.value : null);
34719
+ }
34720
+ onOpen() {
34721
+ this.titleEl.setText(this.title);
34722
+ const textComponent = new import_obsidian134.TextComponent(this.contentEl);
34723
+ const inputEl = textComponent.inputEl;
34724
+ applySpellcheckMode({
34725
+ app: this.app,
34726
+ element: inputEl,
34727
+ spellcheckMode: this.spellcheckMode
34728
+ });
34729
+ const validate = async (shouldReport) => {
34730
+ const errorMessage = await this.valueValidator(inputEl.value);
34731
+ inputEl.setCustomValidity(errorMessage ?? "");
34732
+ if (shouldReport) {
34733
+ inputEl.reportValidity();
34734
+ }
34735
+ };
34736
+ const handleInput = async () => {
34737
+ this.markTouched(inputEl);
34738
+ await validate(true);
34739
+ };
34740
+ const handleFocus = async () => {
34741
+ await validate(this.isTouched);
34742
+ };
34743
+ textComponent.setValue(this.value);
34744
+ textComponent.inputEl.select();
34745
+ textComponent.setPlaceholder(this.placeholder);
34746
+ inputEl.addClass("text-box" /* TextBox */);
34747
+ inputEl.addClass("untouched" /* Untouched */);
34748
+ textComponent.onChange((newValue) => {
34749
+ this.value = newValue;
34750
+ });
34751
+ inputEl.addEventListener("keydown", (event) => {
34752
+ if (event.key === "Enter") {
34753
+ this.handleOk(event, textComponent);
34754
+ } else if (event.key === "Escape") {
34755
+ this.close();
34756
+ }
34757
+ });
34758
+ inputEl.addEventListener("input", convertAsyncToSync(handleInput));
34759
+ inputEl.addEventListener("focus", convertAsyncToSync(handleFocus));
34760
+ invokeAsyncSafely(() => validate(false));
34761
+ const okButton = new import_obsidian134.ButtonComponent(this.contentEl);
34762
+ okButton.setButtonText(this.okButtonText);
34763
+ okButton.setCta();
34764
+ okButton.onClick((event) => {
34765
+ this.handleOk(event, textComponent);
34766
+ });
34767
+ okButton.setClass("ok-button" /* OkButton */);
34768
+ const cancelButton = new import_obsidian134.ButtonComponent(this.contentEl);
34769
+ cancelButton.setButtonText(this.cancelButtonText);
34770
+ cancelButton.onClick(this.close.bind(this));
34771
+ cancelButton.setClass("cancel-button" /* CancelButton */);
34772
+ this.commandBuilder?.build(this, normalizeOptionalProperties({ renderMode: this.commandsRenderMode }));
34773
+ }
34774
+ handleOk(event, textComponent) {
34775
+ event.preventDefault();
34776
+ if (!textComponent.inputEl.checkValidity()) {
34777
+ this.markTouched(textComponent.inputEl);
34778
+ textComponent.inputEl.reportValidity();
34779
+ return;
34780
+ }
34781
+ this.isOkClicked = true;
34782
+ this.close();
34783
+ }
34784
+ markTouched(inputEl) {
34785
+ this.isTouched = true;
34786
+ inputEl.removeClass("untouched" /* Untouched */);
34787
+ }
34788
+ };
34789
+ async function prompt(params) {
34790
+ return await showModal(
34791
+ (promiseResolve) => new PromptModal({
34792
+ ...params,
34793
+ promiseResolve
34794
+ })
34795
+ );
34796
+ }
34797
+
34798
+ // src/obsidian/modals/select-item.ts
34799
+ var select_item_exports = {};
34800
+ __export(select_item_exports, {
34801
+ selectItem: () => selectItem
34824
34802
  });
34825
- var import_compare_versions3 = __toESM(require_umd(), 1);
34826
- init_array();
34827
- init_async_events();
34828
- init_async();
34829
- init_debug();
34830
- init_object_utils();
34831
- init_obsidian_dev_utils_state();
34832
- var PLUGIN_API_DEBUGGER_NAMESPACE = "PluginApi";
34833
- var PLUGIN_API_REGISTRY_STATE_KEY = "pluginApiRegistry";
34834
- var DEFAULT_WHEN_AVAILABLE_TIMEOUT_IN_MILLISECONDS = 1e4;
34835
- var PluginApiPayloadKind = /* @__PURE__ */ ((PluginApiPayloadKind2) => {
34836
- PluginApiPayloadKind2["Input"] = "input";
34837
- PluginApiPayloadKind2["Output"] = "output";
34838
- return PluginApiPayloadKind2;
34839
- })(PluginApiPayloadKind || {});
34840
- var PluginApiUnavailabilityReason = /* @__PURE__ */ ((PluginApiUnavailabilityReason2) => {
34841
- PluginApiUnavailabilityReason2["NotEnabled"] = "notEnabled";
34842
- PluginApiUnavailabilityReason2["NotInstalled"] = "notInstalled";
34843
- PluginApiUnavailabilityReason2["NotPublished"] = "notPublished";
34844
- PluginApiUnavailabilityReason2["Revoked"] = "revoked";
34845
- PluginApiUnavailabilityReason2["ShapeMismatch"] = "shapeMismatch";
34846
- PluginApiUnavailabilityReason2["VersionMismatch"] = "versionMismatch";
34847
- return PluginApiUnavailabilityReason2;
34848
- })(PluginApiUnavailabilityReason || {});
34849
- var PluginApiWatchBase = class {
34850
- // A `protected` constructor is not assignable to `AbstractConstructor<object>`, which is what
34851
- // `mixinAsyncEvents` accepts, so this stays public even though only the subclass below calls it.
34803
+ var import_obsidian135 = require("obsidian");
34804
+ init_css_class();
34805
+ init_plugin_context();
34806
+ init_modal();
34807
+ var ItemSelectModal = class extends import_obsidian135.FuzzySuggestModal {
34808
+ isSelected = false;
34809
+ items;
34810
+ itemTextFunction;
34811
+ placeholder;
34812
+ promiseResolve;
34813
+ spellcheckMode;
34852
34814
  constructor(params) {
34853
- this.params = params;
34815
+ super(params.app);
34816
+ this.items = params.items;
34817
+ this.itemTextFunction = params.itemTextFunction;
34818
+ this.placeholder = params.placeholder;
34819
+ this.promiseResolve = params.promiseResolve;
34820
+ this.spellcheckMode = params.spellcheckMode ?? "Off" /* Off */;
34821
+ this.setPlaceholder(this.placeholder ?? "");
34822
+ addPluginCssClasses(this.containerEl, params.cssClasses);
34823
+ addPluginCssClasses(this.containerEl, "select-item-modal" /* SelectItemModal */);
34854
34824
  }
34855
- params;
34856
- handle = null;
34857
- record = null;
34858
- /**
34859
- * Builds the error describing why the API is not available right now.
34860
- *
34861
- * @returns The classified error.
34862
- */
34863
- createUnavailableError() {
34864
- return new PluginApiUnavailableError({
34865
- pluginId: this.params.pluginId,
34866
- reason: resolveUnavailabilityReason(this.params)
34825
+ getItems() {
34826
+ return this.items;
34827
+ }
34828
+ getItemText(item) {
34829
+ return this.itemTextFunction(item);
34830
+ }
34831
+ onChooseItem(item) {
34832
+ this.promiseResolve(item);
34833
+ }
34834
+ onClose() {
34835
+ if (!this.isSelected) {
34836
+ this.promiseResolve(null);
34837
+ }
34838
+ }
34839
+ onOpen() {
34840
+ super.onOpen();
34841
+ applySpellcheckMode({
34842
+ app: this.app,
34843
+ element: this.inputEl,
34844
+ spellcheckMode: this.spellcheckMode
34867
34845
  });
34868
34846
  }
34847
+ selectSuggestion(value, $event) {
34848
+ this.isSelected = true;
34849
+ super.selectSuggestion(value, $event);
34850
+ }
34869
34851
  };
34870
- var PluginApiRefImpl = class extends mixinAsyncEvents()(PluginApiWatchBase) {
34871
- get value() {
34872
- return castTo(this.handle);
34852
+ async function selectItem(params) {
34853
+ return await showModal(
34854
+ (promiseResolve) => new ItemSelectModal({
34855
+ ...params,
34856
+ promiseResolve
34857
+ })
34858
+ );
34859
+ }
34860
+
34861
+ // src/__merged.ts
34862
+ init_select_option();
34863
+
34864
+ // src/obsidian/modals/suggest-modal-command-builder.ts
34865
+ var suggest_modal_command_builder_exports = {};
34866
+ __export(suggest_modal_command_builder_exports, {
34867
+ SuggestModalCommandBuilder: () => SuggestModalCommandBuilder
34868
+ });
34869
+ var SuggestModalCommandBuilder = ModalCommandBuilder;
34870
+
34871
+ // src/obsidian/note-priority.ts
34872
+ var note_priority_exports = {};
34873
+ __export(note_priority_exports, {
34874
+ NO_PRIORITY_MATCH: () => NO_PRIORITY_MATCH,
34875
+ NoPriorityWinnerReason: () => NoPriorityWinnerReason,
34876
+ filterHighestPriorityNotePaths: () => filterHighestPriorityNotePaths,
34877
+ findNoPriorityWinnerReason: () => findNoPriorityWinnerReason,
34878
+ findNotePriorityRank: () => findNotePriorityRank,
34879
+ pickHighestPriorityNotePath: () => pickHighestPriorityNotePath
34880
+ });
34881
+ init_reg_exp();
34882
+ var PROPERTY_PREFIX = "property:";
34883
+ var REG_EXP_DELIMITER = "/";
34884
+ var EXTENSION_PREFIX = ".";
34885
+ var PROPERTY_VALUE_SEPARATOR = "=";
34886
+ var NoPriorityWinnerReason = /* @__PURE__ */ ((NoPriorityWinnerReason2) => {
34887
+ NoPriorityWinnerReason2["EmptyList"] = "EmptyList";
34888
+ NoPriorityWinnerReason2["NoMatch"] = "NoMatch";
34889
+ NoPriorityWinnerReason2["Tie"] = "Tie";
34890
+ return NoPriorityWinnerReason2;
34891
+ })(NoPriorityWinnerReason || {});
34892
+ var NO_PRIORITY_MATCH = Infinity;
34893
+ function filterHighestPriorityNotePaths(params) {
34894
+ const rankedNotePaths = params.notePaths.map((notePath) => ({
34895
+ notePath,
34896
+ rank: params.rank(notePath)
34897
+ }));
34898
+ let bestRank = NO_PRIORITY_MATCH;
34899
+ for (const ranked of rankedNotePaths) {
34900
+ if (ranked.rank < bestRank) {
34901
+ bestRank = ranked.rank;
34902
+ }
34873
34903
  }
34874
- constructor(params) {
34875
- super(params);
34904
+ return rankedNotePaths.filter((ranked) => ranked.rank === bestRank).map((ranked) => ranked.notePath);
34905
+ }
34906
+ function findNoPriorityWinnerReason(params) {
34907
+ if (params.entries.length === 0) {
34908
+ return "EmptyList" /* EmptyList */;
34876
34909
  }
34877
- /**
34878
- * Recomputes the current value from the registry, firing `change` only when it actually changed.
34879
- */
34880
- refresh() {
34881
- const record = selectRecord(this.params);
34882
- if (record === this.record) {
34883
- return;
34910
+ let bestRank = NO_PRIORITY_MATCH;
34911
+ for (const notePath of params.notePaths) {
34912
+ const currentRank = params.rank(notePath);
34913
+ if (currentRank < bestRank) {
34914
+ bestRank = currentRank;
34884
34915
  }
34885
- this.record = record;
34886
- this.handle = record === null ? null : createRevocableHandle(record, this.params.contract);
34887
- this.trigger("change");
34888
34916
  }
34889
- async whenAvailable(options) {
34890
- const currentValue = this.value;
34891
- if (currentValue !== null) {
34892
- return currentValue;
34917
+ return bestRank === NO_PRIORITY_MATCH ? "NoMatch" /* NoMatch */ : "Tie" /* Tie */;
34918
+ }
34919
+ function findNotePriorityRank(params) {
34920
+ let bestRank = NO_PRIORITY_MATCH;
34921
+ let bestLength = -1;
34922
+ for (const [index2, entry] of params.entries.entries()) {
34923
+ if (entry.length <= bestLength) {
34924
+ continue;
34893
34925
  }
34894
- try {
34895
- return await runWithTimeout({
34896
- operationFunction: async (abortSignal) => await this.waitForValue(abortSignal),
34897
- operationName: `watchPluginApi("${this.params.pluginId}").whenAvailable`,
34898
- timeoutInMilliseconds: options?.timeoutInMilliseconds ?? DEFAULT_WHEN_AVAILABLE_TIMEOUT_IN_MILLISECONDS
34899
- });
34900
- } catch {
34901
- throw this.createUnavailableError();
34926
+ if (!checkEntryMatches(entry, params)) {
34927
+ continue;
34902
34928
  }
34929
+ bestRank = index2;
34930
+ bestLength = entry.length;
34903
34931
  }
34904
- async waitForValue(abortSignal) {
34905
- return await new Promise((resolve2, reject) => {
34906
- const eventRef = this.on("change", () => {
34907
- handleChange();
34908
- });
34909
- function cleanUp() {
34910
- eventRef.asyncEventSource.offref(eventRef);
34911
- abortSignal.removeEventListener("abort", handleAbort);
34912
- }
34913
- function handleAbort() {
34914
- cleanUp();
34915
- reject(abortSignal.reason);
34916
- }
34917
- const handleChange = () => {
34918
- const value = this.value;
34919
- if (value !== null) {
34920
- cleanUp();
34921
- resolve2(value);
34922
- }
34923
- };
34924
- abortSignal.addEventListener("abort", handleAbort);
34925
- handleChange();
34926
- });
34932
+ return bestRank;
34933
+ }
34934
+ function pickHighestPriorityNotePath(params) {
34935
+ let bestRank = NO_PRIORITY_MATCH;
34936
+ let bestNotePath = null;
34937
+ let bestCount = 0;
34938
+ for (const notePath of params.notePaths) {
34939
+ const currentRank = params.rank(notePath);
34940
+ if (currentRank < bestRank) {
34941
+ bestRank = currentRank;
34942
+ bestNotePath = notePath;
34943
+ bestCount = 1;
34944
+ } else if (currentRank === bestRank) {
34945
+ bestCount++;
34946
+ }
34947
+ }
34948
+ if (bestCount !== 1 || bestRank === NO_PRIORITY_MATCH) {
34949
+ return null;
34950
+ }
34951
+ return bestNotePath;
34952
+ }
34953
+ function checkEntryMatches(entry, params) {
34954
+ if (!entry) {
34955
+ return false;
34956
+ }
34957
+ if (entry.startsWith(PROPERTY_PREFIX)) {
34958
+ return checkPropertyMatches(entry.slice(PROPERTY_PREFIX.length), params.frontmatter);
34959
+ }
34960
+ if (entry.length > 1 && entry.startsWith(REG_EXP_DELIMITER) && entry.endsWith(REG_EXP_DELIMITER)) {
34961
+ const source = entry.slice(1, -1);
34962
+ return isValidRegExp(source) && new RegExp(source).test(params.notePath);
34963
+ }
34964
+ if (entry.startsWith(EXTENSION_PREFIX)) {
34965
+ return params.notePath.toLowerCase().endsWith(entry.toLowerCase());
34966
+ }
34967
+ const prefix = entry.endsWith("/") ? entry : `${entry}/`;
34968
+ return params.notePath === entry || params.notePath.startsWith(prefix);
34969
+ }
34970
+ function checkPropertyMatches(specifier, frontmatter) {
34971
+ if (!frontmatter) {
34972
+ return false;
34927
34973
  }
34928
- };
34929
- var PluginApiUnavailableError = class extends Error {
34930
- /**
34931
- * The `manifest.id` of the plugin whose API was requested.
34932
- */
34933
- pluginId;
34974
+ const separatorIndex = specifier.indexOf(PROPERTY_VALUE_SEPARATOR);
34975
+ if (separatorIndex === -1) {
34976
+ return Object.hasOwn(frontmatter, specifier);
34977
+ }
34978
+ const propertyName = specifier.slice(0, separatorIndex);
34979
+ const expectedValue = specifier.slice(separatorIndex + 1);
34980
+ if (!Object.hasOwn(frontmatter, propertyName)) {
34981
+ return false;
34982
+ }
34983
+ const actualValue = frontmatter[propertyName];
34984
+ if (Array.isArray(actualValue)) {
34985
+ return actualValue.some((item) => String(item) === expectedValue);
34986
+ }
34987
+ return String(actualValue) === expectedValue;
34988
+ }
34989
+
34990
+ // src/obsidian/obsidian-protocol-handler-registrar.ts
34991
+ var obsidian_protocol_handler_registrar_exports = {};
34992
+ __export(obsidian_protocol_handler_registrar_exports, {
34993
+ PluginObsidianProtocolHandlerRegistrar: () => PluginObsidianProtocolHandlerRegistrar
34994
+ });
34995
+ init_async();
34996
+ var PluginObsidianProtocolHandlerRegistrar = class {
34934
34997
  /**
34935
- * Which of the five failures occurred.
34998
+ * The Obsidian plugin instance the protocol handlers are registered with.
34936
34999
  */
34937
- reason;
35000
+ plugin;
34938
35001
  /**
34939
- * Creates a {@link PluginApiUnavailableError}.
35002
+ * Creates a new instance of the {@link PluginObsidianProtocolHandlerRegistrar} class.
34940
35003
  *
34941
- * @param params - The parameters for the error.
35004
+ * @param plugin - The plugin to register the protocol handler with.
34942
35005
  */
34943
- constructor(params) {
34944
- super(params.message ?? `The API of the plugin "${params.pluginId}" is unavailable: ${params.reason}.`);
34945
- this.name = "PluginApiUnavailableError";
34946
- this.pluginId = params.pluginId;
34947
- this.reason = params.reason;
35006
+ constructor(plugin) {
35007
+ this.plugin = plugin;
34948
35008
  }
34949
- };
34950
- var PluginApiRevokedError = class extends PluginApiUnavailableError {
34951
35009
  /**
34952
- * Creates a {@link PluginApiRevokedError}.
35010
+ * Registers an Obsidian protocol handler.
34953
35011
  *
34954
- * @param pluginId - The `manifest.id` of the plugin whose API was revoked.
35012
+ * @param params - The parameters for the Obsidian protocol handler registration.
34955
35013
  */
34956
- constructor(pluginId) {
34957
- super({
34958
- message: `The API of the plugin "${pluginId}" has been revoked, because the plugin was unloaded.`,
34959
- pluginId,
34960
- reason: "revoked" /* Revoked */
34961
- });
34962
- this.name = "PluginApiRevokedError";
35014
+ registerObsidianProtocolHandler(params) {
35015
+ this.plugin.registerObsidianProtocolHandler(params.action, (obsidianProtocolData) => normalizePromisable(params.handler(obsidianProtocolData)));
34963
35016
  }
34964
35017
  };
34965
- var PluginApiValidationError = class extends Error {
34966
- /**
34967
- * The issues the schema reported.
34968
- */
34969
- issues;
34970
- /**
34971
- * The name of the method whose payload failed validation.
34972
- */
34973
- methodName;
34974
- /**
34975
- * The `manifest.id` of the providing plugin.
34976
- */
34977
- pluginId;
34978
- /**
34979
- * Creates a {@link PluginApiValidationError}.
34980
- *
34981
- * @param params - The parameters for the error.
34982
- */
34983
- constructor(params) {
34984
- super(
34985
- `The ${params.payloadKind} of "${params.pluginId}" API method "${params.methodName}" failed validation: ${formatIssues(params.issues)}`
34986
- );
34987
- this.name = "PluginApiValidationError";
34988
- this.issues = params.issues;
34989
- this.methodName = params.methodName;
34990
- this.pluginId = params.pluginId;
35018
+
35019
+ // src/obsidian/path-settings.ts
35020
+ var path_settings_exports = {};
35021
+ __export(path_settings_exports, {
35022
+ PathSettings: () => PathSettings,
35023
+ pathsValidator: () => pathsValidator
35024
+ });
35025
+ init_reg_exp();
35026
+ init_string();
35027
+ init_type_guards();
35028
+ init_i18n();
35029
+ var PathSetting = class {
35030
+ constructor(type) {
35031
+ this.type = type;
35032
+ this.defaultRegExp = getDefaultRegExp(type);
35033
+ this.regExp = this.defaultRegExp;
34991
35034
  }
34992
- };
34993
- function publishPluginApi(params) {
34994
- const pluginId = params.plugin.manifest.id;
34995
- const registry = getRegistry();
34996
- const records = registry.records[pluginId] ?? [];
34997
- registry.records[pluginId] = records;
34998
- if (records.some((record2) => record2.apiVersion === params.apiVersion)) {
34999
- throw new Error(`The plugin "${pluginId}" has already published the API version "${params.apiVersion}".`);
35035
+ type;
35036
+ get array() {
35037
+ return this._array;
35000
35038
  }
35001
- const record = {
35002
- api: params.api,
35003
- apiVersion: params.apiVersion,
35004
- contract: params.contract ?? {},
35005
- isRevoked: false,
35006
- pluginId
35007
- };
35008
- records.push(record);
35009
- notifySubscribers();
35010
- params.plugin.register(() => {
35011
- revokeRecord(record);
35012
- });
35013
- }
35014
- function watchPluginApi(params) {
35015
- const ref = new PluginApiRefImpl(params);
35016
- const unsubscribe = subscribe(() => {
35017
- ref.refresh();
35018
- });
35019
- params.component.register(unsubscribe);
35020
- ref.refresh();
35021
- return ref;
35022
- }
35023
- function checkIsPromiseLike(value) {
35024
- return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
35025
- }
35026
- function checkMatchesContractShape(record, consumerContract) {
35027
- const contract = consumerContract ?? record.contract;
35028
- return Object.keys(contract).every((methodName) => typeof getMember(record.api, methodName) === "function");
35029
- }
35030
- function createMethodWrapper(params) {
35031
- return function pluginApiMethod(...$arguments) {
35032
- const method = castTo(getMember(params.target, params.propertyKey));
35033
- const methodContract = params.methodContract;
35034
- if (methodContract === void 0 || !getPluginApiDebugger().enabled) {
35035
- return method.apply(params.target, $arguments);
35036
- }
35037
- if (methodContract.input) {
35038
- validatePayload({
35039
- methodName: params.methodName,
35040
- payloadKind: "input" /* Input */,
35041
- pluginId: params.pluginId,
35042
- schema: methodContract.input,
35043
- value: $arguments
35044
- });
35045
- }
35046
- const returnValue = method.apply(params.target, $arguments);
35047
- const outputSchema = methodContract.output;
35048
- if (!outputSchema) {
35049
- return returnValue;
35050
- }
35051
- if (checkIsPromiseLike(returnValue)) {
35052
- return returnValue.then((resolvedValue) => {
35053
- validatePayload({
35054
- methodName: params.methodName,
35055
- payloadKind: "output" /* Output */,
35056
- pluginId: params.pluginId,
35057
- schema: outputSchema,
35058
- value: resolvedValue
35059
- });
35060
- return resolvedValue;
35061
- });
35062
- }
35063
- validatePayload({
35064
- methodName: params.methodName,
35065
- payloadKind: "output" /* Output */,
35066
- pluginId: params.pluginId,
35067
- schema: outputSchema,
35068
- value: returnValue
35069
- });
35070
- return returnValue;
35071
- };
35072
- }
35073
- function createRevocableHandle(record, consumerContract) {
35074
- const contract = consumerContract ?? record.contract;
35075
- const methodCache = /* @__PURE__ */ new Map();
35076
- return new Proxy(record.api, {
35077
- get(target, propertyKey) {
35078
- if (record.isRevoked) {
35079
- throw new PluginApiRevokedError(record.pluginId);
35039
+ set array(value) {
35040
+ this._array = value.filter(Boolean);
35041
+ this.regExp = makeRegExp(this._array, this.defaultRegExp) ?? this.defaultRegExp;
35042
+ }
35043
+ _array = [];
35044
+ defaultRegExp;
35045
+ regExp;
35046
+ isPathIgnored(path) {
35047
+ switch (this.type) {
35048
+ /* v8 ignore stop */
35049
+ case "Exclude" /* Exclude */: {
35050
+ return this.regExp.test(path);
35080
35051
  }
35081
- const value = getMember(target, propertyKey);
35082
- if (typeof value !== "function") {
35083
- return value;
35052
+ case "Include" /* Include */: {
35053
+ return !this.regExp.test(path);
35084
35054
  }
35085
- let method = methodCache.get(propertyKey);
35086
- if (method === void 0) {
35087
- method = createMethodWrapper({
35088
- methodContract: typeof propertyKey === "string" ? contract[propertyKey] : void 0,
35089
- methodName: String(propertyKey),
35090
- pluginId: record.pluginId,
35091
- propertyKey,
35092
- target
35093
- });
35094
- methodCache.set(propertyKey, method);
35055
+ default: {
35056
+ assertNever(this.type);
35095
35057
  }
35096
- return method;
35097
35058
  }
35098
- });
35099
- }
35100
- function formatIssues(issues) {
35101
- return issues.map((issue) => {
35102
- const path = issue.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
35103
- return path ? `${path}: ${issue.message}` : issue.message;
35104
- }).join("; ");
35105
- }
35106
- function getLiveRecords(pluginId) {
35107
- return (getRegistry().records[pluginId] ?? []).filter((record) => !record.isRevoked);
35108
- }
35109
- function getMember(api, methodName) {
35110
- return Reflect.get(api, methodName);
35111
- }
35112
- function getPluginApiDebugger() {
35113
- return getLibDebugger(PLUGIN_API_DEBUGGER_NAMESPACE);
35114
- }
35115
- function getRegistry() {
35116
- const wrapper = getObsidianDevUtilsState(PLUGIN_API_REGISTRY_STATE_KEY, {});
35117
- const registry = wrapper.value;
35118
- registry.records ??= {};
35119
- registry.subscribers ??= [];
35120
- return castTo(registry);
35121
- }
35122
- function notifySubscribers() {
35123
- for (const subscriber of snapshot(getRegistry().subscribers)) {
35124
- subscriber();
35125
35059
  }
35126
- }
35127
- function resolveUnavailabilityReason(params) {
35128
- if (!Object.hasOwn(params.app.plugins.manifests, params.pluginId)) {
35129
- return "notInstalled" /* NotInstalled */;
35060
+ };
35061
+ var PathSettings = class {
35062
+ /**
35063
+ * Gets the exclude paths.
35064
+ *
35065
+ * @returns The exclude paths.
35066
+ */
35067
+ get excludePaths() {
35068
+ return this._excludePaths.array;
35069
+ }
35070
+ /**
35071
+ * Sets the exclude paths.
35072
+ *
35073
+ * @param value - The exclude paths.
35074
+ */
35075
+ set excludePaths(value) {
35076
+ this._excludePaths.array = value;
35130
35077
  }
35131
- if (!params.app.plugins.enabledPlugins.has(params.pluginId)) {
35132
- return "notEnabled" /* NotEnabled */;
35078
+ /**
35079
+ * Gets the include paths.
35080
+ *
35081
+ * @returns The include paths.
35082
+ */
35083
+ get includePaths() {
35084
+ return this._includePaths.array;
35133
35085
  }
35134
- const records = getLiveRecords(params.pluginId);
35135
- if (records.length === 0) {
35136
- return "notPublished" /* NotPublished */;
35086
+ /**
35087
+ * Sets the include paths.
35088
+ *
35089
+ * @param value - The include paths.
35090
+ */
35091
+ set includePaths(value) {
35092
+ this._includePaths.array = value;
35137
35093
  }
35138
- if (records.every((record) => !(0, import_compare_versions3.satisfies)(record.apiVersion, params.apiVersionRange))) {
35139
- return "versionMismatch" /* VersionMismatch */;
35094
+ _excludePaths = new PathSetting("Exclude" /* Exclude */);
35095
+ _includePaths = new PathSetting("Include" /* Include */);
35096
+ /**
35097
+ * Checks if a path is ignored by the include/exclude path settings.
35098
+ *
35099
+ * @param path - The path to check.
35100
+ * @returns `true` if the path is ignored, `false` otherwise.
35101
+ */
35102
+ isPathIgnored(path) {
35103
+ return this._includePaths.isPathIgnored(path) || this._excludePaths.isPathIgnored(path);
35140
35104
  }
35141
- return "shapeMismatch" /* ShapeMismatch */;
35142
- }
35143
- function revokeRecord(record) {
35144
- record.isRevoked = true;
35145
- const registry = getRegistry();
35146
- const records = registry.records[record.pluginId];
35147
- if (records) {
35148
- registry.records[record.pluginId] = records.filter((candidate) => candidate !== record);
35105
+ };
35106
+ function pathsValidator(paths) {
35107
+ for (const path of paths) {
35108
+ if (path.startsWith("/") && path.endsWith("/") && !isValidRegExp(path.slice(1, -1))) {
35109
+ return t2(($) => $.obsidianDevUtils.pathSettings.invalidRegularExpression, { regExp: path });
35110
+ }
35149
35111
  }
35150
- notifySubscribers();
35151
- }
35152
- function selectRecord(params) {
35153
- const candidates = getLiveRecords(params.pluginId).filter((record) => (0, import_compare_versions3.satisfies)(record.apiVersion, params.apiVersionRange)).filter((record) => checkMatchesContractShape(record, params.contract)).sort((a2, b) => (0, import_compare_versions3.compareVersions)(b.apiVersion, a2.apiVersion));
35154
- return candidates[0] ?? null;
35155
35112
  }
35156
- function subscribe(subscriber) {
35157
- const registry = getRegistry();
35158
- registry.subscribers.push(subscriber);
35159
- return () => {
35160
- const index2 = registry.subscribers.indexOf(subscriber);
35161
- if (index2 !== -1) {
35162
- registry.subscribers.splice(index2, 1);
35113
+ function getDefaultRegExp(type) {
35114
+ switch (type) {
35115
+ /* v8 ignore stop */
35116
+ case "Exclude" /* Exclude */: {
35117
+ return NEVER_MATCH_REG_EXP;
35163
35118
  }
35164
- };
35119
+ case "Include" /* Include */: {
35120
+ return ALWAYS_MATCH_REG_EXP;
35121
+ }
35122
+ default: {
35123
+ assertNever(type);
35124
+ }
35125
+ }
35165
35126
  }
35166
- function validatePayload(params) {
35167
- const result = params.schema["~standard"].validate(params.value);
35168
- if (checkIsPromiseLike(result)) {
35169
- invokeAsyncSafely(async () => {
35170
- const asyncResult = await result;
35171
- if (asyncResult.issues) {
35172
- getPluginApiDebugger()(
35173
- 'Asynchronous validation of the %s of "%s" API method "%s" failed: %s',
35174
- params.payloadKind,
35175
- params.pluginId,
35176
- params.methodName,
35177
- formatIssues(asyncResult.issues)
35178
- );
35179
- }
35127
+ function makeRegExp(paths, defaultRegExp) {
35128
+ if (paths.length === 0) {
35129
+ return defaultRegExp;
35130
+ }
35131
+ const regExpStringCombined = paths.map((path) => {
35132
+ if (path === "/") {
35133
+ return defaultRegExp.source;
35134
+ }
35135
+ if (path.startsWith("/") && path.endsWith("/")) {
35136
+ return path.slice(1, -1);
35137
+ }
35138
+ path = trimEnd({
35139
+ $string: path,
35140
+ suffix: "/"
35180
35141
  });
35181
- return;
35142
+ return `^${escapeRegExp(path)}(/|$)`;
35143
+ }).map((regExpString) => `(${regExpString})`).join("|");
35144
+ try {
35145
+ return new RegExp(regExpStringCombined);
35146
+ } catch {
35147
+ return null;
35182
35148
  }
35183
- if (result.issues) {
35184
- throw new PluginApiValidationError({
35185
- issues: result.issues,
35186
- methodName: params.methodName,
35187
- payloadKind: params.payloadKind,
35188
- pluginId: params.pluginId
35149
+ }
35150
+
35151
+ // src/obsidian/pdf.ts
35152
+ var pdf_exports = {};
35153
+ __export(pdf_exports, {
35154
+ printToPdf: () => printToPdf
35155
+ });
35156
+ var import_obsidian136 = require("obsidian");
35157
+ var ELECTRON_PRINT_TO_PDF_CHANNEL = "print-to-pdf";
35158
+ async function printToPdf(element, options) {
35159
+ if (import_obsidian136.Platform.isMobile) {
35160
+ throw new Error("Printing to PDF is not supported on mobile devices.");
35161
+ }
35162
+ const printDiv = activeDocument.body.createDiv("print");
35163
+ printDiv.append(element);
35164
+ await ensureLoaded(printDiv);
35165
+ const DEFAULT_SCALE_FACTOR = 100;
35166
+ const DEFAULT_OPTIONS = {
35167
+ filepath: "Untitled.pdf",
35168
+ includeName: false,
35169
+ landscape: false,
35170
+ marginsType: 0,
35171
+ open: true,
35172
+ pageSize: "A4",
35173
+ scale: 1,
35174
+ scaleFactor: DEFAULT_SCALE_FACTOR
35175
+ };
35176
+ const fullOptions = { ...DEFAULT_OPTIONS, ...options };
35177
+ try {
35178
+ await new Promise((resolve2) => {
35179
+ activeWindow.electron.ipcRenderer.once(ELECTRON_PRINT_TO_PDF_CHANNEL, resolve2);
35180
+ activeWindow.electron.ipcRenderer.send(ELECTRON_PRINT_TO_PDF_CHANNEL, fullOptions);
35189
35181
  });
35182
+ } finally {
35183
+ printDiv.remove();
35190
35184
  }
35191
35185
  }
35192
35186
 
35187
+ // src/obsidian/plugin-cli-handler-registrar.ts
35188
+ var plugin_cli_handler_registrar_exports = {};
35189
+ __export(plugin_cli_handler_registrar_exports, {
35190
+ PluginCliHandlerRegistrar: () => PluginCliHandlerRegistrar
35191
+ });
35192
+ var import_obsidian137 = require("obsidian");
35193
+ init_async();
35194
+ var PluginCliHandlerRegistrar = class {
35195
+ /**
35196
+ * Creates a new instance of the {@link PluginCliHandlerRegistrar} class.
35197
+ *
35198
+ * @param plugin - The Obsidian plugin instance.
35199
+ */
35200
+ constructor(plugin) {
35201
+ this.plugin = plugin;
35202
+ }
35203
+ plugin;
35204
+ /**
35205
+ * Registers a CLI handler.
35206
+ *
35207
+ * @param params - The parameters for the CLI handler registration.
35208
+ */
35209
+ registerCliHandler(params) {
35210
+ this.plugin.registerCliHandler(params.command, params.description, params.flags, (cliData) => normalizePromisable(params.handler(cliData)));
35211
+ }
35212
+ };
35213
+
35214
+ // src/obsidian/plugin/obsidian-plugin-repo-paths.ts
35215
+ var obsidian_plugin_repo_paths_exports = {};
35216
+ __export(obsidian_plugin_repo_paths_exports, {
35217
+ ObsidianPluginRepoPaths: () => ObsidianPluginRepoPaths
35218
+ });
35219
+ var ObsidianPluginRepoPaths = /* @__PURE__ */ ((ObsidianPluginRepoPaths2) => {
35220
+ ObsidianPluginRepoPaths2["AnyAndroidCaptureIntegrationTestTs"] = "*.android-capture.integration.test.ts";
35221
+ ObsidianPluginRepoPaths2["AnyAndroidIntegrationTestTs"] = "*.android.integration.test.ts";
35222
+ ObsidianPluginRepoPaths2["AnyCjs"] = "*.cjs";
35223
+ ObsidianPluginRepoPaths2["AnyDesktopTs"] = "desktop-*.ts";
35224
+ ObsidianPluginRepoPaths2["AnyIntegrationTestTs"] = "*.integration.test.ts";
35225
+ ObsidianPluginRepoPaths2["AnyJs"] = "*.js";
35226
+ ObsidianPluginRepoPaths2["AnyMjs"] = "*.mjs";
35227
+ ObsidianPluginRepoPaths2["AnyPath"] = "**";
35228
+ ObsidianPluginRepoPaths2["AnyTestTs"] = "*.test.ts";
35229
+ ObsidianPluginRepoPaths2["AnyTs"] = "*.ts";
35230
+ ObsidianPluginRepoPaths2["AnyTsx"] = "*.tsx";
35231
+ ObsidianPluginRepoPaths2["AppJson"] = "app.json";
35232
+ ObsidianPluginRepoPaths2["ChangelogMd"] = "CHANGELOG.md";
35233
+ ObsidianPluginRepoPaths2["CommitlintConfigTs"] = "commitlint.config.ts";
35234
+ ObsidianPluginRepoPaths2["CurrentFolder"] = ".";
35235
+ ObsidianPluginRepoPaths2["DemoVault"] = "demo-vault";
35236
+ ObsidianPluginRepoPaths2["Dist"] = "dist";
35237
+ ObsidianPluginRepoPaths2["DistBuild"] = "dist/build";
35238
+ ObsidianPluginRepoPaths2["DistDev"] = "dist/dev";
35239
+ ObsidianPluginRepoPaths2["DotObsidian"] = ".obsidian";
35240
+ ObsidianPluginRepoPaths2["EslintConfigCjs"] = "eslint.config.cjs";
35241
+ ObsidianPluginRepoPaths2["EslintConfigCts"] = "eslint.config.cts";
35242
+ ObsidianPluginRepoPaths2["EslintConfigJs"] = "eslint.config.js";
35243
+ ObsidianPluginRepoPaths2["EslintConfigMjs"] = "eslint.config.mjs";
35244
+ ObsidianPluginRepoPaths2["EslintConfigMts"] = "eslint.config.mts";
35245
+ ObsidianPluginRepoPaths2["EslintConfigTs"] = "eslint.config.ts";
35246
+ ObsidianPluginRepoPaths2["HotReload"] = ".hotreload";
35247
+ ObsidianPluginRepoPaths2["License"] = "LICENSE";
35248
+ ObsidianPluginRepoPaths2["MainCss"] = "main.css";
35249
+ ObsidianPluginRepoPaths2["MainJs"] = "main.js";
35250
+ ObsidianPluginRepoPaths2["MainTs"] = "main.ts";
35251
+ ObsidianPluginRepoPaths2["ManifestBetaJson"] = "manifest-beta.json";
35252
+ ObsidianPluginRepoPaths2["ManifestJson"] = "manifest.json";
35253
+ ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigCjs"] = ".markdownlint-cli2.cjs";
35254
+ ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigJsonc"] = ".markdownlint-cli2.jsonc";
35255
+ ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigMjs"] = ".markdownlint-cli2.mjs";
35256
+ ObsidianPluginRepoPaths2["MarkdownlintCli2ConfigYaml"] = ".markdownlint-cli2.yaml";
35257
+ ObsidianPluginRepoPaths2["MarkdownlintConfigCjs"] = ".markdownlint.cjs";
35258
+ ObsidianPluginRepoPaths2["MarkdownlintConfigJson"] = ".markdownlint.json";
35259
+ ObsidianPluginRepoPaths2["MarkdownlintConfigJsonc"] = ".markdownlint.jsonc";
35260
+ ObsidianPluginRepoPaths2["MarkdownlintConfigMjs"] = ".markdownlint.mjs";
35261
+ ObsidianPluginRepoPaths2["MarkdownlintConfigYaml"] = ".markdownlint.yaml";
35262
+ ObsidianPluginRepoPaths2["MarkdownlintConfigYml"] = ".markdownlint.yml";
35263
+ ObsidianPluginRepoPaths2["Mocks"] = "src/test-helpers";
35264
+ ObsidianPluginRepoPaths2["NodeModules"] = "node_modules";
35265
+ ObsidianPluginRepoPaths2["NpmShrinkwrapJson"] = "npm-shrinkwrap.json";
35266
+ ObsidianPluginRepoPaths2["PackageJson"] = "package.json";
35267
+ ObsidianPluginRepoPaths2["PackageLockJson"] = "package-lock.json";
35268
+ ObsidianPluginRepoPaths2["Plugins"] = "plugins";
35269
+ ObsidianPluginRepoPaths2["Scripts"] = "scripts";
35270
+ ObsidianPluginRepoPaths2["ScriptUtils"] = "src/script-utils";
35271
+ ObsidianPluginRepoPaths2["Src"] = "src";
35272
+ ObsidianPluginRepoPaths2["StylesCss"] = "styles.css";
35273
+ ObsidianPluginRepoPaths2["Tests"] = "__tests__";
35274
+ ObsidianPluginRepoPaths2["TsConfigJson"] = "tsconfig.json";
35275
+ ObsidianPluginRepoPaths2["VersionsJson"] = "versions.json";
35276
+ ObsidianPluginRepoPaths2["VitestConfigTs"] = "vitest.config.ts";
35277
+ return ObsidianPluginRepoPaths2;
35278
+ })(ObsidianPluginRepoPaths || {});
35279
+
35193
35280
  // src/__merged.ts
35194
35281
  init_plugin_context();
35195
35282
 
@@ -39191,6 +39278,7 @@ __export(components_exports, {
39191
39278
  "registry-component": () => registry_component_exports,
39192
39279
  "rename-delete-handler-component": () => rename_delete_handler_component_exports,
39193
39280
  "rmdir-guard-component": () => rmdir_guard_component_exports,
39281
+ "settings-migration-component": () => settings_migration_component_exports,
39194
39282
  "syntax-highlighting-component": () => syntax_highlighting_component_exports
39195
39283
  });
39196
39284
  init_component_ex();
@@ -39276,10 +39364,14 @@ __export(plugin_exports2, {
39276
39364
  "plugin-api": () => plugin_api_exports,
39277
39365
  "plugin-context": () => plugin_context_exports,
39278
39366
  "plugin-event-source": () => plugin_event_source_exports,
39279
- "plugin-settings-tab": () => plugin_settings_tab_exports
39367
+ "plugin-settings-tab": () => plugin_settings_tab_exports,
39368
+ "settings-migration-api": () => settings_migration_api_exports
39280
39369
  });
39281
39370
  init_plugin_context();
39282
39371
 
39372
+ // src/obsidian/plugin/settings-migration-api.ts
39373
+ var settings_migration_api_exports = {};
39374
+
39283
39375
  // src/obsidian/popovers/index.ts
39284
39376
  var popovers_exports = {};
39285
39377
  __export(popovers_exports, {