@kungal/editor-nuxt 0.17.0 → 0.19.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,75 @@
1
1
  # @kungal/editor-nuxt
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f111b82: Add a desktop **split view** for long-form writing.
8
+
9
+ `<KunEditor>` now has a third view mode, `split`, alongside WYSIWYG and Markdown:
10
+ an editable markdown source pane + a live **read-only WYSIWYG preview**, side by
11
+ side and left/right swappable. The markdown string stays the single source of
12
+ truth and the preview is derived — the industry-standard model (VS Code /
13
+ StackEdit / Dillinger), so there's no two-editor bidirectional-sync problem.
14
+
15
+ - **editor-vue**: the view mode gains `'split'`; `KunEditorViewSwitchApi` gains
16
+ `swap()` / `swapped` and `labels.split` / `labels.swap`. The WYSIWYG becomes a
17
+ read-only preview in split (its editability now reacts to the `readonly` prop).
18
+ The default hand-rolled view switch gets a 分栏 tab + a ⇄ swap button.
19
+ - **editor-nuxt**: `<KunEditorViewSwitch>` gets the 分栏 KunTab + a KunUI swap
20
+ button (shown in split).
21
+ - **Headless**: the layout is class hooks only — `.kun-editor__panes` /
22
+ `.kun-editor__pane` under `[data-mode='split']` (+ `[data-swap]`). Styled by the
23
+ reference `kun-editor.css`: stacked on narrow screens, side-by-side (swappable)
24
+ from `md` up. editor-vue still ships zero CSS.
25
+
26
+ Verified in the docs production build (headless + KunUI view switches): 分栏 shows
27
+ both panes side by side, typing markdown in the source updates the preview live,
28
+ the WYSIWYG preview is `contenteditable=false`, swap reverses the panes; 0 console
29
+ errors. (Synced scrolling between panes is a possible follow-up.)
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [f111b82]
34
+ - @kungal/editor-vue@0.19.0
35
+ - @kungal/editor-core@0.19.0
36
+
37
+ ## 0.18.0
38
+
39
+ ### Minor Changes
40
+
41
+ - 6545866: Toolbar image upload now shows an in-document "uploading…" placeholder.
42
+
43
+ Previously only paste/drop showed the in-flight placeholder (Milkdown's `upload`
44
+ plugin); the toolbar image button uploaded silently — the image just popped in
45
+ when the adapter resolved. Now the toolbar path uses the same placeholder UX
46
+ (ProseMirror's official upload-example pattern).
47
+
48
+ - **editor-core**: `startImageUpload(view, file, { uploadImage, notify, locale })`
49
+ - the `imageUploadPlaceholder` decoration plugin (exported from `/preset`;
50
+ wired by the preset when `uploadImage` is present). Inserts a
51
+ `.kun-editor__uploading` widget at the caret, uploads, then replaces it with the
52
+ image (or removes it + notifies on failure); not an undo step. The paste/drop
53
+ placeholder now uses the same `.kun-editor__uploading` class so both look
54
+ identical.
55
+ - **editor-vue**: `KunEditorToolbarApi` gains `uploadImage(file)`; both the
56
+ headless `EditorToolbar` and the `#toolbar` slot API route uploads through
57
+ `startImageUpload`.
58
+ - **editor-nuxt**: `<KunEditorToolbar>`'s image button uses it.
59
+ - **Headless kept**: the placeholder is a class + widget hook only; style it via
60
+ `.kun-editor__uploading` in the reference `kun-editor.css` (primary-colored).
61
+
62
+ Customizable: style the placeholder via `.kun-editor__uploading`; the text is
63
+ localized (zh/en). Verified in the docs production build: uploading via either
64
+ toolbar inserts the image; the unit tests confirm the placeholder shows during a
65
+ pending upload and is replaced on success / removed on failure.
66
+
67
+ ### Patch Changes
68
+
69
+ - Updated dependencies [6545866]
70
+ - @kungal/editor-core@0.18.0
71
+ - @kungal/editor-vue@0.18.0
72
+
3
73
  ## 0.17.0
4
74
 
5
75
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungal/editor-nuxt",
3
- "version": "0.17.0",
3
+ "version": "0.19.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.17.0",
35
- "@kungal/editor-vue": "0.17.0"
34
+ "@kungal/editor-core": "0.19.0",
35
+ "@kungal/editor-vue": "0.19.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@kungal/ui-vue": "^2.7.0",
@@ -106,22 +106,18 @@ const groups = computed<Tool[][]>(() => [
106
106
  ]
107
107
  ])
