Replace editor with Codemirror (#786)

This commit is contained in:
Joshua Anderson
2025-12-23 01:00:49 -07:00
committed by GitHub
parent 33fb84b4db
commit 98cd537ba8
4 changed files with 561 additions and 441 deletions

View File

@@ -167,16 +167,18 @@
<!-- YAML editor --> <!-- YAML editor -->
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}"> <div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor <code-mirror
ref="editor" ref="editor"
v-model="stack.composeYAML" v-model="stack.composeYAML"
class="yaml-editor" :extensions="extensions"
:highlight="highlighterYAML" minimal
line-numbers :readonly="!isEditMode" wrap="true"
@input="yamlCodeChange" dark="true"
@focus="editorFocus = true" tab="true"
@blur="editorFocus = false" :disabled="!isEditMode"
></prism-editor> :hasFocus="editorFocus"
@change="yamlCodeChange"
/>
</div> </div>
<div v-if="isEditMode" class="mb-3"> <div v-if="isEditMode" class="mb-3">
{{ yamlError }} {{ yamlError }}
@@ -186,15 +188,18 @@
<div v-if="isEditMode"> <div v-if="isEditMode">
<h4 class="mb-3">.env</h4> <h4 class="mb-3">.env</h4>
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}"> <div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor <code-mirror
ref="editor" ref="editor"
v-model="stack.composeENV" v-model="stack.composeENV"
class="env-editor" :extensions="extensionsEnv"
:highlight="highlighterENV" minimal
line-numbers :readonly="!isEditMode" wrap="true"
@focus="editorFocus = true" dark="true"
@blur="editorFocus = false" tab="true"
></prism-editor> :disabled="!isEditMode"
:hasFocus="editorFocus"
@change="yamlCodeChange"
/>
</div> </div>
</div> </div>
@@ -237,13 +242,13 @@
</template> </template>
<script> <script>
import { highlight, languages } from "prismjs/components/prism-core"; import CodeMirror from "vue-codemirror6";
import { PrismEditor } from "vue-prism-editor"; import { yaml } from "@codemirror/lang-yaml";
import "prismjs/components/prism-yaml"; import { python } from "@codemirror/lang-python";
import { dracula as editorTheme } from "thememirror";
import { lineNumbers, EditorView } from "@codemirror/view";
import { parseDocument, Document } from "yaml"; import { parseDocument, Document } from "yaml";
import "prismjs/themes/prism-tomorrow.css";
import "vue-prism-editor/dist/prismeditor.min.css";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { import {
COMBINED_TERMINAL_COLS, COMBINED_TERMINAL_COLS,
@@ -257,6 +262,7 @@ import {
import { BModal } from "bootstrap-vue-next"; import { BModal } from "bootstrap-vue-next";
import NetworkInput from "../components/NetworkInput.vue"; import NetworkInput from "../components/NetworkInput.vue";
import dotenv from "dotenv"; import dotenv from "dotenv";
import { ref } from "vue";
const template = ` const template = `
services: services:
@@ -271,17 +277,12 @@ const envDefault = "# VARIABLE=value #comment";
let yamlErrorTimeout = null; let yamlErrorTimeout = null;
let serviceStatusTimeout = null; let serviceStatusTimeout = null;
let prismjsSymbolDefinition = {
"symbol": {
pattern: /(?<!\$)\$(\{[^{}]*\}|\w+)/,
}
};
export default { export default {
components: { components: {
NetworkInput, NetworkInput,
FontAwesomeIcon, FontAwesomeIcon,
PrismEditor, CodeMirror,
BModal, BModal,
}, },
beforeRouteUpdate(to, from, next) { beforeRouteUpdate(to, from, next) {
@@ -290,10 +291,35 @@ export default {
beforeRouteLeave(to, from, next) { beforeRouteLeave(to, from, next) {
this.exitConfirm(next); this.exitConfirm(next);
}, },
setup() {
const editorFocus = ref(false);
const focusEffectHandler = (state, focusing) => {
editorFocus.value = focusing;
return null;
};
const extensions = [
editorTheme,
yaml(),
lineNumbers(),
EditorView.focusChangeEffect.of(focusEffectHandler)
];
const extensionsEnv = [
editorTheme,
python(),
lineNumbers(),
EditorView.focusChangeEffect.of(focusEffectHandler)
];
return { extensions,
extensionsEnv,
editorFocus };
},
yamlDoc: null, // For keeping the yaml comments yamlDoc: null, // For keeping the yaml comments
data() { data() {
return { return {
editorFocus: false,
jsonConfig: {}, jsonConfig: {},
envsubstJSONConfig: {}, envsubstJSONConfig: {},
yamlError: "", yamlError: "",
@@ -314,7 +340,6 @@ export default {
}; };
}, },
computed: { computed: {
endpointDisplay() { endpointDisplay() {
return this.$root.endpointDisplayFunction(this.endpoint); return this.$root.endpointDisplayFunction(this.endpoint);
}, },
@@ -664,46 +689,6 @@ export default {
this.isEditMode = false; this.isEditMode = false;
}, },
highlighterYAML(code) {
if (!languages.yaml_with_symbols) {
languages.yaml_with_symbols = languages.insertBefore("yaml", "punctuation", {
"symbol": prismjsSymbolDefinition["symbol"]
});
}
return highlight(code, languages.yaml_with_symbols);
},
highlighterENV(code) {
if (!languages.docker_env) {
languages.docker_env = {
"comment": {
pattern: /(^#| #).*$/m,
greedy: true
},
"keyword": {
pattern: /^\w*(?=[:=])/m,
greedy: true
},
"value": {
pattern: /(?<=[:=]).*?((?= #)|$)/m,
greedy: true,
inside: {
"string": [
{
pattern: /^ *'.*?(?<!\\)'/m,
},
{
pattern: /^ *".*?(?<!\\)"|^.*$/m,
inside: prismjsSymbolDefinition
},
],
},
},
};
}
return highlight(code, languages.docker_env);
},
yamlToJSON(yaml) { yamlToJSON(yaml) {
let doc = parseDocument(yaml); let doc = parseDocument(yaml);
if (doc.errors.length > 0) { if (doc.errors.length > 0) {

View File

@@ -593,9 +593,6 @@ optgroup {
color: $primary; color: $primary;
} }
.prism-editor__textarea {
outline: none !important;
}
h5.settings-subheading::after { h5.settings-subheading::after {
content: ""; content: "";
@@ -676,18 +673,25 @@ code {
color: $dark-font-color3; color: $dark-font-color3;
} }
// Vue Prism Editor bug - workaround
// https://github.com/koca/vue-prism-editor/issues/87 .cm-gutters {
/* background-color: transparent !important;
.prism-editor__textarea {
width: 999999px !important;
} }
.prism-editor__editor { .dark [contenteditable="true"] {
white-space: pre !important; background-color: transparent !important;
}
.cm-editor {
background-color: transparent !important;
}
.cm-activeLine, .cm-activeLineGutter {
background-color: transparent !important;
}
.cm-selectionBackground {
background-color: #74c2ff3d !important;
}
.cm-focused {
outline: none !important;
} }
.prism-editor__container {
overflow-x: scroll !important;
}*/
// Localization // Localization
@import "localization.scss"; @import "localization.scss";

830
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "dockge", "name": "dockge",
"version": "1.5.0", "version": "1.5.1",
"type": "module", "type": "module",
"engines": { "engines": {
"node": ">= 22.14.0" "node": ">= 22.14.0"
@@ -16,22 +16,25 @@
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker", "release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker",
"release-beta": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker-beta", "release-beta": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker-beta",
"build:frontend": "vite build --config ./frontend/vite.config.ts", "build:frontend": "vite build --config ./frontend/vite.config.ts",
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push", "build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t andersmmg/dockge:base -f ./docker/Base.Dockerfile . --push",
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION -t louislam/dockge:beta -t louislam/dockge:nightly --target release -f ./docker/Dockerfile . --push", "build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t andersmmg/dockge:latest -t andersmmg/dockge:1 -t andersmmg/dockge:$VERSION -t andersmmg/dockge:beta -t andersmmg/dockge:nightly --target release -f ./docker/Dockerfile . --push",
"build:docker-beta": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:beta -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push", "build:docker-beta": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t andersmmg/dockge:beta -t andersmmg/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
"build:docker-nightly": "npm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push", "build:docker-nightly": "npm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t andersmmg/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push", "build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t andersmmg/dockge:build-healthcheck . --push",
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest", "start-docker": "docker run --rm -p 5001:5001 --name dockge andersmmg/dockge:latest",
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts", "mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
"reformat-changelog": "tsx ./extra/reformat-changelog.ts", "reformat-changelog": "tsx ./extra/reformat-changelog.ts",
"reset-password": "tsx ./extra/reset-password.ts" "reset-password": "tsx ./extra/reset-password.ts"
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-python": "^6.1.7",
"@codemirror/lang-yaml": "^6.1.2",
"@homebridge/node-pty-prebuilt-multiarch": "0.11.14", "@homebridge/node-pty-prebuilt-multiarch": "0.11.14",
"@inventage/envsubst": "^0.16.0", "@inventage/envsubst": "^0.16.0",
"@louislam/sqlite3": "~15.1.6", "@louislam/sqlite3": "~15.1.6",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"check-password-strength": "~2.0.10", "check-password-strength": "~2.0.10",
"codemirror": "^6.0.1",
"command-exists": "~1.2.9", "command-exists": "~1.2.9",
"compare-versions": "~6.1.1", "compare-versions": "~6.1.1",
"composerize": "~1.7.1", "composerize": "~1.7.1",
@@ -51,10 +54,12 @@
"semver": "^7.7.1", "semver": "^7.7.1",
"socket.io": "~4.8.1", "socket.io": "~4.8.1",
"socket.io-client": "~4.8.1", "socket.io-client": "~4.8.1",
"thememirror": "^2.0.1",
"timezones-list": "~3.0.3", "timezones-list": "~3.0.3",
"ts-command-line-args": "~2.5.1", "ts-command-line-args": "~2.5.1",
"tsx": "~4.19.3", "tsx": "~4.19.3",
"type-fest": "~4.3.3", "type-fest": "~4.3.3",
"vue-codemirror6": "^1.3.13",
"yaml": "~2.3.4" "yaml": "~2.3.4"
}, },
"devDependencies": { "devDependencies": {
@@ -82,7 +87,6 @@
"eslint": "~8.50.0", "eslint": "~8.50.0",
"eslint-plugin-jsdoc": "~46.8.2", "eslint-plugin-jsdoc": "~46.8.2",
"eslint-plugin-vue": "~9.32.0", "eslint-plugin-vue": "~9.32.0",
"prismjs": "~1.30.0",
"sass": "~1.68.0", "sass": "~1.68.0",
"typescript": "~5.2.2", "typescript": "~5.2.2",
"unplugin-vue-components": "~0.25.2", "unplugin-vue-components": "~0.25.2",
@@ -91,7 +95,6 @@
"vue": "~3.5.13", "vue": "~3.5.13",
"vue-eslint-parser": "~9.3.2", "vue-eslint-parser": "~9.3.2",
"vue-i18n": "~10.0.6", "vue-i18n": "~10.0.6",
"vue-prism-editor": "2.0.0-alpha.2",
"vue-qrcode": "~2.2.2", "vue-qrcode": "~2.2.2",
"vue-router": "~4.5.0", "vue-router": "~4.5.0",
"vue-toastification": "2.0.0-rc.5", "vue-toastification": "2.0.0-rc.5",