@kungal/editor-nuxt 0.25.2 → 0.26.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 CHANGED
@@ -1,5 +1,38 @@
1
1
  # @kungal/editor-nuxt
2
2
 
3
+ ## 0.26.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 945708d: Add an insert-link feature (click to add a URL).
8
+
9
+ The `link` mark already existed (so `[text](url)` round-trips and pasted URLs
10
+ autolink), but there was no click-to-insert UI. Now there is:
11
+
12
+ - **editor-core**: `insertLinkCommand` (`{ href, text? }`) — applies the link mark
13
+ to the selection, or inserts linked text on an empty selection; clears the
14
+ stored mark afterwards. Re-exported from `@kungal/editor-core/preset`.
15
+ - **editor-vue**: `KunEditorToolbarApi.insertLink(payload)`; a `link` button in the
16
+ default toolbar and the selection bubble (both default-on); `'link'` added to
17
+ `KunToolbarItem` and `KunSelectionItem`. Headless UI prompts for the URL.
18
+ - **editor-nuxt**: `<KunEditorToolbar>` gains a `link` button — a KunPopover with a
19
+ KunInput for the URL.
20
+
21
+ A link is a **mark** (`[text](url)`), deliberately distinct from the custom-NODE
22
+ embeds (mention / quote). A future "article card" or custom component would be a
23
+ node like those (schema + toUrl/fromUrl), not this — a separate mechanism.
24
+
25
+ Verified in the docs production build: the KunUI toolbar popover wraps a selection
26
+ (`[…](https://example.com)`); the selection bubble does too
27
+ (`[linktext](https://kun.com)`); 4 new core tests (wrap / insert / autolink
28
+ fallback / blank-href no-op) pass; 0 console errors.
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [945708d]
33
+ - @kungal/editor-core@0.26.0
34
+ - @kungal/editor-vue@0.26.0
35
+
3
36
  ## 0.25.2
4
37
 
5
38
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungal/editor-nuxt",
3
- "version": "0.25.2",
3
+ "version": "0.26.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.25.2",
35
- "@kungal/editor-vue": "0.25.2"
34
+ "@kungal/editor-core": "0.26.0",
35
+ "@kungal/editor-vue": "0.26.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@kungal/ui-vue": "^2.7.0",
@@ -58,6 +58,7 @@ const DEFAULT_ITEMS: KunToolbarItem[] = [
58
58
  'italic',
59
59
  'strike',
60
60
  'code',
61
+ 'link',
61
62
  '|',
62
63
  'bulletList',
63
64
  'orderedList',
@@ -79,6 +80,9 @@ const t = computed(() => ({
79
80
  italic: en.value ? 'Italic' : '斜体',
80
81
  strike: en.value ? 'Strikethrough' : '删除线',
81
82
  code: en.value ? 'Inline code' : '行内代码',
83
+ link: en.value ? 'Link' : '链接',
84
+ linkPlaceholder: en.value ? 'https://…' : 'https://…',
85
+ linkApply: en.value ? 'Apply' : '确定',
82
86
  bulletList: en.value ? 'Bullet list' : '无序列表',
83
87
  orderedList: en.value ? 'Ordered list' : '有序列表',
84
88
  quote: en.value ? 'Blockquote' : '引用块',
@@ -117,6 +121,7 @@ const commandButtons = computed<Record<string, Tool>>(() => ({
117
121
  type RenderItem =
118
122
  | { kind: 'divider' }
119
123
  | { kind: 'heading' }
124
+ | { kind: 'link' }
120
125
  | { kind: 'image' }
121
126
  | { kind: 'picker' }
122
127
  | { kind: 'button'; tool: Tool }
@@ -133,6 +138,7 @@ const resolvedItems = computed<RenderItem[]>(() => {
133
138
  .map<RenderItem | null>((it) => {
134
139
  if (it === '|') return { kind: 'divider' }
135
140
  if (it === 'heading') return { kind: 'heading' }
141
+ if (it === 'link') return { kind: 'link' }
136
142
  if (it === 'image') return { kind: 'image' }
137
143
  if (it === 'picker') return { kind: 'picker' }
138
144
  const tool = buttons[it]
@@ -172,6 +178,23 @@ const setHeading = (level: number) => {
172
178
  headingPopoverEl?.close()
173
179
  }
174
180
 
181
+ // Link — a KunPopover with a URL input. Applies to the current selection (or
182
+ // inserts the URL as linked text when nothing is selected). The editor keeps its
183
+ // selection while the popover input is focused, so api.insertLink wraps it.
184
+ const linkUrl = ref('')
185
+ let linkPopoverEl: { close: () => void } | null = null
186
+ const setLinkPopover = (el: unknown) => {
187
+ linkPopoverEl = el as { close: () => void } | null
188
+ }
189
+ const applyLink = () => {
190
+ const href = linkUrl.value.trim()
191
+ if (href) {
192
+ props.insertLink({ href })
193
+ }
194
+ linkUrl.value = ''
195
+ linkPopoverEl?.close()
196
+ }
197
+
175
198
  // Image upload — via api.uploadImage (shows the in-document "uploading…"
176
199
  // placeholder). Only rendered when the host supplies uploadImage.
177
200
  const canUpload = computed(() => !!props.adapters.uploadImage)
@@ -245,6 +268,40 @@ const insertSticker = (src: string, name: string) =>
245
268
  </button>
246
269
  </KunPopover>
247
270
 
271
+ <KunPopover
272
+ v-else-if="item.kind === 'link'"
273
+ :ref="setLinkPopover"
274
+ position="bottom-start"
275
+ :opaque="true"
276
+ inner-class="w-64 p-2"
277
+ >
278
+ <template #trigger>
279
+ <KunTooltip :text="t.link" :delay-show="300">
280
+ <KunButton
281
+ variant="light"
282
+ size="sm"
283
+ :is-icon-only="true"
284
+ :aria-label="t.link"
285
+ >
286
+ <KunIcon name="lucide:link" />
287
+ </KunButton>
288
+ </KunTooltip>
289
+ </template>
290
+ <form class="flex items-center gap-1" @submit.prevent="applyLink">
291
+ <KunInput
292
+ v-model="linkUrl"
293
+ type="url"
294
+ size="sm"
295
+ :placeholder="t.linkPlaceholder"
296
+ :autofocus="true"
297
+ class="flex-1"
298
+ />
299
+ <KunButton type="submit" variant="flat" color="primary" size="sm">
300
+ {{ t.linkApply }}
301
+ </KunButton>
302
+ </form>
303
+ </KunPopover>
304
+
248
305
  <template v-else-if="item.kind === 'image'">
249
306
  <KunTooltip :text="t.image" :delay-show="300">
250
307
  <KunButton