tiptapify 0.2.4 → 0.2.5

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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/tiptapify.css +1 -1
  3. package/dist/tiptapify.mjs +84644 -65141
  4. package/dist/tiptapify.umd.js +86 -84
  5. package/index.d.ts +23 -2
  6. package/package.json +45 -45
  7. package/src/components/Tiptapify.vue +83 -1
  8. package/src/components/Toolbar/Index.vue +2 -2
  9. package/src/components/Toolbar/Items.vue +9 -2
  10. package/src/components/UI/TiptapifyDialog.vue +7 -1
  11. package/src/extensions/components/ai/Button.vue +1 -1
  12. package/src/extensions/components/ai/Dialog.vue +337 -27
  13. package/src/extensions/components/ai/charLimit.test.ts +55 -0
  14. package/src/extensions/components/ai/charLimit.ts +30 -0
  15. package/src/extensions/components/ai/characterCount.test.ts +27 -0
  16. package/src/extensions/components/media/iframe/Button.vue +1 -2
  17. package/src/extensions/components/media/iframe/IframeDialog.vue +36 -31
  18. package/src/extensions/components/media/image/Button.vue +1 -1
  19. package/src/extensions/components/media/link/Button.vue +1 -1
  20. package/src/extensions/components/media/link/LinkDialog.vue +19 -36
  21. package/src/extensions/components/media/video/Button.vue +1 -1
  22. package/src/extensions/components/media/video/VideoDialog.vue +17 -17
  23. package/src/extensions/components/misc/source/Button.vue +1 -1
  24. package/src/i18n/locales/ar.json +165 -159
  25. package/src/i18n/locales/bg.json +212 -0
  26. package/src/i18n/locales/ch.json +165 -159
  27. package/src/i18n/locales/cz.json +165 -159
  28. package/src/i18n/locales/de.json +165 -159
  29. package/src/i18n/locales/el.json +212 -0
  30. package/src/i18n/locales/en.json +166 -159
  31. package/src/i18n/locales/es.json +165 -159
  32. package/src/i18n/locales/et.json +212 -0
  33. package/src/i18n/locales/fi.json +165 -159
  34. package/src/i18n/locales/fr.json +165 -159
  35. package/src/i18n/locales/he.json +212 -0
  36. package/src/i18n/locales/hi.json +212 -0
  37. package/src/i18n/locales/hr.json +212 -0
  38. package/src/i18n/locales/hu.json +165 -159
  39. package/src/i18n/locales/id.json +212 -0
  40. package/src/i18n/locales/it.json +165 -159
  41. package/src/i18n/locales/ja.json +165 -159
  42. package/src/i18n/locales/ko.json +165 -159
  43. package/src/i18n/locales/la.json +165 -159
  44. package/src/i18n/locales/lt.json +165 -159
  45. package/src/i18n/locales/nl.json +165 -159
  46. package/src/i18n/locales/no.json +212 -0
  47. package/src/i18n/locales/pl.json +165 -159
  48. package/src/i18n/locales/pt-BR.json +212 -0
  49. package/src/i18n/locales/pt.json +165 -159
  50. package/src/i18n/locales/ro.json +212 -0
  51. package/src/i18n/locales/ru.json +165 -159
  52. package/src/i18n/locales/se.json +165 -159
  53. package/src/i18n/locales/sk.json +212 -0
  54. package/src/i18n/locales/sl.json +212 -0
  55. package/src/i18n/locales/sr.json +212 -0
  56. package/src/i18n/locales/th.json +165 -159
  57. package/src/i18n/locales/tr.json +165 -159
  58. package/src/i18n/locales/uk.json +165 -159
  59. package/src/i18n/locales/vi.json +165 -159
  60. package/src/index.ts +3 -0
  61. package/src/types/editor.ts +23 -2
  62. package/src/utils/validateHref.test.ts +68 -0
  63. package/src/utils/validateHref.ts +21 -0
package/index.d.ts CHANGED
@@ -97,10 +97,17 @@ export interface TiptapifyAiRequest {
97
97
  model?: string
98
98
  messages: TiptapifyAiChatMessage[]
99
99
  temperature?: number
100
- stream?: false
100
+ stream?: boolean
101
101
  [key: string]: unknown
102
102
  }
