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] **自动更新** — electron-updater 集成,启动检查,下载进度,发行说明
|
||||||
- [x] **多标签页** — 单连接支持同时打开 Status/Keys/CLI/SlowLog/内存分析/批量删除 多个标签
|
- [x] **多标签页** — 单连接支持同时打开 Status/Keys/CLI/SlowLog/内存分析/批量删除 多个标签
|
||||||
- [x] **多连接并行** — 同时打开多个连接,各自独立标签页
|
- [x] **多连接并行** — 同时打开多个连接,各自独立标签页
|
||||||
- [ ] **Monaco 编辑器** — 替换 StringEditor 的 textarea,支持 JSON 折叠/展开/语法高亮
|
- [x] **Monaco 编辑器** — 替换 StringEditor 的 textarea,支持 JSON 折叠/展开/语法高亮
|
||||||
- [ ] **Redis 命令自动补全** — CLI 输入时智能提示(基于 commands.js 命令数据库)
|
- [ ] **Redis 命令自动补全** — CLI 输入时智能提示(基于 commands.js 命令数据库)
|
||||||
- [ ] **CLI SUBSCRIBE/MONITOR** — 实时消息流 + MONITOR 命令流 + 停止按钮
|
- [ ] **CLI SUBSCRIBE/MONITOR** — 实时消息流 + MONITOR 命令流 + 停止按钮
|
||||||
- [ ] **CLI MULTI/EXEC** — 事务模式支持
|
- [ ] **CLI MULTI/EXEC** — 事务模式支持
|
||||||
|
|||||||
Generated
+101
@@ -9,9 +9,11 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@guolao/vue-monaco-editor": "^1.6.0",
|
||||||
"electron-store": "^11.0.2",
|
"electron-store": "^11.0.2",
|
||||||
"electron-updater": "^6.8.9",
|
"electron-updater": "^6.8.9",
|
||||||
"ioredis": "^5.7.0",
|
"ioredis": "^5.7.0",
|
||||||
|
"monaco-editor": "^0.55.1",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"ssh2": "^1.17.0"
|
"ssh2": "^1.17.0"
|
||||||
},
|
},
|
||||||
@@ -1131,6 +1133,52 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@ioredis/commands": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
|
||||||
@@ -1344,6 +1392,15 @@
|
|||||||
"node": ">=10"
|
"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": {
|
"node_modules/@npmcli/fs": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
||||||
@@ -1974,6 +2031,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@types/verror": {
|
||||||
"version": "1.10.11",
|
"version": "1.10.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
||||||
@@ -3752,6 +3816,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"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": {
|
"node_modules/dot-prop": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz",
|
||||||
@@ -5568,6 +5641,18 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/matcher": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||||
@@ -5823,6 +5908,16 @@
|
|||||||
"node": ">=10"
|
"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": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -6969,6 +7064,12 @@
|
|||||||
"node": ">= 6"
|
"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": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
|||||||
@@ -64,9 +64,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@guolao/vue-monaco-editor": "^1.6.0",
|
||||||
"electron-store": "^11.0.2",
|
"electron-store": "^11.0.2",
|
||||||
"electron-updater": "^6.8.9",
|
"electron-updater": "^6.8.9",
|
||||||
"ioredis": "^5.7.0",
|
"ioredis": "^5.7.0",
|
||||||
|
"monaco-editor": "^0.55.1",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"ssh2": "^1.17.0"
|
"ssh2": "^1.17.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// src/renderer/src/components/StringEditor.vue
|
// src/components/StringEditor.vue
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, computed } from 'vue'
|
||||||
|
import { VueMonacoEditor } from '@guolao/vue-monaco-editor'
|
||||||
import { useKeyStore } from '@renderer/stores/key'
|
import { useKeyStore } from '@renderer/stores/key'
|
||||||
import { useConnectionStore } from '@renderer/stores/connection'
|
import { useConnectionStore } from '@renderer/stores/connection'
|
||||||
|
import { useAppStore } from '@renderer/stores/app'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const keyStore = useKeyStore()
|
const keyStore = useKeyStore()
|
||||||
const connStore = useConnectionStore()
|
const connStore = useConnectionStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const editedValue = ref('')
|
const editedValue = ref('')
|
||||||
const isJson = ref(false)
|
const isJson = ref(false)
|
||||||
const isEditing = 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) {
|
function formatValue(val: string | null) {
|
||||||
if (val === null) return ''
|
if (val === null) return ''
|
||||||
try {
|
try {
|
||||||
@@ -35,6 +44,25 @@ watch(
|
|||||||
{ immediate: true }
|
{ 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() {
|
async function save() {
|
||||||
if (!connStore.activeId || !keyStore.selectedKey) return
|
if (!connStore.activeId || !keyStore.selectedKey) return
|
||||||
try {
|
try {
|
||||||
@@ -59,14 +87,12 @@ async function save() {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-content">
|
<div class="editor-content">
|
||||||
<el-input
|
<vue-monaco-editor
|
||||||
v-model="editedValue"
|
v-model:value="editedValue"
|
||||||
type="textarea"
|
:theme="monacoTheme"
|
||||||
:rows="12"
|
:language="isJson ? 'json' : 'plaintext'"
|
||||||
:disabled="!isEditing"
|
:options="editorOptions"
|
||||||
:class="{ json: isJson }"
|
height="100%"
|
||||||
spellcheck="false"
|
|
||||||
class="editor-textarea"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,17 +126,6 @@ async function save() {
|
|||||||
|
|
||||||
.editor-content {
|
.editor-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 12px;
|
overflow: hidden;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+15
@@ -4,9 +4,24 @@ import { createPinia } from 'pinia'
|
|||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
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 App from './App.vue'
|
||||||
import './styles/global.css'
|
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)
|
const app = createApp(App)
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.use(ElementPlus, { size: 'small' })
|
app.use(ElementPlus, { size: 'small' })
|
||||||
|
|||||||
Reference in New Issue
Block a user