@skaile/forge-editor 5.0.3 → 5.1.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/code/CodeSurface.vue.d.ts +3 -3
  3. package/dist/code/index.js +1 -1
  4. package/dist/core/toolbar-fit.js +99 -0
  5. package/dist/core/toolbar-fit.js.map +1 -0
  6. package/dist/document/DocumentSurface.js +1 -1
  7. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  8. package/dist/features/ai/agent-ops.js +1 -1
  9. package/dist/features/ai/agent-ops.js.map +1 -1
  10. package/dist/features/ai/document-chat.d.ts +3 -2
  11. package/dist/features/ai/document-chat.js +6 -4
  12. package/dist/features/ai/document-chat.js.map +1 -1
  13. package/dist/features/ai/protocol/block-lines.d.ts +2 -1
  14. package/dist/features/ai/protocol/block-lines.js +2 -1
  15. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  16. package/dist/features/themes/index.d.ts +2 -2
  17. package/dist/features/themes/index.js +2 -2
  18. package/dist/features/themes/theme-runtime.d.ts +15 -4
  19. package/dist/features/themes/theme-runtime.js +76 -11
  20. package/dist/features/themes/theme-runtime.js.map +1 -1
  21. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  22. package/dist/react/EditorToolbar.d.ts +5 -1
  23. package/dist/react/EditorToolbar.js +76 -18
  24. package/dist/react/EditorToolbar.js.map +1 -1
  25. package/dist/server/document-blocks.js +5 -1
  26. package/dist/server/document-blocks.js.map +1 -1
  27. package/dist/server/handlers/ai.d.ts +1 -1
  28. package/dist/styles/themes.css +10 -8
  29. package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
  30. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  31. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  32. package/dist/vue/components/EditorToolbar.js +81 -9
  33. package/dist/vue/components/EditorToolbar.js.map +1 -1
  34. package/dist/vue/components/EditorToolbar.vue.d.ts +7 -1
  35. package/package.json +1 -1
@@ -4,10 +4,16 @@ import { useUiKit } from "../kit/use-ui-kit.js";
4
4
  import { useEditorContext } from "../context.js";
5
5
  import { useEditorI18n } from "../use-editor-i18n.js";
6
6
  import { trackedStore } from "../store-ref.js";
7
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, getCurrentInstance, openBlock, renderList, resolveDynamicComponent, toRaw, unref, withCtx, withModifiers } from "vue";
7
+ import { collapsedGroupMenu, fitToolbar } from "../../core/toolbar-fit.js";
8
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, getCurrentInstance, normalizeClass, onBeforeUnmount, onMounted, onUpdated, openBlock, ref, renderList, resolveDynamicComponent, toRaw, unref, useTemplateRef, withCtx, withModifiers } from "vue";
8
9
  //#region src/vue/components/EditorToolbar.vue
9
10
  const _hoisted_1 = ["aria-label"];
