From 22e3dc8aa8fdbaf0263294663f0c98adbcc0727c Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Wed, 24 Dec 2025 21:07:46 +0100 Subject: [PATCH] feat: add onHighlightComplete callback to Code and Diff renderables --- packages/core/src/renderables/Code.ts | 13 +++++++++++++ packages/core/src/renderables/Diff.ts | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/core/src/renderables/Code.ts b/packages/core/src/renderables/Code.ts index 9abb2026..6e260d35 100644 --- a/packages/core/src/renderables/Code.ts +++ b/packages/core/src/renderables/Code.ts @@ -15,6 +15,7 @@ export interface CodeOptions extends TextBufferOptions { conceal?: boolean drawUnstyledText?: boolean streaming?: boolean + onHighlightComplete?: () => void } export class CodeRenderable extends TextBufferRenderable { @@ -31,6 +32,7 @@ export class CodeRenderable extends TextBufferRenderable { private _streaming: boolean private _hadInitialContent: boolean = false private _lastHighlights: SimpleHighlight[] = [] + private _onHighlightComplete?: () => void protected _contentDefaultOptions = { content: "", @@ -49,6 +51,7 @@ export class CodeRenderable extends TextBufferRenderable { this._conceal = options.conceal ?? this._contentDefaultOptions.conceal this._drawUnstyledText = options.drawUnstyledText ?? this._contentDefaultOptions.drawUnstyledText this._streaming = options.streaming ?? this._contentDefaultOptions.streaming + this._onHighlightComplete = options.onHighlightComplete // Set initial content immediately so lineCount is correct for measure functions // This prevents width glitches in parent components like LineNumberRenderable @@ -148,6 +151,14 @@ export class CodeRenderable extends TextBufferRenderable { } } + get onHighlightComplete(): (() => void) | undefined { + return this._onHighlightComplete + } + + set onHighlightComplete(value: (() => void) | undefined) { + this._onHighlightComplete = value + } + private ensureVisibleTextBeforeHighlight(): void { const content = this._content @@ -247,6 +258,7 @@ export class CodeRenderable extends TextBufferRenderable { this._isHighlighting = false this._highlightsDirty = false this.requestRender() + this._onHighlightComplete?.() } catch (error) { // Check if this result is stale if (snapshotId !== this._highlightSnapshotId) { @@ -261,6 +273,7 @@ export class CodeRenderable extends TextBufferRenderable { this._isHighlighting = false this._highlightsDirty = false this.requestRender() + this._onHighlightComplete?.() } } diff --git a/packages/core/src/renderables/Diff.ts b/packages/core/src/renderables/Diff.ts index 6286e809..c28fb986 100644 --- a/packages/core/src/renderables/Diff.ts +++ b/packages/core/src/renderables/Diff.ts @@ -47,6 +47,7 @@ export interface DiffRenderableOptions extends RenderableOptions removedSignColor?: string | RGBA addedLineNumberBg?: string | RGBA removedLineNumberBg?: string | RGBA + onHighlightComplete?: () => void } export class DiffRenderable extends Renderable { @@ -106,6 +107,9 @@ export class DiffRenderable extends Renderable { private errorTextRenderable: TextRenderable | null = null private errorCodeRenderable: CodeRenderable | null = null + private _onHighlightComplete?: () => void + private _pendingHighlights = 0 + constructor(ctx: RenderContext, options: DiffRenderableOptions) { super(ctx, { ...options, @@ -141,6 +145,7 @@ export class DiffRenderable extends Renderable { this._removedSignColor = parseColor(options.removedSignColor ?? "#ef4444") this._addedLineNumberBg = parseColor(options.addedLineNumberBg ?? "transparent") this._removedLineNumberBg = parseColor(options.removedLineNumberBg ?? "transparent") + this._onHighlightComplete = options.onHighlightComplete // Only parse and build if diff is provided if (this._diff) { @@ -240,6 +245,13 @@ export class DiffRenderable extends Renderable { super.destroyRecursively() } + private handleHighlightComplete(): void { + this._pendingHighlights-- + if (this._pendingHighlights === 0 && this._onHighlightComplete) { + this._onHighlightComplete() + } + } + /** * Create or update a CodeRenderable with the given content and options. * Reuses existing instances to avoid expensive recreation. @@ -315,6 +327,10 @@ export class DiffRenderable extends Renderable { ): CodeRenderable { const existingRenderable = side === "left" ? this.leftCodeRenderable : this.rightCodeRenderable + if (this._filetype) { + this._pendingHighlights++ + } + if (!existingRenderable) { // Create new CodeRenderable const codeOptions: CodeOptions = { @@ -326,6 +342,7 @@ export class DiffRenderable extends Renderable { syntaxStyle: this._syntaxStyle ?? SyntaxStyle.create(), width: "100%", height: "100%", + onHighlightComplete: () => this.handleHighlightComplete(), ...(this._fg !== undefined && { fg: this._fg }), ...(drawUnstyledText !== undefined && { drawUnstyledText }), ...(this._selectionBg !== undefined && { selectionBg: this._selectionBg }), @@ -346,6 +363,7 @@ export class DiffRenderable extends Renderable { existingRenderable.content = content existingRenderable.wrapMode = wrapMode ?? "none" existingRenderable.conceal = this._conceal + existingRenderable.onHighlightComplete = () => this.handleHighlightComplete() if (drawUnstyledText !== undefined) { existingRenderable.drawUnstyledText = drawUnstyledText }