103
103
 
104
+ export type TiptapifyAiReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max'
105
+
106
+ export interface TiptapifyAiReasoningEffortOptions {
107
+ options: TiptapifyAiReasoningEffort[]
108
+ default?: TiptapifyAiReasoningEffort
109
+ }
110
+
104
111
  export interface TiptapifyAiResponse {
105
112
  content: string
106
113
  }
@@ -114,7 +121,17 @@ export interface TiptapifyAiOpenAiResponse {
114
121
  }>
115
122
  }
116
123
 
117
- export type TiptapifyAiProvider = (request: TiptapifyAiRequest, context: TiptapifyAiEditorContext) => Promise<TiptapifyAiResponse | TiptapifyAiOpenAiResponse | string>
124
+ export interface TiptapifyAiStream {
125
+ signal: AbortSignal
126
+ onChunk: (chunk: string) => void
127
+ onReasoning: (chunk: string) => void
128
+ }
129
+
130
+ export type TiptapifyAiProvider = (
131
+ request: TiptapifyAiRequest,
132
+ context: TiptapifyAiEditorContext,
133
+ stream?: TiptapifyAiStream,
134
+ ) => Promise<TiptapifyAiResponse | TiptapifyAiOpenAiResponse | string>
118
135
 
119
136
  export type TiptapifyAiTokenProvider = () => Promise<string | null> | string | null
120
137
 
