@jvs-milkdown/crepe 1.2.17 → 1.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/builder.js +4 -0
- package/lib/cjs/builder.js.map +1 -1
- package/lib/cjs/feature/block-edit/index.js +4 -0
- package/lib/cjs/feature/block-edit/index.js.map +1 -1
- package/lib/cjs/feature/code-mirror/index.js +4 -0
- package/lib/cjs/feature/code-mirror/index.js.map +1 -1
- package/lib/cjs/feature/cursor/index.js +4 -0
- package/lib/cjs/feature/cursor/index.js.map +1 -1
- package/lib/cjs/feature/image-block/index.js +4 -0
- package/lib/cjs/feature/image-block/index.js.map +1 -1
- package/lib/cjs/feature/inline-diff/index.js +4 -0
- package/lib/cjs/feature/inline-diff/index.js.map +1 -1
- package/lib/cjs/feature/latex/index.js +4 -0
- package/lib/cjs/feature/latex/index.js.map +1 -1
- package/lib/cjs/feature/link-tooltip/index.js +4 -0
- package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
- package/lib/cjs/feature/list-item/index.js +4 -0
- package/lib/cjs/feature/list-item/index.js.map +1 -1
- package/lib/cjs/feature/placeholder/index.js +4 -0
- package/lib/cjs/feature/placeholder/index.js.map +1 -1
- package/lib/cjs/feature/table/index.js +4 -0
- package/lib/cjs/feature/table/index.js.map +1 -1
- package/lib/cjs/feature/toolbar/index.js +56 -22
- package/lib/cjs/feature/toolbar/index.js.map +1 -1
- package/lib/cjs/index.js +533 -151
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/builder.js +4 -0
- package/lib/esm/builder.js.map +1 -1
- package/lib/esm/feature/block-edit/index.js +4 -0
- package/lib/esm/feature/block-edit/index.js.map +1 -1
- package/lib/esm/feature/code-mirror/index.js +4 -0
- package/lib/esm/feature/code-mirror/index.js.map +1 -1
- package/lib/esm/feature/cursor/index.js +4 -0
- package/lib/esm/feature/cursor/index.js.map +1 -1
- package/lib/esm/feature/image-block/index.js +4 -0
- package/lib/esm/feature/image-block/index.js.map +1 -1
- package/lib/esm/feature/inline-diff/index.js +4 -0
- package/lib/esm/feature/inline-diff/index.js.map +1 -1
- package/lib/esm/feature/latex/index.js +4 -0
- package/lib/esm/feature/latex/index.js.map +1 -1
- package/lib/esm/feature/link-tooltip/index.js +4 -0
- package/lib/esm/feature/link-tooltip/index.js.map +1 -1
- package/lib/esm/feature/list-item/index.js +4 -0
- package/lib/esm/feature/list-item/index.js.map +1 -1
- package/lib/esm/feature/placeholder/index.js +4 -0
- package/lib/esm/feature/placeholder/index.js.map +1 -1
- package/lib/esm/feature/table/index.js +4 -0
- package/lib/esm/feature/table/index.js.map +1 -1
- package/lib/esm/feature/toolbar/index.js +56 -22
- package/lib/esm/feature/toolbar/index.js.map +1 -1
- package/lib/esm/index.js +700 -318
- package/lib/esm/index.js.map +1 -1
- package/lib/theme/common/toolbar.css +9 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/crepe.d.ts +2 -1
- package/lib/types/core/crepe.d.ts.map +1 -1
- package/lib/types/core/locale.d.ts +4 -0
- package/lib/types/core/locale.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/component.d.ts +2 -0
- package/lib/types/feature/fixed-toolbar/component.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/config.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/index.d.ts +16 -0
- package/lib/types/feature/fixed-toolbar/index.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/menu-bar.d.ts.map +1 -1
- package/lib/types/feature/toolbar/component.d.ts.map +1 -1
- package/lib/types/icons/export.d.ts +2 -0
- package/lib/types/icons/export.d.ts.map +1 -0
- package/lib/types/icons/import.d.ts +2 -0
- package/lib/types/icons/import.d.ts.map +1 -0
- package/lib/types/icons/index.d.ts +4 -0
- package/lib/types/icons/index.d.ts.map +1 -1
- package/lib/types/icons/redo.d.ts +2 -0
- package/lib/types/icons/redo.d.ts.map +1 -0
- package/lib/types/icons/undo.d.ts +2 -0
- package/lib/types/icons/undo.d.ts.map +1 -0
- package/lib/types/utils/fixed-toolbar-popup-state.d.ts +1 -0
- package/lib/types/utils/fixed-toolbar-popup-state.d.ts.map +1 -1
- package/package.json +116 -62
- package/src/core/crepe.ts +122 -7
- package/src/core/locale.ts +4 -0
- package/src/feature/fixed-toolbar/component.tsx +154 -53
- package/src/feature/fixed-toolbar/config.ts +70 -2
- package/src/feature/fixed-toolbar/index.ts +85 -0
- package/src/feature/fixed-toolbar/menu-bar.tsx +17 -3
- package/src/feature/toolbar/component.tsx +61 -23
- package/src/icons/export.ts +5 -0
- package/src/icons/import.ts +6 -0
- package/src/icons/index.ts +4 -0
- package/src/icons/redo.ts +5 -0
- package/src/icons/undo.ts +5 -0
- package/src/theme/common/toolbar.css +19 -0
- package/src/utils/fixed-toolbar-popup-state.ts +5 -0
- package/LICENSE +0 -21
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { Ctx } from '@jvs-milkdown/kit/ctx'
|
|
2
2
|
import type { Selection } from '@jvs-milkdown/kit/prose/state'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { commandsCtx } from '@jvs-milkdown/kit/core'
|
|
5
5
|
// @ts-ignore
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
defineComponent,
|
|
8
|
+
type ShallowRef,
|
|
9
|
+
type Ref,
|
|
10
|
+
ref,
|
|
11
|
+
onMounted,
|
|
12
|
+
onUnmounted,
|
|
13
|
+
} from 'vue'
|
|
7
14
|
|
|
8
15
|
import type { FixedToolbarFeatureConfig } from '.'
|
|
9
16
|
|
|
10
|
-
import {
|
|
11
|
-
import { keyboardIcon } from '../../icons'
|
|
17
|
+
import { undoIcon, redoIcon } from '../../icons'
|
|
12
18
|
import { Toolbar } from '../toolbar/component'
|
|
13
19
|
import { MenuBar } from './menu-bar'
|
|
14
20
|
import { ShortcutHelpModal } from './shortcut-help-modal'
|
|
@@ -19,6 +25,8 @@ type FixedToolbarProps = {
|
|
|
19
25
|
show: Ref<boolean>
|
|
20
26
|
selection: ShallowRef<Selection>
|
|
21
27
|
config?: FixedToolbarFeatureConfig
|
|
28
|
+
canUndo: Ref<boolean>
|
|
29
|
+
canRedo: Ref<boolean>
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
export const FixedToolbarComponent = defineComponent<FixedToolbarProps>({
|
|
@@ -28,62 +36,155 @@ export const FixedToolbarComponent = defineComponent<FixedToolbarProps>({
|
|
|
28
36
|
show: { type: Object, required: true },
|
|
29
37
|
selection: { type: Object, required: true },
|
|
30
38
|
config: { type: Object, required: false },
|
|
39
|
+
canUndo: { type: Object, required: true },
|
|
40
|
+
canRedo: { type: Object, required: true },
|
|
31
41
|
},
|
|
32
42
|
setup(props: any) {
|
|
33
43
|
const showShortcuts = ref(false)
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
onMounted(() => {
|
|
46
|
+
const handleShowShortcuts = () => {
|
|
47
|
+
showShortcuts.value = true
|
|
48
|
+
}
|
|
49
|
+
window.addEventListener('milkdown-show-shortcuts', handleShowShortcuts)
|
|
50
|
+
onUnmounted(() => {
|
|
51
|
+
window.removeEventListener(
|
|
52
|
+
'milkdown-show-shortcuts',
|
|
53
|
+
handleShowShortcuts
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
// Access selection.value to register reactivity dependency for Vue on editor transactions
|
|
60
|
+
const _selection = props.selection.value
|
|
61
|
+
void _selection
|
|
62
|
+
|
|
63
|
+
const canUndo =
|
|
64
|
+
(typeof props.canUndo === 'boolean'
|
|
65
|
+
? props.canUndo
|
|
66
|
+
: props.canUndo?.value) || false
|
|
67
|
+
const canRedo =
|
|
68
|
+
(typeof props.canRedo === 'boolean'
|
|
69
|
+
? props.canRedo
|
|
70
|
+
: props.canRedo?.value) || false
|
|
71
|
+
|
|
72
|
+
return (
|
|
63
73
|
<div
|
|
64
74
|
style={{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/>
|
|
73
|
-
<button
|
|
74
|
-
type="button"
|
|
75
|
-
class="toolbar-shortcut-btn"
|
|
76
|
-
title={i18n(props.ctx, 'shortcuts.title')}
|
|
77
|
-
onClick={() => {
|
|
78
|
-
showShortcuts.value = true
|
|
75
|
+
display: 'flex',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
justifyContent: 'center',
|
|
78
|
+
width: '100%',
|
|
79
|
+
gap: '0px',
|
|
80
|
+
minWidth: '0',
|
|
81
|
+
overflow: 'hidden',
|
|
79
82
|
}}
|
|
80
83
|
>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
{props.config?.showMenuBar !== false && [
|
|
85
|
+
<MenuBar ctx={props.ctx} config={props.config} />,
|
|
86
|
+
<div
|
|
87
|
+
style={{
|
|
88
|
+
width: '1px',
|
|
89
|
+
minWidth: '1px',
|
|
90
|
+
height: '20px',
|
|
91
|
+
backgroundColor:
|
|
92
|
+
'var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%))',
|
|
93
|
+
}}
|
|
94
|
+
/>,
|
|
95
|
+
]}
|
|
96
|
+
{props.config?.showHistory !== false && [
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
class={['toolbar-item', !canUndo && 'disabled']}
|
|
100
|
+
disabled={!canUndo}
|
|
101
|
+
title="撤销"
|
|
102
|
+
onPointerdown={(e: PointerEvent) => {
|
|
103
|
+
e.preventDefault()
|
|
104
|
+
e.stopPropagation()
|
|
105
|
+
}}
|
|
106
|
+
onClick={(e: MouseEvent) => {
|
|
107
|
+
e.preventDefault()
|
|
108
|
+
e.stopPropagation()
|
|
109
|
+
if (canUndo) {
|
|
110
|
+
props.ctx.get(commandsCtx).call('Undo')
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
style={{
|
|
114
|
+
display: 'flex',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
justifyContent: 'center',
|
|
117
|
+
border: 'none',
|
|
118
|
+
background: 'transparent',
|
|
119
|
+
cursor: canUndo ? 'pointer' : 'default',
|
|
120
|
+
padding: '6px',
|
|
121
|
+
borderRadius: '4px',
|
|
122
|
+
color: canUndo ? '#363b4c' : '#c0c4cc',
|
|
123
|
+
'--toolbar-icon-color': canUndo ? '#363b4c' : '#c0c4cc',
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<span
|
|
127
|
+
style={{ display: 'inline-flex', alignItems: 'center' }}
|
|
128
|
+
innerHTML={undoIcon}
|
|
129
|
+
/>
|
|
130
|
+
</button>,
|
|
131
|
+
<button
|
|
132
|
+
type="button"
|
|
133
|
+
class={['toolbar-item', !canRedo && 'disabled']}
|
|
134
|
+
disabled={!canRedo}
|
|
135
|
+
title="恢复"
|
|
136
|
+
onPointerdown={(e: PointerEvent) => {
|
|
137
|
+
e.preventDefault()
|
|
138
|
+
e.stopPropagation()
|
|
139
|
+
}}
|
|
140
|
+
onClick={(e: MouseEvent) => {
|
|
141
|
+
e.preventDefault()
|
|
142
|
+
e.stopPropagation()
|
|
143
|
+
if (canRedo) {
|
|
144
|
+
props.ctx.get(commandsCtx).call('Redo')
|
|
145
|
+
}
|
|
146
|
+
}}
|
|
147
|
+
style={{
|
|
148
|
+
display: 'flex',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
justifyContent: 'center',
|
|
151
|
+
border: 'none',
|
|
152
|
+
background: 'transparent',
|
|
153
|
+
cursor: canRedo ? 'pointer' : 'default',
|
|
154
|
+
padding: '6px',
|
|
155
|
+
borderRadius: '4px',
|
|
156
|
+
color: canRedo ? '#363b4c' : '#c0c4cc',
|
|
157
|
+
'--toolbar-icon-color': canRedo ? '#363b4c' : '#c0c4cc',
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
<span
|
|
161
|
+
style={{ display: 'inline-flex', alignItems: 'center' }}
|
|
162
|
+
innerHTML={redoIcon}
|
|
163
|
+
/>
|
|
164
|
+
</button>,
|
|
165
|
+
<div
|
|
166
|
+
style={{
|
|
167
|
+
width: '1px',
|
|
168
|
+
minWidth: '1px',
|
|
169
|
+
height: '20px',
|
|
170
|
+
backgroundColor:
|
|
171
|
+
'var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%))',
|
|
172
|
+
}}
|
|
173
|
+
/>,
|
|
174
|
+
]}
|
|
175
|
+
<Toolbar
|
|
176
|
+
ctx={props.ctx}
|
|
177
|
+
hide={props.hide}
|
|
178
|
+
show={props.show}
|
|
179
|
+
selection={props.selection}
|
|
180
|
+
config={props.config as any}
|
|
181
|
+
isFixed={true}
|
|
182
|
+
/>
|
|
183
|
+
{showShortcuts.value && (
|
|
184
|
+
<ShortcutHelpModal ctx={props.ctx} visible={showShortcuts} />
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
88
189
|
},
|
|
89
190
|
})
|
|
@@ -29,6 +29,8 @@ import { createTable } from '@jvs-milkdown/kit/preset/gfm'
|
|
|
29
29
|
import { findNodeInSelection } from '@jvs-milkdown/kit/prose'
|
|
30
30
|
import { lift } from '@jvs-milkdown/kit/prose/commands'
|
|
31
31
|
import { wrapInList } from '@jvs-milkdown/kit/prose/schema-list'
|
|
32
|
+
import { getMarkdown } from '@jvs-milkdown/kit/utils'
|
|
33
|
+
import { replaceAll } from '@jvs-milkdown/utils'
|
|
32
34
|
|
|
33
35
|
import type { GroupBuilder } from '../../utils'
|
|
34
36
|
import type { ToolbarItem } from '../toolbar/config'
|
|
@@ -50,6 +52,9 @@ import {
|
|
|
50
52
|
highLineCodeIcon,
|
|
51
53
|
linCodeIcon,
|
|
52
54
|
linkIcon,
|
|
55
|
+
exportIcon,
|
|
56
|
+
importIcon,
|
|
57
|
+
keyboardIcon,
|
|
53
58
|
} from '../../icons'
|
|
54
59
|
import { attachmentSchema } from '../attachment/schema'
|
|
55
60
|
import { CrepeFeature } from '../index'
|
|
@@ -472,7 +477,6 @@ export function buildDefaultFixedToolbar(
|
|
|
472
477
|
},
|
|
473
478
|
})
|
|
474
479
|
|
|
475
|
-
|
|
476
480
|
if (isAttachmentEnabled) {
|
|
477
481
|
blockGroup.addItem('attachment', {
|
|
478
482
|
label: ctx ? i18n(ctx, 'menu.item.attachment') : 'Video or File',
|
|
@@ -532,7 +536,6 @@ export function buildDefaultFixedToolbar(
|
|
|
532
536
|
},
|
|
533
537
|
})
|
|
534
538
|
|
|
535
|
-
|
|
536
539
|
if (isLatexEnabled) {
|
|
537
540
|
blockGroup.addItem('math-block', {
|
|
538
541
|
label: ctx ? i18n(ctx, 'menu.item.math') : 'Math Block',
|
|
@@ -552,5 +555,70 @@ export function buildDefaultFixedToolbar(
|
|
|
552
555
|
})
|
|
553
556
|
}
|
|
554
557
|
|
|
558
|
+
const documentGroup = builder.addGroup('document', 'Document')
|
|
559
|
+
|
|
560
|
+
if (_config?.showExport !== false) {
|
|
561
|
+
documentGroup.addItem('export', {
|
|
562
|
+
label: ctx ? i18n(ctx, 'customMenu.export' as any) || '导出' : '导出',
|
|
563
|
+
icon: exportIcon,
|
|
564
|
+
active: () => false,
|
|
565
|
+
onRun: (ctx) => {
|
|
566
|
+
const markdown = getMarkdown()(ctx)
|
|
567
|
+
if (_config?.onExport) {
|
|
568
|
+
_config.onExport(markdown, ctx)
|
|
569
|
+
} else {
|
|
570
|
+
const blob = new Blob([markdown], {
|
|
571
|
+
type: 'text/markdown;charset=utf-8;',
|
|
572
|
+
})
|
|
573
|
+
const url = URL.createObjectURL(blob)
|
|
574
|
+
const link = document.createElement('a')
|
|
575
|
+
link.href = url
|
|
576
|
+
link.download = 'document.md'
|
|
577
|
+
link.click()
|
|
578
|
+
URL.revokeObjectURL(url)
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
})
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (_config?.showImport !== false) {
|
|
585
|
+
documentGroup.addItem('import', {
|
|
586
|
+
label: ctx ? i18n(ctx, 'customMenu.import' as any) || '导入' : '导入',
|
|
587
|
+
icon: importIcon,
|
|
588
|
+
active: () => false,
|
|
589
|
+
onRun: (ctx) => {
|
|
590
|
+
if (_config?.onImport) {
|
|
591
|
+
_config.onImport((markdown) => replaceAll(markdown)(ctx), ctx)
|
|
592
|
+
} else {
|
|
593
|
+
const input = document.createElement('input')
|
|
594
|
+
input.type = 'file'
|
|
595
|
+
input.accept = '.md'
|
|
596
|
+
input.onchange = (e) => {
|
|
597
|
+
const file = (e.target as HTMLInputElement).files?.[0]
|
|
598
|
+
if (!file) return
|
|
599
|
+
file
|
|
600
|
+
.text()
|
|
601
|
+
.then((text) => {
|
|
602
|
+
replaceAll(text)(ctx)
|
|
603
|
+
})
|
|
604
|
+
.catch((err) => {
|
|
605
|
+
console.error('Failed to read file:', err)
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
input.click()
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
})
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
documentGroup.addItem('shortcuts', {
|
|
615
|
+
label: ctx ? i18n(ctx, 'shortcuts.title') : 'Shortcuts',
|
|
616
|
+
icon: keyboardIcon,
|
|
617
|
+
active: () => false,
|
|
618
|
+
onRun: () => {
|
|
619
|
+
window.dispatchEvent(new CustomEvent('milkdown-show-shortcuts'))
|
|
620
|
+
},
|
|
621
|
+
})
|
|
622
|
+
|
|
555
623
|
return builder.build()
|
|
556
624
|
}
|
|
@@ -4,6 +4,7 @@ import type { EditorView } from '@jvs-milkdown/kit/prose/view'
|
|
|
4
4
|
|
|
5
5
|
import { Plugin, PluginKey, TextSelection } from '@jvs-milkdown/kit/prose/state'
|
|
6
6
|
import { $ctx, $prose } from '@jvs-milkdown/kit/utils'
|
|
7
|
+
import { undo, redo } from '@jvs-milkdown/prose/history'
|
|
7
8
|
// @ts-ignore
|
|
8
9
|
import {
|
|
9
10
|
createApp,
|
|
@@ -44,6 +45,21 @@ export interface FixedToolbarConfig {
|
|
|
44
45
|
outlinePosition?: 'left' | 'right'
|
|
45
46
|
onUploadCover?: (file: File) => Promise<string>
|
|
46
47
|
defaultCoverImages?: string[]
|
|
48
|
+
showMenuBar?: boolean
|
|
49
|
+
menuBarItems?: {
|
|
50
|
+
file?: boolean
|
|
51
|
+
edit?: boolean
|
|
52
|
+
view?: boolean
|
|
53
|
+
insert?: boolean
|
|
54
|
+
format?: boolean
|
|
55
|
+
}
|
|
56
|
+
showHistory?: boolean
|
|
57
|
+
showExport?: boolean
|
|
58
|
+
onExport?: (markdown: string, ctx: Ctx) => void
|
|
59
|
+
showImport?: boolean
|
|
60
|
+
onImport?: (replaceContent: (markdown: string) => void, ctx: Ctx) => void
|
|
61
|
+
useLocalStorage?: boolean
|
|
62
|
+
id?: string
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
export type FixedToolbarFeatureConfig = Partial<FixedToolbarConfig>
|
|
@@ -65,6 +81,8 @@ class FixedToolbarView implements PluginView {
|
|
|
65
81
|
#watcher?: WatchStopHandle
|
|
66
82
|
#selection: ShallowRef<Selection>
|
|
67
83
|
#show = ref(true)
|
|
84
|
+
#canUndo = ref(false)
|
|
85
|
+
#canRedo = ref(false)
|
|
68
86
|
#resizeObserver?: ResizeObserver
|
|
69
87
|
#updateOutlineGeometry?: () => void
|
|
70
88
|
#scrollContainers: Element[] = []
|
|
@@ -75,6 +93,65 @@ class FixedToolbarView implements PluginView {
|
|
|
75
93
|
constructor(ctx: Ctx, view: EditorView) {
|
|
76
94
|
this.#view = view
|
|
77
95
|
const config = ctx.get(fixedToolbarConfig.key)
|
|
96
|
+
const viewState = ctx.get(viewMenuStateCtx.key)
|
|
97
|
+
|
|
98
|
+
// Load initial view menu state from localStorage if useLocalStorage is enabled
|
|
99
|
+
if (config?.useLocalStorage) {
|
|
100
|
+
try {
|
|
101
|
+
const stored = localStorage.getItem('jvs-milkdown-data')
|
|
102
|
+
if (stored) {
|
|
103
|
+
const parsed = JSON.parse(stored)
|
|
104
|
+
if (parsed.outlineVisible !== undefined)
|
|
105
|
+
viewState.outlineVisible = parsed.outlineVisible
|
|
106
|
+
if (parsed.outlinePosition !== undefined)
|
|
107
|
+
viewState.outlinePosition = parsed.outlinePosition
|
|
108
|
+
if (parsed.outlineWidth !== undefined)
|
|
109
|
+
viewState.outlineWidth = parsed.outlineWidth
|
|
110
|
+
if (parsed.documentBackground !== undefined)
|
|
111
|
+
viewState.documentBackground = parsed.documentBackground
|
|
112
|
+
if (parsed.showTitle !== undefined)
|
|
113
|
+
viewState.showTitle = parsed.showTitle
|
|
114
|
+
if (parsed.showCover !== undefined)
|
|
115
|
+
viewState.showCover = parsed.showCover
|
|
116
|
+
if (parsed.coverUrl !== undefined)
|
|
117
|
+
viewState.coverUrl = parsed.coverUrl
|
|
118
|
+
if (parsed.editorWidth !== undefined)
|
|
119
|
+
viewState.editorWidth = parsed.editorWidth
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.error('Error loading view state from localStorage:', e)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Automatically watch and persist view state changes to localStorage
|
|
126
|
+
watch(
|
|
127
|
+
viewState,
|
|
128
|
+
(newState) => {
|
|
129
|
+
try {
|
|
130
|
+
const stored = localStorage.getItem('jvs-milkdown-data')
|
|
131
|
+
let parsed = {}
|
|
132
|
+
if (stored) {
|
|
133
|
+
parsed = JSON.parse(stored)
|
|
134
|
+
}
|
|
135
|
+
const merged = {
|
|
136
|
+
...parsed,
|
|
137
|
+
outlineVisible: newState.outlineVisible,
|
|
138
|
+
outlinePosition: newState.outlinePosition,
|
|
139
|
+
outlineWidth: newState.outlineWidth,
|
|
140
|
+
documentBackground: newState.documentBackground,
|
|
141
|
+
showTitle: newState.showTitle,
|
|
142
|
+
showCover: newState.showCover,
|
|
143
|
+
coverUrl: newState.coverUrl,
|
|
144
|
+
editorWidth: newState.editorWidth,
|
|
145
|
+
}
|
|
146
|
+
localStorage.setItem('jvs-milkdown-data', JSON.stringify(merged))
|
|
147
|
+
} catch (e) {
|
|
148
|
+
console.error('Error saving view state to localStorage:', e)
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{ deep: true }
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
78
155
|
const content = document.createElement('div')
|
|
79
156
|
content.className = 'milkdown-fixed-toolbar'
|
|
80
157
|
this.#selection = shallowRef(view.state.selection)
|
|
@@ -85,6 +162,8 @@ class FixedToolbarView implements PluginView {
|
|
|
85
162
|
config,
|
|
86
163
|
selection: this.#selection,
|
|
87
164
|
show: this.#show,
|
|
165
|
+
canUndo: this.#canUndo,
|
|
166
|
+
canRedo: this.#canRedo,
|
|
88
167
|
})
|
|
89
168
|
app.mount(content)
|
|
90
169
|
this.#content = content
|
|
@@ -317,6 +396,12 @@ class FixedToolbarView implements PluginView {
|
|
|
317
396
|
|
|
318
397
|
update = (view: EditorView) => {
|
|
319
398
|
this.#selection.value = view.state.selection
|
|
399
|
+
try {
|
|
400
|
+
this.#canUndo.value = undo(view.state)
|
|
401
|
+
this.#canRedo.value = redo(view.state)
|
|
402
|
+
} catch (e) {
|
|
403
|
+
// Ignore initialization errors
|
|
404
|
+
}
|
|
320
405
|
}
|
|
321
406
|
|
|
322
407
|
destroy = () => {
|
|
@@ -2,7 +2,14 @@ import type { Ctx } from '@jvs-milkdown/kit/ctx'
|
|
|
2
2
|
|
|
3
3
|
import { Icon } from '@jvs-milkdown/kit/component'
|
|
4
4
|
// @ts-ignore
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
defineComponent,
|
|
7
|
+
ref,
|
|
8
|
+
onUnmounted,
|
|
9
|
+
computed,
|
|
10
|
+
onMounted,
|
|
11
|
+
watch,
|
|
12
|
+
} from 'vue'
|
|
6
13
|
|
|
7
14
|
import type { FixedToolbarConfig } from './index'
|
|
8
15
|
|
|
@@ -143,7 +150,14 @@ export const MenuBar = defineComponent({
|
|
|
143
150
|
{ name: '浅粉', value: '#FDE8E9' },
|
|
144
151
|
]
|
|
145
152
|
|
|
146
|
-
const menuKeys =
|
|
153
|
+
const menuKeys = computed(() => {
|
|
154
|
+
const allKeys = ['file', 'edit', 'view', 'insert', 'format']
|
|
155
|
+
const itemsConfig = props.config?.menuBarItems
|
|
156
|
+
if (!itemsConfig) return allKeys
|
|
157
|
+
return allKeys.filter(
|
|
158
|
+
(key) => itemsConfig[key as keyof typeof itemsConfig] !== false
|
|
159
|
+
)
|
|
160
|
+
})
|
|
147
161
|
const hasSubmenu = (key: string) => key === 'view'
|
|
148
162
|
|
|
149
163
|
return () => {
|
|
@@ -232,7 +246,7 @@ export const MenuBar = defineComponent({
|
|
|
232
246
|
e.stopPropagation()
|
|
233
247
|
}}
|
|
234
248
|
>
|
|
235
|
-
{menuKeys.map((menuKey) => {
|
|
249
|
+
{menuKeys.value.map((menuKey) => {
|
|
236
250
|
const isHovered = activeSubmenu.value === menuKey
|
|
237
251
|
const label =
|
|
238
252
|
i18n(props.ctx, `menuBar.${menuKey}` as any) || menuKey
|