From be4ead64eb13172824354efeec0394bf4cb22d0e Mon Sep 17 00:00:00 2001 From: abdelouahed oumoussa Date: Tue, 3 Jun 2025 20:46:32 +0100 Subject: [PATCH 1/3] feat: add readonly prop to Repl --- src/Repl.vue | 8 +++++--- src/editor/EditorContainer.vue | 3 +++ src/editor/FileExplorer.vue | 7 +++++-- src/editor/FileSelector.vue | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) 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) }} Date: Thu, 5 Jun 2025 00:00:53 +0100 Subject: [PATCH 2/3] chore: add watcher for readonly to update editor options --- src/monaco/Monaco.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/monaco/Monaco.vue b/src/monaco/Monaco.vue index 58d5d41c..38cd9340 100644 --- a/src/monaco/Monaco.vue +++ b/src/monaco/Monaco.vue @@ -113,6 +113,13 @@ onMounted(async () => { monaco.editor.setModelLanguage(editorInstance.getModel()!, lang) ) + watch( + () => props.readonly, + (readonly) => { + editorInstance.updateOptions({ readOnly: readonly }) + } + ) + if (!props.readonly) { watch( () => props.filename, From dffe188346bd6ceed08e552c1ec3b48049caecee Mon Sep 17 00:00:00 2001 From: abdelouahed oumoussa Date: Thu, 5 Jun 2025 00:11:38 +0100 Subject: [PATCH 3/3] refactor: merge multiple watchers into one watchEffect --- src/monaco/Monaco.vue | 98 +++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 64 deletions(-) diff --git a/src/monaco/Monaco.vue b/src/monaco/Monaco.vue index 38cd9340..993adc8e 100644 --- a/src/monaco/Monaco.vue +++ b/src/monaco/Monaco.vue @@ -9,6 +9,7 @@ import { watch, computed, type Ref, + watchEffect, } from 'vue' import * as monaco from 'monaco-editor-core' import { initMonaco } from './env' @@ -44,7 +45,6 @@ const store = inject('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,62 +97,42 @@ onMounted(async () => { } } - watch( - () => props.value, - (value) => { - if (editorInstance.getValue() === value) return - editorInstance.setValue(value || '') - }, - { immediate: true } - ) - - watch(lang, (lang) => - monaco.editor.setModelLanguage(editorInstance.getModel()!, lang) - ) + watchEffect(() => { + if (editorInstance.getValue() !== props.value) + editorInstance.setValue(props.value || '') - watch( - () => props.readonly, - (readonly) => { - editorInstance.updateOptions({ readOnly: readonly }) - } - ) - - 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) @@ -204,13 +181,6 @@ onMounted(async () => { emit('change', code) } }) - - // update theme - watch(replTheme, (n) => { - editorInstance.updateOptions({ - theme: n === 'light' ? theme.light : theme.dark, - }) - }) }) onBeforeUnmount(() => {