feat(editor): apply word wrap to config editors

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-08-13 01:59:04 +08:00
1 parent f88e39563d
commit 1d2a47fee0
13 files changed
+48 -12

No files matched your search

@@ -4,6 +4,7 @@ import { useI18n } from "vue-i18n";
import { Pane, Splitpanes } from "splitpanes";
import "splitpanes/dist/splitpanes.css";
import { useConnectionStore } from "@/stores/connectionStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { Activity, Check, ChevronDown, ChevronRight, Clock3, Copy, Download, FolderClosed, FolderOpen, KeyRound, Loader2, LockKeyhole, Pencil, Plus, RefreshCw, Search, Square, Trash2 } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -237,6 +238,7 @@ const emit = defineEmits<{
const { t } = useI18n();
const { toast } = useToast();
const connectionStore = useConnectionStore();
const settingsStore = useSettingsStore();
const searchInputRef = ref<HTMLInputElement>();
const prefix = ref("");
const keySuggestionOpen = ref(false);
@@ -2024,7 +2026,8 @@ defineExpose({
</Button>
<pre
data-native-clipboard
class="dbx-editor-font-family m-0 max-h-[40vh] min-h-32 overflow-auto rounded-md border bg-muted/20 whitespace-pre-wrap break-words p-3 pr-12 text-sm"
class="dbx-editor-font-family m-0 max-h-[40vh] min-h-32 overflow-auto rounded-md border bg-muted/20 p-3 pr-12 text-sm"
:class="settingsStore.editorSettings.wordWrap ? 'whitespace-pre-wrap break-words' : 'whitespace-pre'"
><template v-for="(segment, index) in selectedValueHighlightSegments" :key="index"><mark v-if="segment.matched" class="rounded-sm bg-amber-300/80 px-0.5 text-foreground dark:bg-amber-500/40">{{ segment.text }}</mark><span v-else>{{ segment.text }}</span></template></pre>
</div>
<div v-if="selectedValueCanPrettyJson" class="mt-2 flex justify-end">
@@ -13,6 +13,7 @@ import { properties } from "@codemirror/legacy-modes/mode/properties";
import { shell } from "@codemirror/legacy-modes/mode/shell";
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
import type { KvValueFormat } from "@/lib/kv/kvValueFormat";
import { useSettingsStore } from "@/stores/settingsStore";
const props = withDefaults(
defineProps<{
@@ -28,8 +29,10 @@ const props = withDefaults(
const emit = defineEmits<{ "update:modelValue": [value: string] }>();
const host = ref<HTMLElement>();
const settingsStore = useSettingsStore();
const languageCompartment = new Compartment();
const readOnlyCompartment = new Compartment();
const wordWrapCompartment = new Compartment();
let view: EditorView | null = null;
function languageExtension(format: KvValueFormat) {
@@ -48,6 +51,10 @@ function readOnlyExtensions(readOnly: boolean) {
return [EditorState.readOnly.of(readOnly), EditorView.editable.of(!readOnly)];
}
function wordWrapExtension(wordWrap = settingsStore.editorSettings.wordWrap) {
return wordWrap ? EditorView.lineWrapping : [];
}
onMounted(() => {
if (!host.value) return;
view = new EditorView({
@@ -58,7 +65,7 @@ onMounted(() => {
basicSetup,
languageCompartment.of(languageExtension(props.format)),
readOnlyCompartment.of(readOnlyExtensions(props.readOnly)),
EditorView.lineWrapping,
wordWrapCompartment.of(wordWrapExtension()),
EditorView.theme({
"&": { height: "100%", fontSize: "13px", backgroundColor: "transparent" },
".cm-scroller": { fontFamily: "var(--dbx-editor-font-family, ui-monospace)", overflow: "auto" },
@@ -91,6 +98,11 @@ watch(
(readOnly) => view?.dispatch({ effects: readOnlyCompartment.reconfigure(readOnlyExtensions(readOnly)) }),
);
watch(
() => settingsStore.editorSettings.wordWrap,
(wordWrap) => view?.dispatch({ effects: wordWrapCompartment.reconfigure(wordWrapExtension(wordWrap)) }),
);
onBeforeUnmount(() => {
view?.destroy();
view = null;
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const browserSource = readFileSync(new URL("../KvKeyBrowser.vue", import.meta.url), "utf8");
const valueEditorSource = readFileSync(new URL("../KvValueEditor.vue", import.meta.url), "utf8");
describe("KvKeyBrowser edit dialog layout", () => {
it("keeps the value format label and selector together in the editor toolbar", () => {
@@ -28,4 +29,16 @@ describe("KvKeyBrowser edit dialog layout", () => {
expect(browserSource).toContain('<Copy v-else class="h-4 w-4" />');
expect(browserSource).toMatch(/<pre\s+data-native-clipboard/);
});
it("keeps the selected value preview word wrapping aligned with the global editor setting", () => {
expect(browserSource).toContain(":class=\"settingsStore.editorSettings.wordWrap ? 'whitespace-pre-wrap break-words' : 'whitespace-pre'\"");
expect(browserSource).not.toContain("bg-muted/20 whitespace-pre-wrap break-words p-3");
});
it("keeps shared KV value editor word wrapping aligned with the global editor setting", () => {
expect(valueEditorSource).toContain("const wordWrapCompartment = new Compartment()");
expect(valueEditorSource).toContain("wordWrapCompartment.of(wordWrapExtension())");
expect(valueEditorSource).toContain("wordWrapCompartment.reconfigure(wordWrapExtension(wordWrap))");
expect(valueEditorSource).not.toContain("\n EditorView.lineWrapping,\n");
});
});
@@ -197,6 +197,7 @@ const importSource = shallowRef<string | File | null>(null);
const importSourceName = ref("");
const configEditorTheme = new Compartment();
const configEditorFontTheme = new Compartment();
const configEditorWordWrap = new Compartment();
const configEditorLanguage = new Compartment();
const configListRequestGuard = createNacosLatestRequestGuard();
const configDetailRequestGuard = createNacosLatestRequestGuard();
@@ -553,7 +554,7 @@ async function mountConfigEditor() {
configEditorLanguage.of(language),
configEditorTheme.of(theme),
configEditorFontTheme.of(editorFontTheme(EditorView, editorSettings.fontSize, editorSettings.fontFamily, { fixedHeight: true, scrollable: true })),
EditorView.lineWrapping,
configEditorWordWrap.of(editorSettings.wordWrap ? EditorView.lineWrapping : []),
EditorState.readOnly.of(!!props.readOnly),
EditorView.editable.of(!props.readOnly),
EditorView.updateListener.of((update) => {
@@ -2314,7 +2315,7 @@ watch(
if (configEditorView.value !== view) return;
configEditorFontSize.value = clampEditorFontSize(settings.fontSize);
view.dispatch({
effects: [configEditorTheme.reconfigure(theme), configEditorFontTheme.reconfigure(editorFontTheme(EditorView, settings.fontSize, settings.fontFamily, { fixedHeight: true, scrollable: true }))],
effects: [configEditorTheme.reconfigure(theme), configEditorFontTheme.reconfigure(editorFontTheme(EditorView, settings.fontSize, settings.fontFamily, { fixedHeight: true, scrollable: true })), configEditorWordWrap.reconfigure(settings.wordWrap ? EditorView.lineWrapping : [])],
});
},
{ deep: true },
@@ -123,6 +123,13 @@ describe("NacosAdminConsole config workbench layout", () => {
expect(source).toContain("configEditorZoomCommitScheduler.dispose()");
});
it("keeps the configuration editor word wrapping aligned with the global editor setting", () => {
expect(source).toContain("const configEditorWordWrap = new Compartment()");
expect(source).toContain("configEditorWordWrap.of(editorSettings.wordWrap ? EditorView.lineWrapping : [])");
expect(source).toContain("configEditorWordWrap.reconfigure(settings.wordWrap ? EditorView.lineWrapping : [])");
expect(source).not.toContain("\n EditorView.lineWrapping,\n");
});
it("allows optional configuration columns to be hidden without hiding the data ID", () => {
const configListHeader = source.indexOf('class="sticky top-0 z-20 grid border-b bg-muted');
const columnVisibility = source.indexOf("nacos.visibleColumns");
+1 -1
View File
@@ -5780,7 +5780,7 @@ export default {
showInsertValueHintsDescription: "When enabled, the SQL editor shows column-name hints next to each value in INSERT ... VALUES clauses.",
previewStatementRunButton: "Preview statement run button",
wordWrap: "Word wrap",
wordWrapDescription: "Wrap long SQL lines within the editor width",
wordWrapDescription: "Wrap long lines within the editor width",
vimMode: "Vim mode",
vimModeDescription: "Use Vim-style modal editing in the SQL editor",
autoCloseBrackets: "Auto-close brackets",
+1 -1
View File
@@ -5505,7 +5505,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "Al activarlo, el editor SQL muestra el nombre de columna junto a cada valor en cláusulas INSERT ... VALUES.",
previewStatementRunButton: "Botón de ejecución de sentencia en vista previa",
wordWrap: "Ajuste de línea",
wordWrapDescription: "Ajustar las líneas largas de SQL al ancho del editor",
wordWrapDescription: "Ajustar las líneas largas al ancho del editor",
vimMode: "Modo Vim",
vimModeDescription: "Usar edición modal estilo Vim en el editor SQL",
autoCloseBrackets: "Cerrar paréntesis automáticamente",
+1 -1
View File
@@ -5505,7 +5505,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "Se attivo, l'editor SQL mostra il nome della colonna accanto a ogni valore nelle clausole INSERT ... VALUES.",
previewStatementRunButton: "Pulsante di esecuzione istruzione in anteprima",
wordWrap: "A capo automatico",
wordWrapDescription: "Incolonna le righe SQL lunghe entro la larghezza dell'editor",
wordWrapDescription: "Manda a capo le righe lunghe entro la larghezza dell'editor",
vimMode: "Modalita Vim",
vimModeDescription: "Usa la modifica modale in stile Vim nell'editor SQL",
autoCloseBrackets: "Chiusura automatica parentesi",
+1 -1
View File
@@ -5534,7 +5534,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "有効にすると、INSERT ... VALUES 句の各値の横に対応する列名ヒントを表示します。",
previewStatementRunButton: "文の実行ボタンのプレビュー",
wordWrap: "折り返し",
wordWrapDescription: "長いSQL行をエディタ幅内で折り返します",
wordWrapDescription: "長い行をエディタ幅内で折り返します",
vimMode: "Vimモード",
vimModeDescription: "SQLエディタでVim形式のモーダル編集を使用します",
autoCloseBrackets: "自動括弧補完",
+1 -1
View File
@@ -5266,7 +5266,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "활성화하면 SQL 편집기가 INSERT ... VALUES 절의 각 값 옆에 컬럼 이름 힌트를 표시합니다.",
previewStatementRunButton: "구문 실행 버튼 미리보기",
wordWrap: "자동 줄바꿈",
wordWrapDescription: "편집기 너비 내에서 긴 SQL 줄을 줄바꿈합니다",
wordWrapDescription: "편집기 너비 내에서 긴 줄을 줄바꿈합니다",
vimMode: "Vim 모드",
vimModeDescription: "SQL 편집기에서 Vim 스타일의 모달 편집을 사용합니다",
autoCloseBrackets: "괄호 자동 닫기",
+1 -1
View File
@@ -5507,7 +5507,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "Quando ativado, o editor SQL mostra o nome da coluna ao lado de cada valor em cláusulas INSERT ... VALUES.",
previewStatementRunButton: "Botão de execução de instrução na prévia",
wordWrap: "Quebra de linha",
wordWrapDescription: "Quebrar linhas SQL longas dentro da largura do editor",
wordWrapDescription: "Quebrar linhas longas dentro da largura do editor",
vimMode: "Modo Vim",
vimModeDescription: "Usar edição modal no estilo Vim no editor SQL",
autoCloseBrackets: "Fechar parênteses automaticamente",
+1 -1
View File
@@ -5701,7 +5701,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "开启后,在 INSERT ... VALUES 子句的每个值旁显示对应列名提示。",
previewStatementRunButton: "预览语句执行按钮",
wordWrap: "自动换行",
wordWrapDescription: "长 SQL 在编辑器宽度内自动折行显示",
wordWrapDescription: "长内容在编辑器宽度内自动折行显示",
vimMode: "Vim 模式",
vimModeDescription: "在 SQL 编辑器中使用 Vim 风格的模态编辑",
autoCloseBrackets: "自动成对补全",
+1 -1
View File
@@ -4844,7 +4844,7 @@ export default withEnglishFallback({
showInsertValueHintsDescription: "啟用後,在 INSERT ... VALUES 子句的每個值旁顯示對應欄位名稱提示。",
previewStatementRunButton: "預覽語句執行按鈕",
wordWrap: "自動換行",
wordWrapDescription: "長 SQL 在編輯器寬度內自動折行顯示",
wordWrapDescription: "長內容在編輯器寬度內自動折行顯示",
vimMode: "Vim 模式",
vimModeDescription: "在 SQL 編輯器中使用 Vim 風格的模態編輯",
autoCloseBrackets: "自動成對補全",