104 lines
2.6 KiB
Vue
104 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import * as monaco from 'monaco-editor';
|
|
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
|
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
|
import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
|
|
import {fetchScriptDefinitions} from './rules';
|
|
|
|
(self as unknown as { MonacoEnvironment: { getWorker(workerId: string, label: string): Worker } }).MonacoEnvironment = {
|
|
getWorker(_workerId: string, label: string) {
|
|
if (label === 'typescript' || label === 'javascript') {
|
|
return new tsWorker();
|
|
}
|
|
return new editorWorker();
|
|
}
|
|
};
|
|
|
|
let extraLibLoaded = false;
|
|
|
|
const loadScriptDefinitions = async () => {
|
|
if (extraLibLoaded) {
|
|
return;
|
|
}
|
|
try {
|
|
const definitions = await fetchScriptDefinitions();
|
|
monaco.typescript.javascriptDefaults.addExtraLib(definitions, 'ts:rule-runner.d.ts');
|
|
extraLibLoaded = true;
|
|
} catch {
|
|
// type definitions are optional — the editor still works without autocomplete
|
|
}
|
|
};
|
|
|
|
const props = defineProps<{ ledgerRefs?: string[] }>();
|
|
|
|
let ledgersLib: monaco.IDisposable | undefined;
|
|
|
|
const updateLedgerRefs = (refs: readonly string[]) => {
|
|
ledgersLib?.dispose();
|
|
const members = refs
|
|
.filter(ref => ref && ref !== 'system')
|
|
.map(ref => ` readonly ${JSON.stringify(ref)}: Ledger;`)
|
|
.join('\n');
|
|
ledgersLib = monaco.typescript.javascriptDefaults.addExtraLib(
|
|
`declare interface Ledgers {\n${members}\n}\n`,
|
|
'ts:rule-runner.ledgers.d.ts'
|
|
);
|
|
};
|
|
|
|
const model = defineModel<string>({default: ''});
|
|
const container = ref<HTMLElement>();
|
|
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
|
|
|
|
onMounted(async () => {
|
|
await loadScriptDefinitions();
|
|
updateLedgerRefs(props.ledgerRefs ?? []);
|
|
|
|
if (!container.value) {
|
|
return;
|
|
}
|
|
|
|
editor = monaco.editor.create(container.value, {
|
|
value: model.value,
|
|
language: 'javascript',
|
|
theme: 'vs-dark',
|
|
automaticLayout: true,
|
|
minimap: {enabled: false},
|
|
scrollBeyondLastLine: false,
|
|
fontSize: 13,
|
|
tabSize: 2,
|
|
});
|
|
|
|
editor.onDidChangeModelContent(() => {
|
|
const value = editor!.getValue();
|
|
|
|
if (value !== model.value) {
|
|
model.value = value;
|
|
}
|
|
});
|
|
});
|
|
|
|
watch(model, value => {
|
|
if (editor && value !== editor.getValue()) {
|
|
editor.setValue(value ?? '');
|
|
}
|
|
});
|
|
|
|
watch(() => props.ledgerRefs, refs => updateLedgerRefs(refs ?? []), {deep: true});
|
|
|
|
onBeforeUnmount(() => {
|
|
editor?.dispose();
|
|
ledgersLib?.dispose();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="container" class="script-editor"></div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.script-editor {
|
|
width: 100%;
|
|
flex: 1 1 auto;
|
|
min-height: 12rem;
|
|
}
|
|
</style> |