10
- const _hoisted_2 = {
11
+ const _hoisted_2 = [
12
+ "data-toolbar-group",
13
+ "data-items",
14
+ "data-collapsed"
15
+ ];
16
+ const _hoisted_3 = {
11
17
  key: 0,
12
18
  role: "separator",
13
19
  "aria-orientation": "vertical",
@@ -17,10 +23,16 @@ const _hoisted_2 = {
17
23
  * The toolbar of an editor, placed anywhere on the page. `editor` is the instance it works on (from the surface's
18
24
  * `ready` event); the preset registered for that instance (`editorEntryOf`) supplies the groups. Without an editor
19
25
  * (not mounted yet, or read-only by the host's choice) it renders no groups. Never reads a global active editor.
26
+ * `overflow`: `wrap` (default) breaks onto further lines; `collapse` keeps one line, and the groups that do not fit
27
+ * collapse from the end into one dropdown each (`component` commands stay beside it). The row then takes the width its
28
+ * container gives it.
20
29
  */
21
30
  const _sfc_main = /*@__PURE__*/ defineComponent({
22
31
  __name: "EditorToolbar",
23
- props: { editor: {} },
32
+ props: {
33
+ editor: {},
34
+ overflow: { default: "wrap" }
35
+ },
24
36
  setup(__props) {
25
37
  const props = __props;
26
38
  const { Button: UButton, DropdownMenu: UDropdownMenu, FieldGroup: UFieldGroup, Tooltip: UTooltip } = useUiKit();
@@ -68,13 +80,73 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
68
80
  function componentOf(cmd) {
69
81
  return "component" in cmd ? cmd.component : void 0;
70
82
  }
83
+ const expandedCount = ref(Number.POSITIVE_INFINITY);
84
+ const row = useTemplateRef("row");
85
+ let fit;
86
+ onMounted(() => {
87
+ if (props.overflow === "collapse" && row.value) fit = fitToolbar(row.value, (count) => {
88
+ expandedCount.value = count;
89
+ });
90
+ });
91
+ onUpdated(() => fit?.refresh());
92
+ onBeforeUnmount(() => fit?.dispose());
93
+ function groupLabel(group) {
94
+ return group.labelKey ? t(group.labelKey) : group.id;
95
+ }
96
+ function menuCount(items) {
97
+ return items.filter(({ cmd }) => !("component" in cmd)).length;
98
+ }
99
+ /** A group past the expanded prefix; one with only `component` commands has nothing to collapse. */
100
+ function isCollapsed(index, items) {
101
+ return props.overflow === "collapse" && index >= expandedCount.value && menuCount(items) > 0;
102
+ }
71
103
  return (_ctx, _cache) => {
72
104
  return openBlock(), createElementBlock("div", {
73
- class: "flex flex-wrap items-center gap-1.5",
105
+ ref_key: "row",
106
+ ref: row,
107
+ class: normalizeClass(["flex items-center gap-1.5", __props.overflow === "collapse" ? "min-w-0 flex-nowrap overflow-hidden" : "flex-wrap"]),
74
108
  role: "toolbar",
75
109
  "aria-label": unref(t)("toolbar.label")
76
110
  }, [(openBlock(true), createElementBlock(Fragment, null, renderList(visibleGroups.value, ({ group, items }, gi) => {
77
- return openBlock(), createElementBlock(Fragment, { key: group.id }, [gi > 0 ? (openBlock(), createElementBlock("span", _hoisted_2)) : createCommentVNode("v-if", true), createVNode(unref(UFieldGroup), { size: "sm" }, {
111
+ return openBlock(), createElementBlock("div", {
112
+ key: group.id,
113
+ class: normalizeClass(__props.overflow === "collapse" ? "flex shrink-0 items-center gap-1.5" : "contents"),
114
+ "data-toolbar-group": group.id,
115
+ "data-items": menuCount(items),
116
+ "data-collapsed": isCollapsed(gi, items) ? "" : void 0
117
+ }, [gi > 0 ? (openBlock(), createElementBlock("span", _hoisted_3)) : createCommentVNode("v-if", true), isCollapsed(gi, items) ? (openBlock(), createBlock(unref(UFieldGroup), {
118
+ key: 1,
119
+ size: "sm"
120
+ }, {
121
+ default: withCtx(() => [createVNode(unref(UDropdownMenu), { items: unref(collapsedGroupMenu)(items, props.editor) }, {
122
+ default: withCtx(() => [createVNode(unref(UTooltip), { text: groupLabel(group) }, {
123
+ default: withCtx(() => [createVNode(unref(UButton), {
124
+ color: "neutral",
125
+ variant: "ghost",
126
+ icon: items.find(({ cmd }) => !("component" in cmd)).cmd.icon,
127
+ "trailing-icon": "i-lucide-chevron-down",
128
+ "aria-label": groupLabel(group),
129
+ disabled: !items.some(({ cmd, state }) => !("component" in cmd) && state.enabled),
130
+ onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
131
+ }, null, 8, [
132
+ "icon",
133
+ "aria-label",
134
+ "disabled"
135
+ ])]),
136
+ _: 2
137
+ }, 1032, ["text"])]),
138
+ _: 2
139
+ }, 1032, ["items"]), (openBlock(true), createElementBlock(Fragment, null, renderList(items, ({ cmd }) => {
140
+ return openBlock(), createElementBlock(Fragment, { key: cmd.id }, [componentOf(cmd) ? (openBlock(), createBlock(resolveDynamicComponent(componentOf(cmd)), {
141
+ key: 0,
142
+ editor: props.editor
143
+ }, null, 8, ["editor"])) : createCommentVNode("v-if", true)], 64);
144
+ }), 128))]),
145
+ _: 2
146
+ }, 1024)) : (openBlock(), createBlock(unref(UFieldGroup), {
147
+ key: 2,
148
+ size: "sm"
149
+ }, {
78
150
  default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(items, ({ cmd, state }) => {
79
151
  return openBlock(), createElementBlock(Fragment, { key: cmd.id }, [componentOf(cmd) ? (openBlock(), createBlock(resolveDynamicComponent(componentOf(cmd)), {
80
152
  key: 0,
@@ -94,7 +166,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
94
166
  icon: cmd.icon,
95
167
  "aria-label": cmd.label,
96
168
  disabled: !state.enabled,
97
- onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
169
+ onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"]))
98
170
  }, null, 8, [
99
171
  "icon",
100
172
  "aria-label",
@@ -119,7 +191,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
119
191
  active: state.active,
120
192
  "aria-pressed": cmd.active ? state.active : void 0,
121
193
  disabled: !state.enabled,
122
- onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])),
194
+ onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"])),
123
195
  onClick: ($event) => run(cmd)
124
196
  }, null, 8, [
125
197
  "icon",
@@ -133,8 +205,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
133
205
  }, 1032, ["text", "kbds"]))], 64);
134
206
  }), 128))]),
135
207
  _: 2
136
- }, 1024)], 64);
137
- }), 128))], 8, _hoisted_1);
208
+ }, 1024))], 10, _hoisted_2);
209
+ }), 128))], 10, _hoisted_1);
138
210
  };
