Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
76d54cc
chore: upgrade codemirror to v6
yanglbme Sep 27, 2025
992884b
feat: auto focus and select in search
zeevenn Sep 28, 2025
a7d878a
fix: custom shortcut key invalid
zeevenn Sep 28, 2025
4b4eba9
refactor: remove codemirror v5 compatible
zeevenn Sep 28, 2025
b27f281
refactor: use v6 api
zeevenn Sep 28, 2025
bfa5269
fix: export MeasureRequest with patching codemirror/view
zeevenn Sep 28, 2025
25de758
fix: css editor scroll
zeevenn Sep 28, 2025
01eb099
feat: upload editor
zeevenn Sep 28, 2025
4fdf34d
style: cm scroll padding
zeevenn Sep 28, 2025
acd48e3
fix: keymap conflict
zeevenn Sep 29, 2025
75d4de2
feat: use custom basic setup
zeevenn Sep 29, 2025
23c2002
chore: merge origin/main
zeevenn Oct 14, 2025
445d863
chore: update package lock
zeevenn Oct 14, 2025
449650d
feat: search match highlight
zeevenn Oct 14, 2025
693c5c3
fix: skip handle alt combinations
zeevenn Oct 14, 2025
aa6a166
fix: build error
zeevenn Oct 14, 2025
20a5ace
refactor: move shortcut key config to @md/shared
zeevenn Oct 14, 2025
b8b2678
feat: lang setup
zeevenn Oct 14, 2025
cfafa11
feat: update setup
zeevenn Oct 14, 2025
a4371ee
refactor: use markdown setup
zeevenn Oct 14, 2025
e1fdd89
refactor: use javascript setup
zeevenn Oct 14, 2025
614d5f6
refactor: use css setup
zeevenn Oct 14, 2025
a26c5bf
chore: clean up
zeevenn Oct 14, 2025
70d5aae
fix: mermaid initial
zeevenn Oct 14, 2025
8c3d79e
chore: update
yanglbme Oct 14, 2025
b7444ec
chore: delete docs
yanglbme Oct 14, 2025
37fcdf5
chore: update docs
yanglbme Oct 14, 2025
af80879
refactor: only register common language
zeevenn Oct 15, 2025
3f1ff85
chore: split codemirror
zeevenn Oct 15, 2025
f6a9492
chore: clean up
zeevenn Oct 15, 2025
9051599
chore: split prettier and cytoscape
zeevenn Oct 15, 2025
a40a34c
chore: use cdn mermaid
zeevenn Oct 15, 2025
c189322
chore: update mermaid cdn
yanglbme Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
id="MathJax-script"
src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/npm/mathjax@3/es5/tex-svg.js"
></script>
<script src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/npm/mermaid@11/dist/mermaid.min.js"></script>
<script type="module" src="/src/main.ts"></script>
<script type="module" src="/src/sidepanel.ts"></script>
</body>
Expand Down
3 changes: 1 addition & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"buffer-from": "^1.1.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"codemirror": "^5.65.19",
"codemirror": "^6.0.1",
"core-js": "^3.45.1",
"cos-js-sdk-v5": "^1.8.7",
"crypto-js": "^4.2.0",
Expand Down Expand Up @@ -83,7 +83,6 @@
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/vite": "^4.1.13",
"@types/buffer-from": "^1.1.3",
"@types/codemirror": "^5.60.15",
"@types/crypto-js": "^4.2.2",
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/assets/less/app.less
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ section {
display: block;
height: 100%;
width: 100%;
padding: 10px;
border: none;
}

Expand Down
45 changes: 39 additions & 6 deletions apps/web/src/assets/less/theme.less
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

.dark {
.container {
// CodeMirror v6 兼容
.cm-editor,
.CodeMirror-wrap {
background-color: @nightCodeMirrorColor;
}
Expand All @@ -34,6 +36,7 @@
}
}

// CodeMirror v5 兼容样式
.CodeMirror {
padding-bottom: 0;
height: 100% !important;
Expand All @@ -51,12 +54,6 @@
overflow-y: scroll !important;
}

.cssEditor-wrapper {
.CodeMirror-scroll {
margin-right: 0;
}
}

.CodeMirror-vscrollbar {
width: 0px;
height: 0px;
Expand All @@ -68,6 +65,42 @@
box-sizing: border-box;
}

