@kungal/editor-nuxt 0.14.0 → 0.16.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,58 @@
1
1
  # @kungal/editor-nuxt
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4e160ee: `<KunEditorToolbar>`: the emoji/sticker picker tabs are now a
8
+ `<KunTab variant="underlined" full-width>` instead of two `<KunButton>`s —
9
+ matching `<KunEditorViewSwitch>` and the forum's picker (keyboard nav, sliding
10
+ indicator, a11y). Only shown when a `stickerSource` adapter is present.
11
+
12
+ ### Patch Changes
13
+
14
+ - @kungal/editor-core@0.16.0
15
+ - @kungal/editor-vue@0.16.0
16
+
17
+ ## 0.15.0
18
+
19
+ ### Minor Changes
20
+
21
+ - a7273bb: Ship `tailwind.css` so the KunUI toolbar/picker/tabs styles render drop-in.
22
+
23
+ `<KunEditorToolbar>` / `<KunEditorViewSwitch>` use Tailwind utility classes
24
+ (grids, aspect, sizes, dividers, menu items). Tailwind v4 doesn't scan
25
+ `node_modules`, so without registration the sticker/emoji grid collapsed (huge
26
+ stickers). The package now ships `tailwind.css` containing a path-relative
27
+ `@source "./runtime"` — the Tailwind-team-recommended pattern for a
28
+ Tailwind-based library, and pnpm-layout-safe (resolved relative to the file, not
29
+ the app's `node_modules`). Consumers add one line to their Tailwind entry:
30
+
31
+ ```css
32
+ @import "@kungal/ui-vue/style.css";
33
+ @import "@kungal/editor-nuxt/tailwind.css";
34
+ ```
35
+
36
+ - a7273bb: Toolbar: headings become one "text size" dropdown; remove the formula button.
37
+
38
+ - **Headings → a single "text size" control** (Paragraph / H1–H6) instead of
39
+ three H1/H2/H3 buttons — the modern standard, and it can set Paragraph (reset),
40
+ which the old toggle buttons couldn't. The headless `EditorToolbar` uses a
41
+ native `<select>` (new `.kun-editor__heading-select` class hook); the KunUI
42
+ `<KunEditorToolbar>` uses a `<KunPopover>` with each level shown at its own size
43
+ (like the forum). Both drive `setHeadingCommand`.
44
+ - **Remove the math/formula button.** With no selection it inserted an empty
45
+ inline-math node (a broken formula box). Math is entered via the existing
46
+ `$…$` / `$$` input rules instead — the ecosystem norm (prosemirror-math). The
47
+ spoiler button stays.
48
+
49
+ ### Patch Changes
50
+
51
+ - Updated dependencies [a7273bb]
52
+ - Updated dependencies [a7273bb]
53
+ - @kungal/editor-core@0.15.0
54
+ - @kungal/editor-vue@0.15.0
55
+
3
56
  ## 0.14.0
4
57
 
5
58
  ### Minor Changes
package/nuxt.config.ts CHANGED
@@ -64,9 +64,9 @@ export default defineNuxtConfig({
64
64
  }
65
65
  })
66
66
 
67
- // NOTE: this layer does NOT own a Tailwind entry or import editor styles. The
68
- // consuming app owns one stylesheet and adds `@kungal/editor-vue/style.css`
69
- // alongside its @kungal/ui-tokens + @kungal/ui-vue setup the @source scan
70
- // path is node_modules-layout-specific, so only the app can write it. It also
71
- // assumes @kungal/ui-nuxt is already extended: <KunEditorToolbar> (and the
72
- // reference editor styles) render KunUI chrome. See this package's README.
67
+ // NOTE: this layer assumes @kungal/ui-nuxt is already extended (its components
68
+ // render KunUI chrome). For the KunUI toolbar/picker/tabs utility classes to be
69
+ // generated, the consuming app @imports this package's shipped `tailwind.css`
70
+ // (which `@source`s the layer's components, path-relative so it's pnpm-safe) in
71
+ // its Tailwind entry — alongside `@import '@kungal/ui-vue/style.css'`. The
72
+ // headless editor itself is still styled by the app's own stylesheet. See README.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungal/editor-nuxt",
3
- "version": "0.14.0",
3
+ "version": "0.16.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": [
@@ -27,11 +27,12 @@
27
27
  "CHANGELOG.md",
28
28
  "app",
29
29
  "runtime",
30
+ "tailwind.css",
30
31
  "nuxt.config.ts"
31
32
  ],
