diff --git a/apps/client/src/features/comment/components/comment-editor.tsx b/apps/client/src/features/comment/components/comment-editor.tsx index 190d3a7e8..ee4009b6e 100644 --- a/apps/client/src/features/comment/components/comment-editor.tsx +++ b/apps/client/src/features/comment/components/comment-editor.tsx @@ -2,8 +2,7 @@ import { EditorContent, ReactNodeViewRenderer, useEditor } from "@tiptap/react"; import { Placeholder } from "@tiptap/extension-placeholder"; import { StarterKit } from "@tiptap/starter-kit"; import { TextStyle } from "@tiptap/extension-text-style"; -import { Color } from "@tiptap/extension-color"; -import { Mention, LinkExtension } from "@docmost/editor-ext"; +import { Mention, LinkExtension, Color } from "@docmost/editor-ext"; import classes from "./comment.module.css"; import { useFocusWithin } from "@mantine/hooks"; import clsx from "clsx"; diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index c72456e6e..dadb38778 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -10,7 +10,6 @@ import { Superscript } from "@tiptap/extension-superscript"; import SubScript from "@tiptap/extension-subscript"; import { Typography } from "@tiptap/extension-typography"; import { TextStyle } from "@tiptap/extension-text-style"; -import { Color } from "@tiptap/extension-color"; import { Youtube } from "@tiptap/extension-youtube"; import SlashCommand, { SlashCommandExtension as Command, @@ -54,6 +53,7 @@ import { Subpages, Heading, Highlight, + Color, Indent, UniqueID, SharedStorage, diff --git a/apps/client/src/features/editor/styles/highlight.css b/apps/client/src/features/editor/styles/highlight.css index 332f48e55..01d3612e3 100644 --- a/apps/client/src/features/editor/styles/highlight.css +++ b/apps/client/src/features/editor/styles/highlight.css @@ -1,6 +1,46 @@ /* Highlight colors with dark mode support */ .ProseMirror { + @mixin dark { + /* Arbitrary (imported) colors have no hand-tuned dark variant, so derive + one: cap lightness and chroma so the fill sits on the dark surface. + Our own palette opts out and keeps the values below. */ + mark[data-color]:not( + [data-color="#98d8f2" i], + [data-color="#7edb6c" i], + [data-color="#e0d6ed" i], + [data-color="#ffc6c2" i], + [data-color="#faf594" i], + [data-color="#f5c8a9" i], + [data-color="#f5cfe0" i], + [data-color="#dfdfd7" i], + [data-color="#d7c4b7" i] + ) { + background-color: oklch( + from var(--mark-bg, #fff) min(l, clamp(0.28, calc(1.22 - l), 0.45)) + min(calc(c * 1.8), 0.09) h + ) !important; + } + + /* Imported text colors are picked for a light page and go unreadable on + the dark surface, so lift their lightness. */ + span[data-text-color]:not( + [data-text-color="#2563EB" i], + [data-text-color="#008A00" i], + [data-text-color="#9333EA" i], + [data-text-color="#E00000" i], + [data-text-color="#EAB308" i], + [data-text-color="#FFA500" i], + [data-text-color="#BA4081" i], + [data-text-color="#A8A29E" i], + [data-text-color="#92400E" i] + ) { + color: oklch( + from var(--text-color, currentcolor) max(l, 0.72) min(c, 0.16) h + ) !important; + } + } + /* Blue */ mark[data-color="#98d8f2"] { background-color: light-dark( diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts index 7ee5ba4aa..6646802d4 100644 --- a/apps/server/src/collaboration/collaboration.util.ts +++ b/apps/server/src/collaboration/collaboration.util.ts @@ -5,7 +5,6 @@ import { Superscript } from '@tiptap/extension-superscript'; import SubScript from '@tiptap/extension-subscript'; import { Typography } from '@tiptap/extension-typography'; import { TextStyle } from '@tiptap/extension-text-style'; -import { Color } from '@tiptap/extension-color'; import { Youtube } from '@tiptap/extension-youtube'; import { TaskList, TaskItem } from '@tiptap/extension-list'; import { @@ -36,6 +35,7 @@ import { Mention, Subpages, Highlight, + Color, Indent, UniqueID, Columns, diff --git a/apps/server/src/ee b/apps/server/src/ee index cbfbb530d..91762fa90 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit cbfbb530d0612db011c58aa63533584f4b77141a +Subproject commit 91762fa902cdfd53ecf8186ceef246d5fd1f7451 diff --git a/packages/editor-ext/src/index.ts b/packages/editor-ext/src/index.ts index 80a67f449..e5aabfcdf 100644 --- a/packages/editor-ext/src/index.ts +++ b/packages/editor-ext/src/index.ts @@ -23,6 +23,7 @@ export * from "./lib/embed-provider"; export * from "./lib/subpages"; export * from "./lib/transclusion"; export * from "./lib/highlight"; +export * from "./lib/text-color"; export * from "./lib/indent"; export * from "./lib/heading/heading"; export * from "./lib/unique-id"; diff --git a/packages/editor-ext/src/lib/highlight.ts b/packages/editor-ext/src/lib/highlight.ts index becef994d..dc16b6f5c 100644 --- a/packages/editor-ext/src/lib/highlight.ts +++ b/packages/editor-ext/src/lib/highlight.ts @@ -16,9 +16,11 @@ export const Highlight = TiptapHighlight.extend({ return {}; } + // --mark-bg lets CSS derive a legible dark-mode variant for + // arbitrary (imported) colors. return { "data-color": attributes.color, - style: `background-color: ${attributes.color}; color: inherit`, + style: `background-color: ${attributes.color}; --mark-bg: ${attributes.color}; color: inherit`, }; }, }, diff --git a/packages/editor-ext/src/lib/text-color.ts b/packages/editor-ext/src/lib/text-color.ts new file mode 100644 index 000000000..3b6428400 --- /dev/null +++ b/packages/editor-ext/src/lib/text-color.ts @@ -0,0 +1,35 @@ +import { getStyleProperty } from "@tiptap/core"; +import { Color as TiptapColor } from "@tiptap/extension-color"; + +export const Color = TiptapColor.extend({ + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + color: { + default: null, + parseHTML: (element) => { + const value = + element.getAttribute("data-text-color") ?? + getStyleProperty(element, "color") ?? + element.style.color; + return value?.replace(/['"]+/g, "") || null; + }, + renderHTML: (attributes) => { + if (!attributes.color) { + return {}; + } + // --text-color lets CSS derive a legible dark-mode variant for + // arbitrary (imported) colors. + return { + "data-text-color": attributes.color, + style: `color: ${attributes.color}; --text-color: ${attributes.color}`, + }; + }, + }, + }, + }, + ]; + }, +});