139
211
  }
140
212
  });
@@ -1 +1 @@
1
- {"version":3,"file":"EditorToolbar.js","names":["_openBlock","_createElementBlock","_unref","_Fragment","_renderList","_createCommentVNode","_createVNode","_withCtx","_createBlock","_resolveDynamicComponent","_withModifiers"],"sources":["../../../src/vue/components/EditorToolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useUiKit } from '../kit/use-ui-kit'\nimport type { Editor } from '@tiptap/core'\nimport { type Component, computed, getCurrentInstance, toRaw } from 'vue'\nimport { type CommandDef, type CommandState, commandState, type MenuItem, type PlacedToolbarGroup, toolbarOf } from '../../core/commands'\nimport { borrowedFeatureContext, editorEntryOf } from '../../core/editor-ui'\nimport { useEditorContext } from '../context'\nimport { useEditorI18n } from '../use-editor-i18n'\nimport { trackedStore } from '../store-ref'\nimport type { EditorPreset } from '../../core/feature'\n\n/**\n * The toolbar of an editor, placed anywhere on the page. `editor` is the instance it works on (from the surface's\n * `ready` event); the preset registered for that instance (`editorEntryOf`) supplies the groups. Without an editor\n * (not mounted yet, or read-only by the host's choice) it renders no groups. Never reads a global active editor.\n */\nconst props = defineProps<{ editor?: Editor }>()\n\nconst { Button: UButton, DropdownMenu: UDropdownMenu, FieldGroup: UFieldGroup, Tooltip: UTooltip } = useUiKit()\n\nconst appContext = useEditorContext()\nconst { t } = useEditorI18n()\nconst instance = getCurrentInstance()\n\n// The context toolbar factories see: settings and fetch of the app, everything per-surface delegates to the editor's\n// registered context while there is one.\nconst ctx = borrowedFeatureContext({\n app: appContext,\n getEditor: () => props.editor,\n unwrap: toRaw,\n // Tracked: `active`/`visible`/`menu` read the settings during render, which must re-run when they change.\n settings: trackedStore(appContext.settings),\n})\n\n// Factories run during setup (they may use composables that inject); a later preset is built within the app context.\nconst built = new WeakMap<EditorPreset, PlacedToolbarGroup[]>()\nfunction groupsOf(preset: EditorPreset | undefined): PlacedToolbarGroup[] {\n if (!preset) return []\n const cached = built.get(preset)\n if (cached) return cached\n const build = () => toolbarOf(preset, ctx)\n const groups = instance ? instance.appContext.app.runWithContext(build) : build()\n built.set(preset, groups)\n return groups\n}\n\n// Every enabled/visible/active state comes from `commandState`. It reads the vue-3 Editor's reactive `state`\n// (through `can()` and the abilities), so this re-evaluates on every transaction. The raw instance is what the\n// registered entry (and with it the insert guards) is keyed by.\nconst NO_EDITOR: CommandState = { visible: true, enabled: false, active: false }\nconst visibleGroups = computed(() => {\n const editor = props.editor && toRaw(props.editor)\n const groups = groupsOf(editorEntryOf(editor)?.preset)\n return groups\n .filter(group => !editor || !group.visible || group.visible(editor))\n .map(group => ({\n group,\n items: group.items\n .map(cmd => ({ cmd, state: editor ? commandState(editor, cmd, 'selection') : NO_EDITOR }))\n .filter(({ state }) => state.visible),\n }))\n .filter(({ items }) => items.length > 0)\n})\n\nfunction run(cmd: CommandDef) {\n if (props.editor && 'run' in cmd) cmd.run(props.editor)\n}\nfunction menuOf(cmd: CommandDef): MenuItem[] {\n return props.editor && 'menu' in cmd ? cmd.menu(props.editor) : []\n}\n/** Descriptors are framework-agnostic (`component: unknown`); in this Vue toolbar they are Vue components. */\nfunction componentOf(cmd: CommandDef): Component | undefined {\n return 'component' in cmd ? cmd.component as Component : undefined\n}\n</script>\n\n<template>\n <div class=\"flex flex-wrap items-center gap-1.5\" role=\"toolbar\" :aria-label=\"t('toolbar.label')\">\n <template v-for=\"({ group, items }, gi) in visibleGroups\" :key=\"group.id\">\n <span v-if=\"gi > 0\" role=\"separator\" aria-orientation=\"vertical\" class=\"mx-2 h-6 w-[2px] shrink-0 bg-[var(--sk-border-accented)]\" />\n <UFieldGroup size=\"sm\">\n <template v-for=\"{ cmd, state } in items\" :key=\"cmd.id\">\n <component :is=\"componentOf(cmd)\" v-if=\"componentOf(cmd)\" :editor=\"props.editor\" />\n <UDropdownMenu v-else-if=\"'menu' in cmd\" :items=\"menuOf(cmd)\">\n <UTooltip :text=\"cmd.label\" :kbds=\"cmd.kbds\">\n <UButton\n :key=\"cmd.icon\"\n color=\"neutral\"\n variant=\"ghost\"\n :icon=\"cmd.icon\"\n :aria-label=\"cmd.label\"\n :disabled=\"!state.enabled\"\n @mousedown.prevent\n />\n </UTooltip>\n </UDropdownMenu>\n <UTooltip v-else :text=\"cmd.label\" :kbds=\"cmd.kbds\">\n <UButton\n :key=\"cmd.icon\"\n color=\"neutral\"\n variant=\"ghost\"\n active-color=\"primary\"\n active-variant=\"soft\"\n :icon=\"cmd.icon\"\n :aria-label=\"cmd.label\"\n :active=\"state.active\"\n :aria-pressed=\"cmd.active ? state.active : undefined\"\n :disabled=\"!state.enabled\"\n @mousedown.prevent\n @click=\"run(cmd)\"\n />\n </UTooltip>\n </template>\n </UFieldGroup>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"EditorToolbar.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_Fragment","_renderList","_createCommentVNode","_createBlock","_withCtx","_createVNode","_withModifiers","_resolveDynamicComponent"],"sources":["../../../src/vue/components/EditorToolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useUiKit } from '../kit/use-ui-kit'\nimport type { Editor } from '@tiptap/core'\nimport { type Component, computed, getCurrentInstance, onBeforeUnmount, onMounted, onUpdated, ref, toRaw, useTemplateRef } from 'vue'\nimport { type CommandDef, type CommandState, commandState, type MenuItem, type PlacedToolbarGroup, toolbarOf } from '../../core/commands'\nimport { collapsedGroupMenu, fitToolbar, type ToolbarFit } from '../../core/toolbar-fit'\nimport { borrowedFeatureContext, editorEntryOf } from '../../core/editor-ui'\nimport { useEditorContext } from '../context'\nimport { useEditorI18n } from '../use-editor-i18n'\nimport { trackedStore } from '../store-ref'\nimport type { EditorPreset } from '../../core/feature'\n\n/**\n * The toolbar of an editor, placed anywhere on the page. `editor` is the instance it works on (from the surface's\n * `ready` event); the preset registered for that instance (`editorEntryOf`) supplies the groups. Without an editor\n * (not mounted yet, or read-only by the host's choice) it renders no groups. Never reads a global active editor.\n * `overflow`: `wrap` (default) breaks onto further lines; `collapse` keeps one line, and the groups that do not fit\n * collapse from the end into one dropdown each (`component` commands stay beside it). The row then takes the width its\n * container gives it.\n */\nconst props = withDefaults(defineProps<{ editor?: Editor, overflow?: 'wrap' | 'collapse' }>(), { overflow: 'wrap' })\n\nconst { Button: UButton, DropdownMenu: UDropdownMenu, FieldGroup: UFieldGroup, Tooltip: UTooltip } = useUiKit()\n\nconst appContext = useEditorContext()\nconst { t } = useEditorI18n()\nconst instance = getCurrentInstance()\n\n// The context toolbar factories see: settings and fetch of the app, everything per-surface delegates to the editor's\n// registered context while there is one.\nconst ctx = borrowedFeatureContext({\n app: appContext,\n getEditor: () => props.editor,\n unwrap: toRaw,\n // Tracked: `active`/`visible`/`menu` read the settings during render, which must re-run when they change.\n settings: trackedStore(appContext.settings),\n})\n\n// Factories run during setup (they may use composables that inject); a later preset is built within the app context.\nconst built = new WeakMap<EditorPreset, PlacedToolbarGroup[]>()\nfunction groupsOf(preset: EditorPreset | undefined): PlacedToolbarGroup[] {\n if (!preset) return []\n const cached = built.get(preset)\n if (cached) return cached\n const build = () => toolbarOf(preset, ctx)\n const groups = instance ? instance.appContext.app.runWithContext(build) : build()\n built.set(preset, groups)\n return groups\n}\n\n// Every enabled/visible/active state comes from `commandState`. It reads the vue-3 Editor's reactive `state`\n// (through `can()` and the abilities), so this re-evaluates on every transaction. The raw instance is what the\n// registered entry (and with it the insert guards) is keyed by.\nconst NO_EDITOR: CommandState = { visible: true, enabled: false, active: false }\nconst visibleGroups = computed(() => {\n const editor = props.editor && toRaw(props.editor)\n const groups = groupsOf(editorEntryOf(editor)?.preset)\n return groups\n .filter(group => !editor || !group.visible || group.visible(editor))\n .map(group => ({\n group,\n items: group.items\n .map(cmd => ({ cmd, state: editor ? commandState(editor, cmd, 'selection') : NO_EDITOR }))\n .filter(({ state }) => state.visible),\n }))\n .filter(({ items }) => items.length > 0)\n})\n\nfunction run(cmd: CommandDef) {\n if (props.editor && 'run' in cmd) cmd.run(props.editor)\n}\nfunction menuOf(cmd: CommandDef): MenuItem[] {\n return props.editor && 'menu' in cmd ? cmd.menu(props.editor) : []\n}\n/** Descriptors are framework-agnostic (`component: unknown`); in this Vue toolbar they are Vue components. */\nfunction componentOf(cmd: CommandDef): Component | undefined {\n return 'component' in cmd ? cmd.component as Component : undefined\n}\n\n// `collapse`: how many leading groups are expanded (all until the first measurement).\nconst expandedCount = ref(Number.POSITIVE_INFINITY)\nconst row = useTemplateRef<HTMLElement>('row')\nlet fit: ToolbarFit | undefined\nonMounted(() => {\n if (props.overflow === 'collapse' && row.value) fit = fitToolbar(row.value, (count) => { expandedCount.value = count })\n})\nonUpdated(() => fit?.refresh())\nonBeforeUnmount(() => fit?.dispose())\n\nfunction groupLabel(group: PlacedToolbarGroup): string {\n return group.labelKey ? t(group.labelKey) : group.id\n}\nfunction menuCount(items: readonly { cmd: CommandDef }[]): number {\n return items.filter(({ cmd }) => !('component' in cmd)).length\n}\n/** A group past the expanded prefix; one with only `component` commands has nothing to collapse. */\nfunction isCollapsed(index: number, items: readonly { cmd: CommandDef }[]): boolean {\n return props.overflow === 'collapse' && index >= expandedCount.value && menuCount(items) > 0\n}\n</script>\n\n<template>\n <div\n ref=\"row\"\n class=\"flex items-center gap-1.5\"\n :class=\"overflow === 'collapse' ? 'min-w-0 flex-nowrap overflow-hidden' : 'flex-wrap'\"\n role=\"toolbar\"\n :aria-label=\"t('toolbar.label')\"\n >\n <div\n v-for=\"({ group, items }, gi) in visibleGroups\"\n :key=\"group.id\"\n :class=\"overflow === 'collapse' ? 'flex shrink-0 items-center gap-1.5' : 'contents'\"\n :data-toolbar-group=\"group.id\"\n :data-items=\"menuCount(items)\"\n :data-collapsed=\"isCollapsed(gi, items) ? '' : undefined\"\n >\n <span v-if=\"gi > 0\" role=\"separator\" aria-orientation=\"vertical\" class=\"mx-2 h-6 w-[2px] shrink-0 bg-[var(--sk-border-accented)]\" />\n <UFieldGroup v-if=\"isCollapsed(gi, items)\" size=\"sm\">\n <UDropdownMenu :items=\"collapsedGroupMenu(items, props.editor)\">\n <UTooltip :text=\"groupLabel(group)\">\n <UButton\n color=\"neutral\"\n variant=\"ghost\"\n :icon=\"items.find(({ cmd }) => !('component' in cmd))!.cmd.icon\"\n trailing-icon=\"i-lucide-chevron-down\"\n :aria-label=\"groupLabel(group)\"\n :disabled=\"!items.some(({ cmd, state }) => !('component' in cmd) && state.enabled)\"\n @mousedown.prevent\n />\n </UTooltip>\n </UDropdownMenu>\n <template v-for=\"{ cmd } in items\" :key=\"cmd.id\">\n <component :is=\"componentOf(cmd)\" v-if=\"componentOf(cmd)\" :editor=\"props.editor\" />\n </template>\n </UFieldGroup>\n <UFieldGroup v-else size=\"sm\">\n <template v-for=\"{ cmd, state } in items\" :key=\"cmd.id\">\n <component :is=\"componentOf(cmd)\" v-if=\"componentOf(cmd)\" :editor=\"props.editor\" />\n <UDropdownMenu v-else-if=\"'menu' in cmd\" :items=\"menuOf(cmd)\">\n <UTooltip :text=\"cmd.label\" :kbds=\"cmd.kbds\">\n <UButton\n :key=\"cmd.icon\"\n color=\"neutral\"\n variant=\"ghost\"\n :icon=\"cmd.icon\"\n :aria-label=\"cmd.label\"\n :disabled=\"!state.enabled\"\n @mousedown.prevent\n />\n </UTooltip>\n </UDropdownMenu>\n <UTooltip v-else :text=\"cmd.label\" :kbds=\"cmd.kbds\">\n <UButton\n :key=\"cmd.icon\"\n color=\"neutral\"\n variant=\"ghost\"\n active-color=\"primary\"\n active-variant=\"soft\"\n :icon=\"cmd.icon\"\n :aria-label=\"cmd.label\"\n :active=\"state.active\"\n :aria-pressed=\"cmd.active ? state.active : undefined\"\n :disabled=\"!state.enabled\"\n @mousedown.prevent\n @click=\"run(cmd)\"\n />\n </UTooltip>\n </template>\n </UFieldGroup>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -4,11 +4,17 @@ import { Editor } from "@tiptap/core";
4
4
  * The toolbar of an editor, placed anywhere on the page. `editor` is the instance it works on (from the surface's
5
5
  * `ready` event); the preset registered for that instance (`editorEntryOf`) supplies the groups. Without an editor
6
6
  * (not mounted yet, or read-only by the host's choice) it renders no groups. Never reads a global active editor.
7
+ * `overflow`: `wrap` (default) breaks onto further lines; `collapse` keeps one line, and the groups that do not fit
8
+ * collapse from the end into one dropdown each (`component` commands stay beside it). The row then takes the width its
9
+ * container gives it.
7
10
  */
8
11
  type __VLS_Props = {
9
12
  editor?: Editor;
13
+ overflow?: 'wrap' | 'collapse';
10
14
  };
11
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ overflow: "wrap" | "collapse";
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
18
  declare const _default: typeof __VLS_export;
13
19
  //#endregion
14
20
  export { _default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaile/forge-editor",
3
- "version": "5.0.3",
3
+ "version": "5.1.0",
4
4
  "type": "module",
5
5
  "description": "Vue and React editor library on TipTap v3 (Vue: Nuxt UI; React: shadcn/ui): framework-free features with Vue and React bindings, simple and advanced presets, tree-shakeable entries. Built ESM with type declarations.",
6
6
  "license": "MIT",