tiptapify 0.4.0 → 0.4.2

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 (49) hide show
  1. package/dist/tiptapify.css +1 -1
  2. package/dist/tiptapify.mjs +7593 -6318
  3. package/dist/tiptapify.umd.js +64 -64
  4. package/index.d.ts +3 -0
  5. package/package.json +2 -1
  6. package/src/components/Toolbar/actions.ts +5 -0
  7. package/src/components/editorExtensions.ts +2 -0
  8. package/src/extensions/components/actions/reset/Button.vue +108 -0
  9. package/src/extensions/components/actions/reset/resetChanges.test.ts +99 -0
  10. package/src/extensions/components/actions/reset/resetChanges.ts +78 -0
  11. package/src/extensions/components/media/image/imageDialog.test.ts +10 -0
  12. package/src/extensions/components/media/image/imageDialog.ts +6 -3
  13. package/src/i18n/locales/ar.json +4 -0
  14. package/src/i18n/locales/bg.json +4 -0
  15. package/src/i18n/locales/ch.json +4 -0
  16. package/src/i18n/locales/cz.json +4 -0
  17. package/src/i18n/locales/de.json +4 -0
  18. package/src/i18n/locales/el.json +4 -0
  19. package/src/i18n/locales/en.json +4 -0
  20. package/src/i18n/locales/es.json +4 -0
  21. package/src/i18n/locales/et.json +4 -0
  22. package/src/i18n/locales/fi.json +4 -0
  23. package/src/i18n/locales/fr.json +4 -0
  24. package/src/i18n/locales/he.json +4 -0
  25. package/src/i18n/locales/hi.json +4 -0
  26. package/src/i18n/locales/hr.json +4 -0
  27. package/src/i18n/locales/hu.json +4 -0
  28. package/src/i18n/locales/id.json +4 -0
  29. package/src/i18n/locales/it.json +4 -0
  30. package/src/i18n/locales/ja.json +4 -0
  31. package/src/i18n/locales/ko.json +4 -0
  32. package/src/i18n/locales/la.json +4 -0
  33. package/src/i18n/locales/lt.json +4 -0
  34. package/src/i18n/locales/nl.json +4 -0
  35. package/src/i18n/locales/no.json +4 -0
  36. package/src/i18n/locales/pl.json +4 -0
  37. package/src/i18n/locales/pt-BR.json +4 -0
  38. package/src/i18n/locales/pt.json +4 -0
  39. package/src/i18n/locales/ro.json +4 -0
  40. package/src/i18n/locales/ru.json +4 -0
  41. package/src/i18n/locales/se.json +4 -0
  42. package/src/i18n/locales/sk.json +4 -0
  43. package/src/i18n/locales/sl.json +4 -0
  44. package/src/i18n/locales/sr.json +4 -0
  45. package/src/i18n/locales/th.json +4 -0
  46. package/src/i18n/locales/tr.json +4 -0
  47. package/src/i18n/locales/uk.json +4 -0
  48. package/src/i18n/locales/vi.json +4 -0
  49. package/src/index.ts +2 -0
