@kungal/editor-nuxt 0.13.0 → 0.15.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,77 @@
1
1
  # @kungal/editor-nuxt
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a7273bb: Ship `tailwind.css` so the KunUI toolbar/picker/tabs styles render drop-in.
8
+
9
+ `<KunEditorToolbar>` / `<KunEditorViewSwitch>` use Tailwind utility classes
10
+ (grids, aspect, sizes, dividers, menu items). Tailwind v4 doesn't scan
11
+ `node_modules`, so without registration the sticker/emoji grid collapsed (huge
12
+ stickers). The package now ships `tailwind.css` containing a path-relative
13
+ `@source "./runtime"` — the Tailwind-team-recommended pattern for a
14
+ Tailwind-based library, and pnpm-layout-safe (resolved relative to the file, not
15
+ the app's `node_modules`). Consumers add one line to their Tailwind entry:
16
+
17
+ ```css
18
+ @import "@kungal/ui-vue/style.css";
19
+ @import "@kungal/editor-nuxt/tailwind.css";
20
+ ```
21
+
22
+ - a7273bb: Toolbar: headings become one "text size" dropdown; remove the formula button.
23
+
24
+ - **Headings → a single "text size" control** (Paragraph / H1–H6) instead of
25
+ three H1/H2/H3 buttons — the modern standard, and it can set Paragraph (reset),
26
+ which the old toggle buttons couldn't. The headless `EditorToolbar` uses a
27
+ native `<select>` (new `.kun-editor__heading-select` class hook); the KunUI
28
+ `<KunEditorToolbar>` uses a `<KunPopover>` with each level shown at its own size
29
+ (like the forum). Both drive `setHeadingCommand`.
30
+ - **Remove the math/formula button.** With no selection it inserted an empty
31
+ inline-math node (a broken formula box). Math is entered via the existing
32
+ `$…$` / `$$` input rules instead — the ecosystem norm (prosemirror-math). The
33
+ spoiler button stays.
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [a7273bb]
38
+ - Updated dependencies [a7273bb]
39
+ - @kungal/editor-core@0.15.0
40
+ - @kungal/editor-vue@0.15.0
41
+
42
+ ## 0.14.0
43
+
44
+ ### Minor Changes
45
+
46
+ - 506813b: Add a `#view-switch` slot and a KunUI view switch — the Preview/Markdown tabs are
47
+ now a swappable layer too.
48
+
49
+ `<KunEditor>` exposes a `#view-switch` scoped slot whose props are
50
+ `KunEditorViewSwitchApi` (`mode` / `setMode` / `labels`). The hand-rolled tabs
51
+ are the slot's fallback — omit the slot and nothing changes. This lets a host
52
+ render a real component (or anything) for the view switch and drive the editor
53
+ via `setMode`, without reaching into internals.
54
+
55
+ `@kungal/editor-nuxt` ships `<KunEditorViewSwitch>` (auto-imported): the tabs as a
56
+ real `<KunTab variant="underlined">` (keyboard nav, sliding indicator, a11y):
57
+
58
+ ```vue
59
+ <KunEditor v-model="md">
60
+ <template #view-switch="s"><KunEditorViewSwitch v-bind="s" /></template>
61
+ <template #toolbar="api"><KunEditorToolbar v-bind="api" /></template>
62
+ </KunEditor>
63
+ ```
64
+
65
+ Same "headless core + optional KunUI layer" pattern as `#toolbar` /
66
+ `<KunEditorToolbar>`: `@kungal/editor-vue` stays UI-kit-free (default tabs), the
67
+ KunUI ecosystem gets native chrome. Exports `KunEditorViewSwitchApi`.
68
+
69
+ ### Patch Changes
70
+
71
+ - Updated dependencies [506813b]
72
+ - @kungal/editor-vue@0.14.0
73
+ - @kungal/editor-core@0.14.0
74
+
3
75
  ## 0.13.0
4
76
 
5
77
  ### Minor Changes
package/nuxt.config.ts CHANGED
@@ -5,10 +5,12 @@ import { defineNuxtModule, addComponent, createResolver } from '@nuxt/kit'
5
5
  // downstream templates use `<KunEditor>` with no import (and Nuxt generates its
6
6
  // types, keeping the tag type-checked in consumer templates).
7
7
  //
8
- // It also ships <KunEditorToolbar>: the KunUI-built toolbar for <KunEditor>'s
9
- // #toolbar slot. It belongs HERE (this layer already assumes @kungal/ui-nuxt),
10
- // not in headless @kungal/editor-vue so editor-vue stays UI-kit-free while the
11
- // KunUI ecosystem gets native chrome. Same shape as TipTap's optional UI pkg.
8
+ // It also ships the KunUI chrome for <KunEditor>'s slots — <KunEditorToolbar>
9
+ // (#toolbar) and <KunEditorViewSwitch> (#view-switch, the Preview/Markdown tabs
10
+ // as a real <KunTab>). They belong HERE (this layer already assumes
11
+ // @kungal/ui-nuxt), not in headless @kungal/editor-vue so editor-vue stays
12
+ // UI-kit-free while the KunUI ecosystem gets native chrome. Same shape as
13
+ // TipTap's optional UI package.
12
14
  export default defineNuxtConfig({
13
15
  modules: [
14
16
  defineNuxtModule({
@@ -24,6 +26,10 @@ export default defineNuxtConfig({
24
26
  name: 'KunEditorToolbar',
25
27
  filePath: resolve('./runtime/KunEditorToolbar.vue')
26
28
  })
29
+ addComponent({
30
+ name: 'KunEditorViewSwitch',
31
+ filePath: resolve('./runtime/KunEditorViewSwitch.vue')
32
+ })
27
33
  }
28
34
  })
29
35
  ],
@@ -58,9 +64,9 @@ export default defineNuxtConfig({
58
64
  }
59
65
  })
60
66
 
61
- // NOTE: this layer does NOT own a Tailwind entry or import editor styles. The
62
- // consuming app owns one stylesheet and adds `@kungal/editor-vue/style.css`
63
- // alongside its @kungal/ui-tokens + @kungal/ui-vue setup the @source scan
64
- // path is node_modules-layout-specific, so only the app can write it. It also
65
- // assumes @kungal/ui-nuxt is already extended: <KunEditorToolbar> (and the
66
- // 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.13.0",
3
+ "version": "0.15.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.13.0",
34
- "@kungal/editor-vue": "0.13.0"
34
+ "@kungal/editor-core": "0.15.0",
35
+ "@kungal/editor-vue": "0.15.0"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "@kungal/ui-vue": "^2.7.0",
@@ -11,8 +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
+ // 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
+ //
14
19
  // KunButton/KunIcon/KunTooltip/KunPopover are auto-imported by @kungal/ui-nuxt in
15
20
  // the consuming app; icons use the app's iconify set (e.g. lucide via @nuxt/icon).
21
+ // The container/menu utility classes are generated via @kungal/editor-nuxt's
22
+ // shipped tailwind.css (@source) — the consumer @imports it once.
16
23
  import { computed, ref } from 'vue'
17
24
  import {
18
25
  createCodeBlockCommand,
@@ -23,13 +30,12 @@ import {
23
30
  toggleStrongCommand,
24
31
  wrapInBlockquoteCommand,
25
32
  wrapInBulletListCommand,
26
- wrapInHeadingCommand,
27
33
  wrapInOrderedListCommand
28
34
  } from '@milkdown/kit/preset/commonmark'
29
35
  import { toggleStrikethroughCommand } from '@milkdown/kit/preset/gfm'
30
36
  import {
31
37
  insertKunSpoilerCommand,
32
- toggleLatexCommand
38
+ setHeadingCommand
33
39
  } from '@kungal/editor-core/preset'
34
40
  import { EMOJI } from '@kungal/editor-vue'
35
41
  import type { KunEditorToolbarApi, StickerPack } from '@kungal/editor-vue'
@@ -38,20 +44,17 @@ const props = defineProps<KunEditorToolbarApi>()
38
44
 
39
45
  const en = computed(() => props.locale.toLowerCase().startsWith('en'))
40
46
  const t = computed(() => ({
47
+ textSize: en.value ? 'Text size' : '文本大小',
41
48
  bold: en.value ? 'Bold' : '加粗',
42
49
  italic: en.value ? 'Italic' : '斜体',
43
50
  strike: en.value ? 'Strikethrough' : '删除线',
44
51
  code: en.value ? 'Inline code' : '行内代码',
45
- h1: en.value ? 'Heading 1' : '一级标题',
46
- h2: en.value ? 'Heading 2' : '二级标题',
47
- h3: en.value ? 'Heading 3' : '三级标题',
48
52
  bulletList: en.value ? 'Bullet list' : '无序列表',
49
53
  orderedList: en.value ? 'Ordered list' : '有序列表',
50
54
  quote: en.value ? 'Blockquote' : '引用块',
51
55
  codeBlock: en.value ? 'Code block' : '代码块',
52
56
  hr: en.value ? 'Divider' : '分隔线',
53
57
  spoiler: en.value ? 'Spoiler' : '隐藏文本',
54
- latex: en.value ? 'Math (LaTeX)' : '公式',
55
58
  image: en.value ? 'Upload image' : '上传图片',
56
59
  picker: en.value ? 'Emoji & stickers' : '表情与贴纸',
57
60
  emoji: en.value ? 'Emoji' : '表情',
@@ -60,14 +63,27 @@ const t = computed(() => ({
60
63
  failed: en.value ? 'Failed to load stickers' : '贴纸加载失败'
61
64
  }))
62
65
 
66
+ // Paragraph (level 0) + H1–H6, each rendered at its own size in the menu.
67
+ const headingOptions = computed(() => {
68
+ const labels = en.value
69
+ ? ['Paragraph', 'Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6']
70
+ : ['正文', '一级标题', '二级标题', '三级标题', '四级标题', '五级标题', '六级标题']
71
+ const sizes = ['1rem', '1.6rem', '1.4rem', '1.25rem', '1.1rem', '1rem', '0.9rem']
72
+ return labels.map((label, level) => ({ level, label, size: sizes[level] }))
73
+ })
74
+ const headingPopover = ref<{ close: () => void } | null>(null)
75
+ const setHeading = (level: number) => {
76
+ props.run(setHeadingCommand.key, level)
77
+ headingPopover.value?.close()
78
+ }
79
+
63
80
  interface Tool {
64
81
  icon: string
65
82
  title: string
66
83
  run: () => void
67
84
  }
68
85
 
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).
86
+ // `.key` is read at click time via props.run (populated once the editor exists).
71
87
  const groups = computed<Tool[][]>(() => [
72
88
  [
73
89
  { icon: 'lucide:bold', title: t.value.bold, run: () => props.run(toggleStrongCommand.key) },
@@ -75,11 +91,6 @@ const groups = computed<Tool[][]>(() => [
75
91
  { icon: 'lucide:strikethrough', title: t.value.strike, run: () => props.run(toggleStrikethroughCommand.key) },
76
92
  { icon: 'lucide:code', title: t.value.code, run: () => props.run(toggleInlineCodeCommand.key) }
77
93
  ],
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
94
  [
84
95
  { icon: 'lucide:list', title: t.value.bulletList, run: () => props.run(wrapInBulletListCommand.key) },
85
96
  { icon: 'lucide:list-ordered', title: t.value.orderedList, run: () => props.run(wrapInOrderedListCommand.key) },
@@ -88,8 +99,7 @@ const groups = computed<Tool[][]>(() => [
88
99
  { icon: 'lucide:minus', title: t.value.hr, run: () => props.run(insertHrCommand.key) }
89
100
  ],
90
101
  [
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) }
102
+ { icon: 'lucide:eye-off', title: t.value.spoiler, run: () => props.run(insertKunSpoilerCommand.key) }
93
103
  ]
94
104
  ])
95
105
 
@@ -136,8 +146,27 @@ const insertSticker = (src: string, name: string) =>
136
146
 
137
147
  <template>
138
148
  <div class="flex flex-wrap items-center gap-0.5">
149
+ <KunPopover ref="headingPopover" position="bottom-start" :opaque="true" inner-class="min-w-40 p-1">
150
+ <template #trigger>
151
+ <KunButton variant="light" size="sm" :aria-label="t.textSize">
152
+ {{ t.textSize }}
153
+ <KunIcon name="lucide:chevron-down" />
154
+ </KunButton>
155
+ </template>
156
+ <button
157
+ v-for="o in headingOptions"
158
+ :key="o.level"
159
+ type="button"
160
+ class="hover:bg-default-100 flex w-full items-center rounded px-3 py-1.5 text-left leading-tight"
161
+ :style="{ fontSize: o.size }"
162
+ @click="setHeading(o.level)"
163
+ >
164
+ {{ o.label }}
165
+ </button>
166
+ </KunPopover>
167
+
139
168
  <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" />
169
+ <span class="bg-default-200 mx-1 h-5 w-px" aria-hidden="true" />
141
170
  <KunTooltip v-for="(b, bi) in group" :key="bi" :text="b.title" :delay-show="300">
142
171
  <KunButton
143
172
  variant="light"
@@ -0,0 +1,36 @@
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.
6
+ //
7
+ // <KunEditor v-model="md">
8
+ // <template #view-switch="s"><KunEditorViewSwitch v-bind="s" /></template>
9
+ // </KunEditor>
10
+ //
11
+ // KunTab is auto-imported by @kungal/ui-nuxt in the consuming app.
12
+ import { computed } from 'vue'
13
+ import type { KunEditorViewSwitchApi } from '@kungal/editor-vue'
14
+
15
+ const props = defineProps<KunEditorViewSwitchApi>()
16
+
17
+ const items = computed(() => [
18
+ { value: 'wysiwyg', textValue: props.labels.wysiwyg },
19
+ { value: 'source', textValue: props.labels.source }
20
+ ])
21
+
22
+ const onChange = (value: string) => {
23
+ props.setMode(value === 'source' ? 'source' : 'wysiwyg')
24
+ }
25
+ </script>
26
+
27
+ <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
+ />
36
+ </template>
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";