diff --git a/src/Repl.vue b/src/Repl.vue index 991ee2ed..6bb06494 100644 --- a/src/Repl.vue +++ b/src/Repl.vue @@ -19,6 +19,7 @@ export interface Props { sfcOptions?: SFCOptions layout?: 'horizontal' | 'vertical' ssr?: boolean + readonly?: boolean previewOptions?: { headHTML?: string bodyHTML?: string @@ -38,6 +39,7 @@ const props = withDefaults(defineProps(), { showTsConfig: true, clearConsole: true, ssr: false, + readonly: false, previewOptions: () => ({ headHTML: '', bodyHTML: '', @@ -79,6 +81,7 @@ provide('tsconfig', toRef(props, 'showTsConfig')) provide('clear-console', toRef(props, 'clearConsole')) provide('preview-options', props.previewOptions) provide('theme', toRef(props, 'theme')) +provide('readonly', toRef(props, 'readonly')) /** * Reload the preview iframe */ @@ -128,9 +131,8 @@ defineExpose({ reload }) margin: 0; overflow: hidden; font-size: 13px; - font-family: - -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, - Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, + Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background-color: var(--bg-soft); } diff --git a/src/editor/EditorContainer.vue b/src/editor/EditorContainer.vue index 917248db..533329a2 100644 --- a/src/editor/EditorContainer.vue +++ b/src/editor/EditorContainer.vue @@ -16,10 +16,12 @@ const props = defineProps<{ }>() const store = inject('store') as Store +const readonly = inject('readonly', ref(false)) const showMessage = ref((getItem(SHOW_ERROR_KEY) ?? 'true') === 'true') store.state.wordWrap = (getItem(TOGGLE_WRAP_KEY) ?? 'false') === 'true' const onChange = debounce((code: string, filename: string) => { + if (readonly.value) return store.state.files[filename].code = code }, 250) @@ -50,6 +52,7 @@ watch( @change="onChange($event, store.state.activeFile.filename)" :value="store.state.activeFile.code" :filename="store.state.activeFile.filename" + :readonly="readonly" /> diff --git a/src/editor/FileExplorer.vue b/src/editor/FileExplorer.vue index c31f6cde..4d209dfc 100644 --- a/src/editor/FileExplorer.vue +++ b/src/editor/FileExplorer.vue @@ -12,6 +12,7 @@ {{ stripSrcPrefix(file) }} ('store')! initMonaco(store) -const lang = computed(() => (props.mode === 'css' ? 'css' : 'javascript')) const extension = computed(() => props.filename.split('.').at(-1)) const replTheme = inject>('theme')! @@ -58,9 +58,6 @@ onMounted(async () => { } const editorInstance = monaco.editor.create(containerRef.value, { - ...(props.readonly - ? { value: props.value, language: lang.value } - : { model: null }), fontSize: 13, theme: replTheme.value === 'light' ? theme.light : theme.dark, readOnly: props.readonly, @@ -100,55 +97,42 @@ onMounted(async () => { } } - watch( - () => props.value, - (value) => { - if (editorInstance.getValue() === value) return - editorInstance.setValue(value || '') - }, - { immediate: true } - ) + watchEffect(() => { + if (editorInstance.getValue() !== props.value) + editorInstance.setValue(props.value || '') - watch(lang, (lang) => - monaco.editor.setModelLanguage(editorInstance.getModel()!, lang) - ) - - if (!props.readonly) { - watch( - () => props.filename, - (_, oldFilename) => { - if (!editorInstance) return - const file = store.state.files[props.filename] - if (!file) return null - const model = getOrCreateModel( - monaco.Uri.parse(`file:///${props.filename}`), - file.language, - file.code - ) - - const oldFile = oldFilename ? store.state.files[oldFilename] : null - if (oldFile) { - oldFile.editorViewState = editorInstance.saveViewState() - } - - editorInstance.setModel(model) - - if (file.editorViewState) { - editorInstance.restoreViewState(file.editorViewState) - editorInstance.focus() - } - }, - { immediate: true } - ) - } + editorInstance.updateOptions({ + readOnly: props.readonly, + wordWrap: store.state.wordWrap ? 'on' : 'off', + theme: replTheme.value === 'light' ? theme.light : theme.dark, + }) + }) watch( - () => store.state.wordWrap, - () => { - editorInstance.updateOptions({ - wordWrap: store.state.wordWrap ? 'on' : 'off', - }) - } + () => props.filename, + (_, oldFilename) => { + if (!editorInstance) return + const file = store.state.files[props.filename] + if (!file) return null + const model = getOrCreateModel( + monaco.Uri.parse(`file:///${props.filename}`), + file.language, + file.code + ) + + const oldFile = oldFilename ? store.state.files[oldFilename] : null + if (oldFile) { + oldFile.editorViewState = editorInstance.saveViewState() + } + + editorInstance.setModel(model) + + if (file.editorViewState) { + editorInstance.restoreViewState(file.editorViewState) + editorInstance.focus() + } + }, + { immediate: true } ) await loadGrammars(monaco, editorInstance) @@ -197,13 +181,6 @@ onMounted(async () => { emit('change', code) } }) - - // update theme - watch(replTheme, (n) => { - editorInstance.updateOptions({ - theme: n === 'light' ? theme.light : theme.dark, - }) - }) }) onBeforeUnmount(() => {