From 5dc7e5bfa56b4dd5d03f1d3c7868841a2aa98582 Mon Sep 17 00:00:00 2001 From: finn Date: Mon, 22 Jan 2024 13:42:10 +0900 Subject: [PATCH] =?UTF-8?q?tiptap=20=EB=85=B8=EB=93=9C=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/tiptap/extensions/font-family.ts | 45 ------- .../src/lib/tiptap/extensions/index.ts | 5 - .../lib/tiptap/extensions/letter-spacing.ts | 47 ------- .../src/lib/tiptap/extensions/line-height.ts | 47 ------- .../src/lib/tiptap/extensions/node-id.ts | 43 ------ .../src/lib/tiptap/extensions/text-align.ts | 42 ------ .../src/lib/tiptap/legacies/font-family.ts | 31 +++++ .../src/lib/tiptap/legacies/index.ts | 5 + .../src/lib/tiptap/legacies/letter-spacing.ts | 26 ++++ .../src/lib/tiptap/legacies/line-height.ts | 25 ++++ .../src/lib/tiptap/legacies/text-align.ts | 22 +++ .../src/lib/tiptap/legacies/text-color.ts | 32 +++++ apps/penxle.com/src/lib/tiptap/marks/bold.ts | 6 +- .../src/lib/tiptap/marks/font-color.ts | 55 ++++++++ .../src/lib/tiptap/marks/font-family.ts | 48 +++++++ .../src/lib/tiptap/marks/font-size.ts | 48 +++++++ apps/penxle.com/src/lib/tiptap/marks/index.ts | 4 +- .../penxle.com/src/lib/tiptap/marks/italic.ts | 6 +- apps/penxle.com/src/lib/tiptap/marks/link.ts | 19 ++- .../penxle.com/src/lib/tiptap/marks/strike.ts | 6 +- .../src/lib/tiptap/marks/text-color.ts | 46 ------- .../src/lib/tiptap/marks/underline.ts | 6 +- .../tiptap/node-views/embed/Component.svelte | 2 +- .../src/lib/tiptap/nodes/bullet-list.ts | 4 +- .../src/lib/tiptap/nodes/heading.ts | 9 +- .../src/lib/tiptap/nodes/ordered-list.ts | 4 +- .../src/lib/tiptap/nodes/paragraph.ts | 51 +++++-- apps/penxle.com/src/lib/tiptap/preset.ts | 33 ++--- .../routes/editor/ArticleBubbleMenu.svelte | 68 +++------- .../src/routes/editor/formats.svelte | 127 +++++------------- apps/penxle.com/src/styles/prose.css | 98 +------------- apps/penxle.com/uno.config.ts | 1 + 32 files changed, 445 insertions(+), 566 deletions(-) delete mode 100644 apps/penxle.com/src/lib/tiptap/extensions/font-family.ts delete mode 100644 apps/penxle.com/src/lib/tiptap/extensions/letter-spacing.ts delete mode 100644 apps/penxle.com/src/lib/tiptap/extensions/line-height.ts delete mode 100644 apps/penxle.com/src/lib/tiptap/extensions/node-id.ts delete mode 100644 apps/penxle.com/src/lib/tiptap/extensions/text-align.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/font-family.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/index.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/letter-spacing.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/line-height.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/text-align.ts create mode 100644 apps/penxle.com/src/lib/tiptap/legacies/text-color.ts create mode 100644 apps/penxle.com/src/lib/tiptap/marks/font-color.ts create mode 100644 apps/penxle.com/src/lib/tiptap/marks/font-family.ts create mode 100644 apps/penxle.com/src/lib/tiptap/marks/font-size.ts delete mode 100644 apps/penxle.com/src/lib/tiptap/marks/text-color.ts diff --git a/apps/penxle.com/src/lib/tiptap/extensions/font-family.ts b/apps/penxle.com/src/lib/tiptap/extensions/font-family.ts deleted file mode 100644 index b7c9e0cdd3..0000000000 --- a/apps/penxle.com/src/lib/tiptap/extensions/font-family.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Extension } from '@tiptap/core'; -import { Heading, Paragraph } from '$lib/tiptap/nodes'; -import type { FontFamily as TFontFamily } from '@penxle/lib/unocss'; - -declare module '@tiptap/core' { - // eslint-disable-next-line @typescript-eslint/consistent-type-definitions - interface Commands { - fontFamily: { - setFontFamily: (fontFamily: TFontFamily) => ReturnType; - }; - } -} - -const types = [Heading.name, Paragraph.name]; - -export const FontFamily = Extension.create({ - name: 'font_family', - - addGlobalAttributes() { - return [ - { - types, - attributes: { - 'font-family': { - default: 'sans', - parseHTML: (element) => element.dataset.fontFamily, - renderHTML: (attributes) => ({ - 'data-font-family': attributes['font-family'] as string, - }), - }, - }, - }, - ]; - }, - - addCommands() { - return { - setFontFamily: - (fontFamily) => - ({ commands }) => { - return types.every((type) => commands.updateAttributes(type, { 'font-family': fontFamily })); - }, - }; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/extensions/index.ts b/apps/penxle.com/src/lib/tiptap/extensions/index.ts index 47fd8dbf48..b07ea8d4a8 100644 --- a/apps/penxle.com/src/lib/tiptap/extensions/index.ts +++ b/apps/penxle.com/src/lib/tiptap/extensions/index.ts @@ -1,9 +1,4 @@ export * from './drop-cursor'; -export * from './font-family'; export * from './gap-cursor'; export * from './history'; -export * from './letter-spacing'; -export * from './line-height'; -export * from './node-id'; export * from './placeholder'; -export * from './text-align'; diff --git a/apps/penxle.com/src/lib/tiptap/extensions/letter-spacing.ts b/apps/penxle.com/src/lib/tiptap/extensions/letter-spacing.ts deleted file mode 100644 index 71828b6aea..0000000000 --- a/apps/penxle.com/src/lib/tiptap/extensions/letter-spacing.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Extension } from '@tiptap/core'; -import { Heading, Paragraph } from '$lib/tiptap/nodes'; - -// value reference from classname: https://tailwindcss.com/docs/letter-spacing -export type Spacing = 'tighter' | 'tight' | 'normal' | 'wide' | 'wider' | 'widest'; - -declare module '@tiptap/core' { - // eslint-disable-next-line @typescript-eslint/consistent-type-definitions - interface Commands { - letterSpacing: { - setLetterSpacing: (spacing: Spacing) => ReturnType; - }; - } -} - -const types = [Heading.name, Paragraph.name]; - -export const LetterSpacing = Extension.create({ - name: 'letter_spacing', - - addGlobalAttributes() { - return [ - { - types, - attributes: { - 'letter-spacing': { - default: 'normal', - parseHTML: (element) => element.dataset.letterSpacing, - renderHTML: (attributes) => ({ - 'data-letter-spacing': attributes['letter-spacing'] as string, - }), - }, - }, - }, - ]; - }, - - addCommands() { - return { - setLetterSpacing: - (height) => - ({ commands }) => { - return types.every((type) => commands.updateAttributes(type, { 'letter-spacing': height })); - }, - }; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/extensions/line-height.ts b/apps/penxle.com/src/lib/tiptap/extensions/line-height.ts deleted file mode 100644 index 7ee52e2d89..0000000000 --- a/apps/penxle.com/src/lib/tiptap/extensions/line-height.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Extension } from '@tiptap/core'; -import { Heading, Paragraph } from '$lib/tiptap/nodes'; - -// value reference from tailwind classnames: https://tailwindcss.com/docs/line-height -export type Height = 'none' | 'tight' | 'snug' | 'normal' | 'relaxed' | 'loose'; - -declare module '@tiptap/core' { - // eslint-disable-next-line @typescript-eslint/consistent-type-definitions - interface Commands { - lineHeight: { - setLineHeight: (height: Height) => ReturnType; - }; - } -} - -const types = [Heading.name, Paragraph.name]; - -export const LineHeight = Extension.create({ - name: 'line_height', - - addGlobalAttributes() { - return [ - { - types, - attributes: { - 'line-height': { - default: 'normal', - parseHTML: (element) => element.dataset.lineHeight, - renderHTML: (attributes) => ({ - 'data-line-height': attributes['line-height'] as string, - }), - }, - }, - }, - ]; - }, - - addCommands() { - return { - setLineHeight: - (height) => - ({ commands }) => { - return types.every((type) => commands.updateAttributes(type, { 'line-height': height })); - }, - }; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/extensions/node-id.ts b/apps/penxle.com/src/lib/tiptap/extensions/node-id.ts deleted file mode 100644 index 8bbbc9734b..0000000000 --- a/apps/penxle.com/src/lib/tiptap/extensions/node-id.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Extension } from '@tiptap/core'; -import { Plugin } from '@tiptap/pm/state'; -import { nanoid } from 'nanoid'; - -const types = new Set(['paragraph']); - -export const NodeId = Extension.create({ - name: 'node_id', - - addGlobalAttributes() { - return [ - { - types, - attributes: { - 'node-id': { - rendered: false, - keepOnSplit: false, - }, - }, - }, - ]; - }, - - addProseMirrorPlugins() { - return [ - new Plugin({ - appendTransaction: (_, __, newState) => { - const { tr, doc } = newState; - - doc.descendants((node, pos) => { - if (types.has(node.type.name) && !node.attrs['node-id']) { - tr.setNodeAttribute(pos, 'node-id', nanoid()); - } - - return true; - }); - - return tr; - }, - }), - ]; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/extensions/text-align.ts b/apps/penxle.com/src/lib/tiptap/extensions/text-align.ts deleted file mode 100644 index 19d6319e49..0000000000 --- a/apps/penxle.com/src/lib/tiptap/extensions/text-align.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Extension } from '@tiptap/core'; - -export type Alignment = 'left' | 'center' | 'right' | 'justify'; - -declare module '@tiptap/core' { - // eslint-disable-next-line @typescript-eslint/consistent-type-definitions - interface Commands { - textAlign: { - setTextAlign: (alignment: Alignment) => ReturnType; - }; - } -} - -export const TextAlign = Extension.create({ - name: 'text_align', - - addGlobalAttributes() { - return [ - { - types: ['heading', 'paragraph'], - attributes: { - 'text-align': { - parseHTML: (element) => element.dataset.textAlign, - renderHTML: (attributes) => ({ - 'data-text-align': attributes['text-align'] as string, - }), - }, - }, - }, - ]; - }, - - addCommands() { - return { - setTextAlign: - (alignment) => - ({ commands }) => { - return ['heading', 'paragraph'].every((type) => commands.updateAttributes(type, { 'text-align': alignment })); - }, - }; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/legacies/font-family.ts b/apps/penxle.com/src/lib/tiptap/legacies/font-family.ts new file mode 100644 index 0000000000..b9d3d7647e --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/font-family.ts @@ -0,0 +1,31 @@ +import { Extension } from '@tiptap/core'; +import clsx from 'clsx'; +import { Heading, Paragraph } from '$lib/tiptap/nodes'; + +const types = [Heading.name, Paragraph.name]; + +export const LegacyFontFamily = Extension.create({ + name: 'legacy_font_family', + + addGlobalAttributes() { + return [ + { + types, + attributes: { + 'font-family': { + default: undefined, + renderHTML: (attributes) => ({ + class: clsx( + attributes['font-family'] === 'sans' && 'font-sans', + attributes['font-family'] === 'serif' && 'font-serif', + attributes['font-family'] === 'serif2' && 'font-serif2', + attributes['font-family'] === 'serif3' && 'font-serif3', + attributes['font-family'] === 'mono' && 'font-mono', + ), + }), + }, + }, + }, + ]; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/legacies/index.ts b/apps/penxle.com/src/lib/tiptap/legacies/index.ts new file mode 100644 index 0000000000..d42fa291e0 --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/index.ts @@ -0,0 +1,5 @@ +export * from './font-family'; +export * from './letter-spacing'; +export * from './line-height'; +export * from './text-align'; +export * from './text-color'; diff --git a/apps/penxle.com/src/lib/tiptap/legacies/letter-spacing.ts b/apps/penxle.com/src/lib/tiptap/legacies/letter-spacing.ts new file mode 100644 index 0000000000..6d58834c05 --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/letter-spacing.ts @@ -0,0 +1,26 @@ +import { Extension } from '@tiptap/core'; +import { Heading, Paragraph } from '$lib/tiptap/nodes'; + +// value reference from classname: https://tailwindcss.com/docs/letter-spacing +const types = [Heading.name, Paragraph.name]; + +export const LegacyLetterSpacing = Extension.create({ + name: 'legacy_letter_spacing', + + addGlobalAttributes() { + return [ + { + types, + attributes: { + 'letter-spacing': { + default: undefined, + parseHTML: (element) => element.dataset.letterSpacing, + renderHTML: (attributes) => ({ + 'data-letter-spacing': attributes['letter-spacing'] as string, + }), + }, + }, + }, + ]; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/legacies/line-height.ts b/apps/penxle.com/src/lib/tiptap/legacies/line-height.ts new file mode 100644 index 0000000000..dd0bb124e4 --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/line-height.ts @@ -0,0 +1,25 @@ +import { Extension } from '@tiptap/core'; +import { Heading, Paragraph } from '$lib/tiptap/nodes'; + +const types = [Heading.name, Paragraph.name]; + +export const LegacyLineHeight = Extension.create({ + name: 'legacy_line_height', + + addGlobalAttributes() { + return [ + { + types, + attributes: { + 'line-height': { + default: undefined, + parseHTML: (element) => element.dataset.lineHeight, + renderHTML: (attributes) => ({ + 'data-line-height': attributes['line-height'] as string, + }), + }, + }, + }, + ]; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/legacies/text-align.ts b/apps/penxle.com/src/lib/tiptap/legacies/text-align.ts new file mode 100644 index 0000000000..cc936693bd --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/text-align.ts @@ -0,0 +1,22 @@ +import { Extension } from '@tiptap/core'; + +export const LegacyTextAlign = Extension.create({ + name: 'legacy_text_align', + + addGlobalAttributes() { + return [ + { + types: ['heading', 'paragraph'], + attributes: { + 'text-align': { + default: undefined, + parseHTML: (element) => element.dataset.textAlign, + renderHTML: (attributes) => ({ + 'data-text-align': attributes['text-align'] as string, + }), + }, + }, + }, + ]; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/legacies/text-color.ts b/apps/penxle.com/src/lib/tiptap/legacies/text-color.ts new file mode 100644 index 0000000000..0d024af17e --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/legacies/text-color.ts @@ -0,0 +1,32 @@ +import { Mark } from '@tiptap/core'; +import clsx from 'clsx'; + +export const LegacyTextColor = Mark.create({ + name: 'text-color', + + addAttributes() { + return { + 'data-text-color': { + parseHTML: (element) => ({ 'data-text-color': element.dataset.textColor }), + renderHTML: ({ 'data-text-color': color }) => ({ + 'class': clsx( + (color === 'text-gray-50' || color === 'text-post-gray') && 'text-post-gray', + (color === 'text-gray-40' || color === 'text-post-gray2' || color === 'text-post-lightgray') && + 'text-post-lightgray', + (color === 'text-red-60' || color === 'text-post-red') && 'text-post-red', + (color === 'text-blue-60' || color === 'text-post-blue') && 'text-post-blue', + (color === 'text-orange-70' || color === 'text-post-brown') && 'text-post-brown', + (color === 'text-green-60' || color === 'text-post-green') && 'text-post-green', + (color === 'text-purple-60' || color === 'text-post-purple') && 'text-post-purple', + (color === 'text-white' || color === 'text-post-white') && 'text-white', + ), + 'data-text-color': color, + }), + }, + }; + }, + + renderHTML({ HTMLAttributes }) { + return ['span', HTMLAttributes, 0]; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/marks/bold.ts b/apps/penxle.com/src/lib/tiptap/marks/bold.ts index 2042ff7f4c..007db4f9a9 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/bold.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/bold.ts @@ -1,4 +1,4 @@ -import { Mark } from '@tiptap/core'; +import { Mark, mergeAttributes } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -16,8 +16,8 @@ export const Bold = Mark.create({ return [{ tag: 'b' }]; }, - renderHTML() { - return ['b', 0]; + renderHTML({ HTMLAttributes }) { + return ['b', mergeAttributes(HTMLAttributes, { class: 'font-bold' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/marks/font-color.ts b/apps/penxle.com/src/lib/tiptap/marks/font-color.ts new file mode 100644 index 0000000000..6cfbf44937 --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/marks/font-color.ts @@ -0,0 +1,55 @@ +import { Mark } from '@tiptap/core'; + +declare module '@tiptap/core' { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface Commands { + fontColor: { + setFontColor: (fontColor: string) => ReturnType; + unsetFontColor: () => ReturnType; + }; + } +} + +export const FontColor = Mark.create({ + name: 'font_color', + + addAttributes() { + return { + fontColor: { + parseHTML: (element) => element.style.color, + renderHTML: ({ fontColor }) => ({ + style: `color: ${fontColor}`, + }), + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'span', + getAttrs: (element) => (element instanceof HTMLElement && element.style.color ? null : false), + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return ['span', HTMLAttributes, 0]; + }, + + addCommands() { + return { + setFontColor: + (fontColor) => + ({ commands }) => { + return commands.setMark(this.name, { fontColor }); + }, + + unsetFontColor: + () => + ({ commands }) => { + return commands.unsetMark(this.name); + }, + }; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/marks/font-family.ts b/apps/penxle.com/src/lib/tiptap/marks/font-family.ts new file mode 100644 index 0000000000..29e43a0032 --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/marks/font-family.ts @@ -0,0 +1,48 @@ +import { Mark } from '@tiptap/core'; + +declare module '@tiptap/core' { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface Commands { + fontFamily: { + setFontFamily: (fontFamily: string) => ReturnType; + }; + } +} + +export const FontFamily = Mark.create({ + name: 'font_family', + + addAttributes() { + return { + fontFamily: { + parseHTML: (element) => element.style.fontFamily.replace(/^PNXL_/, ''), + renderHTML: ({ fontFamily }) => ({ + style: `font-family: PNXL_${fontFamily}`, + }), + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'span', + getAttrs: (element) => (element instanceof HTMLElement && element.style.fontFamily ? null : false), + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return ['span', HTMLAttributes, 0]; + }, + + addCommands() { + return { + setFontFamily: + (fontFamily) => + ({ commands }) => { + return commands.setMark(this.name, { fontFamily }); + }, + }; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/marks/font-size.ts b/apps/penxle.com/src/lib/tiptap/marks/font-size.ts new file mode 100644 index 0000000000..edce2fbcee --- /dev/null +++ b/apps/penxle.com/src/lib/tiptap/marks/font-size.ts @@ -0,0 +1,48 @@ +import { Mark } from '@tiptap/core'; + +declare module '@tiptap/core' { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface Commands { + fontSize: { + setFontSize: (fontSize: number) => ReturnType; + }; + } +} + +export const FontSize = Mark.create({ + name: 'font_size', + + addAttributes() { + return { + fontSize: { + parseHTML: (element) => element.style.fontSize, + renderHTML: ({ fontSize }) => ({ + style: `font-size: ${fontSize}px`, + }), + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'span', + getAttrs: (element) => (element instanceof HTMLElement && element.style.fontSize ? null : false), + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return ['span', HTMLAttributes, 0]; + }, + + addCommands() { + return { + setFontSize: + (fontSize) => + ({ commands }) => { + return commands.setMark(this.name, { fontSize }); + }, + }; + }, +}); diff --git a/apps/penxle.com/src/lib/tiptap/marks/index.ts b/apps/penxle.com/src/lib/tiptap/marks/index.ts index 2d25c2bf3a..11b22a4ee2 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/index.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/index.ts @@ -1,6 +1,8 @@ export * from './bold'; +export * from './font-color'; +export * from './font-family'; +export * from './font-size'; export * from './italic'; export * from './link'; export * from './strike'; -export * from './text-color'; export * from './underline'; diff --git a/apps/penxle.com/src/lib/tiptap/marks/italic.ts b/apps/penxle.com/src/lib/tiptap/marks/italic.ts index 99a9947794..57301b824e 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/italic.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/italic.ts @@ -1,4 +1,4 @@ -import { Mark } from '@tiptap/core'; +import { Mark, mergeAttributes } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -16,8 +16,8 @@ export const Italic = Mark.create({ return [{ tag: 'i' }]; }, - renderHTML() { - return ['i', 0]; + renderHTML({ HTMLAttributes }) { + return ['i', mergeAttributes(HTMLAttributes, { class: 'font-italic' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/marks/link.ts b/apps/penxle.com/src/lib/tiptap/marks/link.ts index b8af14284b..ea5a2cd603 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/link.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/link.ts @@ -1,4 +1,11 @@ -import { combineTransactionSteps, findChildrenInRange, getChangedRanges, getMarksBetween, Mark } from '@tiptap/core'; +import { + combineTransactionSteps, + findChildrenInRange, + getChangedRanges, + getMarksBetween, + Mark, + mergeAttributes, +} from '@tiptap/core'; import { Plugin } from '@tiptap/pm/state'; import { find } from 'linkifyjs'; @@ -30,7 +37,15 @@ export const Link = Mark.create({ }, renderHTML({ HTMLAttributes }) { - return ['a', { target: '_blank', rel: 'noreferrer nofollow', ...HTMLAttributes }, 0]; + return [ + 'a', + mergeAttributes(HTMLAttributes, { + class: 'text-disabled underline', + target: '_blank', + rel: 'noreferrer nofollow', + }), + 0, + ]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/marks/strike.ts b/apps/penxle.com/src/lib/tiptap/marks/strike.ts index ce2354a91b..2cd5f67d5b 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/strike.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/strike.ts @@ -1,4 +1,4 @@ -import { Mark } from '@tiptap/core'; +import { Mark, mergeAttributes } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -16,8 +16,8 @@ export const Strike = Mark.create({ return [{ tag: 's' }]; }, - renderHTML() { - return ['s', 0]; + renderHTML({ HTMLAttributes }) { + return ['s', mergeAttributes(HTMLAttributes, { class: 'line-through' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/marks/text-color.ts b/apps/penxle.com/src/lib/tiptap/marks/text-color.ts deleted file mode 100644 index 75cf829b34..0000000000 --- a/apps/penxle.com/src/lib/tiptap/marks/text-color.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Mark } from '@tiptap/core'; - -declare module '@tiptap/core' { - // eslint-disable-next-line @typescript-eslint/consistent-type-definitions - interface Commands { - color: { - setTextColor: (attributes: { 'data-text-color': string }) => ReturnType; - unsetTextColor: () => ReturnType; - }; - } -} - -export const TextColor = Mark.create({ - name: 'text-color', - - addAttributes() { - return { - 'data-text-color': { - rendered: true, - }, - }; - }, - - parseHTML() { - return [{ tag: 'span' }]; - }, - - renderHTML({ HTMLAttributes }) { - return ['span', HTMLAttributes, 0]; - }, - - addCommands() { - return { - setTextColor: - (attributes) => - ({ commands }) => { - return commands.setMark(this.name, attributes); - }, - unsetTextColor: - () => - ({ commands }) => { - return commands.unsetMark(this.name); - }, - }; - }, -}); diff --git a/apps/penxle.com/src/lib/tiptap/marks/underline.ts b/apps/penxle.com/src/lib/tiptap/marks/underline.ts index bd5e938307..572930b089 100644 --- a/apps/penxle.com/src/lib/tiptap/marks/underline.ts +++ b/apps/penxle.com/src/lib/tiptap/marks/underline.ts @@ -1,4 +1,4 @@ -import { Mark } from '@tiptap/core'; +import { Mark, mergeAttributes } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -16,8 +16,8 @@ export const Underline = Mark.create({ return [{ tag: 'u' }]; }, - renderHTML() { - return ['u', 0]; + renderHTML({ HTMLAttributes }) { + return ['u', mergeAttributes(HTMLAttributes, { class: 'underline' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/node-views/embed/Component.svelte b/apps/penxle.com/src/lib/tiptap/node-views/embed/Component.svelte index 7c80cdc89a..becfc17be7 100644 --- a/apps/penxle.com/src/lib/tiptap/node-views/embed/Component.svelte +++ b/apps/penxle.com/src/lib/tiptap/node-views/embed/Component.svelte @@ -60,7 +60,7 @@ - + {#if node.attrs.__data} {#if node.attrs.__data.html}
diff --git a/apps/penxle.com/src/lib/tiptap/nodes/bullet-list.ts b/apps/penxle.com/src/lib/tiptap/nodes/bullet-list.ts index dc001fe107..7f7314b3d9 100644 --- a/apps/penxle.com/src/lib/tiptap/nodes/bullet-list.ts +++ b/apps/penxle.com/src/lib/tiptap/nodes/bullet-list.ts @@ -1,4 +1,4 @@ -import { Node } from '@tiptap/core'; +import { mergeAttributes, Node } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -19,7 +19,7 @@ export const BulletList = Node.create({ }, renderHTML({ HTMLAttributes }) { - return ['ul', HTMLAttributes, 0]; + return ['ul', mergeAttributes(HTMLAttributes, { class: 'list-outside list-disc ml-1.25rem' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/nodes/heading.ts b/apps/penxle.com/src/lib/tiptap/nodes/heading.ts index cf80237bfe..beecb2376d 100644 --- a/apps/penxle.com/src/lib/tiptap/nodes/heading.ts +++ b/apps/penxle.com/src/lib/tiptap/nodes/heading.ts @@ -1,4 +1,5 @@ -import { Node } from '@tiptap/core'; +import { mergeAttributes, Node } from '@tiptap/core'; +import clsx from 'clsx'; type Level = 1 | 2 | 3; @@ -20,7 +21,9 @@ export const Heading = Node.create({ addAttributes() { return { level: { - rendered: false, + renderHTML: ({ level }) => ({ + class: clsx(level === 1 && 'title-24-b', level === 2 && 'title-20-b', level === 3 && 'subtitle-18-b'), + }), }, }; }, @@ -34,7 +37,7 @@ export const Heading = Node.create({ }, renderHTML({ node, HTMLAttributes }) { - return [`h${node.attrs.level}`, HTMLAttributes, 0]; + return [`h${node.attrs.level}`, mergeAttributes(HTMLAttributes, { class: 'my-4' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/nodes/ordered-list.ts b/apps/penxle.com/src/lib/tiptap/nodes/ordered-list.ts index b627a2f72d..d05ea6a6c2 100644 --- a/apps/penxle.com/src/lib/tiptap/nodes/ordered-list.ts +++ b/apps/penxle.com/src/lib/tiptap/nodes/ordered-list.ts @@ -1,4 +1,4 @@ -import { Node } from '@tiptap/core'; +import { mergeAttributes, Node } from '@tiptap/core'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -19,7 +19,7 @@ export const OrderedList = Node.create({ }, renderHTML({ HTMLAttributes }) { - return ['ol', HTMLAttributes, 0]; + return ['ol', mergeAttributes(HTMLAttributes, { class: 'list-outside list-decimal ml-1.25rem' }), 0]; }, addCommands() { diff --git a/apps/penxle.com/src/lib/tiptap/nodes/paragraph.ts b/apps/penxle.com/src/lib/tiptap/nodes/paragraph.ts index 1fdb86448d..3d0eecde51 100644 --- a/apps/penxle.com/src/lib/tiptap/nodes/paragraph.ts +++ b/apps/penxle.com/src/lib/tiptap/nodes/paragraph.ts @@ -1,13 +1,11 @@ -import { Node } from '@tiptap/core'; - -type Level = 1 | 2; -type Attributes = { level: Level }; +import { mergeAttributes, Node } from '@tiptap/core'; +import clsx from 'clsx'; declare module '@tiptap/core' { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface Commands { paragraph: { - setParagraph: (level: Level) => ReturnType; + setParagraph: () => ReturnType; }; } } @@ -21,9 +19,33 @@ export const Paragraph = Node.create({ addAttributes() { return { level: { - default: 1, - parseHTML: (element) => element.dataset.level && Number.parseInt(element.dataset.level, 10), - renderHTML: (attributes: Attributes) => ({ 'data-level': attributes.level.toString() }), + default: undefined, + renderHTML: ({ level }) => ({ + class: clsx(level === 2 && 'body-13-m'), + }), + }, + + textAlign: { + default: 'left', + renderHTML: ({ textAlign }) => ({ + style: `text-align: ${textAlign}`, + }), + }, + + lineHeight: { + default: 1.6, + parseHTML: (element) => Number.parseFloat(element.style.lineHeight), + renderHTML: ({ lineHeight }) => ({ + style: `line-height: ${lineHeight}`, + }), + }, + + letterSpacing: { + default: 0, + parseHTML: (element) => Number.parseInt(element.style.letterSpacing.replace('em', '')), + renderHTML: ({ letterSpacing }) => ({ + style: `letter-spacing: ${letterSpacing}em`, + }), }, }; }, @@ -33,16 +55,19 @@ export const Paragraph = Node.create({ }, renderHTML({ node, HTMLAttributes }) { - return !this.editor?.isEditable && node.childCount === 0 ? ['p', HTMLAttributes, ['br']] : ['p', HTMLAttributes, 0]; + return [ + 'p', + mergeAttributes(HTMLAttributes, { class: 'py-0.5' }), + !this.editor?.isEditable && node.childCount === 0 ? ['br'] : 0, + ]; }, addCommands() { return { setParagraph: - (level) => - ({ commands, state }) => { - const previousAttrs = state.selection.$head.parent.attrs; - return commands.setNode(this.name, { ...previousAttrs, level }); + () => + ({ commands }) => { + return commands.setNode(this.name); }, }; }, diff --git a/apps/penxle.com/src/lib/tiptap/preset.ts b/apps/penxle.com/src/lib/tiptap/preset.ts index e60e8f9515..bb79547922 100644 --- a/apps/penxle.com/src/lib/tiptap/preset.ts +++ b/apps/penxle.com/src/lib/tiptap/preset.ts @@ -1,14 +1,12 @@ +import { DropCursor, GapCursor, History, Placeholder } from '$lib/tiptap/extensions'; import { - DropCursor, - FontFamily, - GapCursor, - History, - LetterSpacing, - LineHeight, - Placeholder, - TextAlign, -} from '$lib/tiptap/extensions'; -import { Bold, Italic, Link, Strike, TextColor, Underline } from '$lib/tiptap/marks'; + LegacyFontFamily, + LegacyLetterSpacing, + LegacyLineHeight, + LegacyTextAlign, + LegacyTextColor, +} from '$lib/tiptap/legacies'; +import { Bold, FontColor, FontFamily, FontSize, Italic, Link, Strike, Underline } from '$lib/tiptap/marks'; import { AccessBarrier, Embed, File, Image } from '$lib/tiptap/node-views'; import { BulletList, @@ -40,9 +38,11 @@ export const extensions = [ // marks Bold, + FontColor, + FontFamily, + FontSize, Italic, Strike, - TextColor, Underline, Link, @@ -51,14 +51,17 @@ export const extensions = [ GapCursor, History, Placeholder, - TextAlign, - FontFamily, - LineHeight, - LetterSpacing, // node views AccessBarrier, Embed, File, Image, + + // legacies + LegacyTextColor, + LegacyTextAlign, + LegacyFontFamily, + LegacyLineHeight, + LegacyLetterSpacing, ]; diff --git a/apps/penxle.com/src/routes/editor/ArticleBubbleMenu.svelte b/apps/penxle.com/src/routes/editor/ArticleBubbleMenu.svelte index 4d29c7b4a9..43578e172b 100644 --- a/apps/penxle.com/src/routes/editor/ArticleBubbleMenu.svelte +++ b/apps/penxle.com/src/routes/editor/ArticleBubbleMenu.svelte @@ -5,7 +5,7 @@ import { Tooltip } from '$lib/components'; import { Menu, MenuItem } from '$lib/components/menu'; import { TiptapBubbleMenu } from '$lib/tiptap/components'; - import { alignments, colors, fonts, getToggledFormat, heading, heights, spacing, texts } from './formats.svelte'; + import { alignments, colors, fonts, heights, spacing } from './formats.svelte'; import type { Editor } from '@tiptap/core'; export let editor: Editor; @@ -25,9 +25,6 @@ } const offset = 32; - - $: currentNode = editor.state.selection.$head.parent; - $: toggledFormat = getToggledFormat(currentNode); @@ -70,55 +67,29 @@ - - -
- {toggledFormat.text.label} - -
- - {#each texts as text (`${text.name}-${text.level}}`)} - { - const commands = editor.chain().focus(); - if (text.name === heading) { - commands.setHeading(text.level).run(); - } else { - commands.setParagraph(text.level).run(); - } - }} - > -
{text.label}
- {#if editor.isActive(text.name, { level: text.level })} - - {/if} -
- {/each} -
-
- - +
- {toggledFormat.font.label} + {editor.getAttributes('font_family').fontFamily}
{#each fonts as font (font.value)} { editor.chain().focus().setFontFamily(font.value).run(); }} > - {font.label} + + {font.label} + @@ -173,14 +144,15 @@ {offset} placement="bottom-start" > - + + {editor.getAttributes('text_align').textAlign} {#each alignments as alignment (alignment.value)} @@ -243,16 +215,16 @@ { - editor.chain().focus().setLineHeight(height.value).run(); + editor.chain().focus().updateAttributes('paragraph', { lineHeight: height.value }).run(); }} > {height.label} @@ -268,16 +240,16 @@ { - editor.chain().focus().setLetterSpacing(space.value).run(); + editor.chain().focus().updateAttributes('paragraph', { letterSpacing: space.value }).run(); }} > {space.label} diff --git a/apps/penxle.com/src/routes/editor/formats.svelte b/apps/penxle.com/src/routes/editor/formats.svelte index 8160238e7f..e7eed1ca7a 100644 --- a/apps/penxle.com/src/routes/editor/formats.svelte +++ b/apps/penxle.com/src/routes/editor/formats.svelte @@ -1,89 +1,43 @@ diff --git a/apps/penxle.com/src/styles/prose.css b/apps/penxle.com/src/styles/prose.css index e19bbe890d..c2620e8629 100644 --- a/apps/penxle.com/src/styles/prose.css +++ b/apps/penxle.com/src/styles/prose.css @@ -1,29 +1,9 @@ -.ProseMirror:not(:has(.tiptap-embedded)) { +.ProseMirror2 { /* * nodes */ --uno: whitespace-pre-wrap break-all; - & p { - --uno: py-0.5; - } - - & p[data-level='2'] { - --uno: body-13-m; - } - - & h1 { - --uno: title-24-b my-4; - } - - & h2 { - --uno: title-20-b my-4; - } - - & h3 { - --uno: subtitle-18-b my-4; - } - & hr { --uno: bg-no-repeat border-none bg-center m-y-xs m-x-auto; } @@ -88,21 +68,6 @@ } } - & ul, - ol { - list-style-position: outside; - white-space: pre-wrap; - margin-left: 1.25rem; - } - - & ul { - list-style-type: disc; - } - - & ol { - list-style-type: decimal; - } - & li > p:first-child { display: inline; } @@ -111,67 +76,6 @@ * marks */ - & a { - --uno: underline text-disabled; - } - - & b { - --uno: font-bold; - } - - & i { - --uno: font-italic; - } - - & s { - --uno: line-through; - } - - & u { - --uno: underline; - } - - & [data-text-color='text-gray-50'], - & [data-text-color='text-post-gray'] { - --uno: text-post-gray; - } - - & [data-text-color='text-gray-40'], - & [data-text-color='text-post-gray2'], - & [data-text-color='text-post-lightgray'] { - --uno: color-post-lightgray; - } - - & [data-text-color='text-red-60'], - & [data-text-color='text-post-red'] { - --uno: text-post-red; - } - - & [data-text-color='text-blue-60'], - & [data-text-color='text-post-blue'] { - --uno: text-post-blue; - } - - & [data-text-color='text-orange-70'], - & [data-text-color='text-post-brown'] { - --uno: text-post-brown; - } - - & [data-text-color='text-green-60'], - & [data-text-color='text-post-green'] { - --uno: text-post-green; - } - - & [data-text-color='text-purple-60'], - & [data-text-color='text-post-purple'] { - --uno: text-post-purple; - } - - & [data-text-color='text-white'], - & [data-text-color='text-post-white'] { - --uno: text-post-white; - } - /* * extensions */ diff --git a/apps/penxle.com/uno.config.ts b/apps/penxle.com/uno.config.ts index b839a87ab8..f853958874 100644 --- a/apps/penxle.com/uno.config.ts +++ b/apps/penxle.com/uno.config.ts @@ -2,6 +2,7 @@ import { presetPenxle } from '@penxle/lib/unocss'; import { defineConfig, transformerDirectives, transformerVariantGroup } from 'unocss'; export default defineConfig({ + content: { pipeline: { include: ['**/*.{svelte,ts}'] } }, presets: [presetPenxle()], transformers: [transformerDirectives(), transformerVariantGroup()], });