173 lines
4.2 KiB
JavaScript
173 lines
4.2 KiB
JavaScript
import { defineComponent, ref, onMounted, watch, nextTick, h, renderList, computed } from 'vue';
|
|
import { createMagicMoveMachine, codeToKeyedTokens, toKeyedTokens, syncTokenKeys } from './core.mjs';
|
|
import { MagicMoveRenderer } from './renderer.mjs';
|
|
import 'diff-match-patch-es';
|
|
import 'ohash';
|
|
|
|
/**
|
|
* A wrapper component to `MagicMoveRenderer`
|
|
*/
|
|
const ShikiMagicMoveRenderer = /* #__PURE__ */defineComponent({
|
|
name: 'ShikiMagicMoveRenderer',
|
|
props: {
|
|
animate: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
tokens: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
previous: {
|
|
type: Object,
|
|
required: false
|
|
},
|
|
options: {
|
|
type: Object
|
|
}
|
|
},
|
|
emits: ['end', 'start'],
|
|
setup(props, {
|
|
emit
|
|
}) {
|
|
const container = ref();
|
|
let isMounted = false;
|
|
onMounted(() => {
|
|
// Remove previous content
|
|
container.value.innerHTML = '';
|
|
isMounted = true;
|
|
const renderer = new MagicMoveRenderer(container.value);
|
|
watch(() => props.tokens, async tokens => {
|
|
Object.assign(renderer.options, props.options);
|
|
if (props.animate) {
|
|
if (props.previous) renderer.replace(props.previous);
|
|
await nextTick();
|
|
const process = renderer.render(tokens);
|
|
await nextTick();
|
|
emit('start');
|
|
await process;
|
|
emit('end');
|
|
} else {
|
|
renderer.replace(tokens);
|
|
}
|
|
}, {
|
|
immediate: true
|
|
});
|
|
});
|
|
return () => h('pre', {
|
|
ref: container,
|
|
class: 'shiki-magic-move-container'
|
|
},
|
|
// Render initial tokens for SSR
|
|
isMounted ? undefined : renderList(props.tokens.tokens, token => {
|
|
if (token.content === '\n') return h('br', {
|
|
key: token.key
|
|
});
|
|
return h('span', {
|
|
style: [{
|
|
color: token.color
|
|
}, token.htmlStyle],
|
|
class: ['shiki-magic-move-item', token.htmlClass],
|
|
key: token.key
|
|
}, token.content);
|
|
}));
|
|
}
|
|
});
|
|
|
|
const ShikiMagicMove = /* #__PURE__ */defineComponent({
|
|
name: 'ShikiMagicMove',
|
|
props: {
|
|
highlighter: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
lang: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
theme: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
code: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
options: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
},
|
|
emits: ['start', 'end'],
|
|
setup(props, {
|
|
emit
|
|
}) {
|
|
const machine = createMagicMoveMachine(code => codeToKeyedTokens(props.highlighter, code, {
|
|
lang: props.lang,
|
|
theme: props.theme
|
|
}, props.options.lineNumbers), props.options);
|
|
const result = computed(() => machine.commit(props.code));
|
|
return () => h(ShikiMagicMoveRenderer, {
|
|
tokens: result.value.current,
|
|
options: props.options,
|
|
previous: result.value.previous,
|
|
onStart: () => emit('start'),
|
|
onEnd: () => emit('end')
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Component to render a compiled magic move step,
|
|
* Where the tokens can be generated on build time.
|
|
*/
|
|
const ShikiMagicMovePrecompiled = /* #__PURE__ */defineComponent({
|
|
name: 'ShikiMagicMovePrecompiled',
|
|
props: {
|
|
steps: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
step: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
animate: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
options: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
},
|
|
emits: ['start', 'end'],
|
|
setup(props, {
|
|
emit
|
|
}) {
|
|
const EMPTY = toKeyedTokens('', []);
|
|
let previous = EMPTY;
|
|
const result = computed(() => {
|
|
const res = syncTokenKeys(previous, props.steps[Math.min(props.step, props.steps.length - 1)], props.options);
|
|
previous = res.to;
|
|
return res;
|
|
});
|
|
return () => h(ShikiMagicMoveRenderer, {
|
|
tokens: result.value.to,
|
|
previous: result.value.from,
|
|
options: props.options,
|
|
animate: props.animate,
|
|
onStart: () => emit('start'),
|
|
onEnd: () => emit('end')
|
|
});
|
|
}
|
|
});
|
|
|
|
function install(app) {
|
|
app.component('ShikiMagicMove', ShikiMagicMove);
|
|
app.component('ShikiMagicMovePrecompiled', ShikiMagicMovePrecompiled);
|
|
app.component('ShikiMagicMoveRenderer', ShikiMagicMoveRenderer);
|
|
}
|
|
|
|
export { ShikiMagicMove, ShikiMagicMovePrecompiled, ShikiMagicMoveRenderer, install };
|