Skip to content

Commit 3f998bc

Browse files
committed
fix: update editor
1 parent f932fdb commit 3f998bc

16 files changed

+830
-318
lines changed

Diff for: .editorconfig

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,4 @@ end_of_line = lf
88
charset = utf-8
99
trim_trailing_whitespace = true
1010
insert_final_newline = true
11-
12-
[*.md]
13-
trim_trailing_whitespace = false
11+
quote_type = single

Diff for: assets/css/default.css

+13-8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ button {
3434
&:hover {
3535
@apply bg-gray-200 dark:bg-gray-700;
3636
}
37+
&:disabled {
38+
@apply cursor-not-allowed opacity-60;
39+
}
3740
}
3841

3942
.menu-item {
@@ -52,11 +55,10 @@ button {
5255
}
5356

5457
.t-code {
55-
@apply t-border;
56-
> .CodeMirror {
57-
height: 400px;
58-
font-family: Monaco, Consolas, monospace;
59-
}
58+
@apply t-border h-[400px];
59+
}
60+
.cm-editor {
61+
@apply h-full;
6062
}
6163

6264
.t-url {
@@ -79,13 +81,13 @@ button {
7981

8082
&.anim-toast-enter-active,
8183
&.anim-toast-leave-active {
82-
transition: all .3s;
84+
transition: all 0.3s;
8385
}
8486
&.anim-toast-enter-from {
8587
transform: translateX(150%);
8688
}
8789
&.anim-toast-leave-to {
88-
transform: translate(-7vw,20px);
90+
transform: translate(-7vw, 20px);
8991
opacity: 0;
9092
}
9193
}
@@ -113,8 +115,11 @@ button {
113115
}
114116
}
115117

118+
.modal {
119+
@apply fixed inset-0 bg-gray-900/50 dark:bg-gray-900/80;
120+
}
116121
.modal-content {
117-
@apply mx-auto p-4 rounded shadow bg-white w-1/2;
122+
@apply mx-auto p-4 rounded shadow bg-white dark:bg-black w-1/2;
118123
}
119124

120125
code {

Diff for: components/code-editor.vue

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<template>
2+
<div ref="refCode" :class="{ 'child-error': hasError }"></div>
3+
</template>
4+
5+
<script lang="ts" setup>
6+
import { computed, onMounted, ref, watch } from "vue";
7+
import { EditorView, basicSetup } from "codemirror";
8+
import { linter, lintGutter } from "@codemirror/lint";
9+
import { json, jsonParseLinter } from "@codemirror/lang-json";
10+
import { html } from "@codemirror/lang-html";
11+
import { yaml } from "@codemirror/lang-yaml";
12+
import { EditorState, Compartment, type Extension } from "@codemirror/state";
13+
import { indentWithTab } from "@codemirror/commands";
14+
import { keymap } from "@codemirror/view";
15+
import { oneDark } from "@codemirror/theme-one-dark";
16+
17+
const model = defineModel<string>();
18+
const props = defineProps<{
19+
lang?: string;
20+
readonly?: boolean;
21+
contentType?: string;
22+
}>();
23+
const emit = defineEmits<{
24+
cursorMove: [line: number];
25+
focus: [];
26+
blur: [];
27+
}>();
28+
defineExpose({ replaceLine });
29+
30+
const langExtMap: Record<string, () => Extension[]> = {
31+
json: () => [json(), linter(jsonParseLinter()), lintGutter()],
32+
html: () => [html()],
33+
yaml: () => [yaml()],
34+
};
35+
36+
const refCode = ref<HTMLElement>();
37+
38+
let view: EditorView | undefined;
39+
let lastModel = "";
40+
41+
const langExtComp = new Compartment();
42+
const themeComp = new Compartment();
43+
44+
const hasError = ref(false);
45+
const langExt = computed(() => langExtMap[props.lang || ""]?.() || []);
46+
47+
const result = window.matchMedia("(prefers-color-scheme: dark");
48+
const isDark = ref(result.matches);
49+
result.addEventListener("change", (e) => {
50+
isDark.value = e.matches;
51+
});
52+
53+
watch(langExt, (ext) => {
54+
view?.dispatch({
55+
effects: langExtComp.reconfigure(ext),
56+
});
57+
});
58+
59+
watch(isDark, (dark) => {
60+
view?.dispatch({
61+
effects: themeComp.reconfigure(dark ? oneDark : []),
62+
});
63+
});
64+
65+
watch(model, (value) => {
66+
if (!view || value === lastModel) return;
67+
view.dispatch({
68+
changes: {
69+
from: 0,
70+
to: view.state.doc.length,
71+
insert: value,
72+
},
73+
});
74+
});
75+
76+
function replaceLine(lineNo: number, content: string) {
77+
if (!view) return;
78+
const line = view.state.doc.line(lineNo);
79+
view.dispatch({
80+
changes: {
81+
from: line.from,
82+
to: line.to,
83+
insert: content,
84+
},
85+
scrollIntoView: true,
86+
});
87+
}
88+
89+
onMounted(() => {
90+
view = new EditorView({
91+
doc: model.value || "",
92+
extensions: [
93+
basicSetup,
94+
keymap.of([indentWithTab]),
95+
EditorState.readOnly.of(props.readonly),
96+
langExtComp.of(langExt.value),
97+
themeComp.of(isDark.value ? oneDark : []),
98+
EditorView.updateListener.of((update) => {
99+
if (update.focusChanged) {
100+
if (update.view.hasFocus) emit("focus");
101+
else emit("blur");
102+
}
103+
if (update.selectionSet) {
104+
const lineNo = update.view.state.doc.lineAt(
105+
update.view.state.selection.main.head
106+
).number;
107+
emit("cursorMove", lineNo);
108+
}
109+
if (update.docChanged) {
110+
lastModel = update.view.state.doc.toString();
111+
model.value = lastModel;
112+
}
113+
}),
114+
],
115+
parent: refCode.value,
116+
});
117+
});
118+
</script>

0 commit comments

Comments
 (0)