@kungal/editor-nuxt 0.19.0 → 0.21.0
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/CHANGELOG.md +68 -0
- package/package.json +3 -3
- package/runtime/KunEditorToolbar.vue +189 -105
- package/runtime/KunEditorViewSwitch.vue +22 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
# @kungal/editor-nuxt
|
|
2
2
|
|
|
3
|
+
## 0.21.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9556a51: `<KunEditorToolbar>` gains an `items` prop to reorder / subset the buttons.
|
|
8
|
+
|
|
9
|
+
Previously the toolbar's button order was fixed, so a host that wanted a different
|
|
10
|
+
order had to rebuild a whole custom toolbar via the `#toolbar` slot — which drifts
|
|
11
|
+
from the default toolbar used elsewhere (e.g. a topic-edit page vs reply/comment).
|
|
12
|
+
|
|
13
|
+
Now pass `:items` — an ordered `KunToolbarItem[]` (`'heading' | 'bold' | 'italic'
|
|
14
|
+
| 'strike' | 'code' | 'bulletList' | 'orderedList' | 'quote' | 'codeBlock' | 'hr'
|
|
15
|
+
| 'spoiler' | 'image' | 'picker' | '|'`, where `'|'` is a divider):
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<KunEditorToolbar
|
|
19
|
+
v-bind="api"
|
|
20
|
+
:items="['picker', '|', 'bold', 'italic', '|', 'image', '|', 'heading']"
|
|
21
|
+
/>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Apply the same array to every editor for a consistent site-wide order without
|
|
25
|
+
rebuilding a toolbar. Defaults to the standard layout. `image`/`picker` are still
|
|
26
|
+
auto-dropped without their adapter/feature, and dividers collapse around removed
|
|
27
|
+
items. `KunToolbarItem` is exported from `@kungal/editor-vue`.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies [9556a51]
|
|
32
|
+
- @kungal/editor-vue@0.21.0
|
|
33
|
+
- @kungal/editor-core@0.21.0
|
|
34
|
+
|
|
35
|
+
## 0.20.0
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- 62e14f5: Split view: sync scrolling between the panes, with an off switch.
|
|
40
|
+
|
|
41
|
+
Scrolling the markdown source pane now scrolls the WYSIWYG preview to the same
|
|
42
|
+
position (and vice-versa) — proportional/percentage sync (source↔preview line
|
|
43
|
+
mapping is impractical across two separate renderers), with an active-pane +
|
|
44
|
+
idle-timeout guard so the echoed scroll doesn't feed back into a loop.
|
|
45
|
+
|
|
46
|
+
Turn it off two ways (for downstream customization):
|
|
47
|
+
|
|
48
|
+
- **`scrollSync` prop** on `<KunEditor>` (default `true`) — pass
|
|
49
|
+
`:scroll-sync="false"` to disable entirely.
|
|
50
|
+
- **A runtime toggle** in the view switch: `KunEditorViewSwitchApi` gains
|
|
51
|
+
`scrollSync` / `toggleScrollSync()` (+ `labels.scrollSync`). The default view
|
|
52
|
+
switch shows a 同步滚动 toggle in split mode; `<KunEditorViewSwitch>` (editor-nuxt)
|
|
53
|
+
shows a KunUI link/unlink toggle button. Or replace the whole control via the
|
|
54
|
+
`#view-switch` slot.
|
|
55
|
+
|
|
56
|
+
Sync needs each pane to scroll internally, so the reference `kun-editor.css` now
|
|
57
|
+
gives split panes a bounded height (`--kun-editor-split-height`, default `60vh`,
|
|
58
|
+
overridable) with `overflow: auto`. editor-vue still ships zero CSS.
|
|
59
|
+
|
|
60
|
+
Verified in the docs production build (headless + KunUI view switches): with the
|
|
61
|
+
panes overflowing, scrolling the source drives the preview to the same ratio;
|
|
62
|
+
toggling off makes them scroll independently; toggling on resumes; 0 console
|
|
63
|
+
errors.
|
|
64
|
+
|
|
65
|
+
### Patch Changes
|
|
66
|
+
|
|
67
|
+
- Updated dependencies [62e14f5]
|
|
68
|
+
- @kungal/editor-vue@0.20.0
|
|
69
|
+
- @kungal/editor-core@0.20.0
|
|
70
|
+
|
|
3
71
|
## 0.19.0
|
|
4
72
|
|
|
5
73
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungal/editor-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "KunEditor Nuxt Layer — wraps @kungal/editor-vue and auto-imports <KunEditor> so an existing Nuxt app keeps its exact DX (no import needed).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"nuxt.config.ts"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@kungal/editor-core": "0.
|
|
35
|
-
"@kungal/editor-vue": "0.
|
|
34
|
+
"@kungal/editor-core": "0.21.0",
|
|
35
|
+
"@kungal/editor-vue": "0.21.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@kungal/ui-vue": "^2.7.0",
|
|
@@ -11,18 +11,15 @@
|
|
|
11
11
|
// KunButton / KunIcon / KunTooltip / KunPopover chrome. It drives the editor
|
|
12
12
|
// purely through the slot API props (KunEditorToolbarApi) — no useInstance.
|
|
13
13
|
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
14
|
+
// Button order/set is customizable via `:items` (a list of KunToolbarItem ids,
|
|
15
|
+
// '|' = divider) so a host can reorder or subset the built-in buttons and apply
|
|
16
|
+
// the SAME order across every editor, instead of rebuilding a custom toolbar.
|
|
17
|
+
// Defaults to the standard layout. image/picker are auto-dropped without their
|
|
18
|
+
// adapter/feature, and dividers collapse around removed items.
|
|
18
19
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// KunButton/KunIcon/KunTooltip/KunPopover/KunTab are auto-imported by ui-nuxt in
|
|
23
|
-
// the consuming app; icons use the app's iconify set (e.g. lucide via @nuxt/icon).
|
|
24
|
-
// The container/menu utility classes are generated via @kungal/editor-nuxt's
|
|
25
|
-
// shipped tailwind.css (@source) — the consumer @imports it once.
|
|
20
|
+
// Headings are ONE "text size" KunPopover (Paragraph / H1–H6). There is NO math
|
|
21
|
+
// button — an empty inline-math atom is a broken node; math is via `$…$` / `$$`.
|
|
22
|
+
// KunButton/KunIcon/KunTooltip/KunPopover/KunTab are auto-imported by ui-nuxt.
|
|
26
23
|
import { computed, ref } from 'vue'
|
|
27
24
|
import {
|
|
28
25
|
createCodeBlockCommand,
|
|
@@ -41,9 +38,39 @@ import {
|
|
|
41
38
|
setHeadingCommand
|
|
42
39
|
} from '@kungal/editor-core/preset'
|
|
43
40
|
import { EMOJI } from '@kungal/editor-vue'
|
|
44
|
-
import type {
|
|
41
|
+
import type {
|
|
42
|
+
KunEditorToolbarApi,
|
|
43
|
+
KunToolbarItem,
|
|
44
|
+
StickerPack
|
|
45
|
+
} from '@kungal/editor-vue'
|
|
46
|
+
|
|
47
|
+
const props = defineProps<
|
|
48
|
+
KunEditorToolbarApi & {
|
|
49
|
+
/** Ordered button ids ('|' = divider). Defaults to the standard layout. */
|
|
50
|
+
items?: KunToolbarItem[]
|
|
51
|
+
}
|
|
52
|
+
>()
|
|
45
53
|
|
|
46
|
-
const
|
|
54
|
+
const DEFAULT_ITEMS: KunToolbarItem[] = [
|
|
55
|
+
'heading',
|
|
56
|
+
'|',
|
|
57
|
+
'bold',
|
|
58
|
+
'italic',
|
|
59
|
+
'strike',
|
|
60
|
+
'code',
|
|
61
|
+
'|',
|
|
62
|
+
'bulletList',
|
|
63
|
+
'orderedList',
|
|
64
|
+
'quote',
|
|
65
|
+
'codeBlock',
|
|
66
|
+
'hr',
|
|
67
|
+
'|',
|
|
68
|
+
'spoiler',
|
|
69
|
+
'|',
|
|
70
|
+
'image',
|
|
71
|
+
'|',
|
|
72
|
+
'picker'
|
|
73
|
+
]
|
|
47
74
|
|
|
48
75
|
const en = computed(() => props.locale.toLowerCase().startsWith('en'))
|
|
49
76
|
const t = computed(() => ({
|
|
@@ -66,6 +93,66 @@ const t = computed(() => ({
|
|
|
66
93
|
failed: en.value ? 'Failed to load stickers' : '贴纸加载失败'
|
|
67
94
|
}))
|
|
68
95
|
|
|
96
|
+
interface Tool {
|
|
97
|
+
icon: string
|
|
98
|
+
title: string
|
|
99
|
+
run: () => void
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The plain command buttons (everything except heading / image / picker).
|
|
103
|
+
// `.key` is read at click time via props.run (populated once the editor exists).
|
|
104
|
+
const commandButtons = computed<Record<string, Tool>>(() => ({
|
|
105
|
+
bold: { icon: 'lucide:bold', title: t.value.bold, run: () => props.run(toggleStrongCommand.key) },
|
|
106
|
+
italic: { icon: 'lucide:italic', title: t.value.italic, run: () => props.run(toggleEmphasisCommand.key) },
|
|
107
|
+
strike: { icon: 'lucide:strikethrough', title: t.value.strike, run: () => props.run(toggleStrikethroughCommand.key) },
|
|
108
|
+
code: { icon: 'lucide:code', title: t.value.code, run: () => props.run(toggleInlineCodeCommand.key) },
|
|
109
|
+
bulletList: { icon: 'lucide:list', title: t.value.bulletList, run: () => props.run(wrapInBulletListCommand.key) },
|
|
110
|
+
orderedList: { icon: 'lucide:list-ordered', title: t.value.orderedList, run: () => props.run(wrapInOrderedListCommand.key) },
|
|
111
|
+
quote: { icon: 'lucide:text-quote', title: t.value.quote, run: () => props.run(wrapInBlockquoteCommand.key) },
|
|
112
|
+
codeBlock: { icon: 'lucide:square-code', title: t.value.codeBlock, run: () => props.run(createCodeBlockCommand.key, '') },
|
|
113
|
+
hr: { icon: 'lucide:minus', title: t.value.hr, run: () => props.run(insertHrCommand.key) },
|
|
114
|
+
spoiler: { icon: 'lucide:eye-off', title: t.value.spoiler, run: () => props.run(insertKunSpoilerCommand.key) }
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
type RenderItem =
|
|
118
|
+
| { kind: 'divider' }
|
|
119
|
+
| { kind: 'heading' }
|
|
120
|
+
| { kind: 'image' }
|
|
121
|
+
| { kind: 'picker' }
|
|
122
|
+
| { kind: 'button'; tool: Tool }
|
|
123
|
+
|
|
124
|
+
// Resolve `items` → renderables: drop image/picker when unavailable, map command
|
|
125
|
+
// ids to their button, then collapse consecutive/leading/trailing dividers so a
|
|
126
|
+
// removed item never leaves a doubled or dangling separator.
|
|
127
|
+
const resolvedItems = computed<RenderItem[]>(() => {
|
|
128
|
+
const buttons = commandButtons.value
|
|
129
|
+
const mapped = (props.items ?? DEFAULT_ITEMS)
|
|
130
|
+
.filter((it) =>
|
|
131
|
+
it === 'image' ? canUpload.value : it === 'picker' ? showPicker.value : true
|
|
132
|
+
)
|
|
133
|
+
.map<RenderItem | null>((it) => {
|
|
134
|
+
if (it === '|') return { kind: 'divider' }
|
|
135
|
+
if (it === 'heading') return { kind: 'heading' }
|
|
136
|
+
if (it === 'image') return { kind: 'image' }
|
|
137
|
+
if (it === 'picker') return { kind: 'picker' }
|
|
138
|
+
const tool = buttons[it]
|
|
139
|
+
return tool ? { kind: 'button', tool } : null
|
|
140
|
+
})
|
|
141
|
+
.filter((x): x is RenderItem => x !== null)
|
|
142
|
+
|
|
143
|
+
const out: RenderItem[] = []
|
|
144
|
+
for (const it of mapped) {
|
|
145
|
+
if (it.kind === 'divider' && (out.length === 0 || out[out.length - 1]?.kind === 'divider')) {
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
out.push(it)
|
|
149
|
+
}
|
|
150
|
+
while (out.length && out[out.length - 1]?.kind === 'divider') {
|
|
151
|
+
out.pop()
|
|
152
|
+
}
|
|
153
|
+
return out
|
|
154
|
+
})
|
|
155
|
+
|
|
69
156
|
// Paragraph (level 0) + H1–H6, each rendered at its own size in the menu.
|
|
70
157
|
const headingOptions = computed(() => {
|
|
71
158
|
const labels = en.value
|
|
@@ -74,44 +161,25 @@ const headingOptions = computed(() => {
|
|
|
74
161
|
const sizes = ['1rem', '1.6rem', '1.4rem', '1.25rem', '1.1rem', '1rem', '0.9rem']
|
|
75
162
|
return labels.map((label, level) => ({ level, label, size: sizes[level] }))
|
|
76
163
|
})
|
|
77
|
-
|
|
164
|
+
// Function refs (these elements live inside a v-for, where string refs collect
|
|
165
|
+
// into arrays). Each renders at most once.
|
|
166
|
+
let headingPopoverEl: { close: () => void } | null = null
|
|
167
|
+
const setHeadingPopover = (el: unknown) => {
|
|
168
|
+
headingPopoverEl = el as { close: () => void } | null
|
|
169
|
+
}
|
|
78
170
|
const setHeading = (level: number) => {
|
|
79
171
|
props.run(setHeadingCommand.key, level)
|
|
80
|
-
|
|
172
|
+
headingPopoverEl?.close()
|
|
81
173
|
}
|
|
82
174
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
title: string
|
|
86
|
-
run: () => void
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// `.key` is read at click time via props.run (populated once the editor exists).
|
|
90
|
-
const groups = computed<Tool[][]>(() => [
|
|
91
|
-
[
|
|
92
|
-
{ icon: 'lucide:bold', title: t.value.bold, run: () => props.run(toggleStrongCommand.key) },
|
|
93
|
-
{ icon: 'lucide:italic', title: t.value.italic, run: () => props.run(toggleEmphasisCommand.key) },
|
|
94
|
-
{ icon: 'lucide:strikethrough', title: t.value.strike, run: () => props.run(toggleStrikethroughCommand.key) },
|
|
95
|
-
{ icon: 'lucide:code', title: t.value.code, run: () => props.run(toggleInlineCodeCommand.key) }
|
|
96
|
-
],
|
|
97
|
-
[
|
|
98
|
-
{ icon: 'lucide:list', title: t.value.bulletList, run: () => props.run(wrapInBulletListCommand.key) },
|
|
99
|
-
{ icon: 'lucide:list-ordered', title: t.value.orderedList, run: () => props.run(wrapInOrderedListCommand.key) },
|
|
100
|
-
{ icon: 'lucide:text-quote', title: t.value.quote, run: () => props.run(wrapInBlockquoteCommand.key) },
|
|
101
|
-
{ icon: 'lucide:square-code', title: t.value.codeBlock, run: () => props.run(createCodeBlockCommand.key, '') },
|
|
102
|
-
{ icon: 'lucide:minus', title: t.value.hr, run: () => props.run(insertHrCommand.key) }
|
|
103
|
-
],
|
|
104
|
-
[
|
|
105
|
-
{ icon: 'lucide:eye-off', title: t.value.spoiler, run: () => props.run(insertKunSpoilerCommand.key) }
|
|
106
|
-
]
|
|
107
|
-
])
|
|
108
|
-
|
|
109
|
-
// Image upload button — shown only when the host supplies uploadImage. The
|
|
110
|
-
// upload itself goes through api.uploadImage, which shows an in-document
|
|
111
|
-
// "uploading…" placeholder at the caret (same UX as paste/drop).
|
|
175
|
+
// Image upload — via api.uploadImage (shows the in-document "uploading…"
|
|
176
|
+
// placeholder). Only rendered when the host supplies uploadImage.
|
|
112
177
|
const canUpload = computed(() => !!props.adapters.uploadImage)
|
|
113
|
-
|
|
114
|
-
const
|
|
178
|
+
let fileInputEl: HTMLInputElement | null = null
|
|
179
|
+
const setFileInput = (el: unknown) => {
|
|
180
|
+
fileInputEl = el as HTMLInputElement | null
|
|
181
|
+
}
|
|
182
|
+
const pickImage = () => fileInputEl?.click()
|
|
115
183
|
const onFileChange = (e: Event) => {
|
|
116
184
|
const input = e.target as HTMLInputElement
|
|
117
185
|
const file = input.files?.[0]
|
|
@@ -145,65 +213,65 @@ const insertSticker = (src: string, name: string) =>
|
|
|
145
213
|
|
|
146
214
|
<template>
|
|
147
215
|
<div class="flex flex-wrap items-center gap-0.5">
|
|
148
|
-
<
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
</template>
|
|
155
|
-
<button
|
|
156
|
-
v-for="o in headingOptions"
|
|
157
|
-
:key="o.level"
|
|
158
|
-
type="button"
|
|
159
|
-
class="hover:bg-default-100 flex w-full items-center rounded px-3 py-1.5 text-left leading-tight"
|
|
160
|
-
:style="{ fontSize: o.size }"
|
|
161
|
-
@click="setHeading(o.level)"
|
|
162
|
-
>
|
|
163
|
-
{{ o.label }}
|
|
164
|
-
</button>
|
|
165
|
-
</KunPopover>
|
|
216
|
+
<template v-for="(item, i) in resolvedItems" :key="i">
|
|
217
|
+
<span
|
|
218
|
+
v-if="item.kind === 'divider'"
|
|
219
|
+
class="bg-default-200 mx-1 h-5 w-px"
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
/>
|
|
166
222
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
223
|
+
<KunPopover
|
|
224
|
+
v-else-if="item.kind === 'heading'"
|
|
225
|
+
:ref="setHeadingPopover"
|
|
226
|
+
position="bottom-start"
|
|
227
|
+
:opaque="true"
|
|
228
|
+
inner-class="min-w-40 p-1"
|
|
229
|
+
>
|
|
230
|
+
<template #trigger>
|
|
231
|
+
<KunButton variant="light" size="sm" :aria-label="t.textSize">
|
|
232
|
+
{{ t.textSize }}
|
|
233
|
+
<KunIcon name="lucide:chevron-down" />
|
|
234
|
+
</KunButton>
|
|
235
|
+
</template>
|
|
236
|
+
<button
|
|
237
|
+
v-for="o in headingOptions"
|
|
238
|
+
:key="o.level"
|
|
239
|
+
type="button"
|
|
240
|
+
class="hover:bg-default-100 flex w-full items-center rounded px-3 py-1.5 text-left leading-tight"
|
|
241
|
+
:style="{ fontSize: o.size }"
|
|
242
|
+
@click="setHeading(o.level)"
|
|
176
243
|
>
|
|
177
|
-
|
|
178
|
-
</
|
|
179
|
-
</
|
|
180
|
-
</template>
|
|
244
|
+
{{ o.label }}
|
|
245
|
+
</button>
|
|
246
|
+
</KunPopover>
|
|
181
247
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
</
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</template>
|
|
248
|
+
<template v-else-if="item.kind === 'image'">
|
|
249
|
+
<KunTooltip :text="t.image" :delay-show="300">
|
|
250
|
+
<KunButton
|
|
251
|
+
variant="light"
|
|
252
|
+
size="sm"
|
|
253
|
+
:is-icon-only="true"
|
|
254
|
+
:aria-label="t.image"
|
|
255
|
+
@click="pickImage"
|
|
256
|
+
>
|
|
257
|
+
<KunIcon name="lucide:image" />
|
|
258
|
+
</KunButton>
|
|
259
|
+
</KunTooltip>
|
|
260
|
+
<input
|
|
261
|
+
:ref="setFileInput"
|
|
262
|
+
type="file"
|
|
263
|
+
accept=".jpg,.jpeg,.png,.webp,.gif"
|
|
264
|
+
hidden
|
|
265
|
+
@change="onFileChange"
|
|
266
|
+
/>
|
|
267
|
+
</template>
|
|
203
268
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
269
|
+
<KunPopover
|
|
270
|
+
v-else-if="item.kind === 'picker'"
|
|
271
|
+
position="bottom-start"
|
|
272
|
+
:opaque="true"
|
|
273
|
+
inner-class="w-72 p-2"
|
|
274
|
+
>
|
|
207
275
|
<template #trigger>
|
|
208
276
|
<KunButton
|
|
209
277
|
variant="light"
|
|
@@ -234,8 +302,8 @@ const insertSticker = (src: string, name: string) =>
|
|
|
234
302
|
class="grid max-h-56 grid-cols-8 gap-0.5 overflow-y-auto"
|
|
235
303
|
>
|
|
236
304
|
<button
|
|
237
|
-
v-for="(e,
|
|
238
|
-
:key="
|
|
305
|
+
v-for="(e, ei) in EMOJI"
|
|
306
|
+
:key="ei"
|
|
239
307
|
type="button"
|
|
240
308
|
class="hover:bg-default-100 rounded p-1 text-lg leading-none"
|
|
241
309
|
@click="props.insertText(e)"
|
|
@@ -250,8 +318,8 @@ const insertSticker = (src: string, name: string) =>
|
|
|
250
318
|
<div class="text-default-500 mb-1 text-xs">{{ pack.name }}</div>
|
|
251
319
|
<div class="grid grid-cols-4 gap-1">
|
|
252
320
|
<button
|
|
253
|
-
v-for="(s,
|
|
254
|
-
:key="
|
|
321
|
+
v-for="(s, si) in pack.stickers"
|
|
322
|
+
:key="si"
|
|
255
323
|
type="button"
|
|
256
324
|
class="hover:bg-default-100 rounded p-1"
|
|
257
325
|
:title="s.name"
|
|
@@ -267,6 +335,22 @@ const insertSticker = (src: string, name: string) =>
|
|
|
267
335
|
</div>
|
|
268
336
|
</div>
|
|
269
337
|
</KunPopover>
|
|
338
|
+
|
|
339
|
+
<KunTooltip
|
|
340
|
+
v-else-if="item.kind === 'button'"
|
|
341
|
+
:text="item.tool.title"
|
|
342
|
+
:delay-show="300"
|
|
343
|
+
>
|
|
344
|
+
<KunButton
|
|
345
|
+
variant="light"
|
|
346
|
+
size="sm"
|
|
347
|
+
:is-icon-only="true"
|
|
348
|
+
:aria-label="item.tool.title"
|
|
349
|
+
@click="item.tool.run()"
|
|
350
|
+
>
|
|
351
|
+
<KunIcon :name="item.tool.icon" />
|
|
352
|
+
</KunButton>
|
|
353
|
+
</KunTooltip>
|
|
270
354
|
</template>
|
|
271
355
|
</div>
|
|
272
356
|
</template>
|
|
@@ -38,15 +38,27 @@ const onChange = (value: string) => {
|
|
|
38
38
|
size="sm"
|
|
39
39
|
@update:model-value="onChange"
|
|
40
40
|
/>
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
<template v-if="mode === 'split'">
|
|
42
|
+
<KunButton
|
|
43
|
+
variant="light"
|
|
44
|
+
size="sm"
|
|
45
|
+
:is-icon-only="true"
|
|
46
|
+
:aria-label="labels.swap"
|
|
47
|
+
@click="swap()"
|
|
48
|
+
>
|
|
49
|
+
<KunIcon name="lucide:arrow-left-right" />
|
|
50
|
+
</KunButton>
|
|
51
|
+
<KunButton
|
|
52
|
+
:variant="scrollSync ? 'flat' : 'light'"
|
|
53
|
+
:color="scrollSync ? 'primary' : 'default'"
|
|
54
|
+
size="sm"
|
|
55
|
+
:is-icon-only="true"
|
|
56
|
+
:aria-label="labels.scrollSync"
|
|
57
|
+
:aria-pressed="scrollSync"
|
|
58
|
+
@click="toggleScrollSync()"
|
|
59
|
+
>
|
|
60
|
+
<KunIcon :name="scrollSync ? 'lucide:link' : 'lucide:unlink'" />
|
|
61
|
+
</KunButton>
|
|
62
|
+
</template>
|
|
51
63
|
</div>
|
|
52
64
|
</template>
|