feat(editor): apply word wrap to config editors
This commit is contained in:
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");
|
||||
|
||||
Reference in new issue
Block a user