Skip to content

Commit 776b322

Browse files
committed
refactor: extract startup timer
1 parent 5843e88 commit 776b322

3 files changed

Lines changed: 52 additions & 16 deletions

File tree

RefactorPlan.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ validated, and what remains uncertain.
2323
| 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 |
2424
| 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 |
2525
| 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 |
26+
| 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 |
2627
| 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 |
2728
| All later phases | Planned | Begin only after the preceding checkpoint is validated |
2829

@@ -54,6 +55,7 @@ validated, and what remains uncertain.
5455
| 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 |
5556
| 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 |
5657
| 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 |
58+
| 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 |
5759

5860
## Executive recommendation
5961

src/core/main.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ import type { StencilLibraryData } from "src/types/stencilLibraryTypes";
114114
import { PluginSettingsManager } from "./managers/PluginSettingsManager";
115115
import { FooterSafeAreaManager } from "./managers/FooterSafeAreaManager";
116116
import { FontManager } from "./managers/FontManager";
117+
import { StartupTimer } from "./managers/StartupTimer";
117118

118119
declare const PLUGIN_VERSION: string;
119120
declare const INITIAL_TIMESTAMP: number;
@@ -158,6 +159,7 @@ export default class ExcalidrawPlugin extends Plugin {
158159
private settingsManager: PluginSettingsManager;
159160
private footerSafeAreaManager: FooterSafeAreaManager;
160161
private fontManager: FontManager;
162+
private startupTimer: StartupTimer;
161163
public stencilLibraryManager: StencilLibraryManager;
162164
public eaInstances = new WeakArray<ExcalidrawAutomate>();
163165
public fourthFontLoaded: boolean = false;
@@ -192,8 +194,6 @@ export default class ExcalidrawPlugin extends Plugin {
192194
//private slob:string;
193195
public loadTimestamp: number;
194196
public isReady = false;
195-
private startupAnalytics: string[] = [];
196-
private lastLogTimestamp: number;
197197
private settingsReady: boolean = false;
198198
public wasPenModeActivePreviously: boolean = false;
199199
public popScope: (() => void) | null = null;
@@ -203,7 +203,7 @@ export default class ExcalidrawPlugin extends Plugin {
203203
constructor(app: App, manifest: PluginManifest) {
204204
super(app, manifest);
205205
this.loadTimestamp = INITIAL_TIMESTAMP;
206-
this.lastLogTimestamp = this.loadTimestamp;
206+
this.startupTimer = new StartupTimer(this.loadTimestamp, PLUGIN_VERSION);
207207
this.filesMaster = new Map<
208208
FileId,
209209
{
@@ -233,20 +233,14 @@ export default class ExcalidrawPlugin extends Plugin {
233233
}*/
234234
}
235235

236-
public logStartupEvent(message: string) {
237-
const timestamp = Date.now();
238-
this.startupAnalytics.push(
239-
`${message}\nTotal: ${timestamp - this.loadTimestamp}ms Delta: ${timestamp - this.lastLogTimestamp}ms\n`,
240-
);
241-
this.lastLogTimestamp = timestamp;
236+
/** Records a startup timing event without changing lifecycle ordering. */
237+
public logStartupEvent(message: string): void {
238+
this.startupTimer.logEvent(message, this.loadTimestamp);
242239
}
243240

244-
public printStarupBreakdown() {
245-
log(
246-
`Excalidraw ${PLUGIN_VERSION} startup breakdown:\n${this.startupAnalytics.join(
247-
"\n",
248-
)}`,
249-
);
241+
/** Prints the startup breakdown; spelling retained for compatibility. */
242+
public printStarupBreakdown(): void {
243+
this.startupTimer.printBreakdown();
250244
}
251245

252246
get locale() {
@@ -409,7 +403,7 @@ export default class ExcalidrawPlugin extends Plugin {
409403

410404
private async onloadOnLayoutReady() {
411405
this.loadTimestamp = Date.now();
412-
this.lastLogTimestamp = this.loadTimestamp;
406+
this.startupTimer.reset(this.loadTimestamp);
413407
this.logStartupEvent(
414408
"\n----------------------------------\nWorkspace onLayoutReady event fired (these actions are outside the plugin initialization)",
415409
);

src/core/managers/StartupTimer.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { log } from "src/utils/debugHelper";
2+
3+
/** Stores and formats plugin startup timing events without owning startup order. */
4+
export class StartupTimer {
5+
private readonly events: string[] = [];
6+
private lastLogTimestamp: number;
7+
8+
public constructor(
9+
initialTimestamp: number,
10+
private readonly pluginVersion: string,
11+
) {
12+
this.lastLogTimestamp = initialTimestamp;
13+
}
14+
15+
/**
16+
* Starts a new timing baseline while retaining events recorded before the
17+
* reset, matching the existing constructor-to-layout startup breakdown.
18+
*/
19+
public reset(timestamp: number): void {
20+
this.lastLogTimestamp = timestamp;
21+
}
22+
23+
/** Records one startup event with total and previous-event elapsed times. */
24+
public logEvent(message: string, loadTimestamp: number): void {
25+
const timestamp = Date.now();
26+
this.events.push(
27+
`${message}\nTotal: ${timestamp - loadTimestamp}ms Delta: ${timestamp - this.lastLogTimestamp}ms\n`,
28+
);
29+
this.lastLogTimestamp = timestamp;
30+
}
31+
32+
/** Writes the complete startup timing breakdown to the debug log. */
33+
public printBreakdown(): void {
34+
log(
35+
`Excalidraw ${this.pluginVersion} startup breakdown:\n${this.events.join(
36+
"\n",
37+
)}`,
38+
);
39+
}
40+
}

0 commit comments

Comments
 (0)