241 lines
6.2 KiB
JavaScript
Raw Permalink Normal View History

2026-05-31 13:21:13 +02:00
import { createMagicMoveMachine, codeToKeyedTokens, toKeyedTokens, syncTokenKeys } from './core.mjs';
import { MagicMoveRenderer } from './renderer.mjs';
import 'diff-match-patch-es';
import 'ohash';
class ShikiMagicMoveRenderer extends HTMLElement {
_animated = true;
_class = '';
get animated() {
return this._animated;
}
set animated(value) {
this._animated = value;
this.propertyChangedCallback();
}
get tokens() {
return this._tokens;
}
set tokens(value) {
this._tokens = value;
this.propertyChangedCallback();
}
get previous() {
return this._previous;
}
set previous(value) {
this._previous = value;
this.propertyChangedCallback();
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
if (this.renderer) Object.assign(this.renderer.options, this.options);
this.propertyChangedCallback();
}
get class() {
return this._class;
}
set class(value) {
this._class = value;
this.propertyChangedCallback();
}
batchUpdate = false;
hasUpdated = false;
constructor() {
super();
}
connectedCallback() {
this.container = document.createElement('pre');
this.container.classList.add('shiki-magic-move-container');
this.container.className += ` ${this.class}`;
this.renderer = new MagicMoveRenderer(this.container);
this.renderer.render(this.tokens);
this.appendChild(this.container);
}
propertyChangedCallback() {
if (!this.batchUpdate) {
this.batchUpdate = true;
setTimeout(() => {
this.batchUpdate = false;
if (!this.hasUpdated) {
this.hasUpdated = true;
return;
}
if (this.renderer) this.transform();
}, 0);
}
}
async transform() {
if (!this.renderer) return;
if (!this.animated) return;
if (this.previous && this.previous === this.tokens) return;
if (this.previous) {
this.renderer.replace(this.previous);
this.dispatchEvent(new CustomEvent('onStart'));
await this.renderer?.render(this.tokens);
this.dispatchEvent(new CustomEvent('onEnd'));
} else {
this.renderer.replace(this.tokens);
}
}
}
customElements.define('shiki-magic-move-renderer', ShikiMagicMoveRenderer);
class ShikiMagicMove extends HTMLElement {
get highlighter() {
return this._highlighter;
}
set highlighter(value) {
this._highlighter = value;
this.propertyChangedCallback();
}
get lang() {
return this._lang;
}
set lang(value) {
this._lang = value;
this.propertyChangedCallback();
}
get theme() {
return this._theme;
}
set theme(value) {
this._theme = value;
this.propertyChangedCallback();
}
get code() {
return this._code;
}
set code(value) {
this._code = value;
this.propertyChangedCallback();
}
get class() {
return this._class;
}
set class(value) {
this._class = value;
this.propertyChangedCallback();
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
this.propertyChangedCallback();
}
batchUpdate = false;
hasUpdated = false;
constructor() {
super();
}
connectedCallback() {
this.renderer = document.createElement('shiki-magic-move-renderer');
this.renderer.addEventListener('onStart', () => this.dispatchEvent(new CustomEvent('onStart')));
this.renderer.addEventListener('onEnd', () => this.dispatchEvent(new CustomEvent('onEnd')));
this.machine = createMagicMoveMachine(code => codeToKeyedTokens(this.highlighter, code, {
lang: this.lang,
theme: this.theme
}, this.options?.lineNumbers), this.options);
this.updateRenderer();
this.appendChild(this.renderer);
}
propertyChangedCallback() {
if (!this.batchUpdate) {
this.batchUpdate = true;
setTimeout(() => {
this.batchUpdate = false;
if (!this.hasUpdated) {
this.hasUpdated = true;
return;
}
this.updateRenderer();
}, 0);
}
}
updateRenderer() {
if (this.machine && this.renderer) {
this.result = this.machine.commit(this.code, this.options);
this.renderer.tokens = this.result.current;
this.renderer.previous = this.result.previous;
this.renderer.options = this.options;
this.renderer.class = this.class ?? '';
}
}
}
customElements.define('shiki-magic-move', ShikiMagicMove);
class ShikiMagicMovePrecompiled extends HTMLElement {
_steps = [];
_step = 0;
_animated = true;
get steps() {
return this._steps;
}
set steps(value) {
this._steps = value;
this.propertyChangedCallback();
}
get step() {
return this._step;
}
set step(value) {
this._step = value;
this.propertyChangedCallback();
}
get animated() {
return this._animated;
}
set animated(value) {
this._animated = value;
this.propertyChangedCallback();
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
this.propertyChangedCallback();
}
previous = toKeyedTokens('', []);
batchUpdate = false;
hasUpdated = false;
constructor() {
super();
}
connectedCallback() {
this.renderer = document.createElement('shiki-magic-move-renderer');
this.renderer.addEventListener('onStart', () => this.dispatchEvent(new CustomEvent('onStart')));
this.renderer.addEventListener('onEnd', () => this.dispatchEvent(new CustomEvent('onEnd')));
this.updateRenderer();
this.appendChild(this.renderer);
}
propertyChangedCallback() {
if (!this.batchUpdate) {
this.batchUpdate = true;
setTimeout(() => {
this.batchUpdate = false;
if (!this.hasUpdated) {
this.hasUpdated = true;
return;
}
this.updateRenderer();
}, 0);
}
}
updateRenderer() {
const result = syncTokenKeys(this.previous, this.steps[Math.min(this.step, this.steps.length - 1)], this.options);
this.previous = result.to;
this.renderer.tokens = result.to;
this.renderer.previous = result.from;
this.renderer.options = this.options;
this.renderer.animated = this.animated;
}
}
customElements.define('shiki-magic-move-precompiled', ShikiMagicMovePrecompiled);
export { ShikiMagicMove, ShikiMagicMovePrecompiled, ShikiMagicMoveRenderer };