32
33
  "dependencies": {
33
- "@kungal/editor-core": "0.14.0",
34
- "@kungal/editor-vue": "0.14.0"
34
+ "@kungal/editor-vue": "0.16.0",
35
+ "@kungal/editor-core": "0.16.0"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "@kungal/ui-vue": "^2.7.0",
@@ -11,8 +11,18 @@
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
- // KunButton/KunIcon/KunTooltip/KunPopover are auto-imported by @kungal/ui-nuxt in
14
+ // Headings are ONE "text size" KunPopover (Paragraph / H1–H6, each shown at its
15
+ // own size — like the forum's header menu), driven by setHeadingCommand (absolute
16
+ // set). There is deliberately NO math button — an empty inline-math atom is a
17
+ // broken node; math is entered via the `$…$` / `$$` input rules.
18
+ //
19
+ // The emoji/sticker picker switches tabs with a <KunTab variant="underlined">
20
+ // (matching <KunEditorViewSwitch> and the forum's picker).
21
+ //
22
+ // KunButton/KunIcon/KunTooltip/KunPopover/KunTab are auto-imported by ui-nuxt in
15
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.
16
26
  import { computed, ref } from 'vue'
17
27
  import {
18
28
  createCodeBlockCommand,
@@ -23,13 +33,12 @@ import {
23
33
  toggleStrongCommand,
24
34
  wrapInBlockquoteCommand,
25
35
  wrapInBulletListCommand,
26
- wrapInHeadingCommand,
27
36
  wrapInOrderedListCommand
28
37
  } from '@milkdown/kit/preset/commonmark'
29
38
  import { toggleStrikethroughCommand } from '@milkdown/kit/preset/gfm'
30
39
  import {
31
40
  insertKunSpoilerCommand,
32
- toggleLatexCommand
41
+ setHeadingCommand
33
42
  } from '@kungal/editor-core/preset'
34
43
  import { EMOJI } from '@kungal/editor-vue'
35
44
  import type { KunEditorToolbarApi, StickerPack } from '@kungal/editor-vue'
@@ -38,20 +47,17 @@ const props = defineProps<KunEditorToolbarApi>()
38
47
 
39
48
  const en = computed(() => props.locale.toLowerCase().startsWith('en'))
40
49
  const t = computed(() => ({
50
+ textSize: en.value ? 'Text size' : '文本大小',
41
51
  bold: en.value ? 'Bold' : '加粗',
42
52
  italic: en.value ? 'Italic' : '斜体',
43
53
  strike: en.value ? 'Strikethrough' : '删除线',
44
54
  code: en.value ? 'Inline code' : '行内代码',
45
- h1: en.value ? 'Heading 1' : '一级标题',
46
- h2: en.value ? 'Heading 2' : '二级标题',
47
- h3: en.value ? 'Heading 3' : '三级标题',
48
55
  bulletList: en.value ? 'Bullet list' : '无序列表',
49
56
  orderedList: en.value ? 'Ordered list' : '有序列表',
50
57
  quote: en.value ? 'Blockquote' : '引用块',
51
58
  codeBlock: en.value ? 'Code block' : '代码块',
52
59
  hr: en.value ? 'Divider' : '分隔线',
53
60
  spoiler: en.value ? 'Spoiler' : '隐藏文本',
54
- latex: en.value ? 'Math (LaTeX)' : '公式',
55
61
  image: en.value ? 'Upload image' : '上传图片',
56
62
  picker: en.value ? 'Emoji & stickers' : '表情与贴纸',
57
63
  emoji: en.value ? 'Emoji' : '表情',
@@ -60,14 +66,27 @@ const t = computed(() => ({
60
66
  failed: en.value ? 'Failed to load stickers' : '贴纸加载失败'
61
67
  }))
62
68
 
69
+ // Paragraph (level 0) + H1–H6, each rendered at its own size in the menu.
70
+ const headingOptions = computed(() => {
71
+ const labels = en.value
72
+ ? ['Paragraph', 'Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6']
73
+ : ['正文', '一级标题', '二级标题', '三级标题', '四级标题', '五级标题', '六级标题']
74
+ const sizes = ['1rem', '1.6rem', '1.4rem', '1.25rem', '1.1rem', '1rem', '0.9rem']
75
+ return labels.map((label, level) => ({ level, label, size: sizes[level] }))
76
+ })
77
+ const headingPopover = ref<{ close: () => void } | null>(null)
78
+ const setHeading = (level: number) => {
79
+ props.run(setHeadingCommand.key, level)
80
+ headingPopover.value?.close()
81
+ }
82
+
63
83
  interface Tool {
64
84
  icon: string
65
85
  title: string
66
86
  run: () => void
67
87
  }
68
88
 
69
- // Same command set + grouping as the default toolbar; `.key` is read at click
70
- // time via api.run (populated once the editor is created).
89
+ // `.key` is read at click time via props.run (populated once the editor exists).
71
90
  const groups = computed<Tool[][]>(() => [
72
91
  [
73
92
  { icon: 'lucide:bold', title: t.value.bold, run: () => props.run(toggleStrongCommand.key) },
@@ -75,11 +94,6 @@ const groups = computed<Tool[][]>(() => [
75
94
  { icon: 'lucide:strikethrough', title: t.value.strike, run: () => props.run(toggleStrikethroughCommand.key) },
76
95
  { icon: 'lucide:code', title: t.value.code, run: () => props.run(toggleInlineCodeCommand.key) }
77
96
  ],
78
- [
79
- { icon: 'lucide:heading-1', title: t.value.h1, run: () => props.run(wrapInHeadingCommand.key, 1) },
80
- { icon: 'lucide:heading-2', title: t.value.h2, run: () => props.run(wrapInHeadingCommand.key, 2) },
81
- { icon: 'lucide:heading-3', title: t.value.h3, run: () => props.run(wrapInHeadingCommand.key, 3) }
82
- ],
83
97
  [
84
98
  { icon: 'lucide:list', title: t.value.bulletList, run: () => props.run(wrapInBulletListCommand.key) },
85
99
  { icon: 'lucide:list-ordered', title: t.value.orderedList, run: () => props.run(wrapInOrderedListCommand.key) },
@@ -88,8 +102,7 @@ const groups = computed<Tool[][]>(() => [
88
102
  { icon: 'lucide:minus', title: t.value.hr, run: () => props.run(insertHrCommand.key) }
89
103
  ],
90
104
  [
91
- { icon: 'lucide:eye-off', title: t.value.spoiler, run: () => props.run(insertKunSpoilerCommand.key) },
92
- { icon: 'lucide:sigma', title: t.value.latex, run: () => props.run(toggleLatexCommand.key) }
105
+ { icon: 'lucide:eye-off', title: t.value.spoiler, run: () => props.run(insertKunSpoilerCommand.key) }
93
106
  ]
94
107
  ])
95
108
 
@@ -116,7 +129,7 @@ const onFileChange = async (e: Event) => {
116
129
  const showPicker = computed(() => props.features.sticker !== false)
117
130
  const stickerSource = computed(() => props.adapters.stickerSource)
118
131
  const hasSticker = computed(() => !!stickerSource.value)
119
- const tab = ref<'emoji' | 'sticker'>('emoji')
132
+ const tab = ref('emoji') // 'emoji' | 'sticker' — widened to string for KunTab v-model
120
133
  const packs = ref<StickerPack[]>([])
121
134
  const loaded = ref(false)
122
135
  const loadFailed = ref(false)
@@ -136,8 +149,27 @@ const insertSticker = (src: string, name: string) =>
136
149
 
137
150
  <template>
138
151
  <div class="flex flex-wrap items-center gap-0.5">
152
+ <KunPopover ref="headingPopover" position="bottom-start" :opaque="true" inner-class="min-w-40 p-1">
153
+ <template #trigger>
154
+ <KunButton variant="light" size="sm" :aria-label="t.textSize">
155
+ {{ t.textSize }}
156
+ <KunIcon name="lucide:chevron-down" />
157
+ </KunButton>
158
+ </template>
159
+ <button
160
+ v-for="o in headingOptions"
161
+ :key="o.level"
162
+ type="button"
163
+ class="hover:bg-default-100 flex w-full items-center rounded px-3 py-1.5 text-left leading-tight"
164
+ :style="{ fontSize: o.size }"
165
+ @click="setHeading(o.level)"
166
+ >
167
+ {{ o.label }}
168
+ </button>
169
+ </KunPopover>
170
+
139
171
  <template v-for="(group, gi) in groups" :key="gi">
140
- <span v-if="gi > 0" class="bg-default-200 mx-1 h-5 w-px" aria-hidden="true" />
172
+ <span class="bg-default-200 mx-1 h-5 w-px" aria-hidden="true" />
141
173
  <KunTooltip v-for="(b, bi) in group" :key="bi" :text="b.title" :delay-show="300">
142
174
  <KunButton
143
175
  variant="light"
@@ -188,20 +220,18 @@ const insertSticker = (src: string, name: string) =>
188
220
  </KunButton>
189
221
  </template>
190
222
 
191
- <div v-if="hasSticker" class="mb-2 flex gap-1">
192
- <KunButton
193
- :variant="tab === 'emoji' ? 'flat' : 'light'"
194
- size="sm"
195
- @click="tab = 'emoji'"
196
- >{{ t.emoji }}</KunButton
197
- >
198
- <KunButton
199
- :variant="tab === 'sticker' ? 'flat' : 'light'"
200
- size="sm"
201
- @click="tab = 'sticker'"
202
- >{{ t.sticker }}</KunButton
203
- >
204
- </div>
223
+ <KunTab
224
+ v-if="hasSticker"
225
+ v-model="tab"
226
+ :items="[
227
+ { value: 'emoji', textValue: t.emoji },
228
+ { value: 'sticker', textValue: t.sticker }
229
+ ]"
230
+ variant="underlined"
231
+ color="primary"
232
+ :full-width="true"
233
+ class="mb-2"
234
+ />
205
235
 
206
236
  <div
207
237
  v-show="!hasSticker || tab === 'emoji'"
package/tailwind.css ADDED
@@ -0,0 +1,17 @@
1
+ /*
2
+ * @kungal/editor-nuxt — Tailwind source registration.
3
+ *
4
+ * <KunEditorToolbar> / <KunEditorViewSwitch> are built with Tailwind utility
5
+ * classes (grids, aspect, sizes, dividers, menu items…). Tailwind v4 does NOT
6
+ * scan node_modules by default, so without this those classes aren't generated
7
+ * and the toolbar / sticker grid render broken (collapsed cells, huge stickers).
8
+ *
9
+ * `@source` resolves relative to THIS file, so it registers the layer's own
10
+ * components regardless of the consumer's node_modules layout (pnpm-safe). Import
11
+ * it ONCE in your Tailwind entry, next to `@import '@kungal/ui-vue/style.css'`:
12
+ *
13
+ * @import 'tailwindcss';
14
+ * @import '@kungal/ui-vue/style.css';
15
+ * @import '@kungal/editor-nuxt/tailwind.css';
16
+ */
17
+ @source "./runtime";