feat: Monaco editor for StringEditor

- Install monaco-editor + @guolao/vue-monaco-editor
- main.ts: loader.config + MonacoEnvironment worker setup
- StringEditor.vue: replace textarea with Monaco editor
- JSON syntax highlighting, folding, word wrap
- Read-only mode when not editing, auto-detect JSON
- Theme follows app theme (vs-dark/vs)
- Font size follows app settings
This commit is contained in:
2026-07-07 20:37:04 +08:00
parent 6a1f94cd48
commit 05a6f6aa17
5 changed files with 156 additions and 23 deletions
+37 -22
View File
@@ -1,17 +1,26 @@
<script setup lang="ts">
// src/renderer/src/components/StringEditor.vue
import { ref, watch } from 'vue'
// src/components/StringEditor.vue
import { ref, watch, computed } from 'vue'
import { VueMonacoEditor } from '@guolao/vue-monaco-editor'
import { useKeyStore } from '@renderer/stores/key'
import { useConnectionStore } from '@renderer/stores/connection'
import { useAppStore } from '@renderer/stores/app'
import { ElMessage } from 'element-plus'
const keyStore = useKeyStore()
const connStore = useConnectionStore()
const appStore = useAppStore()
const editedValue = ref('')
const isJson = ref(false)
const isEditing = ref(false)
const monacoTheme = computed(() => {
if (appStore.theme === 'light') return 'vs'
if (appStore.theme === 'dark') return 'vs-dark'
return 'vs-dark' // system default (will be refined)
})
function formatValue(val: string | null) {
if (val === null) return ''
try {
@@ -35,6 +44,25 @@ watch(
{ immediate: true }
)
const editorOptions = computed(() => ({
readOnly: !isEditing.value,
minimap: { enabled: false },
fontSize: appStore.fontSize,
wordWrap: 'on' as const,
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
lineNumbers: 'on' as const,
folding: true,
renderWhitespace: 'none',
scrollbar: {
vertical: 'auto' as const,
horizontal: 'auto' as const,
verticalScrollbarSize: 6,
horizontalScrollbarSize: 6,
},
}))
async function save() {
if (!connStore.activeId || !keyStore.selectedKey) return
try {
@@ -59,14 +87,12 @@ async function save() {
</template>
</div>
<div class="editor-content">
<el-input
v-model="editedValue"
type="textarea"
:rows="12"
:disabled="!isEditing"
:class="{ json: isJson }"
spellcheck="false"
class="editor-textarea"
<vue-monaco-editor
v-model:value="editedValue"
:theme="monacoTheme"
:language="isJson ? 'json' : 'plaintext'"
:options="editorOptions"
height="100%"
/>
</div>
</div>
@@ -100,17 +126,6 @@ async function save() {
.editor-content {
flex: 1;
padding: 12px;
overflow: auto;
}
.editor-textarea :deep(.el-textarea__inner) {
background: var(--bg-primary);
border-color: var(--border-color);
color: var(--text-primary);
font-family: 'Cascadia Code', 'Fira Code', monospace;
font-size: 13px;
line-height: 1.6;
resize: none;
overflow: hidden;
}
</style>
+15
View File
@@ -4,9 +4,24 @@ import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import { loader } from '@guolao/vue-monaco-editor'
import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import App from './App.vue'
import './styles/global.css'
// Monaco worker setup for Vite
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') return new jsonWorker()
return new editorWorker()
},
}
// Use local monaco-editor
loader.config({ monaco })
const app = createApp(App)
app.use(createPinia())
app.use(ElementPlus, { size: 'small' })