@@ -132,6 +149,10 @@ export interface TiptapifyAiOptions {
132
149
  defaultPrompt?: string
133
150
  systemPrompt?: string
134
151
  temperature?: number
152
+ stream?: boolean
153
+ thinking?: boolean
154
+ reasoningEffort?: TiptapifyAiReasoningEffortOptions
155
+ showReasoning?: boolean
135
156
  chatCompletionOptions?: Record<string, unknown>
136
157
  createMessages?: (context: TiptapifyAiEditorContext) => TiptapifyAiChatMessage[]
137
158
  tokenProvider?: TiptapifyAiTokenProvider
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tiptapify",
3
3
  "types": "./index.d.ts",
4
- "version": "0.2.4",
4
+ "version": "0.2.5",
5
5
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
6
6
  "exports": {
7
7
  ".": {
@@ -70,47 +70,47 @@
70
70
  "packageManager": "pnpm@11.8.0",
71
71
  "dependencies": {
72
72
  "@floating-ui/dom": "^1.7.6",
73
- "@tiptap/core": "^3.27.1",
74
- "@tiptap/extension-blockquote": "^3.27.1",
75
- "@tiptap/extension-bold": "^3.27.1",
76
- "@tiptap/extension-bubble-menu": "^3.27.1",
77
- "@tiptap/extension-code": "^3.27.1",
78
- "@tiptap/extension-code-block": "^3.27.1",
79
- "@tiptap/extension-code-block-lowlight": "^3.27.1",
80
- "@tiptap/extension-color": "^3.27.1",
81
- "@tiptap/extension-document": "^3.27.1",
82
- "@tiptap/extension-floating-menu": "^3.27.1",
83
- "@tiptap/extension-font-family": "^3.27.1",
84
- "@tiptap/extension-hard-break": "^3.27.1",
85
- "@tiptap/extension-heading": "^3.27.1",
86
- "@tiptap/extension-highlight": "^3.27.1",
87
- "@tiptap/extension-horizontal-rule": "^3.27.1",
88
- "@tiptap/extension-image": "^3.27.1",
89
- "@tiptap/extension-invisible-characters": "^3.27.1",
90
- "@tiptap/extension-italic": "^3.27.1",
91
- "@tiptap/extension-link": "^3.27.1",
92
- "@tiptap/extension-list": "^3.27.1",
93
- "@tiptap/extension-list-item": "^3.27.1",
94
- "@tiptap/extension-paragraph": "^3.27.1",
95
- "@tiptap/extension-placeholder": "^3.27.1",
96
- "@tiptap/extension-strike": "^3.27.1",
97
- "@tiptap/extension-subscript": "^3.27.1",
98
- "@tiptap/extension-superscript": "^3.27.1",
99
- "@tiptap/extension-table": "^3.27.1",
100
- "@tiptap/extension-task-item": "^3.27.1",
101
- "@tiptap/extension-task-list": "^3.27.1",
102
- "@tiptap/extension-text": "^3.27.1",
103
- "@tiptap/extension-text-align": "^3.27.1",
104
- "@tiptap/extension-text-style": "^3.27.1",
105
- "@tiptap/extension-typography": "^3.27.1",
106
- "@tiptap/extension-underline": "^3.27.1",
107
- "@tiptap/extension-youtube": "^3.27.1",
108
- "@tiptap/extensions": "^3.27.1",
109
- "@tiptap/pm": "^3.27.1",
110
- "@tiptap/suggestion": "^3.27.1",
111
- "@tiptap/vue-3": "^3.27.1",
73
+ "@tiptap/core": "^3.31.0",
74
+ "@tiptap/extension-blockquote": "^3.31.0",
75
+ "@tiptap/extension-bold": "^3.31.0",
76
+ "@tiptap/extension-bubble-menu": "^3.31.0",
77
+ "@tiptap/extension-code": "^3.31.0",
78
+ "@tiptap/extension-code-block": "^3.31.0",
79
+ "@tiptap/extension-code-block-lowlight": "^3.31.0",
80
+ "@tiptap/extension-color": "^3.31.0",
81
+ "@tiptap/extension-document": "^3.31.0",
82
+ "@tiptap/extension-floating-menu": "^3.31.0",
83
+ "@tiptap/extension-font-family": "^3.31.0",
84
+ "@tiptap/extension-hard-break": "^3.31.0",
85
+ "@tiptap/extension-heading": "^3.31.0",
86
+ "@tiptap/extension-highlight": "^3.31.0",
87
+ "@tiptap/extension-horizontal-rule": "^3.31.0",
88
+ "@tiptap/extension-image": "^3.31.0",
89
+ "@tiptap/extension-invisible-characters": "^3.31.0",
90
+ "@tiptap/extension-italic": "^3.31.0",
91
+ "@tiptap/extension-link": "^3.31.0",
92
+ "@tiptap/extension-list": "^3.31.0",
93
+ "@tiptap/extension-list-item": "^3.31.0",
94
+ "@tiptap/extension-paragraph": "^3.31.0",
95
+ "@tiptap/extension-placeholder": "^3.31.0",
96
+ "@tiptap/extension-strike": "^3.31.0",
97
+ "@tiptap/extension-subscript": "^3.31.0",
98
+ "@tiptap/extension-superscript": "^3.31.0",
99
+ "@tiptap/extension-table": "^3.31.0",
100
+ "@tiptap/extension-task-item": "^3.31.0",
101
+ "@tiptap/extension-task-list": "^3.31.0",
102
+ "@tiptap/extension-text": "^3.31.0",
103
+ "@tiptap/extension-text-align": "^3.31.0",
104
+ "@tiptap/extension-text-style": "^3.31.0",
105
+ "@tiptap/extension-typography": "^3.31.0",
106
+ "@tiptap/extension-underline": "^3.31.0",
107
+ "@tiptap/extension-youtube": "^3.31.0",
108
+ "@tiptap/extensions": "^3.31.0",
109
+ "@tiptap/pm": "^3.31.0",
110
+ "@tiptap/suggestion": "^3.31.0",
111
+ "@tiptap/vue-3": "^3.31.0",
112
112
  "emoji.json": "^16.0.0",
113
- "highlight.js": "^11.11.1",
113
+ "highlight.js": "^11.12.0",
114
114
  "linkifyjs": "^4.3.3",
115
115
  "lowlight": "^3.3.0"
116
116
  },
@@ -142,11 +142,11 @@
142
142
  "typescript": "^6.0.3",
143
143
  "typescript-eslint": "^8.61.1",
144
144
  "unplugin-vue-components": "^32.1.0",
145
- "vite": "^8.0.16",
145
+ "vite": "^8.2.2",
146
146
  "vite-plugin-vuetify": "^2.1.3",
147
- "vite-svg-loader": "^5.1.1",
148
- "vitest": "^4.1.9",
147
+ "vite-svg-loader": "^5.1.3",
148
+ "vitest": "^4.1.11",
149
149
  "vue-eslint-parser": "^10.4.1",
150
- "vue-tsc": "^3.3.5"
150
+ "vue-tsc": "^3.3.11"
151
151
  }
152
152
  }
@@ -92,6 +92,14 @@ function contentChanged() {
92
92
  emit('content-changed', { html: editor.value?.getHTML(), json: editor.value?.getJSON() })
93
93
  }
94
94
 
95
+ function onContainerClick(event: MouseEvent) {
96
+ if ((event.target as Element).closest('.ProseMirror')) {
97
+ return
98
+ }
99
+
100
+ editor.value?.chain().focus().run()
101
+ }
102
+
95
103
  const editor: ShallowRef<Editor | undefined> = getTiptapEditor(
96
104
  props.content,
97
105
  computed(() => props.placeholder || t('content.placeholder')).value,
@@ -150,7 +158,7 @@ onBeforeUnmount(() => {
150
158
 
151
159
  <VProgressLinear v-model="loadingProgress" :color="loadingColor" :height="loadingHeight" :indeterminate="loading" />
152
160
 
153
- <div class="pa-2 tiptapify-container resizable" :style="computeResizableHeight">
161
+ <div class="pa-2 tiptapify-container resizable" :style="computeResizableHeight" @click="onContainerClick">
154
162
  <MenuFloating v-if="floatingMenu" :variant="variantBtn" :theme="currentTheme" />
155
163
 
156
164
  <MenuBubble v-if="bubbleMenu" :variant="variantBtn" :theme="currentTheme" />
@@ -197,6 +205,7 @@ onBeforeUnmount(() => {
197
205
  --yellow: rgba(250, 204, 21, .4);
198
206
  --yellow-light: #FFFAE5;
199
207
  --red: #FF5C33;
208
+ --red-soft: #B24A40;
200
209
  --red-light: #FFEBE5;
201
210
  --shadow: 0px 12px 33px 0px rgba(0, 0, 0, .06), 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04);
202
211
  --border: 1px solid var(--gray-2);
@@ -207,6 +216,21 @@ onBeforeUnmount(() => {
207
216
  overflow: auto;
208
217
  }
209
218
 
219
+ .tiptapify-container {
220
+ display: flex;
221
+ flex-direction: column;
222
+ }
223
+
224
+ .tiptapify-container .tiptapify-editor {
225
+ display: flex;
226
+ flex: 1;
227
+ flex-direction: column;
228
+ }
229
+
230
+ .tiptapify-container .tiptapify-editor > .ProseMirror {
231
+ flex: 1;
232
+ }
233
+
210
234
  /* Basic editor styles */
211
235
  .tiptap {
212
236
  :first-child {
@@ -560,4 +584,62 @@ onBeforeUnmount(() => {
560
584
  }
561
585
  }
562
586
  }
587
+
588
+ /*
589
+ * Dialog layout primitives.
590
+ *
591
+ * Dialogs lay their fields out with a self-contained 12-column grid instead
592
+ * of VRow/VCol. The bundled Vuetify 3 components emit v3 grid classes
593
+ * (.v-col-*, .v-col-md-*), but Vuetify 4 renamed them (.v-col--cols-*), so
594
+ * the consumer's Vuetify stylesheet does not size the columns and every
595
+ * field collapses onto a single row. Keeping the layout inside tiptapify
596
+ * makes it work with Vuetify 3 and 4 alike, regardless of the CSS the
597
+ * consumer loads.
598
+ *
599
+ * The 960px breakpoint preserves the Vuetify 3 "md" threshold
600
+ * (Vuetify 4 lowered it to 840px).
601
+ */
602
+ .tiptapify-dialog-grid {
603
+ display: grid;
604
+ gap: 24px;
605
+ grid-template-columns: repeat(12, minmax(0, 1fr));
606
+
607
+ > .tiptapify-dialog-col {
608
+ grid-column: 1 / -1;
609
+ min-width: 0;
610
+ }
611
+
612
+ @media (min-width: 960px) {
613
+ > .tiptapify-dialog-col--3 {
614
+ grid-column: span 3;
615
+ }
616
+
617
+ > .tiptapify-dialog-col--4 {
618
+ grid-column: span 4;
619
+ }
620
+
621
+ > .tiptapify-dialog-col--6 {
622
+ grid-column: span 6;
623
+ }
624
+
625
+ > .tiptapify-dialog-col--8 {
626
+ grid-column: span 8;
627
+ }
628
+ }
629
+ }
630
+
631
+ .tiptapify-dialog-actions {
632
+ display: flex;
633
+ flex-wrap: wrap;
634
+ align-items: center;
635
+ justify-content: space-between;
636
+ gap: 8px 16px;
637
+
638
+ > .tiptapify-dialog-actions__start,
639
+ > .tiptapify-dialog-actions__end {
640
+ display: flex;
641
+ align-items: center;
642
+ gap: 8px;
643
+ }
644
+ }
563
645
  </style>
@@ -178,10 +178,10 @@ Object.keys(props.customExtensions).forEach((key: any) => {
178
178
  <div v-if="editor" class="tiptapify-toolbar">
179
179
  <VToolbar elevation="1" :theme="theme" height="auto" :class="`ps-1 pr-1 rounded-t-${rounded}`">
180
180
  <VSlideGroup v-if="toolbarScrollable">
181
- <Items :items="toolbarItems" />
181
+ <Items :items="toolbarItems" :variant-btn="variantBtn" />
182
182
  </VSlideGroup>
183
183
 
184
- <Items v-else :items="toolbarItems" />
184
+ <Items v-else :items="toolbarItems" :variant-btn="variantBtn" />
185
185
  </VToolbar>
186
186
  </div>
187
187
  </template>
@@ -20,9 +20,9 @@ defineProps({
20
20
  <div class="menu-divider" />
21
21
  </template>
22
22
  <template v-else>
23
- <VBtnGroup v-if="item.group" elevation="4">
23
+ <VBtnGroup v-if="item.group" elevation="4" :class="{ 'tiptapify-btn-group--elevated': variantBtn === 'elevated' }">
24
24
  <template v-for="sectionItem in item.components" :key="sectionItem.name">
25
- <component :is="sectionItem.component" v-bind="{ ...sectionItem.props ?? {} }" />
25
+ <component :is="sectionItem.component" v-bind="{ variantBtn, ...sectionItem.props ?? {} }" />
26
26
  </template>
27
27
  </VBtnGroup>
28
28
  <template v-else>
@@ -40,6 +40,13 @@ defineProps({
40
40
  </template>
41
41
 
42
42
  <style lang="scss" scoped>
43
+ .tiptapify-btn-group--elevated {
44
+ box-shadow:
45
+ 0 3px 1px -2px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)),
46
+ 0 2px 2px 0 var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)),
47
+ 0 1px 5px 0 var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12));
48
+ }
49
+
43
50
  :deep(.v-btn-group) {
44
51
  height: 32px !important;
45
52
  }
@@ -144,7 +144,8 @@ onBeforeUnmount(() => {
144
144
  <VCard>
145
145
  <VCardTitle
146
146
  ref="movableHandler"
147
- :class="`d-flex ${!fullscreen ? 'tiptapify-movable-handler' : ''}`"
147
+ class="tiptapify-dialog-header"
148
+ :class="!fullscreen ? 'tiptapify-movable-handler' : ''"
148
149
  style="user-select: none;"
149
150
  >
150
151
  <VLabel>
@@ -168,6 +169,11 @@ onBeforeUnmount(() => {
168
169
  </template>
169
170
 
170
171
  <style lang="scss" scoped>
172
+ .tiptapify-dialog-header {
173
+ align-items: center;
174
+ display: flex;
175
+ }
176
+
171
177
  :deep(.v-overlay__content) {
172
178
  position: fixed;
173
179
  }
@@ -38,5 +38,5 @@ function showDialog() {
38
38
  <BtnIcon :icon="icon" />
39
39
  </VBtn>
40
40
 
41
- <AiDialog ref="dialog" />
41
+ <AiDialog ref="dialog" :variant-btn="variantBtn" />
42
42
  </template>