diff --git a/RefactorPlan.md b/RefactorPlan.md index 484910c7..3ce73e51 100644 --- a/RefactorPlan.md +++ b/RefactorPlan.md @@ -1,6 +1,6 @@ # Incremental refactor assessment and plan -Status: active plan for the 2.27.0 refactor, last updated 2026-08-08 +Status: active plan for the 2.27.0 refactor, last updated 2026-08-09 This document is the working plan for reducing the size and coupling of `src/core/main.ts` and `src/view/ExcalidrawView.ts` without destabilizing the @@ -20,6 +20,11 @@ validated, and what remains uncertain. | --- | --- | --- | | Assessment and baseline design | Complete | Initial architecture, risks, sequencing, and validation matrix documented | | Retire legacy AI settings and fallbacks | Complete | Removed the retired migration, schema/default fields, GPT reset, and AI runtime fallbacks without filtering unknown persisted keys; manual testing found no issues | +| Extract settings implementation service | Implemented; awaiting manual validation | `PluginSettingsManager` now owns persistence, default assembly, remaining migrations, and API-key obfuscation; plugin methods and startup readiness remain intact, while temporary autosave enablement is now explicit plugin-instance state rather than persisted settings state | +| Extract footer safe-area styling | Implemented; awaiting manual validation | `FooterSafeAreaManager` now owns device-specific stylesheet injection, open-document traversal, and unload cleanup; the plugin method remains as a settings-UI compatibility delegate | +| Extract font management | Implemented; awaiting manual validation | `FontManager` now owns CJK discovery/loading, custom-font registration, document stylesheets, readiness, and cleanup; plugin methods and the externally read `fourthFontLoaded` field remain intact | +| Extract startup instrumentation | Implemented; awaiting manual validation | `StartupTimer` now owns startup event history, delta tracking, and breakdown formatting; lifecycle calls, public methods, and the public `loadTimestamp` field remain intact | +| Remove confirmed dead `main.ts` code | Implemented; awaiting manual validation | Removed the uncalled cache-registration method and the never-assigned duplicate file-explorer observer field/cleanup; the active observer in `ObserverManager` remains unchanged | | Audit and consolidate duplicate logic | In progress | Consolidated `updateFrontmatterInString()`, `arrayToMap()`, `wrapTextAtCharLength()`, `getLinkParts()`/`LinkParts`, `getBinaryFileFromDataURL()`, `svgToBase64()`, `getFontDataURL()`, `cropCanvas()`, `getImageSize()`, `promiseTry()`, `isVersionNewerThanOther()`, `repositionElementsToCursor()`, the internal `cloneElement()`, and `getBoundTextElementId()`; continue one independently testable helper family at a time | | All later phases | Planned | Begin only after the preceding checkpoint is validated | @@ -46,6 +51,13 @@ validated, and what remains uncertain. | 2026-08-08 | Consolidated `getImageSize()` and `promiseTry()` | Kept the identical embedded-asset implementations as canonical, documented their native image-loading and promise-boundary semantics, and preserved the `utils.ts` import surface through re-exports. The former `getImageSize()` differed only in bracing and comments; both `promiseTry()` copies were textually identical. No caller used distinct behavior | Repository search confirms one implementation of each helper; `npm run build` passed after each code change; targeted ESLint passed | | 2026-08-08 | Consolidated `isVersionNewerThanOther()` | Extracted the identical implementations to dependency-light `versionUtils.ts` and preserved both `utils.ts` and `sceneDataUtils.ts` import paths. Missing, malformed, prerelease, and numeric comparison behavior remains unchanged | Repository search confirms one implementation; `npm run build` and targeted ESLint passed | | 2026-08-08 | Consolidated element positioning, cloning, and bound-text helpers | Extracted `estimateBounds()`, `repositionElementsToCursor()`, the internal ID-preserving `cloneElement()`, and `getBoundTextElementId()` to documented `excalidrawElementUtils.ts`, preserving exports from both former owner modules. Repositioning and cloning were identical. The only bound-text difference was a redundant optional chain after an equivalent non-null/length guard, so callers could not observe it. The public `ExcalidrawAutomate.cloneElement()` API, which assigns a new ID, was not changed | Repository search confirms one implementation of each helper; targeted ESLint, `npm run build`, `npm run lib`, and `git diff --check` passed; bundle size remains 5,095,379 bytes because Rollup had already eliminated the duplicate paths; `npm run madge` could not run because `madge` is not installed | +| 2026-08-08 | Extracted plugin settings implementation from `main.ts` | Added documented `PluginSettingsManager` with a narrow host contract to own load/save, default assembly, existing library/Markdown-image/oEmbed/preview migrations, and API-key obfuscation. At this extraction checkpoint, retained `plugin.settings`, all public plugin method signatures, startup autosave behavior, settings readiness, settings-tab timing, external-change library invalidation, persisted format, and unknown-key behavior. After delegate TSDoc, `main.ts` decreased from 1,676 to 1,563 lines | `npm run build` passed after each code change; `npm run lib` and a scoped `git diff --check` passed; the new manager and changed delegate lines have no ESLint diagnostics, while targeted lint still reports four unrelated existing `any` diagnostics in the startup-script block of `main.ts`; the initial extraction build was 5,094,122 bytes, 1,257 bytes below the preceding checkpoint. A concurrent unrelated `nanoid` dependency update changed the final workspace build to 5,094,522 bytes; `npm run madge` could not run because `madge` is not installed; manual settings validation remains pending | +| 2026-08-09 | Reviewed and retained `reEnableAutosave` | An attempted removal was reversed after confirming that this is not migration code: it resets the session-scoped temporary disable/enable autosave commands when the plugin starts. Restored `LoadSettingsOptions`, startup `{ reEnableAutosave: true }`, and the post-load in-memory assignment exactly as extracted. No autosave behavior change remains | Repository search confirms the complete option flow is restored; `npm run build`, `npm run lib`, targeted manager ESLint, and scoped `git diff --check` passed; the existing 34 circular dependency warnings are unchanged; `main.ts` is again 1,563 lines and the bundle is again 5,094,522 bytes; manual settings validation remains pending | +| 2026-08-09 | Moved temporary autosave enablement out of persisted settings | A full runtime and history review confirmed that `settings.autosave` had no settings UI and served only as the global gate for the temporary enable/disable commands. Replaced it with documented plugin-instance state initialized to enabled, updated commands and view scheduling to use that state, removed `autosave` from `ExcalidrawSettings` and `DEFAULT_SETTINGS`, and removed `LoadSettingsOptions`/`reEnableAutosave`. Desktop and mobile interval settings remain unchanged. Existing persisted `autosave` keys are intentionally left inert under the no-sanitizer policy | Repository search confirms there are no remaining supported-setting or runtime references to `settings.autosave`; the settings interface and defaults contain the same 182 keys; `npm run build`, `npm run lib`, and scoped `git diff --check` passed with the existing 34 circular dependency warnings. Broad lint reports only the existing backlog and no diagnostics on changed lines; bundle size decreased by 151 bytes to 5,094,371 bytes; manual session-command validation remains pending | +| 2026-08-09 | Extracted footer safe-area styling from `main.ts` | Added documented `FooterSafeAreaManager` with a narrow host contract to own phone/tablet CSS injection, exact device-aware open-document traversal, setting-driven removal, and unload cleanup. Kept `plugin.updateFooterSafeAreaPadding()` and its layout-ready and settings UI call sites unchanged as delegates. Font document traversal remains in `main.ts` until the separate font extraction | `npm run build`, targeted manager ESLint, and scoped `git diff --check` passed; `main.ts` decreased by 46 lines from 1,558 to 1,512; bundle size increased by 99 bytes to 5,094,470 bytes from manager/delegate overhead; `npm run madge` could not run because `madge` is not installed; phone/tablet manual validation remains pending | +| 2026-08-09 | Extracted font management from `main.ts` | Added documented `FontManager` to own the existing CJK asset cache, vault reads, CJK/custom stylesheet lifecycle, custom font metrics and package registration, readiness state, and device-aware document traversal. Preserved every plugin-facing font method as a delegate, retained `plugin.fourthFontLoaded` for view compatibility, kept the initial readiness value and 100ms timer unchanged, and injected lazy package-map access without changing `PackageManager` construction or ownership | `npm run build`, `npm run lib`, targeted manager ESLint, and scoped `git diff --check` passed; `main.ts` decreased by 130 lines from 1,512 to 1,382; bundle size increased by 379 bytes to 5,094,849 bytes from manager/facade overhead; `npm run madge` could not run because `madge` is not installed; desktop, mobile, CJK, custom-font, and popout manual validation remains pending | +| 2026-08-09 | Extracted startup timing instrumentation from `main.ts` | Added documented `StartupTimer` to own the private event list, previous-event timestamp, total/delta formatting, and debug output. Kept every timing call in its original lifecycle position, retained `logStartupEvent()` and the misspelled `printStarupBreakdown()` as plugin delegates, preserved pre-layout events across the layout-ready baseline reset, and retained `loadTimestamp` as an own public field with unchanged assignment behavior | `npm run build`, `npm run lib`, targeted manager ESLint, and scoped `git diff --check` passed; `main.ts` decreased by 6 lines from 1,382 to 1,376; the latest bundle is 5,095,747 bytes, 898 bytes above the preceding checkpoint due to the manager and compatibility facade; `npm run madge` could not run because `madge` is not installed; startup breakdown inspection remains pending | +| 2026-08-09 | Rejected a catch-all Markdown integration manager and removed proven dead code from `main.ts` | Markdown post-processing, install-codeblock handling, observer setup, and rerender behavior have different lifecycle and ownership constraints, so they will remain explicit rather than being grouped under a weak abstraction. Removed the uncalled private `registerEventListeners()`, its `MetadataCache` import, the never-assigned `main.ts` `fileExplorerObserver`, and its inert unload check. The active file-explorer observer and teardown in `ObserverManager` were not changed | Repository-wide reference searches confirmed both removed members had no callers or assignments and that `PluginFileManager.initialize()` owns the active initial cache walk; `npm run build` passed with the existing circular-dependency warnings; targeted lint reports only the same four pre-existing startup-script `any` diagnostics and none on changed lines; `git diff --check` passed; `main.ts` decreased by 29 lines from 1,376 to 1,347 and the bundle decreased by 381 bytes to 5,095,366 bytes; manual unload/reload validation remains pending | ## Executive recommendation @@ -286,7 +298,6 @@ src/core/ PluginSettingsManager.ts Load/save/encryption/default assembly FontManager.ts Per-document fonts and package registration ViewportStyleManager.ts Phone/tablet safe-area behavior - MarkdownIntegrationManager.ts Post processors and install code blocks src/view/ ExcalidrawView.ts Obsidian host, public facade, composition @@ -468,19 +479,20 @@ popout creation, so it should not be combined with package-manager changes. Candidates should be extracted one at a time: -1. Move install-codeblock registration and Markdown integration setup behind a - `MarkdownIntegrationManager`, while still registering the Markdown post - processor from `onload()`. +1. Keep Markdown post-processing, install-codeblock registration, observer + setup, and rerender behavior explicit in their current owners. A proposed + `MarkdownIntegrationManager` was rejected because these responsibilities do + not form a cohesive lifecycle unit. 2. Move startup-script execution behind a focused runner owned by the script subsystem. -3. Move startup timing storage/formatting into a small `StartupTimer` while - retaining `plugin.logStartupEvent()` as a delegate if consumers need it. +3. Completed: startup timing storage/formatting now belongs to `StartupTimer`, + with the plugin methods retained as compatibility delegates. 4. Group initialization and cleanup of managers, but keep a readable ordered list in `onloadOnLayoutReady()` and `onunload()`. -5. Only after reference searches and runtime validation, remove confirmed dead - members. Current review candidates include the unused private - `registerEventListeners()` and a `fileExplorerObserver` field in `main.ts` - that appears separate from the observer owned by `ObserverManager`. +5. Completed: after repository-wide reference searches, removed the unused + private `registerEventListeners()` and the never-assigned + `fileExplorerObserver` field and unload check from `main.ts`. The active + observer owned by `ObserverManager` remains intact. Do not hide lifecycle ordering inside a generic service container. The desired `main.ts` is a readable composition root, not an empty forwarding shell. diff --git a/package-lock.json b/package-lock.json index 89294aa3..6fb7e7ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "gl-matrix": "^3.4.3", "lz-string": "^1.5.0", "monkey-around": "^2.3.0", - "nanoid": "^5.1.11", + "nanoid": "^5.1.16", "opentype.js": "^1.3.4", "pako": "^2.1.0", "points-on-path": "^0.2.1", @@ -10239,9 +10239,9 @@ } }, "node_modules/nanoid": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", - "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", "funding": [ { "type": "github", diff --git a/package.json b/package.json index bbdb8cb1..54c96515 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "gl-matrix": "^3.4.3", "lz-string": "^1.5.0", "monkey-around": "^2.3.0", - "nanoid": "^5.1.11", + "nanoid": "^5.1.16", "opentype.js": "^1.3.4", "pako": "^2.1.0", "points-on-path": "^0.2.1", @@ -96,7 +96,7 @@ "lodash": "^4.18.1", "lodash-es": "^4.18.1", "serialize-javascript": "^7.0.5", - "nanoid": "^5.1.11", + "nanoid": "^5.1.16", "dompurify": "^3.4.12", "fast-uri": "^3.1.4", "immutable": "^4.3.9", diff --git a/src/core/main.ts b/src/core/main.ts index 40d9e032..df2ffc03 100644 --- a/src/core/main.ts +++ b/src/core/main.ts @@ -11,7 +11,6 @@ import { ViewState, ViewStateResult, Notice, - MetadataCache, TAbstractFile, FrontMatterCache, } from "obsidian"; @@ -33,13 +32,10 @@ import { LOCALE, setExcalidrawPlugin, DEVICE, - FONTS_STYLE_ID, - CJK_STYLE_ID, setRootElementSize, } from "../constants/constants"; import { ExcalidrawSettings, - DEFAULT_SETTINGS, ExcalidrawSettingTab, } from "./settings"; import { ExcalidrawAutomate } from "../shared/ExcalidrawAutomate"; @@ -52,11 +48,9 @@ import { getNewUniqueFilepath, } from "../utils/fileUtils"; import { - getFontDataURL, errorlog, isVersionNewerThanOther, versionUpdateCheckTimer, - getFontMetrics, calculateUIModeValue, } from "../utils/utils"; import { @@ -75,7 +69,7 @@ import { import { FieldSuggester } from "../shared/Suggesters/FieldSuggester"; import { ReleaseNotes } from "../shared/Dialogs/ReleaseNotes"; import { DeviceType, Packages } from "../types/types"; -import { PaneTarget, PreviewImageType } from "../types/utilTypes"; +import { PaneTarget } from "../types/utilTypes"; import { emulateCTRLClickForLinks, linkClickModifierType, @@ -92,7 +86,6 @@ import { terminateCompressionWorker, } from "../shared/Workers/compression-worker"; import { WeakArray } from "../shared/WeakArray"; -import { getCJKDataURLs } from "../utils/CJKLoader"; import { ExcalidrawLoading, switchToExcalidraw, @@ -111,22 +104,20 @@ import { getHighlightColor } from "src/utils/dynamicStyling"; import { InlineLinkSuggester } from "src/shared/Suggesters/InlineLinkSuggester"; import { KeyBlocker } from "src/types/excalidrawAutomateTypes"; import { UIMode } from "src/shared/Dialogs/UIModeSettingComponent"; -import { - decryptPersistedAPIKeys, - encryptPersistedAPIKeys, -} from "src/utils/settingsKeyObfuscation"; import { hideElement, setButtonBgColor } from "src/utils/styleUtils"; import { installButton } from "src/utils/scriptLibraryUtils"; -import { isInstanceOfHTMLStyleElement } from "src/utils/typechecks"; import { insertLaTeXToView } from "src/utils/excalidrawViewHelpers"; import type { MarkdownImageData } from "src/types/markdownImageTypes"; import { StencilLibraryManager } from "./managers/StencilLibraryManager"; import type { StencilLibraryData } from "src/types/stencilLibraryTypes"; +import { PluginSettingsManager } from "./managers/PluginSettingsManager"; +import { FooterSafeAreaManager } from "./managers/FooterSafeAreaManager"; +import { FontManager } from "./managers/FontManager"; +import { StartupTimer } from "./managers/StartupTimer"; declare const PLUGIN_VERSION: string; declare const INITIAL_TIMESTAMP: number; declare const mainDocument: Document; -declare const deliberateCreateElement: (document: Document, tagName: string) => HTMLStyleElement; type FileMasterInfo = { isHyperLink: boolean; @@ -137,16 +128,6 @@ type FileMasterInfo = { colorMapJSON?: string; }; -const PHONE_FOOTER_SAFE_AREA_STYLE_ID = "excalidraw-phone-footer-safe-area"; -const PHONE_FOOTER_SAFE_AREA_CSS = ` -.excalidraw .App-bottom-bar { - padding-bottom: 50px; -} -`; - -type PersistedExcalidrawSettings = Partial & - Record; - /** * Compatibility labels consumed by upstream Excalidraw via ExcalidrawPlugin.getLabel(). * Keep these keys present in `en.ts`, and keep maintained locales in sync. @@ -174,12 +155,18 @@ export default class ExcalidrawPlugin extends Plugin { private monkeyPatchManager: MonkeyPatchManager; private commandManager: CommandManager; private eventManager: EventManager; + private settingsManager: PluginSettingsManager; + private footerSafeAreaManager: FooterSafeAreaManager; + private fontManager: FontManager; + private startupTimer: StartupTimer; public stencilLibraryManager: StencilLibraryManager; public eaInstances = new WeakArray(); public fourthFontLoaded: boolean = false; public excalidrawConfig: ExcalidrawConfig; public excalidrawFileModes: { [file: string]: string } = {}; public declare settings: ExcalidrawSettings; + /** Session-scoped autosave gate controlled by the temporary commands. */ + public autosaveEnabled: boolean = true; public activeExcalidrawView: ExcalidrawView = null; public lastActiveExcalidrawFilePath: string = null; public lastActiveExcalidrawLeafID: string = null; @@ -190,7 +177,6 @@ export default class ExcalidrawPlugin extends Plugin { private legacyExcalidrawPopoverObserver: | MutationObserver | CustomMutationObserver; - private fileExplorerObserver: MutationObserver | CustomMutationObserver; public opencount: number = 0; public ea: ExcalidrawAutomate; //A master list of fileIds to facilitate copy / paste @@ -205,11 +191,7 @@ export default class ExcalidrawPlugin extends Plugin { public forceToOpenInMarkdownFilepath: string = null; //private slob:string; public loadTimestamp: number; - private isLocalCJKFontAvailabe: boolean = undefined; public isReady = false; - private fontsReady = true; //setting this to true allows for a race condition during startup loading fonts and rendering Excalidraw - private startupAnalytics: string[] = []; - private lastLogTimestamp: number; private settingsReady: boolean = false; public wasPenModeActivePreviously: boolean = false; public popScope: (() => void) | null = null; @@ -219,7 +201,7 @@ export default class ExcalidrawPlugin extends Plugin { constructor(app: App, manifest: PluginManifest) { super(app, manifest); this.loadTimestamp = INITIAL_TIMESTAMP; - this.lastLogTimestamp = this.loadTimestamp; + this.startupTimer = new StartupTimer(this.loadTimestamp, PLUGIN_VERSION); this.filesMaster = new Map< FileId, { @@ -237,6 +219,11 @@ export default class ExcalidrawPlugin extends Plugin { //isExcalidraw function is used already is already used by MarkdownPostProcessor in onLoad before onLayoutReady this.fileManager = new PluginFileManager(this); + this.settingsManager = new PluginSettingsManager(this); + this.footerSafeAreaManager = new FooterSafeAreaManager(this); + this.fontManager = new FontManager(this, () => + this.packageManager.getPackageMap(), + ); setExcalidrawPlugin(this); /*if((process.env.NODE_ENV === 'development')) { @@ -244,20 +231,14 @@ export default class ExcalidrawPlugin extends Plugin { }*/ } - public logStartupEvent(message: string) { - const timestamp = Date.now(); - this.startupAnalytics.push( - `${message}\nTotal: ${timestamp - this.loadTimestamp}ms Delta: ${timestamp - this.lastLogTimestamp}ms\n`, - ); - this.lastLogTimestamp = timestamp; + /** Records a startup timing event without changing lifecycle ordering. */ + public logStartupEvent(message: string): void { + this.startupTimer.logEvent(message, this.loadTimestamp); } - public printStarupBreakdown() { - log( - `Excalidraw ${PLUGIN_VERSION} startup breakdown:\n${this.startupAnalytics.join( - "\n", - )}`, - ); + /** Prints the startup breakdown; spelling retained for compatibility. */ + public printStarupBreakdown(): void { + this.startupTimer.printBreakdown(); } get locale() { @@ -342,38 +323,16 @@ export default class ExcalidrawPlugin extends Plugin { ); } - public getCJKFontSettings() { - const assetsFoler = this.settings.fontAssetsPath; - if (typeof this.isLocalCJKFontAvailabe === "undefined") { - this.isLocalCJKFontAvailabe = this.app.vault - .getFiles() - .some((f) => f.path.startsWith(assetsFoler)); - } - if (!this.isLocalCJKFontAvailabe) { - return { c: false, j: false, k: false }; - } - return { - c: this.settings.loadChineseFonts, - j: this.settings.loadJapaneseFonts, - k: this.settings.loadKoreanFonts, - }; + /** Returns the configured CJK ranges when local font assets are available. */ + public getCJKFontSettings(): { c: boolean; j: boolean; k: boolean } { + return this.fontManager.getCJKFontSettings(); } + /** Reads a configured CJK font file from the vault. */ public async loadFontFromFile( fontName: string, ): Promise { - const assetsFoler = this.settings.fontAssetsPath; - - if (!this.isLocalCJKFontAvailabe) { - return; - } - const file = this.app.vault.getFileByPath( - normalizePath(`${assetsFoler}/${fontName}`), - ); - if (!file || !(file instanceof TFile)) { - return; - } - return await this.app.vault.readBinary(file); + return await this.fontManager.loadFontFromFile(fontName); } async onload() { @@ -399,7 +358,7 @@ export default class ExcalidrawPlugin extends Plugin { ); try { - void this.loadSettings({ reEnableAutosave: true }).then(() => + void this.loadSettings().then(() => this.onloadCheckForOnceOffSettingsUpdates(), ); } catch (e) { @@ -442,7 +401,7 @@ export default class ExcalidrawPlugin extends Plugin { private async onloadOnLayoutReady() { this.loadTimestamp = Date.now(); - this.lastLogTimestamp = this.loadTimestamp; + this.startupTimer.reset(this.loadTimestamp); this.logStartupEvent( "\n----------------------------------\nWorkspace onLayoutReady event fired (these actions are outside the plugin initialization)", ); @@ -613,7 +572,7 @@ export default class ExcalidrawPlugin extends Plugin { public async awaitInit() { let counter = 0; - while ((!this.isReady || !this.fontsReady) && counter++ < 200) { + while ((!this.isReady || !this.fontManager.isReady) && counter++ < 200) { await sleep(50); } } @@ -639,174 +598,28 @@ export default class ExcalidrawPlugin extends Plugin { ); } - public async initializeFonts() { - const cjkFontDataURLs = await getCJKDataURLs(this); - if (typeof cjkFontDataURLs === "boolean" && !cjkFontDataURLs) { - new Notice(t("FONTS_LOAD_ERROR") + this.settings.fontAssetsPath, 6000); - } - - if (typeof cjkFontDataURLs === "object") { - const fontDeclarations = cjkFontDataURLs.map( - (dataURL) => - `@font-face { font-family: 'Xiaolai'; src: url("${dataURL}"); font-display: swap; font-weight: 400; }`, - ); - for (const ownerDocument of this.getOpenObsidianDocuments()) { - await this.addFonts(fontDeclarations, ownerDocument, CJK_STYLE_ID); - } - new Notice(t("FONTS_LOADED")); - } - - const font = await getFontDataURL( - this.app, - this.settings.experimantalFourthFont, - "", - "Local Font", - ); - - if (font.dataURL === "") { - this.fourthFontLoaded = true; - return; - } - - const fourthFontDataURL = font.dataURL; - - const f = this.app.metadataCache.getFirstLinkpathDest( - this.settings.experimantalFourthFont, - "", - ); - // Call getFontMetrics with the fourthFontDataURL - let fontMetrics = f.extension.startsWith("woff") - ? undefined - : await getFontMetrics(fourthFontDataURL, "Local Font"); - - if (!fontMetrics) { - //console.log("Font Metrics not found, using default"); - fontMetrics = { - unitsPerEm: 1000, - ascender: 750, - descender: -250, - lineHeight: 1.2, - fontName: "Local Font", - }; - } - this.packageManager.getPackageMap().forEach(({ excalidrawLib }) => { - if (!fontMetrics) { - return; - } - excalidrawLib.registerLocalFont( - { metrics: fontMetrics }, - fourthFontDataURL, - ); - }); - // Add fonts to open Obsidian documents - for (const ownerDocument of this.getOpenObsidianDocuments()) { - await this.addFonts( - [ - `@font-face{font-family:'Local Font';src:url("${fourthFontDataURL}");font-display: swap;font-weight: 400;`, - ], - ownerDocument, - ); - } - if (!this.fourthFontLoaded) { - window.setTimeout(() => { - this.fourthFontLoaded = true; - }, 100); - } - this.fontsReady = true; + /** Initializes configured CJK and custom fonts across open documents. */ + public async initializeFonts(): Promise { + await this.fontManager.initializeFonts(); } + /** Adds or replaces a plugin-owned font stylesheet. */ public async addFonts( declarations: string[], - ownerDocument: Document = mainDocument, - styleId: string = FONTS_STYLE_ID, - ) { - // replace the old local font