// CodeMirror v6 样式
.cm-editor {
height: 100% !important;
font-size: 16px;
font-family: Consolas, 'Courier New', monospace !important;

.cm-scroller {
overflow-x: hidden !important;
overflow-y: auto !important;
}

.cm-content {
padding-bottom: 20px;
}

&.cm-focused {
outline: none;
}
}

.codemirror-container {
height: 100%;
width: 100%;

.cm-scroller {
padding: 10px;
}
}

.cssEditor-wrapper {
.CodeMirror-scroll,
.cm-scroller {
margin-right: 0;
}
}

.cm-em {
font-style: normal;
}
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/ai/SidebarAIToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ const toolBoxVisible = ref(false)
// 检查选中文本的函数
function getSelectedText() {
try {
return editor.value?.getSelection()?.trim() || ``
if (!editor.value)
return ``
const selection = editor.value.state.selection.main
return editor.value.state.doc.sliceString(selection.from, selection.to).trim()
}
catch {
return ``
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/ai/chat-box/AIAssistantPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ async function sendMessage() {
? [{
role: `system`,
content:
`下面是一篇 Markdown 文章全文,请严格以此为主完成后续指令:\n\n${editor.value!.getValue()}`,
`下面是一篇 Markdown 文章全文,请严格以此为主完成后续指令:\n\n${editor.value?.state.doc.toString()}`,
}]
: []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,12 +504,11 @@ function insertImageToCursor(imageUrl: string) {
const markdownImage = `![${altText}](${imageUrl})`

// 获取当前光标位置并插入
const cursor = editor.value.getCursor()
editor.value.replaceRange(markdownImage, cursor)

// 将光标移动到插入内容后面
const newCursor = { line: cursor.line, ch: cursor.ch + markdownImage.length }
editor.value.setCursor(newCursor)
const pos = editor.value.state.selection.main.head
editor.value.dispatch({
changes: { from: pos, insert: markdownImage },
selection: { anchor: pos + markdownImage.length },
})

// 聚焦编辑器
editor.value.focus()
Expand Down
16 changes: 10 additions & 6 deletions apps/web/src/components/ai/tool-box/ToolBoxPopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,16 @@ function stopAI() {

/* -------------------- actions -------------------- */
function replaceText() {
const cm = toRaw(store.editor!)!
const start = cm.getCursor(`start`)
cm.replaceSelection(message.value)
const end = cm.getCursor(`end`)
cm.setSelection(start, end)
cm.focus()
const editorView = toRaw(store.editor!)!
const selection = editorView.state.selection.main
editorView.dispatch(editorView.state.replaceSelection(message.value))

// 选中替换后的文本
const newSelection = editorView.state.selection.main
editorView.dispatch({
selection: { anchor: selection.from, head: newSelection.head },
})
editorView.focus()

currentText.value = message.value
resetState()
Expand Down
9 changes: 6 additions & 3 deletions apps/web/src/components/ai/tool-box/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Editor } from 'codemirror'
import type { EditorView } from '@codemirror/view'
import AIPolishButton from './ToolBoxButton.vue'
import AIPolishPopover from './ToolBoxPopover.vue'

Expand All @@ -10,9 +10,12 @@ function useAIPolish() {
const selectedText = ref(``)

// 获取当前编辑器选中文本的简单函数
function getCurrentSelection(editor: Editor | null): string {
function getCurrentSelection(editor: EditorView | null): string {
try {
return editor?.getSelection()?.trim() || ``
if (!editor)
return ``
const selection = editor.state.selection.main
return editor.state.doc.sliceString(selection.from, selection.to).trim()
}
catch {
return ``
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/editor/CssEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function tabChanged(tabName: string | number) {
<transition enter-active-class="bounceInRight">
<div
v-show="displayStore.isShowCssEditor"
class="cssEditor-wrapper h-full flex flex-col mobile-css-editor"
class="cssEditor-wrapper h-full flex flex-col mobile-css-editor overflow-y-auto"
:class="{
// 移动端样式
'fixed top-0 right-0 w-full h-full z-100 bg-background border-l shadow-lg': store.isMobile,
Expand Down
54 changes: 33 additions & 21 deletions apps/web/src/components/editor/CustomUploadForm.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<script setup lang='ts'>
import CodeMirror from 'codemirror'
import { useStore } from '@/stores'
import { Compartment } from '@codemirror/state'
import { EditorView } from '@codemirror/view'
import { javascriptSetup, theme } from '@md/shared'
import { removeLeft } from '@/utils'

const store = useStore()

const code = useLocalStorage(`formCustomConfig`, removeLeft(`
const {file, util, okCb, errCb} = CUSTOM_ARG
const { file, util, okCb, errCb } = CUSTOM_ARG
const param = new FormData()
param.append('file', file)
util.axios.post('${window.location.origin}/upload', param, {
Expand All @@ -18,38 +17,51 @@ const code = useLocalStorage(`formCustomConfig`, removeLeft(`
})
`).trim())

const formCustomTextarea = useTemplateRef<HTMLTextAreaElement>(`formCustomTextarea`)
const formCustomTextarea = useTemplateRef<HTMLDivElement>(`formCustomTextarea`)

const store = useStore()
const { isDark } = storeToRefs(store)

const editor = ref<EditorView | null>(null)

const editor = ref<CodeMirror.EditorFromTextArea | null>(null)
const themeCompartment = new Compartment()

onMounted(() => {
editor.value = markRaw(CodeMirror.fromTextArea(formCustomTextarea.value!, {
mode: `javascript`,
theme: store.isDark ? `darcula` : `xq-light`,
lineNumbers: true,
}))
const editorView = new EditorView({
parent: formCustomTextarea.value!,
extensions: [javascriptSetup(), themeCompartment.of(theme(isDark.value))],
doc: code.value,
})

// 嵌套使用 nextTick 才能确保生效,具体原因未知
nextTick(() => {
nextTick(() => {
editor.value?.setValue(code.value)
})
editor.value = editorView
})

watch(isDark, (dark) => {
editor.value?.dispatch({
effects: themeCompartment.reconfigure(theme(dark)),
})
})

onUnmounted(() => {
if (editor.value) {
editor.value.destroy()
}
})

function formCustomSave() {
const str = editor.value!.getValue()
const str = editor.value!.state.doc.toString()
localStorage.setItem(`formCustomConfig`, str)
code.value = str
toast.success(`保存成功`)
}
</script>

<template>
<div class="space-y-4 min-w-0">
<div class="h-60 border flex flex-col">
<textarea
<div class="h-60 border border-gray-200 dark:border-gray-700 rounded-lg flex flex-col overflow-y-auto">
<div
ref="formCustomTextarea"
placeholder="Your custom code here."
class="flex-1 custom-codemirror"
/>
</div>
<Button
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/editor/EditorContextMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang='ts'>
import { altSign, ctrlSign, shiftSign } from '@/configs/shortcut-key'
import { altSign, ctrlSign, shiftSign } from '@md/shared/configs'
import { useDisplayStore, useStore } from '@/stores'

const {
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/components/editor/InsertFormDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ function insertTable() {
cols: colNum.value,
data: tableData.value,
})
toRaw(store.editor!).replaceSelection(`\n${table}\n`, `end`)
const editor = toRaw(store.editor!)
const selection = editor.state.selection.main
editor.dispatch({
changes: { from: selection.from, to: selection.to, insert: `\n${table}\n` },
})
resetVal()
toggleShowInsertFormDialog()
}
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/components/editor/InsertMpCardDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ function buildMpHtml(config: Config) {
function submit(formValues: any) {
config.value = formValues as Config
const html = buildMpHtml(formValues as Config)
toRaw(store.editor!).replaceSelection(`\n${html}\n`, `end`)
const editor = toRaw(store.editor!)
const selection = editor.state.selection.main
editor.dispatch({
changes: { from: selection.from, to: selection.to, insert: `\n${html}\n` },
})
toast.success(`公众号名片插入成功`)
toggleShowInsertMpCardDialog(false)
}
Expand Down
3 changes: 0 additions & 3 deletions apps/web/src/components/editor/RightSlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ const { isDark, primaryColor } = storeToRefs(store)

function customStyle() {
displayStore.toggleShowCssEditor()
setTimeout(() => {
store.cssEditor!.refresh()
}, 50)
}

const isOpen = ref(false)
Expand Down
Loading