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:
+1
-1
@@ -18,7 +18,7 @@
|
||||
- [x] **自动更新** — electron-updater 集成,启动检查,下载进度,发行说明
|
||||
- [x] **多标签页** — 单连接支持同时打开 Status/Keys/CLI/SlowLog/内存分析/批量删除 多个标签
|
||||
- [x] **多连接并行** — 同时打开多个连接,各自独立标签页
|
||||
- [ ] **Monaco 编辑器** — 替换 StringEditor 的 textarea,支持 JSON 折叠/展开/语法高亮
|
||||
- [x] **Monaco 编辑器** — 替换 StringEditor 的 textarea,支持 JSON 折叠/展开/语法高亮
|
||||
- [ ] **Redis 命令自动补全** — CLI 输入时智能提示(基于 commands.js 命令数据库)
|
||||
- [ ] **CLI SUBSCRIBE/MONITOR** — 实时消息流 + MONITOR 命令流 + 停止按钮
|
||||
- [ ] **CLI MULTI/EXEC** — 事务模式支持
|
||||
|
||||
Generated
+101
@@ -9,9 +9,11 @@
|
||||
"version": "2.0.0",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@guolao/vue-monaco-editor": "^1.6.0",
|
||||
"electron-store": "^11.0.2",
|
||||
"electron-updater": "^6.8.9",
|
||||
"ioredis": "^5.7.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"pinia": "^3.0.4",
|
||||
"ssh2": "^1.17.0"
|
||||
},
|
||||
@@ -1131,6 +1133,52 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@guolao/vue-monaco-editor": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@guolao/vue-monaco-editor/-/vue-monaco-editor-1.6.0.tgz",
|
||||
"integrity": "sha512-w2IiJ6eJGGeuIgCK6EKZOAfhHTTUB5aZwslzwGbZ5e89Hb4avx6++GkLTW8p84Sng/arFMjLPPxSBI56cFudyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@monaco-editor/loader": "^1.6.1",
|
||||
"vue-demi": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.7.2",
|
||||
"monaco-editor": ">=0.43.0",
|
||||
"vue": "^2.6.14 || >=3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@guolao/vue-monaco-editor/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
|
||||
@@ -1344,6 +1392,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/loader": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz",
|
||||
"integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"state-local": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/fs": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
||||
@@ -1974,6 +2031,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/verror": {
|
||||
"version": "1.10.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
||||
@@ -3752,6 +3816,15 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
|
||||
"integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-prop": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz",
|
||||
@@ -5568,6 +5641,18 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
|
||||
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/matcher": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||
@@ -5823,6 +5908,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.55.1",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dompurify": "3.2.7",
|
||||
"marked": "14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -6969,6 +7064,12 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
|
||||
@@ -64,9 +64,11 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@guolao/vue-monaco-editor": "^1.6.0",
|
||||
"electron-store": "^11.0.2",
|
||||
"electron-updater": "^6.8.9",
|
||||
"ioredis": "^5.7.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"pinia": "^3.0.4",
|
||||
"ssh2": "^1.17.0"
|
||||
},
|
||||
|
||||
@@ -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
@@ -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' })
|
||||
|
||||
Reference in New Issue
Block a user