package/index.d.ts CHANGED
@@ -360,6 +360,9 @@ export type {
360
360
 
361
361
  export type { TiptapifyConfig }
362
362
 
363
+ /** The logic of the image dialog, for a host that replaces the dialog with its own */
364
+ export * as imageDialog from './src/extensions/components/media/image/imageDialog'
365
+
363
366
  declare module '@vue/runtime-core' {
364
367
  interface GlobalComponents {
365
368
  Tiptapify: typeof Tiptapify
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tiptapify",
3
3
  "types": "./index.d.ts",
4
- "version": "0.4.0",
4
+ "version": "0.4.2",
5
5
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
6
6
  "exports": {
7
7
  ".": {
@@ -30,6 +30,7 @@
30
30
  "scripts": {
31
31
  "dev": "vite",
32
32
  "build": "vite build",
33
+ "prepublishOnly": "pnpm test && pnpm build",
33
34
  "lint": "eslint resources/ts --fix --ext .ts,.js,.cjs,.vue,.tsx,.jsx",
34
35
  "test": "vitest run",
35
36
  "test:watch": "vitest",
@@ -1,5 +1,6 @@
1
1
  import { default as UndoButton } from '@tiptapify/extensions/components/actions/undo/Button.vue'
2
2
  import { default as RedoButton } from '@tiptapify/extensions/components/actions/redo/Button.vue'
3
+ import { default as ResetButton } from '@tiptapify/extensions/components/actions/reset/Button.vue'
3
4
  import { markRaw } from 'vue'
4
5
 
5
6
  export default {
@@ -13,6 +14,10 @@ export default {
13
14
  {
14
15
  name: 'redo',
15
16
  component: markRaw(RedoButton),
17
+ },
18
+ {
19
+ name: 'resetChanges',
20
+ component: markRaw(ResetButton),
16
21
  }
17
22
  ]
18
23
  }
@@ -27,6 +27,7 @@ import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
27
27
  import { InvisibleCharacters } from '@tiptap/extension-invisible-characters'
28
28
 
29
29
  import { default as Iframe } from '@tiptapify/extensions/components/media/iframe'
30
+ import { ResetChanges } from '@tiptapify/extensions/components/actions/reset/resetChanges'
30
31
  import { BulletListCircle, BulletListSquare } from '@tiptapify/extensions/components/list/bullet'
31
32
  import { ListStyle } from '@tiptapify/extensions/components/list/styles'
32
33
  import { TiptapifyImage } from '@tiptapify/extensions/components/media/image'
@@ -149,6 +150,7 @@ export function editorExtensions (
149
150
  TableKit,
150
151
  Code,
151
152
  UndoRedo,
153
+ ResetChanges,
152
154
  Focus,
153
155
  CodeBlockLowlight
154
156
  .extend({
@@ -0,0 +1,108 @@
1
+ <script lang="ts" setup>
2
+
3
+ import * as mdi from '@mdi/js'
4
+ import { Editor } from '@tiptap/vue-3'
5
+ import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
6
+ import TiptapifyDialog from '@tiptapify/components/UI/TiptapifyDialog.vue'
7
+ import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
8
+ import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
9
+ import {
10
+ resetChanges,
11
+ resetConfirmationOff,
12
+ turnOffResetConfirmation,
13
+ } from '@tiptapify/extensions/components/actions/reset/resetChanges'
14
+ import { inject, Ref, ref, useTemplateRef } from 'vue'
15
+
16
+ import { ComposerTranslation } from 'vue-i18n'
17
+
18
+ const { variantBtn } = useTiptapifyConfig()
19
+
20
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
21
+
22
+ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
23
+
24
+ const dialog = useTemplateRef('dialog')
25
+ const dontAskAgain = ref(false)
26
+
27
+ function requestReset() {
28
+ if (resetConfirmationOff()) {
29
+ reset()
30
+
31
+ return
32
+ }
33
+
34
+ dontAskAgain.value = false
35
+ dialog.value?.open()
36
+ }
37
+
38
+ function confirmReset() {
39
+ if (dontAskAgain.value) {
40
+ turnOffResetConfirmation()
41
+ }
42
+
43
+ dialog.value?.close()
44
+ reset()
45
+ }
46
+
47
+ function reset() {
48
+ resetChanges(editor.value)
49
+ editor.value.commands.focus()
50
+ }
51
+
52
+ </script>
53
+
54
+ <template>
55
+ <VBtn
56
+ :disabled="!editor.can().undo() && !editor.can().redo()"
57
+ size="32"
58
+ color=""
59
+ :variant="variantBtn"
60
+ @click="requestReset"
61
+ >
62
+ <Tooltip :label="t('action.reset_changes')" />
63
+ <BtnIcon :icon="`mdiSvg:${mdi.mdiRestore}`" />
64
+ </VBtn>
65
+
66
+ <TiptapifyDialog
67
+ ref="dialog"
68
+ module="resetChanges"
69
+ :title="t('action.reset_changes')"
70
+ :max-width="440"
71
+ @submit="confirmReset"
72
+ >
73
+ <template #content>
74
+ <VCardText>
75
+ <p>{{ t('action.reset_changes_confirm') }}</p>
76
+ <VCheckbox
77
+ v-model="dontAskAgain"
78
+ class="tiptapify-reset-changes__dont-ask"
79
+ :label="t('action.reset_changes_dont_ask')"
80
+ density="compact"
81
+ hide-details
82
+ />
83
+ </VCardText>
84
+ </template>
85
+
86
+ <template #actions>
87
+ <VCardActions>
88
+ <div class="tiptapify-dialog-actions">
89
+ <div class="tiptapify-dialog-actions__start" />
90
+ <div class="tiptapify-dialog-actions__end">
91
+ <VBtn :variant="variantBtn" @click="dialog?.close()">
92
+ {{ t('dialog.cancel') }}
93
+ </VBtn>
94
+ <VBtn type="submit" color="error" :variant="variantBtn">
95
+ {{ t('action.reset') }}
96
+ </VBtn>
97
+ </div>
98
+ </div>
99
+ </VCardActions>
100
+ </template>
101
+ </TiptapifyDialog>
102
+ </template>
103
+
104
+ <style lang="scss" scoped>
105
+ .tiptapify-reset-changes__dont-ask {
106
+ margin-top: 12px;
107
+ }
108
+ </style>
@@ -0,0 +1,99 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import { Editor } from '@tiptap/vue-3'
3
+ import { Document } from '@tiptap/extension-document'
4
+ import { Paragraph } from '@tiptap/extension-paragraph'
5
+ import { Text } from '@tiptap/extension-text'
6
+ import { UndoRedo } from '@tiptap/extensions'
7
+ import {
8
+ ResetChanges,
9
+ resetChanges,
10
+ resetConfirmationOff,
11
+ turnOffResetConfirmation,
12
+ } from '@tiptapify/extensions/components/actions/reset/resetChanges'
13
+
14
+ let editor: Editor | null = null
15
+
16
+ function createEditor(content: string, onUpdate: () => void = () => {}) {
17
+ editor = new Editor({
18
+ element: document.createElement('div'),
19
+ content,
20
+ extensions: [Document, Paragraph, Text, UndoRedo, ResetChanges],
21
+ onUpdate,
22
+ })
23
+
24
+ return editor
25
+ }
26
+
27
+ afterEach(() => {
28
+ editor?.destroy()
29
+ editor = null
30
+ vi.restoreAllMocks()
31
+ localStorage.clear()
32
+ })
33
+
34
+ describe('the reset confirmation', () => {
35
+ it('asks until the user turns it off, and remembers that', () => {
36
+ expect(resetConfirmationOff()).toBe(false)
37
+
38
+ turnOffResetConfirmation()
39
+
40
+ expect(resetConfirmationOff()).toBe(true)
41
+ expect(localStorage.getItem('tiptapify:reset-changes:confirm')).toBe('off')
42
+ })
43
+
44
+ it('keeps asking where the storage is blocked', () => {
45
+ vi.spyOn(window, 'localStorage', 'get').mockImplementation(() => {
46
+ throw new Error('blocked')
47
+ })
48
+
49
+ expect(() => turnOffResetConfirmation()).not.toThrow()
50
+ expect(resetConfirmationOff()).toBe(false)
51
+ })
52
+ })
53
+
54
+ describe('resetChanges', () => {
55
+ it('returns the document to the content the editor opened with', () => {
56
+ const instance = createEditor('<p>Original</p>')
57
+ instance.commands.insertContentAt(instance.state.doc.content.size, '<p>Added</p>')
58
+ instance.commands.setContent('<p>Replaced</p>')
59
+
60
+ expect(resetChanges(instance)).toBe(true)
61
+
62
+ expect(instance.getHTML()).toBe('<p>Original</p>')
63
+ })
64
+
65
+ it('empties the undo and redo history, so the reset itself cannot be undone', () => {
66
+ const instance = createEditor('<p>Original</p>')
67
+ instance.commands.insertContentAt(instance.state.doc.content.size, '<p>Added</p>')
68
+ instance.commands.insertContentAt(instance.state.doc.content.size, '<p>More</p>')
69
+ instance.commands.undo()
70
+ expect(instance.can().redo()).toBe(true)
71
+
72
+ resetChanges(instance)
73
+
74
+ expect(instance.can().undo()).toBe(false)
75
+ expect(instance.can().redo()).toBe(false)
76
+
77
+ instance.commands.insertContentAt(instance.state.doc.content.size, '<p>After</p>')
78
+ instance.commands.undo()
79
+ expect(instance.getHTML()).toBe('<p>Original</p>')
80
+ })
81
+
82
+ it('does nothing before the first change', () => {
83
+ const instance = createEditor('<p>Original</p>')
84
+
85
+ expect(resetChanges(instance)).toBe(false)
86
+ expect(instance.getHTML()).toBe('<p>Original</p>')
87
+ })
88
+
89
+ it('reports the reset as a content change, for the host to save', () => {
90
+ let updates = 0
91
+ const instance = createEditor('<p>Original</p>', () => updates++)
92
+ instance.commands.insertContentAt(instance.state.doc.content.size, '<p>Added</p>')
93
+ updates = 0
94
+
95
+ resetChanges(instance)
96
+
97
+ expect(updates).toBe(1)
98
+ })
99
+ })
@@ -0,0 +1,78 @@
1
+ import { Editor, Extension, JSONContent } from '@tiptap/core'
2
+ import { EditorState } from '@tiptap/pm/state'
3
+
4
+ const name = 'resetChanges'
5
+
6
+ declare module '@tiptap/core' {
7
+ interface Storage {
8
+ resetChanges: { initialContent: JSONContent | null }
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Keeps the document the editor opened with, for resetChanges to return to: the document before its first change
14
+ * (the create event comes later than the first changes can).
15
+ */
16
+ export const ResetChanges = Extension.create({
17
+ name,
18
+
19
+ addStorage() {
20
+ return { initialContent: null as JSONContent | null }
21
+ },
22
+
23
+ onTransaction({ transaction }) {
24
+ if (this.storage.initialContent === null && transaction.docChanged) {
25
+ this.storage.initialContent = transaction.before.toJSON()
26
+ }
27
+ },
28
+ })
29
+
30
+ const confirmKey = 'tiptapify:reset-changes:confirm'
31
+
32
+ // Storage may be blocked (private mode, sandboxed iframe): the reset then keeps asking
33
+ function storage(): Storage | null {
34
+ try {
35
+ return window.localStorage
36
+ } catch {
37
+ return null
38
+ }
39
+ }
40
+
41
+ /**
42
+ * The user chose "Don't ask again" in the reset confirmation (kept in this browser).
43
+ */
44
+ export function resetConfirmationOff(): boolean {
45
+ try {
46
+ return storage()?.getItem(confirmKey) === 'off'
47
+ } catch {
48
+ return false
49
+ }
50
+ }
51
+
52
+ export function turnOffResetConfirmation(): void {
53
+ try {
54
+ storage()?.setItem(confirmKey, 'off')
55
+ } catch {
56
+ // The choice is not kept: the next reset asks again
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Returns the document to the content the editor opened with and empties the undo and redo history,
62
+ * so the reset itself cannot be undone.
63
+ */
64
+ export function resetChanges(editor: Editor): boolean {
65
+ const initialContent = editor.storage.resetChanges?.initialContent
66
+ if (!initialContent) {
67
+ return false
68
+ }
69
+
70
+ editor.commands.setContent(initialContent, { emitUpdate: true })
71
+
72
+ const { state } = editor
73
+ editor.view.updateState(EditorState.create({ schema: state.schema, doc: state.doc, plugins: state.plugins }))
74
+ // An empty transaction re-renders what reads the history, as the undo and redo buttons
75
+ editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false))
76
+
77
+ return true
78
+ }
@@ -140,6 +140,16 @@ describe('image dialog apply', () => {
140
140
  expect(selectedImageAttrs(editor!.state)).toMatchObject({ src: 'https://example.com/a.png', width: 640 })
141
141
  })
142
142
 
143
+ it('reads a node selection made by another copy of prosemirror-state', () => {
144
+ createEditor('<p>Text</p><img src="https://example.com/a.png" width="640">')
145
+ const { from, to } = editor!.state.selection
146
+ const node = editor!.state.doc.nodeAt(6)
147
+ const foreignSelection = { from, to, node }
148
+ const state = { ...editor!.state, selection: foreignSelection } as unknown as Parameters<typeof selectedImageAttrs>[0]
149
+
150
+ expect(selectedImageAttrs(state)).toMatchObject({ src: 'https://example.com/a.png', width: 640 })
151
+ })
152
+
143
153
  it('sends the selected image to the dialog, or none to insert one', () => {
144
154
  createEditor('<p>Text</p><img src="https://example.com/a.png">')
145
155
  const details: Array<{ image: Record<string, unknown> | null }> = []
@@ -5,6 +5,8 @@ import { ImageAlign } from '@tiptapify/extensions/components/media/image/caption
5
5
 
6
6
  export { resizeSide, toSize }
7
7
 
8
+ export type { ImageAlign }
9
+
8
10
  export type ImageSizeMode = 'original' | 'text' | 'custom'
9
11
 
10
12
  export type ImageSize = MediaSize
@@ -27,12 +29,13 @@ const typeName = 'image'
27
29
  * The attributes of the image selected as a node, or null when no image is selected (the dialog inserts one).
28
30
  */
29
31
  export function selectedImageAttrs(state: EditorState): Record<string, unknown> | null {
30
- const { selection } = state
31
- if (!(selection instanceof NodeSelection) || selection.node.type.name !== typeName) {
32
+ // Not instanceof: a host that imports this module from the source runs another copy of prosemirror-state than the editor
33
+ const { node } = state.selection as Partial<NodeSelection>
34
+ if (node?.type.name !== typeName) {
32
35
  return null
33
36
  }
34
37
 
35
- return { ...selection.node.attrs }
38
+ return { ...node.attrs }
36
39
  }
37
40
 
38
41
  /**
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "إعادة",
4
+ "reset": "إعادة تعيين",
5
+ "reset_changes": "إعادة تعيين التغييرات",
6
+ "reset_changes_confirm": "سيتم تجاهل جميع التغييرات منذ فتح المحرر. لا يمكن التراجع عن ذلك.",
7
+ "reset_changes_dont_ask": "عدم السؤال مرة أخرى",
4
8
  "undo": "تراجع"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Повтори",
4
+ "reset": "Нулирай",
5
+ "reset_changes": "Нулиране на промените",
6
+ "reset_changes_confirm": "Всички промени от отварянето на редактора ще бъдат отхвърлени. Това не може да бъде отменено.",
7
+ "reset_changes_dont_ask": "Не питай повече",
4
8
  "undo": "Отмъкни"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "重做",
4
+ "reset": "重置",
5
+ "reset_changes": "重置更改",
6
+ "reset_changes_confirm": "自打开编辑器以来的所有更改都将被丢弃。此操作无法撤销。",
7
+ "reset_changes_dont_ask": "不再询问",
4
8
  "undo": "撤销"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Znovu",
4
+ "reset": "Obnovit",
5
+ "reset_changes": "Zahodit změny",
6
+ "reset_changes_confirm": "Všechny změny od otevření editoru budou zahozeny. Tuto akci nelze vrátit zpět.",
7
+ "reset_changes_dont_ask": "Příště se neptat",
4
8
  "undo": "Zpět"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Wiederholen",
4
+ "reset": "Zurücksetzen",
5
+ "reset_changes": "Änderungen zurücksetzen",
6
+ "reset_changes_confirm": "Alle Änderungen seit dem Öffnen des Editors werden verworfen. Dies kann nicht rückgängig gemacht werden.",
7
+ "reset_changes_dont_ask": "Nicht mehr fragen",
4
8
  "undo": "Rückgängig"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Επανάληψη",
4
+ "reset": "Επαναφορά",
5
+ "reset_changes": "Επαναφορά αλλαγών",
6
+ "reset_changes_confirm": "Όλες οι αλλαγές από το άνοιγμα του επεξεργαστή θα απορριφθούν. Αυτό δεν μπορεί να αναιρεθεί.",
7
+ "reset_changes_dont_ask": "Να μην ερωτηθώ ξανά",
4
8
  "undo": "Αναίρεση"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Redo",
4
+ "reset": "Reset",
5
+ "reset_changes": "Reset changes",
6
+ "reset_changes_confirm": "All changes made since the editor opened will be discarded. This cannot be undone.",
7
+ "reset_changes_dont_ask": "Don’t ask again",
4
8
  "undo": "Undo"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Rehacer",
4
+ "reset": "Restablecer",
5
+ "reset_changes": "Restablecer cambios",
6
+ "reset_changes_confirm": "Se descartarán todos los cambios desde que se abrió el editor. Esta acción no se puede deshacer.",
7
+ "reset_changes_dont_ask": "No volver a preguntar",
4
8
  "undo": "Deshacer"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Tee uuesti",
4
+ "reset": "Lähtesta",
5
+ "reset_changes": "Lähtesta muudatused",
6
+ "reset_changes_confirm": "Kõik muudatused alates redaktori avamisest tühistatakse. Seda ei saa tagasi võtta.",
7
+ "reset_changes_dont_ask": "Ära küsi enam",
4
8
  "undo": "Tühista"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Tee uudelleen",
4
+ "reset": "Palauta",
5
+ "reset_changes": "Palauta muutokset",
6
+ "reset_changes_confirm": "Kaikki muutokset editorin avaamisen jälkeen hylätään. Tätä ei voi kumota.",
7
+ "reset_changes_dont_ask": "Älä kysy uudelleen",
4
8
  "undo": "Kumoa"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Refaire",
4
+ "reset": "Réinitialiser",
5
+ "reset_changes": "Réinitialiser les modifications",
6
+ "reset_changes_confirm": "Toutes les modifications depuis l’ouverture de l’éditeur seront abandonnées. Cette action est irréversible.",
7
+ "reset_changes_dont_ask": "Ne plus demander",
4
8
  "undo": "Annuler"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "ביצוע מחדש",
4
+ "reset": "איפוס",
5
+ "reset_changes": "איפוס שינויים",
6
+ "reset_changes_confirm": "כל השינויים מאז פתיחת העורך יבוטלו. לא ניתן לבטל פעולה זו.",
7
+ "reset_changes_dont_ask": "אל תשאל שוב",
4
8
  "undo": "ביטול"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "फिर से करें",
4
+ "reset": "रीसेट करें",
5
+ "reset_changes": "बदलाव रीसेट करें",
6
+ "reset_changes_confirm": "संपादक खुलने के बाद से किए गए सभी बदलाव हटा दिए जाएंगे। इसे पूर्ववत नहीं किया जा सकता।",
7
+ "reset_changes_dont_ask": "फिर से न पूछें",
4
8
  "undo": "पूर्ववत् करें"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Ponovno",
4
+ "reset": "Poništi sve",
5
+ "reset_changes": "Poništi promjene",
6
+ "reset_changes_confirm": "Sve promjene od otvaranja uređivača bit će odbačene. Ovo se ne može poništiti.",
7
+ "reset_changes_dont_ask": "Ne pitaj ponovno",
4
8
  "undo": "Poništi"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Ismétlés",
4
+ "reset": "Visszaállítás",
5
+ "reset_changes": "Változtatások visszaállítása",
6
+ "reset_changes_confirm": "A szerkesztő megnyitása óta végzett összes változtatás elvész. Ez nem vonható vissza.",
7
+ "reset_changes_dont_ask": "Ne kérdezze újra",
4
8
  "undo": "Visszavonás"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Ulang",
4
+ "reset": "Atur ulang",
5
+ "reset_changes": "Atur ulang perubahan",
6
+ "reset_changes_confirm": "Semua perubahan sejak editor dibuka akan dibuang. Tindakan ini tidak dapat diurungkan.",
7
+ "reset_changes_dont_ask": "Jangan tanya lagi",
4
8
  "undo": "Urungkan"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Ripeti",
4
+ "reset": "Ripristina",
5
+ "reset_changes": "Ripristina modifiche",
6
+ "reset_changes_confirm": "Tutte le modifiche dall’apertura dell’editor verranno annullate. L’operazione non può essere annullata.",
7
+ "reset_changes_dont_ask": "Non chiedere più",
4
8
  "undo": "Annulla"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "やり直し",
4
+ "reset": "リセット",
5
+ "reset_changes": "変更をリセット",
6
+ "reset_changes_confirm": "エディターを開いてからのすべての変更が破棄されます。この操作は元に戻せません。",
7
+ "reset_changes_dont_ask": "今後は確認しない",
4
8
  "undo": "取り消し"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "다시 실행",
4
+ "reset": "초기화",
5
+ "reset_changes": "변경 사항 초기화",
6
+ "reset_changes_confirm": "편집기를 연 이후의 모든 변경 사항이 삭제됩니다. 이 작업은 되돌릴 수 없습니다.",
7
+ "reset_changes_dont_ask": "다시 묻지 않기",
4
8
  "undo": "실행 취소"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Atcelt atsaukšanu",
4
+ "reset": "Atiestatīt",
5
+ "reset_changes": "Atiestatīt izmaiņas",
6
+ "reset_changes_confirm": "Visas izmaiņas kopš redaktora atvēršanas tiks atmestas. To nevar atsaukt.",
7
+ "reset_changes_dont_ask": "Vairs nejautāt",
4
8
  "undo": "Atsaukt"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Pakartoti",
4
+ "reset": "Atkurti",
5
+ "reset_changes": "Atkurti pakeitimus",
6
+ "reset_changes_confirm": "Visi pakeitimai nuo redaktoriaus atidarymo bus atmesti. To negalima atšaukti.",
7
+ "reset_changes_dont_ask": "Daugiau neklausti",
4
8
  "undo": "Atšaukti"
5
9
  },
6
10
  "ai": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "action": {
3
3
  "redo": "Opnieuw",
4
+ "reset": "Herstellen",
5
+ "reset_changes": "Wijzigingen herstellen",
6
+ "reset_changes_confirm": "Alle wijzigingen sinds het openen van de editor worden verwijderd. Dit kan niet ongedaan worden gemaakt.",
7
+ "reset_changes_dont_ask": "Niet meer vragen",
4
8
  "undo": "Ongedaan maken"
5
9
  },
6
10
  "ai": {