108
108
 
109
- // Image upload — only when the host supplies uploadImage. Same adapter as
110
- // paste/drop; on success inserts an image node at the cursor.
111
- const uploadImage = computed(() => props.adapters.uploadImage)
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).
112
+ const canUpload = computed(() => !!props.adapters.uploadImage)
112
113
  const fileInput = ref<HTMLInputElement | null>(null)
113
114
  const pickImage = () => fileInput.value?.click()
114
- const onFileChange = async (e: Event) => {
115
+ const onFileChange = (e: Event) => {
115
116
  const input = e.target as HTMLInputElement
116
117
  const file = input.files?.[0]
117
118
  input.value = ''
118
- const upload = uploadImage.value
119
- if (!file || !upload) return
120
- try {
121
- const src = await upload(file)
122
- props.run(insertImageCommand.key, { src, title: file.name, alt: file.name })
123
- } catch {
124
- props.adapters.notify?.(en.value ? 'Image upload failed' : '图片上传失败', 'error')
119
+ if (file) {
120
+ props.uploadImage(file)
125
121
  }
126
122
  }
127
123
 
@@ -183,7 +179,7 @@ const insertSticker = (src: string, name: string) =>
183
179
  </KunTooltip>
184
180
  </template>
185
181
 
186
- <template v-if="uploadImage">
182
+ <template v-if="canUpload">
187
183
  <span class="bg-default-200 mx-1 h-5 w-px" aria-hidden="true" />
188
184
  <KunTooltip :text="t.image" :delay-show="300">
189
185
  <KunButton
@@ -1,14 +1,15 @@
1
1
  <script setup lang="ts">
2
- // KunUI view switch for <KunEditor>'s #view-switch slot — the Preview/Markdown
3
- // tabs as a real <KunTab variant="underlined"> (keyboard nav, sliding indicator,
4
- // a11y). Same pattern as <KunEditorToolbar>: it lives in this ui-nuxt-assuming
5
- // layer (not headless editor-vue) and is driven purely by the slot API.
2
+ // KunUI view switch for <KunEditor>'s #view-switch slot — 预览 / Markdown / 分栏
3
+ // as a real <KunTab variant="underlined"> (keyboard nav, sliding indicator, a11y),
4
+ // plus a swap button in split mode. Same pattern as <KunEditorToolbar>: it lives
5
+ // in this ui-nuxt-assuming layer (not headless editor-vue) and is driven purely
6
+ // by the slot API.
6
7
  //
7
8
  // <KunEditor v-model="md">
8
9
  // <template #view-switch="s"><KunEditorViewSwitch v-bind="s" /></template>
9
10
  // </KunEditor>
10
11
  //
11
- // KunTab is auto-imported by @kungal/ui-nuxt in the consuming app.
12
+ // KunTab / KunButton / KunIcon are auto-imported by @kungal/ui-nuxt.
12
13
  import { computed } from 'vue'
13
14
  import type { KunEditorViewSwitchApi } from '@kungal/editor-vue'
14
15
 
@@ -16,21 +17,36 @@ const props = defineProps<KunEditorViewSwitchApi>()
16
17
 
17
18
  const items = computed(() => [
18
19
  { value: 'wysiwyg', textValue: props.labels.wysiwyg },
19
- { value: 'source', textValue: props.labels.source }
20
+ { value: 'source', textValue: props.labels.source },
21
+ { value: 'split', textValue: props.labels.split }
20
22
  ])
21
23
 
22
24
  const onChange = (value: string) => {
23
- props.setMode(value === 'source' ? 'source' : 'wysiwyg')
25
+ props.setMode(
26
+ value === 'source' ? 'source' : value === 'split' ? 'split' : 'wysiwyg'
27
+ )
24
28
  }
25
29
  </script>
26
30
 
27
31
  <template>
28
- <KunTab
29
- :model-value="mode"
30
- :items="items"
31
- variant="underlined"
32
- color="primary"
33
- size="sm"
34
- @update:model-value="onChange"
35
- />
32
+ <div class="flex items-center gap-1">
33
+ <KunTab
34
+ :model-value="mode"
35
+ :items="items"
36
+ variant="underlined"
37
+ color="primary"
38
+ size="sm"
39
+ @update:model-value="onChange"
40
+ />
41
+ <KunButton
42
+ v-if="mode === 'split'"
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
+ </div>
36
52
  </template>