tiptapify 0.1.5 → 0.2.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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/tiptapify.css +1 -1
  3. package/dist/tiptapify.mjs +30351 -26471
  4. package/dist/tiptapify.umd.js +57 -52
  5. package/index.d.ts +71 -1
  6. package/package.json +52 -45
  7. package/src/components/Tiptapify.vue +38 -1
  8. package/src/components/Toolbar/ai.ts +13 -0
  9. package/src/components/Toolbar/items.ts +2 -0
  10. package/src/components/editorExtensions.ts +9 -3
  11. package/src/components/index.ts +4 -3
  12. package/src/constants/style.test.ts +49 -0
  13. package/src/extensions/PickerEventBus.test.ts +38 -0
  14. package/src/extensions/components/ai/Button.vue +42 -0
  15. package/src/extensions/components/ai/Dialog.vue +262 -0
  16. package/src/extensions/components/media/link/LinkDialog.vue +5 -5
  17. package/src/i18n/locales/ar.json +29 -0
  18. package/src/i18n/locales/ch.json +29 -0
  19. package/src/i18n/locales/cz.json +29 -0
  20. package/src/i18n/locales/de.json +29 -0
  21. package/src/i18n/locales/en.json +29 -0
  22. package/src/i18n/locales/es.json +29 -0
  23. package/src/i18n/locales/fi.json +29 -0
  24. package/src/i18n/locales/fr.json +29 -0
  25. package/src/i18n/locales/hu.json +29 -0
  26. package/src/i18n/locales/it.json +29 -0
  27. package/src/i18n/locales/ja.json +29 -0
  28. package/src/i18n/locales/ko.json +29 -0
  29. package/src/i18n/locales/la.json +29 -0
  30. package/src/i18n/locales/lt.json +29 -0
  31. package/src/i18n/locales/nl.json +29 -0
  32. package/src/i18n/locales/pl.json +29 -0
  33. package/src/i18n/locales/pt.json +29 -0
  34. package/src/i18n/locales/ru.json +29 -0
  35. package/src/i18n/locales/se.json +29 -0
  36. package/src/i18n/locales/th.json +29 -0
  37. package/src/i18n/locales/tr.json +29 -0
  38. package/src/i18n/locales/uk.json +29 -0
  39. package/src/i18n/locales/vi.json +29 -0
  40. package/src/index.ts +15 -1
  41. package/src/types/editor.ts +74 -1
  42. package/src/types/toolbarTypes.ts +2 -1
  43. package/src/utils/helpers.test.ts +32 -0
