tiptapify 0.0.4 → 0.0.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.
package/package.json CHANGED
@@ -1,16 +1,29 @@
1
1
  {
2
2
  "name": "tiptapify",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
5
5
  "exports": {
6
6
  ".": {
7
- "import": "./dist/tiptapify.es.js",
8
- "require": "./dist/tiptapify.umd.js"
7
+ "import": {
8
+ "default": "./dist/tiptapify.es.js",
9
+ "development": "./src/index.ts"
10
+ },
11
+ "require": {
12
+ "default": "./dist/tiptapify.umd.js",
13
+ "development": "./src/index.ts"
14
+ }
9
15
  },
16
+ "./src/*": "./src/*",
17
+ "./dist/*": "./dist/*",
10
18
  "./style.css": "./dist/tiptapify.css"
11
19
  },
12
20
  "main": "./dist/tiptapify.umd.cjs",
13
21
  "module": "./dist/tiptapify.js",
22
+ "source": "./src/index.ts",
23
+ "files": [
24
+ "dist",
25
+ "src"
26
+ ],
14
27
  "scripts": {
15
28
  "dev": "vite",
16
29
  "build": "vite build",
@@ -38,7 +51,6 @@
38
51
  "repository": "https://github.com/IVoyt/tiptapify",
39
52
  "packageManager": "pnpm@10.11.0",
40
53
  "dependencies": {
41
- "@intlify/unplugin-vue-i18n": "^6.0.8",
42
54
  "@tiptap/core": "next",
43
55
  "@tiptap/extension-blockquote": "next",
44
56
  "@tiptap/extension-bold": "next",
@@ -77,15 +89,9 @@
77
89
  "@tiptap/starter-kit": "next",
78
90
  "@tiptap/suggestion": "next",
79
91
  "@tiptap/vue-3": "next",
80
- "@vitejs/plugin-vue": "^5.2.4",
81
- "@vitejs/plugin-vue-jsx": "^4.2.0",
82
92
  "highlight.js": "^11.11.1",
83
93
  "linkifyjs": "^4.3.1",
84
94
  "lowlight": "^3.3.0",
85
- "unplugin-vue-components": "^28.5.0",
86
- "vite": "^6.3.5",
87
- "vite-plugin-vuetify": "^2.1.1",
88
- "vite-svg-loader": "^5.1.0",
89
95
  "vue-i18n": "^11.1.4"
90
96
  },
91
97
  "peerDependencies": {
@@ -94,11 +100,18 @@
94
100
  "vuetify": "^3.8.5"
95
101
  },
96
102
  "devDependencies": {
103
+ "@intlify/unplugin-vue-i18n": "^6.0.8",
97
104
  "@rollup/plugin-alias": "^5.1.1",
98
105
  "@types/node": "^22.15.21",
106
+ "@vitejs/plugin-vue": "^5.2.4",
107
+ "@vitejs/plugin-vue-jsx": "^4.2.0",
99
108
  "rollup-plugin-tsconfig-paths": "^1.5.2",
100
109
  "sass-embedded": "^1.89.0",
101
110
  "typescript": "^5.8.3",
111
+ "unplugin-vue-components": "^28.5.0",
112
+ "vite": "^6.3.5",
113
+ "vite-plugin-vuetify": "^2.1.1",
114
+ "vite-svg-loader": "^5.1.0",
102
115
  "vite-tsconfig-paths": "^5.1.4",
103
116
  "vue-tsc": "^2.2.10"
104
117
  }
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import LinkDialog from "@tiptapify/components/extensions/components/LinkDialog.vue";
3
+ import ShowSource from "@tiptapify/components/extensions/components/ShowSource.vue";
3
4
  import { useEditor } from "@tiptapify/composable/useEditor";
4
5
  import { computed, defineProps, Ref, ref } from 'vue'
5
6
  import { useI18n } from "vue-i18n";
@@ -111,6 +112,7 @@ const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
111
112
  </template>
112
113
 
113
114
  <LinkDialog ref="toolbarLinkButton" />
115
+ <ShowSource />
114
116
  </div>
115
117
  </template>
116
118
 
@@ -0,0 +1,118 @@
1
+ export const fonts = [
2
+ {
3
+ name: 'Arial',
4
+ fontFamily: 'arial'
5
+ },
6
+ {
7
+ name: 'Arial Black',
8
+ fontFamily: 'arial black'
9
+ },
10
+ {
11
+ name: 'Baskerville',
12
+ fontFamily: 'baskerville'
13
+ },
14
+ {
15
+ name: 'Bodoni MT',
16
+ fontFamily: 'bodoni mt'
17
+ },
18
+ {
19
+ name: 'Brush Script MT',
20
+ fontFamily: 'brush script mt'
21
+ },
22
+ {
23
+ name: 'Calibri',
24
+ fontFamily: 'calibri'
25
+ },
26
+ {
27
+ name: 'Calisto MT',
28
+ fontFamily: 'calisto mt'
29
+ },
30
+ {
31
+ name: 'Cambria',
32
+ fontFamily: 'cambria'
33
+ },
34
+ {
35
+ name: 'Century Gothic',
36
+ fontFamily: 'century gothic'
37
+ },
38
+ {
39
+ name: 'Consolas',
40
+ fontFamily: 'consolas'
41
+ },
42
+ {
43
+ name: 'Comic Sans',
44
+ fontFamily: 'comic sans ms, comic sans'
45
+ },
46
+ {
47
+ name: 'Courier',
48
+ fontFamily: 'Courier'
49
+ },
50
+ {
51
+ name: 'Courier New',
52
+ fontFamily: 'courier new'
53
+ },
54
+ {
55
+ name: 'Cursive',
56
+ fontFamily: 'cursive'
57
+ },
58
+ {
59
+ name: 'Dejavu Sans',
60
+ fontFamily: 'dejavu sans'
61
+ },
62
+ {
63
+ name: 'Franklin Gothic',
64
+ fontFamily: 'franklin gothic'
65
+ },
66
+ {
67
+ name: 'Garamond',
68
+ fontFamily: 'garamond'
69
+ },
70
+ {
71
+ name: 'Georgia',
72
+ fontFamily: 'georgia'
73
+ },
74
+ {
75
+ name: 'Helvetica',
76
+ fontFamily: 'helvetica'
77
+ },
78
+ {
79
+ name: 'Impact',
80
+ fontFamily: 'impact'
81
+ },
82
+ {
83
+ name: 'Inter',
84
+ fontFamily: 'inter'
85
+ },
86
+ {
87
+ name: 'Monospace',
88
+ fontFamily: 'monospace'
89
+ },
90
+ {
91
+ name: 'Optima',
92
+ fontFamily: 'optima'
93
+ },
94
+ {
95
+ name: 'Segoe UI',
96
+ fontFamily: 'segoe ui'
97
+ },
98
+ {
99
+ name: 'Serif',
100
+ fontFamily: 'serif'
101
+ },
102
+ {
103
+ name: 'Tahoma',
104
+ fontFamily: 'tahoma'
105
+ },
106
+ {
107
+ name: 'Time New Roman',
108
+ fontFamily: 'times new roman'
109
+ },
110
+ {
111
+ name: 'Trebuchet MS',
112
+ fontFamily: 'trebuchet ms'
113
+ },
114
+ {
115
+ name: 'Verdana',
116
+ fontFamily: 'verdana'
117
+ },
118
+ ]
@@ -1,124 +1,6 @@
1
1
  import { computed, ComputedRef, Ref, ref } from "vue";
2
2
  import * as mdi from '@mdi/js'
3
-
4
- const fonts = ref([
5
- {
6
- name: 'Arial',
7
- fontFamily: 'arial'
8
- },
9
- {
10
- name: 'Arial Black',
11
- fontFamily: 'arial black'
12
- },
13
- {
14
- name: 'Baskerville',
15
- fontFamily: 'baskerville'
16
- },
17
- {
18
- name: 'Bodoni MT',
19
- fontFamily: 'bodoni mt'
20
- },
21
- {
22
- name: 'Brush Script MT',
23
- fontFamily: 'brush script mt'
24
- },
25
- {
26
- name: 'Calibri',
27
- fontFamily: 'calibri'
28
- },
29
- {
30
- name: 'Calisto MT',
31
- fontFamily: 'calisto mt'
32
- },
33
- {
34
- name: 'Cambria',
35
- fontFamily: 'cambria'
36
- },
37
- {
38
- name: 'Century Gothic',
39
- fontFamily: 'century gothic'
40
- },
41
- {
42
- name: 'Consolas',
43
- fontFamily: 'consolas'
44
- },
45
- {
46
- name: 'Comic Sans',
47
- fontFamily: 'comic sans ms, comic sans'
48
- },
49
- {
50
- name: 'Courier',
51
- fontFamily: 'Courier'
52
- },
53
- {
54
- name: 'Courier New',
55
- fontFamily: 'courier new'
56
- },
57
- {
58
- name: 'Cursive',
59
- fontFamily: 'cursive'
60
- },
61
- {
62
- name: 'Dejavu Sans',
63
- fontFamily: 'dejavu sans'
64
- },
65
- {
66
- name: 'Franklin Gothic',
67
- fontFamily: 'franklin gothic'
68
- },
69
- {
70
- name: 'Garamond',
71
- fontFamily: 'garamond'
72
- },
73
- {
74
- name: 'Georgia',
75
- fontFamily: 'georgia'
76
- },
77
- {
78
- name: 'Helvetica',
79
- fontFamily: 'helvetica'
80
- },
81
- {
82
- name: 'Impact',
83
- fontFamily: 'impact'
84
- },
85
- {
86
- name: 'Inter',
87
- fontFamily: 'inter'
88
- },
89
- {
90
- name: 'Monospace',
91
- fontFamily: 'monospace'
92
- },
93
- {
94
- name: 'Optima',
95
- fontFamily: 'optima'
96
- },
97
- {
98
- name: 'Segoe UI',
99
- fontFamily: 'segoe ui'
100
- },
101
- {
102
- name: 'Serif',
103
- fontFamily: 'serif'
104
- },
105
- {
106
- name: 'Tahoma',
107
- fontFamily: 'tahoma'
108
- },
109
- {
110
- name: 'Time New Roman',
111
- fontFamily: 'times new roman'
112
- },
113
- {
114
- name: 'Trebuchet MS',
115
- fontFamily: 'trebuchet ms'
116
- },
117
- {
118
- name: 'Verdana',
119
- fontFamily: 'verdana'
120
- },
121
- ])
3
+ import { fonts } from './fonts'
122
4
 
123
5
  interface MDIIcons {
124
6
  [key: string]: string
@@ -184,7 +66,6 @@ export function toolbarItems(
184
66
  * font color, backgroundcolor
185
67
  * tables
186
68
  * media (image, video)
187
- * unsetmarks, clearnodes
188
69
  */
189
70
  heading: {
190
71
  name: 'heading',
@@ -199,23 +80,41 @@ export function toolbarItems(
199
80
  props: {
200
81
  color: computed(() => editor.value.isActive('heading') ? 'primary' : ''),
201
82
  },
202
- children: headingLevels.value.map(level => {
203
- return {
204
- name: `H${level}`,
205
- tooltip: `style.headings.h${level}`,
206
- icon: mdiIcons[`mdiFormatHeader${level}`],
83
+ children: [
84
+ {
85
+ name: `paragraph`,
86
+ tooltip: `style.paragraph`,
87
+ icon: mdiIcons[`mdiFormatParagraph`],
207
88
  noI18n: true,
208
89
  enabled: true,
209
90
  props: {
210
91
  color: computed(() => {
211
- return editor.value.isActive('heading', { level }) ? 'primary' : ''
92
+ return editor.value.isActive('paragraph') ? 'primary' : ''
212
93
  }),
213
94
  },
214
95
  attrs: {
215
- click: () => editor.value.chain().focus().toggleHeading({ level }).run()
96
+ click: () => editor.value.chain().focus().setParagraph().run()
216
97
  }
217
98
  }
218
- })
99
+ ].concat(
100
+ headingLevels.value.map(level => {
101
+ return {
102
+ name: `H${level}`,
103
+ tooltip: `style.headings.h${level}`,
104
+ icon: mdiIcons[`mdiFormatHeader${level}`],
105
+ noI18n: true,
106
+ enabled: true,
107
+ props: {
108
+ color: computed(() => {
109
+ return editor.value.isActive('heading', { level }) ? 'primary' : ''
110
+ }),
111
+ },
112
+ attrs: {
113
+ click: () => editor.value.chain().focus().toggleHeading({ level }).run()
114
+ }
115
+ }
116
+ })
117
+ )
219
118
  },
220
119
  fontFamily: {
221
120
  name: 'font-family',
@@ -227,7 +126,7 @@ export function toolbarItems(
227
126
  attrs: {
228
127
  click: () => editor.value.chain().focus().unsetFontFamily().run()
229
128
  },
230
- children: fonts.value.map((font) => {
129
+ children: fonts.map((font) => {
231
130
  return {
232
131
  name: font.name,
233
132
  tooltip: '',
@@ -651,6 +550,17 @@ export function toolbarItems(
651
550
  click: () => editor.value.chain().focus().setHardBreak().run()
652
551
  }
653
552
  },
553
+ source: {
554
+ name: 'source',
555
+ tooltip: 'misc.source',
556
+ icon: mdi.mdiCodeTags,
557
+ section: 'misc',
558
+ enabled: true,
559
+ props: {},
560
+ attrs: {
561
+ click: () => editor.value.commands.showSource()
562
+ }
563
+ },
654
564
  }
655
565
 
656
566
 
@@ -25,6 +25,7 @@ import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
25
25
 
26
26
  import { Link } from '@tiptap/extension-link'
27
27
  import CodeBlockComponent from '@tiptapify/components/CodeBlockComponent.vue'
28
+ import { ViewSource } from '@tiptapify/components/extensions/view-source'
28
29
  import SlashCommands from '@tiptapify/components/extensions/slash-commands'
29
30
  import suggestion from '@tiptapify/components/extensions/components/slashCommands/suggestion'
30
31
 
@@ -92,7 +93,8 @@ export function editorExtensions (placeholder: string, slashCommands: boolean) {
92
93
  types: ['heading', 'paragraph'],
93
94
  }),
94
95
  Placeholder.configure({ placeholder }),
95
- CharacterCount
96
+ CharacterCount,
97
+ ViewSource
96
98
  ]
97
99
 
98
100
  if (slashCommands) {
@@ -90,7 +90,7 @@ watch(dialog, val => {
90
90
 
91
91
  <VCardActions>
92
92
  <VBtn :disabled="isDisabled" @click="apply">
93
- {{ t('dialog.link.apply') }}
93
+ {{ t('dialog.apply') }}
94
94
  </VBtn>
95
95
  </VCardActions>
96
96
  </VCard>
@@ -0,0 +1,124 @@
1
+ <script setup lang="ts">
2
+ import { useEditor } from "@tiptapify/composable/useEditor";
3
+ import { ref, onMounted, onUnmounted, watch } from 'vue'
4
+ import { useI18n } from "vue-i18n";
5
+
6
+ const props = defineProps({
7
+ indent: { type: Number, default: 2 },
8
+ })
9
+
10
+ const { t } = useI18n();
11
+
12
+ const editor = useEditor().editor.getInstance()
13
+
14
+ const dialog = ref(false)
15
+ const formatted = ref(false)
16
+ const sourceCode = ref('')
17
+
18
+ const formatHtml = (html: string): string => {
19
+ let formatted = html.replace(/>/g, '>\n');
20
+
21
+ formatted = formatted.replace(/\n</g, '\n<');
22
+ formatted = formatted.replace(/([^>\n])</g, '$1\n<');
23
+
24
+ const lines = formatted.split('\n');
25
+ let indentLevel = 0;
26
+
27
+ return lines
28
+ .map(line => {
29
+ if (line.match(/<\//)) {
30
+ indentLevel = Math.max(0, indentLevel - 1);
31
+ }
32
+
33
+ const indentedLine = ' '.repeat(indentLevel * props.indent) + line;
34
+
35
+ if (line.match(/<[^\/][^>]*>/) && !line.match(/<.*\/>/)) {
36
+ indentLevel++;
37
+ }
38
+
39
+ return indentedLine;
40
+ })
41
+ .filter(line => line.trim())
42
+ .join('\n');
43
+ }
44
+
45
+ const unformatHtml = (html: string): string => {
46
+ return html
47
+ .replace(/\n/g, '')
48
+ .replace(/\s+/g, ' ')
49
+ .replace(/>\s+</g, '><')
50
+ }
51
+
52
+ const showDialog = (event: CustomEvent) => {
53
+ sourceCode.value = event.detail.html
54
+ dialog.value = true;
55
+ }
56
+
57
+ const saveChanges = () => {
58
+ dialog.value = false
59
+
60
+ editor.value.commands.setContent(sourceCode.value, true)
61
+ }
62
+
63
+ onMounted(() => {
64
+ window.addEventListener('tiptapify-show-source', showDialog as EventListener)
65
+ })
66
+
67
+ onUnmounted(() => {
68
+ window.removeEventListener('tiptapify-show-source', showDialog as EventListener)
69
+ })
70
+
71
+ watch(() => formatted.value, () => {
72
+ sourceCode.value = formatted.value ? formatHtml(sourceCode.value) : unformatHtml(sourceCode.value)
73
+ })
74
+ </script>
75
+
76
+ <template>
77
+ <VDialog v-model="dialog" max-width="1500">
78
+ <VCard>
79
+ <VCardTitle>{{ t('dialog.source.title') }}</VCardTitle>
80
+
81
+ <VCardText>
82
+ <VContainer fluid class="pt-0 pl-0 pr-0">
83
+ <VRow>
84
+ <VCol>
85
+ <VBtn v-model="formatted" :color="`${formatted ? 'primary' : ''}`" @click="formatted = !formatted">
86
+ {{ t('dialog.source.prettify') }}
87
+ </VBtn>
88
+ </VCol>
89
+ </VRow>
90
+ </VContainer>
91
+
92
+ <VTextarea
93
+ v-model="sourceCode"
94
+ no-resize
95
+ rows="100"
96
+ variant="outlined"
97
+ class="source-code-area"
98
+ />
99
+ </VCardText>
100
+
101
+ <VCardActions>
102
+ <VSpacer></VSpacer>
103
+ <VBtn color="primary" @click="dialog = false">
104
+ {{ t('dialog.close') }}
105
+ </VBtn>
106
+ <VBtn color="primary" @click="saveChanges">
107
+ {{ t('dialog.apply') }}
108
+ </VBtn>
109
+ </VCardActions>
110
+ </VCard>
111
+ </VDialog>
112
+ </template>
113
+
114
+ <style scoped lang="scss">
115
+ .source-code-area {
116
+ font-family: monospace;
117
+ white-space: pre-wrap;
118
+ }
119
+
120
+ :deep(.source-code-area textarea) {
121
+ max-height: 900px;
122
+ overflow-y: auto;
123
+ }
124
+ </style>
@@ -0,0 +1,53 @@
1
+ import { Extension } from '@tiptap/core'
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
3
+
4
+ export interface ViewSourceOptions {
5
+ HTMLAttributes: Record<string, any>
6
+ }
7
+
8
+ declare module '@tiptap/core' {
9
+ interface Commands<ReturnType> {
10
+ viewSource: {
11
+ /**
12
+ * Показать исходный HTML-код
13
+ */
14
+ showSource: () => ReturnType
15
+ }
16
+ }
17
+ }
18
+
19
+ export const ViewSource = Extension.create<ViewSourceOptions>({
20
+ name: 'viewSource',
21
+
22
+ addOptions() {
23
+ return {
24
+ HTMLAttributes: {},
25
+ }
26
+ },
27
+
28
+ addCommands() {
29
+ return {
30
+ showSource: () => ({ editor }) => {
31
+ const event = new CustomEvent('tiptapify-show-source', {
32
+ detail: {
33
+ // html: editor.getHTML()
34
+ html: editor.getHTML({ blockSeparator: '\n\n' })
35
+ // html: editor.getText({ blockSeparator: '\n\n' })
36
+ }
37
+ })
38
+
39
+ window.dispatchEvent(event)
40
+
41
+ return true
42
+ },
43
+ }
44
+ },
45
+
46
+ addProseMirrorPlugins() {
47
+ return [
48
+ new Plugin({
49
+ key: new PluginKey('viewSource'),
50
+ }),
51
+ ]
52
+ },
53
+ })
@@ -0,0 +1,67 @@
1
+ {
2
+ "style": {
3
+ "paragraph": "Absatz",
4
+ "heading": "Überschrift",
5
+ "headings": {
6
+ "h1": "Überschrift Ebene 1",
7
+ "h2": "Überschrift Ebene 2",
8
+ "h3": "Überschrift Ebene 3",
9
+ "h4": "Überschrift Ebene 4",
10
+ "h5": "Überschrift Ebene 5",
11
+ "h6": "Überschrift Ebene 6"
12
+ },
13
+ "fontFamily": "Schriftart",
14
+ "fontSize": "Schriftgröße",
15
+ "lineHeight": "Zeilenhöhe"
16
+ },
17
+ "format": {
18
+ "bold": "Fett",
19
+ "italic": "Kursiv",
20
+ "strike": "Durchgestrichen",
21
+ "underline": "Unterstrichen",
22
+ "sup": "Hochgestellt",
23
+ "sub": "Tiefgestellt",
24
+ "break": "Zeilenumbruch",
25
+ "highlight": "Hervorheben",
26
+ "line": "Horizontale Linie",
27
+ "blockquote": "Zitat",
28
+ "code": "Code",
29
+ "codeblock": "Codeblock",
30
+ "link": "Externer Link",
31
+ "formatClear": "Formatierung löschen"
32
+ },
33
+ "action": {
34
+ "undo": "Rückgängig",
35
+ "redo": "Wiederherstellen"
36
+ },
37
+ "alignment": "Ausrichtung",
38
+ "alignments": {
39
+ "left": "Linksbündig",
40
+ "center": "Zentriert",
41
+ "right": "Rechtsbündig",
42
+ "justify": "Blocksatz"
43
+ },
44
+ "list": "Liste",
45
+ "lists": {
46
+ "bullet": "Aufzählungsliste",
47
+ "numbered": "Nummerierte Liste",
48
+ "task": "Aufgabenliste",
49
+ "indent": "Einzug vergrößern",
50
+ "outdent": "Einzug verkleinern"
51
+ },
52
+ "dialog": {
53
+ "close": "Schließen",
54
+ "apply": "Anwenden",
55
+ "link": {
56
+ "title": "Link hinzufügen/bearbeiten",
57
+ "placeholder": "Linkadresse"
58
+ },
59
+ "source": {
60
+ "title": "Quellcode anzeigen",
61
+ "prettify": "prettify"
62
+ }
63
+ },
64
+ "misc": {
65
+ "source": "Quellcode anzeigen"
66
+ }
67
+ }