tiptapify 0.2.6 → 0.3.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.
- package/README.md +1 -1
- package/dist/tiptapify.css +1 -1
- package/dist/tiptapify.mjs +23435 -25368
- package/dist/tiptapify.umd.js +70 -73
- package/package.json +1 -1
- package/src/components/Footer.vue +1 -1
- package/src/components/Tiptapify.vue +6 -2
- package/src/components/Toolbar/Index.vue +3 -4
- package/src/components/Toolbar/Items.vue +3 -3
- package/src/components/UI/HotkeyTooltip.test.ts +65 -0
- package/src/components/UI/HotkeyTooltip.vue +54 -0
- package/src/components/UI/Tooltip.vue +20 -0
- package/src/composables/useTiptapifyConfig.test.ts +66 -0
- package/src/composables/useTiptapifyConfig.ts +17 -0
- package/src/constants/defaults.ts +4 -2
- package/src/extensions/components/actions/redo/Button.vue +5 -9
- package/src/extensions/components/actions/undo/Button.vue +5 -9
- package/src/extensions/components/ai/Button.vue +7 -10
- package/src/extensions/components/ai/Dialog.vue +4 -9
- package/src/extensions/components/alignment/center/Button.vue +5 -9
- package/src/extensions/components/alignment/justify/Button.vue +5 -9
- package/src/extensions/components/alignment/left/Button.vue +5 -9
- package/src/extensions/components/alignment/right/Button.vue +5 -9
- package/src/extensions/components/format/bold/Button.vue +5 -9
- package/src/extensions/components/format/italic/Button.vue +5 -9
- package/src/extensions/components/format/strike/Button.vue +5 -9
- package/src/extensions/components/format/underline/Button.vue +5 -9
- package/src/extensions/components/formatExtra/code/Button.vue +5 -9
- package/src/extensions/components/formatExtra/codeBlock/Button.vue +5 -9
- package/src/extensions/components/formatExtra/quote/Button.vue +5 -9
- package/src/extensions/components/formatExtra/sub/Button.vue +5 -9
- package/src/extensions/components/formatExtra/sup/Button.vue +5 -9
- package/src/extensions/components/list/bullet/Button.vue +8 -13
- package/src/extensions/components/list/indent/Button.vue +5 -9
- package/src/extensions/components/list/numbered/Button.vue +5 -9
- package/src/extensions/components/list/outdent/Button.vue +5 -9
- package/src/extensions/components/list/task/Button.vue +5 -9
- package/src/extensions/components/media/charmap/Button.vue +5 -8
- package/src/extensions/components/media/charmap/Picker.vue +2 -3
- package/src/extensions/components/media/emoji/Button.vue +6 -9
- package/src/extensions/components/media/emoji/Picker.vue +2 -3
- package/src/extensions/components/media/iframe/Button.vue +6 -10
- package/src/extensions/components/media/iframe/IframeDialog.vue +3 -7
- package/src/extensions/components/media/image/Button.vue +6 -10
- package/src/extensions/components/media/image/ImageDialog.vue +5 -10
- package/src/extensions/components/media/link/Button.vue +7 -10
- package/src/extensions/components/media/link/LinkDialog.vue +3 -7
- package/src/extensions/components/media/table/Button.vue +10 -24
- package/src/extensions/components/media/table/ColumnActions.vue +4 -9
- package/src/extensions/components/media/table/RowActions.vue +4 -9
- package/src/extensions/components/media/video/Button.vue +6 -10
- package/src/extensions/components/media/video/VideoDialog.vue +3 -7
- package/src/extensions/components/misc/break/Button.vue +5 -9
- package/src/extensions/components/misc/formatClear/Button.vue +5 -9
- package/src/extensions/components/misc/fullscreen/Button.vue +5 -9
- package/src/extensions/components/misc/invisibleChar/Button.vue +5 -9
- package/src/extensions/components/misc/line/Button.vue +5 -9
- package/src/extensions/components/misc/preview/Button.vue +5 -9
- package/src/extensions/components/misc/source/Button.vue +6 -10
- package/src/extensions/components/misc/source/ShowSourceDialog.vue +4 -5
- package/src/extensions/components/style/color/Button.vue +5 -9
- package/src/extensions/components/style/fontFamily/Button.vue +5 -9
- package/src/extensions/components/style/fontSize/Button.vue +6 -7
- package/src/extensions/components/style/heading/Button.vue +6 -8
- package/src/extensions/components/style/highlight/Button.vue +5 -9
- package/src/extensions/components/style/lineHeight/Button.vue +5 -9
- package/src/index.ts +3 -0
- package/src/utils/hotkeys.test.ts +146 -0
- package/src/utils/hotkeys.ts +65 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import defaults from '@tiptapify/constants/defaults'
|
|
4
4
|
import { itemsPropType, toolbarSections } from '@tiptapify/types/toolbarTypes'
|
|
5
5
|
import { SlashCommandsConfig } from '@tiptapify/types/slashCommandsTypes'
|
|
6
|
-
import { computed, onBeforeUnmount, PropType, provide, ref, ShallowRef } from 'vue'
|
|
6
|
+
import { computed, onBeforeUnmount, PropType, provide, ref, ShallowRef, toRef } from 'vue'
|
|
7
7
|
import { default as Toolbar } from '@tiptapify/components/Toolbar/Index.vue'
|
|
8
8
|
import { Editor, EditorContent } from '@tiptap/vue-3'
|
|
9
9
|
import { TiptapifyAiOptions, TiptapifyAiResolvedOptions, TiptapifyEditor, TiptapifyFooterAlignment, variantBtnTypes, variantFieldTypes } from '@tiptapify/types/editor'
|
|
@@ -13,6 +13,7 @@ import MenuFloating from '@tiptapify/components/MenuFloating.vue'
|
|
|
13
13
|
import { useI18n } from 'vue-i18n'
|
|
14
14
|
|
|
15
15
|
import { getTiptapEditor } from '@tiptapify/components/index'
|
|
16
|
+
import { TIPTAPIFY_CONFIG_KEY } from '@tiptapify/composables/useTiptapifyConfig'
|
|
16
17
|
import { createAiBackendProvider } from '@tiptapify/extensions/components/ai/backend'
|
|
17
18
|
|
|
18
19
|
import Footer from '@tiptapify/components/Footer.vue'
|
|
@@ -139,6 +140,10 @@ const editor: ShallowRef<Editor | undefined> = getTiptapEditor(
|
|
|
139
140
|
provide('tiptapifyEditor', editor)
|
|
140
141
|
provide('tiptapifyI18n', { t })
|
|
141
142
|
provide('tiptapifyAi', tiptapifyAi)
|
|
143
|
+
provide(TIPTAPIFY_CONFIG_KEY, {
|
|
144
|
+
variantBtn: toRef(props, 'variantBtn'),
|
|
145
|
+
variantField: toRef(props, 'variantField'),
|
|
146
|
+
})
|
|
142
147
|
|
|
143
148
|
editor.value?.chain().setFontFamily(props.defaultFontFamily).run()
|
|
144
149
|
|
|
@@ -162,7 +167,6 @@ onBeforeUnmount(() => {
|
|
|
162
167
|
<template v-if="toolbar">
|
|
163
168
|
<Toolbar
|
|
164
169
|
v-if="editor"
|
|
165
|
-
:variant-btn="variantBtn"
|
|
166
170
|
:variant-field="variantField"
|
|
167
171
|
:font-measure="fontMeasure"
|
|
168
172
|
:items="items"
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
import { Editor } from '@tiptap/vue-3'
|
|
3
3
|
import Items from '@tiptapify/components/Toolbar/Items.vue'
|
|
4
4
|
import defaults from '@tiptapify/constants/defaults'
|
|
5
|
-
import {
|
|
5
|
+
import { variantFieldTypes } from '@tiptapify/types/editor'
|
|
6
6
|
import { computed, inject, PropType, Ref } from 'vue'
|
|
7
7
|
import { itemsPropType, toolbarSections } from '@tiptapify/types/toolbarTypes'
|
|
8
8
|
|
|
9
9
|
import { default as toolbarItemsGroups, availableItems } from '@tiptapify/components/Toolbar/items'
|
|
10
10
|
|
|
11
11
|
const props = defineProps({
|
|
12
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
13
12
|
variantField: { type: String as PropType<variantFieldTypes>, default() { return defaults.variantField } },
|
|
14
13
|
items: { type: [Array, Object] as PropType<itemsPropType>, default() { return [] } },
|
|
15
14
|
itemsExclude: { type: Boolean, default() { return false } },
|
|
@@ -178,10 +177,10 @@ Object.keys(props.customExtensions).forEach((key: any) => {
|
|
|
178
177
|
<div v-if="editor" class="tiptapify-toolbar">
|
|
179
178
|
<VToolbar elevation="1" :theme="theme" height="auto" :class="`ps-1 pr-1 rounded-t-${rounded}`">
|
|
180
179
|
<VSlideGroup v-if="toolbarScrollable">
|
|
181
|
-
<Items :items="toolbarItems"
|
|
180
|
+
<Items :items="toolbarItems" />
|
|
182
181
|
</VSlideGroup>
|
|
183
182
|
|
|
184
|
-
<Items v-else :items="toolbarItems"
|
|
183
|
+
<Items v-else :items="toolbarItems" />
|
|
185
184
|
</VToolbar>
|
|
186
185
|
</div>
|
|
187
186
|
</template>
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { variantBtnTypes } from '@tiptapify/types/editor'
|
|
3
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
5
4
|
import { PropType } from 'vue'
|
|
6
5
|
|
|
7
6
|
import { toolbarSections } from '@tiptapify/types/toolbarTypes'
|
|
8
7
|
|
|
9
8
|
defineProps({
|
|
10
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
11
9
|
items: { type: Array as PropType<toolbarSections>, default() { return {} } },
|
|
12
10
|
})
|
|
13
11
|
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
13
|
+
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
16
|
<template>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { createSSRApp, h } from 'vue'
|
|
3
|
+
import { renderToString } from 'vue/server-renderer'
|
|
4
|
+
import { createVuetify } from 'vuetify'
|
|
5
|
+
import HotkeyTooltip from './HotkeyTooltip.vue'
|
|
6
|
+
|
|
7
|
+
const MAC_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
|
|
8
|
+
const WIN_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
|
|
9
|
+
|
|
10
|
+
function mockUserAgent (userAgent: string) {
|
|
11
|
+
Object.defineProperty(window.navigator, 'userAgent', {
|
|
12
|
+
value: userAgent,
|
|
13
|
+
configurable: true,
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function renderTooltip (label: string, hotkey: string): Promise<string> {
|
|
18
|
+
const app = createSSRApp({
|
|
19
|
+
render: () => h(HotkeyTooltip, { label, hotkey })
|
|
20
|
+
})
|
|
21
|
+
app.use(createVuetify())
|
|
22
|
+
|
|
23
|
+
return renderToString(app)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('HotkeyTooltip', () => {
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.unstubAllGlobals()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('renders label and spelled-out hotkey on two lines on Windows', async () => {
|
|
32
|
+
mockUserAgent(WIN_UA)
|
|
33
|
+
const html = await renderTooltip('Bold', 'Mod-Shift-b')
|
|
34
|
+
|
|
35
|
+
expect(html).toContain('t-hotkey-tooltip__label')
|
|
36
|
+
expect(html).toContain('t-hotkey-tooltip__hotkey')
|
|
37
|
+
expect(html).toContain('Bold')
|
|
38
|
+
expect(html).toContain('Ctrl')
|
|
39
|
+
expect(html).toContain('Shift')
|
|
40
|
+
expect(html).toContain('>B</span>')
|
|
41
|
+
expect(html).not.toContain('mdiSvg:')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('renders the hotkey with icons on macOS without spelled-out names', async () => {
|
|
45
|
+
mockUserAgent(MAC_UA)
|
|
46
|
+
const html = await renderTooltip('Heading', 'Mod-Alt-1..6')
|
|
47
|
+
|
|
48
|
+
expect(html).toContain('t-hotkey-tooltip__label')
|
|
49
|
+
expect(html).toContain('t-hotkey-tooltip__hotkey')
|
|
50
|
+
expect(html).toContain('Heading')
|
|
51
|
+
expect(html).toContain('mdiSvg:')
|
|
52
|
+
expect(html).toContain('1–6')
|
|
53
|
+
expect(html).not.toContain('Ctrl')
|
|
54
|
+
expect(html).not.toContain('>Command<')
|
|
55
|
+
expect(html).not.toContain('>Option<')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('renders Enter as an icon on macOS', async () => {
|
|
59
|
+
mockUserAgent(MAC_UA)
|
|
60
|
+
const html = await renderTooltip('Hard break', 'Mod-Enter')
|
|
61
|
+
|
|
62
|
+
expect(html).toContain('mdiSvg:')
|
|
63
|
+
expect(html).not.toContain('>Enter<')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { hotkeyParts } from '@tiptapify/utils/hotkeys'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
label: { type: String, required: true },
|
|
8
|
+
hotkey: { type: String, required: true },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const parts = computed(() => hotkeyParts(props.hotkey))
|
|
12
|
+
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<span class="t-hotkey-tooltip">
|
|
17
|
+
<span class="t-hotkey-tooltip__label">{{ label }}</span>
|
|
18
|
+
<span class="t-hotkey-tooltip__hotkey">
|
|
19
|
+
<template v-for="(part, index) in parts" :key="index">
|
|
20
|
+
<span v-if="index" class="t-hotkey-sep">+</span>
|
|
21
|
+
<VIcon v-if="part.icon" :icon="part.icon" size="12" class="t-hotkey-icon" />
|
|
22
|
+
<span v-else>{{ part.label }}</span>
|
|
23
|
+
</template>
|
|
24
|
+
</span>
|
|
25
|
+
</span>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style scoped lang="scss">
|
|
29
|
+
.t-hotkey-tooltip {
|
|
30
|
+
align-items: center;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: 3px;
|
|
34
|
+
padding: 4px 0;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.t-hotkey-tooltip__hotkey {
|
|
39
|
+
align-items: center;
|
|
40
|
+
background: rgba(127, 127, 127, .2);
|
|
41
|
+
background: color-mix(in srgb, currentColor 14%, transparent);
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
font-family: 'JetBrainsMono', ui-monospace, Menlo, Consolas, monospace;
|
|
45
|
+
font-size: 11px;
|
|
46
|
+
gap: 4px;
|
|
47
|
+
letter-spacing: .03em;
|
|
48
|
+
padding: 3px 6px;
|
|
49
|
+
|
|
50
|
+
.t-hotkey-icon {
|
|
51
|
+
font-size: 12px;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import HotkeyTooltip from '@tiptapify/components/UI/HotkeyTooltip.vue'
|
|
4
|
+
|
|
5
|
+
defineProps({
|
|
6
|
+
label: { type: String, default: '' },
|
|
7
|
+
hotkey: { type: String, default: '' },
|
|
8
|
+
openDelay: { type: Number, default: 500 },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<VTooltip activator="parent" :open-delay="openDelay">
|
|
15
|
+
<HotkeyTooltip v-if="hotkey" :label="label" :hotkey="hotkey" />
|
|
16
|
+
<slot v-else>
|
|
17
|
+
{{ label }}
|
|
18
|
+
</slot>
|
|
19
|
+
</VTooltip>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { createSSRApp, h, provide, ref } from 'vue'
|
|
3
|
+
import { renderToString } from 'vue/server-renderer'
|
|
4
|
+
import { createVuetify } from 'vuetify'
|
|
5
|
+
import BoldButton from '@tiptapify/extensions/components/format/bold/Button.vue'
|
|
6
|
+
import { TIPTAPIFY_CONFIG_KEY, useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
7
|
+
|
|
8
|
+
const editor = {
|
|
9
|
+
isActive: () => false,
|
|
10
|
+
can: () => ({ chain: () => ({ focus: () => ({ toggleBold: () => ({ run: () => false }) }) }) }),
|
|
11
|
+
commands: { toggleBold: () => {} },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Consumer = {
|
|
15
|
+
setup () {
|
|
16
|
+
const { variantBtn, variantField } = useTiptapifyConfig()
|
|
17
|
+
return () => h('span', [
|
|
18
|
+
h('span', { id: 'variant-btn' }, variantBtn.value),
|
|
19
|
+
h('span', { id: 'variant-field' }, variantField.value),
|
|
20
|
+
])
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function render (root: () => any, withConfig: boolean) {
|
|
25
|
+
const app = createSSRApp({
|
|
26
|
+
setup () {
|
|
27
|
+
if (withConfig) {
|
|
28
|
+
provide(TIPTAPIFY_CONFIG_KEY, {
|
|
29
|
+
variantBtn: ref('tonal'),
|
|
30
|
+
variantField: ref('outlined'),
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
provide('tiptapifyEditor', ref(editor))
|
|
34
|
+
provide('tiptapifyI18n', { t: (key: string) => key })
|
|
35
|
+
return root
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
app.use(createVuetify())
|
|
39
|
+
|
|
40
|
+
return renderToString(app)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe('useTiptapifyConfig', () => {
|
|
44
|
+
it('exposes provided config values', async () => {
|
|
45
|
+
const html = await render(() => h(Consumer), true)
|
|
46
|
+
|
|
47
|
+
expect(html).toContain('<span id="variant-btn">tonal</span>')
|
|
48
|
+
expect(html).toContain('<span id="variant-field">outlined</span>')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('falls back to defaults when no provider is present', async () => {
|
|
52
|
+
const html = await render(() => h(Consumer), false)
|
|
53
|
+
|
|
54
|
+
expect(html).toContain('<span id="variant-btn">flat</span>')
|
|
55
|
+
expect(html).toContain('<span id="variant-field">solo</span>')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('is consumed by toolbar extension buttons', async () => {
|
|
59
|
+
const custom = await render(() => h(BoldButton), true)
|
|
60
|
+
const fallback = await render(() => h(BoldButton), false)
|
|
61
|
+
|
|
62
|
+
expect(custom).toContain('tonal')
|
|
63
|
+
expect(fallback).toContain('flat')
|
|
64
|
+
expect(custom).not.toEqual(fallback)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { computed, inject, InjectionKey, Ref } from 'vue'
|
|
2
|
+
import defaults from '@tiptapify/constants/defaults'
|
|
3
|
+
import { variantBtnTypes, variantFieldTypes } from '@tiptapify/types/editor'
|
|
4
|
+
|
|
5
|
+
export interface TiptapifyConfig {
|
|
6
|
+
variantBtn: Ref<variantBtnTypes>
|
|
7
|
+
variantField: Ref<variantFieldTypes>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const TIPTAPIFY_CONFIG_KEY: InjectionKey<TiptapifyConfig> = Symbol('tiptapifyConfig')
|
|
11
|
+
|
|
12
|
+
export function useTiptapifyConfig(): TiptapifyConfig {
|
|
13
|
+
return inject(TIPTAPIFY_CONFIG_KEY, {
|
|
14
|
+
variantBtn: computed(() => defaults.variantBtn),
|
|
15
|
+
variantField: computed(() => defaults.variantField),
|
|
16
|
+
})
|
|
17
|
+
}
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -27,9 +25,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
27
25
|
:variant="variantBtn"
|
|
28
26
|
@click="editor.chain().focus().redo().run()"
|
|
29
27
|
>
|
|
30
|
-
<
|
|
31
|
-
{{ t('action.redo') }}
|
|
32
|
-
</VTooltip>
|
|
28
|
+
<Tooltip :label="t('action.redo')" hotkey="Mod-Shift-z" />
|
|
33
29
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiRedo}`" />
|
|
34
30
|
</VBtn>
|
|
35
31
|
</template>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -27,9 +25,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
27
25
|
:variant="variantBtn"
|
|
28
26
|
@click="editor.chain().focus().undo().run()"
|
|
29
27
|
>
|
|
30
|
-
<
|
|
31
|
-
{{ t('action.undo') }}
|
|
32
|
-
</VTooltip>
|
|
28
|
+
<Tooltip :label="t('action.undo')" hotkey="Mod-z" />
|
|
33
29
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiUndo}`" />
|
|
34
30
|
</VBtn>
|
|
35
31
|
</template>
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
5
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
5
6
|
import AiDialog from '@tiptapify/extensions/components/ai/Dialog.vue'
|
|
6
|
-
import
|
|
7
|
-
import { TiptapifyAiResolvedOptions
|
|
8
|
-
import { computed, ComputedRef, inject,
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { TiptapifyAiResolvedOptions } from '@tiptapify/types/editor'
|
|
9
|
+
import { computed, ComputedRef, inject, useTemplateRef } from 'vue'
|
|
9
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
13
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
14
13
|
|
|
15
14
|
const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
16
15
|
const ai = inject('tiptapifyAi') as ComputedRef<TiptapifyAiResolvedOptions | false>
|
|
@@ -32,11 +31,9 @@ function showDialog() {
|
|
|
32
31
|
|
|
33
32
|
<template>
|
|
34
33
|
<VBtn color="" :variant="variantBtn" size="32" :disabled="!isAvailable" @click="showDialog">
|
|
35
|
-
<
|
|
36
|
-
{{ isAvailable ? t('ai.title') : t('ai.unavailable') }}
|
|
37
|
-
</VTooltip>
|
|
34
|
+
<Tooltip :label="isAvailable ? t('ai.title') : t('ai.unavailable')" />
|
|
38
35
|
<BtnIcon :icon="icon" />
|
|
39
36
|
</VBtn>
|
|
40
37
|
|
|
41
|
-
<AiDialog ref="dialog"
|
|
38
|
+
<AiDialog ref="dialog" />
|
|
42
39
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
|
|
3
3
|
import TiptapifyDialog from '@tiptapify/components/UI/TiptapifyDialog.vue'
|
|
4
|
-
import
|
|
4
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
5
5
|
import {
|
|
6
6
|
TiptapifyAiEditorContext,
|
|
7
7
|
TiptapifyAiMode,
|
|
@@ -11,20 +11,15 @@ import {
|
|
|
11
11
|
TiptapifyAiResponse,
|
|
12
12
|
TiptapifyAiResolvedOptions,
|
|
13
13
|
TiptapifyAiStream,
|
|
14
|
-
TiptapifyEditor
|
|
15
|
-
variantBtnTypes,
|
|
16
|
-
variantFieldTypes
|
|
14
|
+
TiptapifyEditor
|
|
17
15
|
} from '@tiptapify/types/editor'
|
|
18
|
-
import { computed, ComputedRef, inject, nextTick,
|
|
16
|
+
import { computed, ComputedRef, inject, nextTick, Ref, ref, useTemplateRef, watch } from 'vue'
|
|
19
17
|
import * as mdi from '@mdi/js'
|
|
20
18
|
import { ComposerTranslation } from 'vue-i18n'
|
|
21
19
|
import { exceedsLimit } from './charLimit'
|
|
22
20
|
import { DEFAULT_AI_INSTRUCTION } from './backend'
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
26
|
-
variantField: { type: String as PropType<variantFieldTypes>, default() { return defaults.variantField } },
|
|
27
|
-
})
|
|
22
|
+
const { variantBtn, variantField } = useTiptapifyConfig()
|
|
28
23
|
|
|
29
24
|
const editor = inject('tiptapifyEditor') as Ref<TiptapifyEditor>
|
|
30
25
|
const ai = inject('tiptapifyAi') as ComputedRef<TiptapifyAiResolvedOptions | false>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -26,9 +24,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
26
24
|
size="32"
|
|
27
25
|
@click="editor.chain().focus().toggleTextAlign('center').run()"
|
|
28
26
|
>
|
|
29
|
-
<
|
|
30
|
-
{{ t('alignments.center') }}
|
|
31
|
-
</VTooltip>
|
|
27
|
+
<Tooltip :label="t('alignments.center')" hotkey="Mod-Shift-e" />
|
|
32
28
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiFormatAlignCenter}`" />
|
|
33
29
|
</VBtn>
|
|
34
30
|
</template>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -26,9 +24,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
26
24
|
size="32"
|
|
27
25
|
@click="editor.chain().focus().toggleTextAlign('justify').run()"
|
|
28
26
|
>
|
|
29
|
-
<
|
|
30
|
-
{{ t('alignments.justify') }}
|
|
31
|
-
</VTooltip>
|
|
27
|
+
<Tooltip :label="t('alignments.justify')" hotkey="Mod-Shift-j" />
|
|
32
28
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiFormatAlignJustify}`" />
|
|
33
29
|
</VBtn>
|
|
34
30
|
</template>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -26,9 +24,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
26
24
|
size="32"
|
|
27
25
|
@click="editor.chain().focus().toggleTextAlign('left').run()"
|
|
28
26
|
>
|
|
29
|
-
<
|
|
30
|
-
{{ t('alignments.left') }}
|
|
31
|
-
</VTooltip>
|
|
27
|
+
<Tooltip :label="t('alignments.left')" hotkey="Mod-Shift-l" />
|
|
32
28
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiFormatAlignLeft}`" />
|
|
33
29
|
</VBtn>
|
|
34
30
|
</template>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -26,9 +24,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
26
24
|
size="32"
|
|
27
25
|
@click="editor.chain().focus().toggleTextAlign('right').run()"
|
|
28
26
|
>
|
|
29
|
-
<
|
|
30
|
-
{{ t('alignments.right') }}
|
|
31
|
-
</VTooltip>
|
|
27
|
+
<Tooltip :label="t('alignments.right')" hotkey="Mod-Shift-r" />
|
|
32
28
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiFormatAlignRight}`" />
|
|
33
29
|
</VBtn>
|
|
34
30
|
</template>
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import * as mdi from '@mdi/js'
|
|
4
4
|
import { Editor } from '@tiptap/vue-3'
|
|
5
5
|
import BtnIcon from '@tiptapify/components/UI/BtnIcon.vue'
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Tooltip from '@tiptapify/components/UI/Tooltip.vue'
|
|
7
|
+
import { useTiptapifyConfig } from '@tiptapify/composables/useTiptapifyConfig'
|
|
8
|
+
import { inject, Ref } from 'vue'
|
|
8
9
|
|
|
9
|
-
import defaults from '@tiptapify/constants/defaults'
|
|
10
10
|
import { ComposerTranslation } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
variantBtn: { type: String as PropType<variantBtnTypes>, default() { return defaults.variantBtn } },
|
|
14
|
-
})
|
|
12
|
+
const { variantBtn } = useTiptapifyConfig()
|
|
15
13
|
|
|
16
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
17
15
|
|
|
@@ -27,9 +25,7 @@ const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
|
|
|
27
25
|
size="32"
|
|
28
26
|
@click="editor.commands.toggleBold()"
|
|
29
27
|
>
|
|
30
|
-
<
|
|
31
|
-
{{ t('format.bold') }}
|
|
32
|
-
</VTooltip>
|
|
28
|
+
<Tooltip :label="t('format.bold')" hotkey="Mod-b" />
|
|
33
29
|
<BtnIcon :icon="`mdiSvg:${mdi.mdiFormatBold}`" />
|
|
34
30
|
</VBtn>
|
|
35
31
|
</template>
|