This commit is contained in:
Baobhan Sith
2025-06-04 12:36:07 +08:00
parent 9f5342d1e4
commit 065e3a0272
2 changed files with 141 additions and 5 deletions
@@ -0,0 +1,124 @@
<template>
<div ref="editorRef" class="codemirror-mobile-editor-container"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, shallowRef } from 'vue';
import { EditorState, Compartment } from '@codemirror/state';
import { EditorView, keymap } from '@codemirror/view';
import { basicSetup } from 'codemirror'; // Use basicSetup from the main 'codemirror' package
const props = defineProps({
modelValue: {
type: String,
default: '',
},
language: {
type: String,
default: 'plaintext', // Default to plaintext if no language is specified
},
});
const emit = defineEmits(['update:modelValue', 'request-save']);
const editorRef = ref<HTMLDivElement | null>(null);
const view = shallowRef<EditorView | null>(null);
const languageCompartment = new Compartment(); // For dynamic language switching
const createEditorState = (doc: string, languageExtension: any) => {
return EditorState.create({
doc,
extensions: [
basicSetup, // Includes many common features like line numbers, history, default keymaps etc.
keymap.of([
{ key: "Mod-s", run: () => { emit('request-save'); return true; } }
]),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
emit('update:modelValue', update.state.doc.toString());
}
}),
languageCompartment.of(languageExtension), // Initial language
],
});
};
const getLanguageExtension = async (lang: string) => {
if (lang === 'javascript') {
const { javascript } = await import('@codemirror/lang-javascript');
return javascript();
}
if (lang === 'css') {
const { css } = await import('@codemirror/lang-css');
return css();
}
if (lang === 'html') {
const { html } = await import('@codemirror/lang-html');
return html();
}
return [];
};
onMounted(async () => {
if (editorRef.value) {
const langExt = await getLanguageExtension(props.language);
const startState = createEditorState(props.modelValue, langExt);
view.value = new EditorView({
state: startState,
parent: editorRef.value,
});
}
});
onBeforeUnmount(() => {
if (view.value) {
view.value.destroy();
view.value = null;
}
});
watch(() => props.modelValue, (newValue) => {
if (view.value && newValue !== view.value.state.doc.toString()) {
view.value.dispatch({
changes: { from: 0, to: view.value.state.doc.length, insert: newValue },
});
}
});
watch(() => props.language, async (newLanguage, oldLanguage) => {
if (view.value && newLanguage !== oldLanguage) {
console.log(`Language changing from ${oldLanguage} to: ${newLanguage}.`);
const langExt = await getLanguageExtension(newLanguage);
view.value.dispatch({
effects: languageCompartment.reconfigure(langExt)
});
}
});
defineExpose({
focus: () => view.value?.focus(),
});
</script>
<style scoped>
.codemirror-mobile-editor-container {
width: 100%;
height: 100%;
min-height: 200px; /* Adjust as needed for mobile */
text-align: left;
overflow: auto; /* Enable both horizontal and vertical scrolling */
}
.codemirror-mobile-editor-container :deep(.cm-gutters) {
background-color: #1E1E1E !important; /* Match typical dark editor background */
color: #858585 !important; /* Ensure line numbers are visible */
border-right: 1px solid var(--border-color, #cccccc) !important; /* Use theme border color */
}
.codemirror-mobile-editor-container :deep(.cm-selectionBackground) {
background-color: #5264ac !important;
}
</style>
@@ -3,6 +3,7 @@ import { computed, ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import MonacoEditor from './MonacoEditor.vue';
import CodeMirrorMobileEditor from './CodeMirrorMobileEditor.vue'; // +++ Import new mobile editor
import FileEditorTabs from './FileEditorTabs.vue';
import { useFileEditorStore, type FileTab } from '../stores/fileEditor.store';
import { useSettingsStore } from '../stores/settings.store';
@@ -575,9 +576,11 @@ onBeforeUnmount(() => {
<div class="editor-content-area">
<div v-if="currentTabIsLoading" class="editor-loading">{{ t('fileManager.loadingFile') }}</div>
<div v-else-if="currentTabLoadingError" class="editor-error">{{ currentTabLoadingError }}</div>
<!-- Desktop Editor -->
<MonacoEditor
v-else-if="activeTab"
:key="activeTab.id"
v-else-if="activeTab && !props.isMobile"
:key="`monaco-${activeTab.id}`"
v-model="activeEditorContent"
:language="currentTabLanguage"
:font-family="currentEditorFontFamily"
@@ -586,9 +589,18 @@ onBeforeUnmount(() => {
:font-size="currentEditorFontSize"
@request-save="handleSaveRequest"
@update:fontSize="handleEditorFontSizeUpdate"
:initialScrollTop="activeTab?.scrollTop ?? 0"
:initialScrollLeft="activeTab?.scrollLeft ?? 0"
@update:scrollPosition="handleEditorScroll"
:initialScrollTop="activeTab?.scrollTop ?? 0"
:initialScrollLeft="activeTab?.scrollLeft ?? 0"
@update:scrollPosition="handleEditorScroll"
/>
<!-- Mobile Editor -->
<CodeMirrorMobileEditor
v-else-if="activeTab && props.isMobile"
:key="`cm-${activeTab.id}`"
v-model="activeEditorContent"
:language="currentTabLanguage"
class="editor-instance"
@request-save="handleSaveRequest"
/>
<!-- 如果容器可见但没有活动标签页 -->
<div v-else class="editor-placeholder">{{ t('fileManager.selectFileToEdit') }}</div>