@@ -0,0 +1,262 @@
1
+ <script lang="ts" setup>
2
+
3
+ import TiptapifyDialog from '@tiptapify/components/UI/TiptapifyDialog.vue'
4
+ import defaults from '@tiptapify/constants/defaults'
5
+ import {
6
+ TiptapifyAiEditorContext,
7
+ TiptapifyAiMode,
8
+ TiptapifyAiOpenAiResponse,
9
+ TiptapifyAiRequest,
10
+ TiptapifyAiResponse,
11
+ TiptapifyAiResolvedOptions,
12
+ TiptapifyEditor,
13
+ variantBtnTypes,
14
+ variantFieldTypes
15
+ } from '@tiptapify/types/editor'
16
+ import { computed, ComputedRef, inject, PropType, Ref, ref, useTemplateRef } from 'vue'
17
+ import { ComposerTranslation } from 'vue-i18n'
18
+
19
+ defineProps({
20
+ variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
21
+ variantField: { type: String as PropType<variantFieldTypes>, default() { return defaults.variantField } },
22
+ })
23
+
24
+ const editor = inject('tiptapifyEditor') as Ref<TiptapifyEditor>
25
+ const ai = inject('tiptapifyAi') as ComputedRef<TiptapifyAiResolvedOptions | false>
26
+ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
27
+
28
+ const dialog = useTemplateRef('dialog')
29
+ const prompt = ref('')
30
+ const result = ref('')
31
+ const error = ref('')
32
+ const loading = ref(false)
33
+ const requestRange = ref<{ from: number, to: number } | null>(null)
34
+ const requestMode = ref<TiptapifyAiMode>('insert')
35
+
36
+ const aiProvider = computed(() => ai?.value ? ai.value.aiProvider : undefined)
37
+ const promptExamples = computed(() => ai?.value ? ai.value.promptExamples : [])
38
+ const isGenerateDisabled = computed(() => !aiProvider.value || !prompt.value.trim() || loading.value)
39
+ const isApplyDisabled = computed(() => !result.value.trim() || loading.value)
40
+
41
+ defineExpose({ showDialog })
42
+
43
+ function showDialog() {
44
+ prompt.value = ai?.value && ai.value.defaultPrompt ? ai.value.defaultPrompt : ''
45
+ result.value = ''
46
+ error.value = ''
47
+ requestRange.value = null
48
+ requestMode.value = 'insert'
49
+
50
+ dialog.value?.open()
51
+ }
52
+
53
+ function close() {
54
+ prompt.value = ''
55
+ result.value = ''
56
+ error.value = ''
57
+ requestRange.value = null
58
+ requestMode.value = 'insert'
59
+
60
+ dialog.value?.close()
61
+ }
62
+
63
+ function applyExample(examplePrompt: string) {
64
+ prompt.value = examplePrompt
65
+ }
66
+
67
+ function getSelectedText(from: number, to: number) {
68
+ if (from === to) {
69
+ return ''
70
+ }
71
+
72
+ return editor.value.state.doc.textBetween(from, to, '\n')
73
+ }
74
+
75
+ function buildDefaultMessages(context: TiptapifyAiEditorContext) {
76
+ const systemPrompt = ai.value && ai.value.systemPrompt
77
+ ? ai.value.systemPrompt
78
+ : 'You are an assistant inside a rich text editor. Return only the final text to insert.'
79
+ const contextText = context.selectedText || context.text
80
+ const contextLabel = context.selectedText ? 'Selected text' : 'Editor text'
81
+
82
+ return [
83
+ {
84
+ role: 'system' as const,
85
+ content: systemPrompt,
86
+ },
87
+ {
88
+ role: 'user' as const,
89
+ content: contextText
90
+ ? `${contextLabel}:\n${contextText}\n\nUser request:\n${context.prompt}`
91
+ : context.prompt,
92
+ },
93
+ ]
94
+ }
95
+
96
+ function buildOpenAiRequest(context: TiptapifyAiEditorContext): TiptapifyAiRequest {
97
+ const options = ai.value || {}
98
+ const request: TiptapifyAiRequest = {
99
+ ...options.chatCompletionOptions,
100
+ messages: options.createMessages ? options.createMessages(context) : buildDefaultMessages(context),
101
+ stream: false,
102
+ }
103
+
104
+ if (options.model) {
105
+ request.model = options.model
106
+ }
107
+
108
+ if (typeof options.temperature === 'number') {
109
+ request.temperature = options.temperature
110
+ }
111
+
112
+ return request
113
+ }
114
+
115
+ function isTiptapifyAiResponse(response: unknown): response is TiptapifyAiResponse {
116
+ return typeof response === 'object' && response !== null && typeof (response as TiptapifyAiResponse).content === 'string'
117
+ }
118
+
119
+ function isOpenAiResponse(response: unknown): response is TiptapifyAiOpenAiResponse {
120
+ return typeof response === 'object' && response !== null && Array.isArray((response as TiptapifyAiOpenAiResponse).choices)
121
+ }
122
+
123
+ function getResponseContent(response: TiptapifyAiResponse | TiptapifyAiOpenAiResponse | string) {
124
+ let content = ''
125
+
126
+ if (typeof response === 'string') {
127
+ content = response
128
+ } else if (isTiptapifyAiResponse(response)) {
129
+ content = response.content
130
+ } else if (isOpenAiResponse(response)) {
131
+ content = response.choices?.[0]?.message?.content ?? response.choices?.[0]?.text ?? ''
132
+ }
133
+
134
+ return content.trimStart()
135
+ }
136
+
137
+ async function generate() {
138
+ if (isGenerateDisabled.value || !aiProvider.value) {
139
+ return
140
+ }
141
+
142
+ error.value = ''
143
+ result.value = ''
144
+ loading.value = true
145
+
146
+ const { from, to } = editor.value.state.selection
147
+ const selectedText = getSelectedText(from, to)
148
+ requestRange.value = selectedText ? { from, to } : null
149
+ requestMode.value = ai.value ? ai.value.mode ?? (selectedText ? 'replace' : 'insert') : 'insert'
150
+
151
+ const context: TiptapifyAiEditorContext = {
152
+ prompt: prompt.value.trim(),
153
+ selectedText,
154
+ text: selectedText || editor.value.getText(),
155
+ html: editor.value.getHTML(),
156
+ json: editor.value.getJSON(),
157
+ mode: requestMode.value,
158
+ }
159
+ const request = buildOpenAiRequest(context)
160
+
161
+ try {
162
+ const response = await aiProvider.value(request, context)
163
+ result.value = getResponseContent(response)
164
+ } catch (err) {
165
+ error.value = err instanceof Error ? err.message : t('ai.error')
166
+ } finally {
167
+ loading.value = false
168
+ }
169
+ }
170
+
171
+ function apply() {
172
+ if (isApplyDisabled.value) {
173
+ return
174
+ }
175
+
176
+ const chain = editor.value.chain().focus()
177
+ if (requestRange.value) {
178
+ chain.deleteRange(requestRange.value)
179
+ }
180
+
181
+ if (!requestRange.value && requestMode.value === 'append') {
182
+ chain.insertContentAt(editor.value.state.doc.content.size, result.value).run()
183
+ } else {
184
+ chain.insertContent(result.value).run()
185
+ }
186
+
187
+ close()
188
+ }
189
+
190
+ </script>
191
+
192
+ <template>
193
+ <TiptapifyDialog ref="dialog" module="ai" :title="t('ai.title')" :max-width="720" @close-dialog="close">
194
+ <template #content>
195
+ <VCardText>
196
+ <VAlert v-if="!aiProvider" type="warning" variant="tonal" density="compact" class="mb-4">
197
+ {{ t('ai.unavailable') }}
198
+ </VAlert>
199
+
200
+ <VTextarea
201
+ v-model="prompt"
202
+ :label="t('ai.prompt')"
203
+ :variant="variantField"
204
+ :disabled="loading || !aiProvider"
205
+ rows="4"
206
+ auto-grow
207
+ autofocus
208
+ />
209
+
210
+ <div v-if="promptExamples.length" class="mb-4">
211
+ <VLabel class="mb-2 d-block">
212
+ {{ t('ai.examples') }}
213
+ </VLabel>
214
+ <div class="d-flex flex-wrap ga-2">
215
+ <VChip
216
+ v-for="example in promptExamples"
217
+ :key="`${example.title}:${example.prompt}`"
218
+ variant="tonal"
219
+ :disabled="loading || !aiProvider"
220
+ @click="applyExample(example.prompt)"
221
+ >
222
+ {{ example.title }}
223
+ </VChip>
224
+ </div>
225
+ </div>
226
+
227
+ <VTextarea
228
+ v-model="result"
229
+ :label="t('ai.result')"
230
+ :variant="variantField"
231
+ :loading="loading"
232
+ :disabled="loading"
233
+ rows="5"
234
+ auto-grow
235
+ />
236
+
237
+ <VAlert v-if="loading" type="info" variant="tonal" density="compact" class="mt-4">
238
+ {{ t('ai.loading') }}
239
+ </VAlert>
240
+
241
+ <VAlert v-if="error" type="error" variant="tonal" density="compact" class="mt-4">
242
+ {{ error }}
243
+ </VAlert>
244
+ </VCardText>
245
+ </template>
246
+
247
+ <template #actions>
248
+ <VCardActions>
249
+ <VSpacer />
250
+ <VBtn :variant="variantBtn" @click="close">
251
+ {{ t('dialog.close') }}
252
+ </VBtn>
253
+ <VBtn color="primary" :variant="variantBtn" :disabled="isGenerateDisabled" :loading="loading" @click="generate">
254
+ {{ t('ai.generate') }}
255
+ </VBtn>
256
+ <VBtn color="primary" :variant="variantBtn" :disabled="isApplyDisabled" @click="apply">
257
+ {{ t('ai.apply') }}
258
+ </VBtn>
259
+ </VCardActions>
260
+ </template>
261
+ </TiptapifyDialog>
262
+ </template>
@@ -21,7 +21,7 @@ const generateLinkAttrs = () => ({
21
21
  href: '',
22
22
  target: targetAttrs.value[0],
23
23
  cssClass: '',
24
- rel: ''
24
+ rel: []
25
25
  })
26
26
 
27
27
  const relAttrs = ['alternate', 'author', 'bookmark', 'external', 'help', 'license', 'me', 'next', 'nofollow', 'noopener', 'noreferrer', 'opener', 'prev', 'privacy-policy', 'search', 'tag', 'terms-of-service']
@@ -43,10 +43,10 @@ const isDisabled = computed(() => {
43
43
 
44
44
  function apply() {
45
45
  let { href, target, rel, cssClass } = attrs.value
46
- rel = rel?.length ? rel.join(' ') : null
46
+ const relStr = rel?.length ? rel.join(' ') : null
47
47
 
48
48
  if (href) {
49
- editor.value.chain().focus().extendMarkRange('link').setLink({ href, target: target.value, rel, class: cssClass }).run()
49
+ editor.value.chain().focus().extendMarkRange('link').setLink({ href, target: target.value, rel: relStr, class: cssClass }).run()
50
50
  }
51
51
 
52
52
  close()
@@ -61,7 +61,7 @@ function clear() {
61
61
  function close() {
62
62
  attrs.value = generateLinkAttrs()
63
63
 
64
- dialog.value.close()
64
+ dialog.value?.close()
65
65
  }
66
66
 
67
67
  const showLink = (event: CustomEvent) => {
@@ -74,7 +74,7 @@ const showLink = (event: CustomEvent) => {
74
74
  attrs.value.rel = event.detail.link?.rel?.split(' ')
75
75
  attrs.value.cssClass = event.detail.link?.class
76
76
 
77
- dialog.value.open()
77
+ dialog.value?.open()
78
78
  }
79
79
 
80
80
  onMounted(() => {
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "كلمات",
174
174
  "chars": "حروف"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "单词",
174
174
  "chars": "字符"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "slova",
174
174
  "chars": "znaky"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "Wörter",
174
174
  "chars": "Zeichen"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -171,5 +171,34 @@
171
171
  "footer": {
172
172
  "words": "words",
173
173
  "chars": "characters"
174
+ },
175
+ "ai": {
176
+ "title": "AI assistant",
177
+ "prompt": "Prompt",
178
+ "examples": "Examples",
179
+ "generate": "Generate",
180
+ "apply": "Apply",
181
+ "result": "Result",
182
+ "loading": "Generating...",
183
+ "unavailable": "Configure an AI provider to use this action",
184
+ "error": "AI generation failed",
185
+ "defaultExamples": {
186
+ "improve": {
187
+ "title": "Improve writing",
188
+ "prompt": "Improve the clarity and tone of this text."
189
+ },
190
+ "proofReading": {
191
+ "title": "Proof-reading",
192
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
193
+ },
194
+ "summarize": {
195
+ "title": "Summarize",
196
+ "prompt": "Summarize this text in a concise paragraph."
197
+ },
198
+ "expand": {
199
+ "title": "Expand",
200
+ "prompt": "Expand this into a more detailed version."
201
+ }
202
+ }
174
203
  }
175
204
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "palabras",
174
174
  "chars": "caracteres"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "sanaa",
174
174
  "chars": "merkkiä"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "mots",
174
174
  "chars": "caractères"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "szavak",
174
174
  "chars": "karakterek"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }
@@ -172,5 +172,34 @@
172
172
  "footer": {
173
173
  "words": "parole",
174
174
  "chars": "caratteri"
175
+ },
176
+ "ai": {
177
+ "title": "AI assistant",
178
+ "prompt": "Prompt",
179
+ "examples": "Examples",
180
+ "generate": "Generate",
181
+ "apply": "Apply",
182
+ "result": "Result",
183
+ "loading": "Generating...",
184
+ "unavailable": "Configure an AI provider to use this action",
185
+ "error": "AI generation failed",
186
+ "defaultExamples": {
187
+ "improve": {
188
+ "title": "Improve writing",
189
+ "prompt": "Improve the clarity and tone of this text."
190
+ },
191
+ "proofReading": {
192
+ "title": "Proof-reading",
193
+ "prompt": "Correct grammar, spelling, and punctuation without changing the meaning."
194
+ },
195
+ "summarize": {
196
+ "title": "Summarize",
197
+ "prompt": "Summarize this text in a concise paragraph."
198
+ },
199
+ "expand": {
200
+ "title": "Expand",
201
+ "prompt": "Expand this into a more detailed version."
202
+ }
203
+ }
175
204
  }
176
205
  }