Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/super-editor/src/assets/styles/elements/prosemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -226,21 +226,21 @@ https://github.com/ProseMirror/prosemirror-tables/blob/master/demo/index.html
}

.ProseMirror .track-insert-dec.highlighted {
border-top: 1px dashed #00853d;
border-bottom: 1px dashed #00853d;
background-color: #399c7222;
border-top: 1px dashed var(--sd-track-insert-border, #00853d);
border-bottom: 1px dashed var(--sd-track-insert-border, #00853d);
background-color: var(--sd-track-insert-bg, #399c7222);
}

.ProseMirror .track-delete-dec.highlighted {
border-top: 1px dashed #cb0e47;
border-bottom: 1px dashed #cb0e47;
background-color: #cb0e4722;
border-top: 1px dashed var(--sd-track-delete-border, #cb0e47);
border-bottom: 1px dashed var(--sd-track-delete-border, #cb0e47);
background-color: var(--sd-track-delete-bg, #cb0e4722);
text-decoration: line-through !important;
text-decoration-thickness: 2px !important;
}

.ProseMirror .track-format-dec.highlighted {
border-bottom: 2px solid gold;
border-bottom: 2px solid var(--sd-track-format-border, gold);
}

.ProseMirror .track-delete-widget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
}

.sd-editor-comment-highlight:hover {
background-color: #1354ff55;
background-color: var(--sd-comment-highlight-hover, #1354ff55);
}

.sd-editor-comment-highlight.sd-custom-selection {
Expand Down
14 changes: 14 additions & 0 deletions packages/super-editor/src/core/types/EditorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ export interface EditorOptions {
/** Whether comments are enabled */
isCommentsEnabled?: boolean;

/** Comment highlight configuration */
comments?: {
highlightColors?: {
internal?: string;
external?: string;
activeInternal?: string;
activeExternal?: string;
};
highlightOpacity?: {
active?: number;
inactive?: number;
};
};

/** Whether this is a new file */
isNewFile?: boolean;

Expand Down
43 changes: 40 additions & 3 deletions packages/super-editor/src/extensions/comment/comments-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,10 +718,47 @@ export const translateFormatChangesToEnglish = (attrs = {}) => {
* @param {EditorView} param0.editor The current editor view
* @returns {String} The color to use for the highlight
*/
const DEFAULT_ACTIVE_ALPHA = 0x44 / 0xff;
const DEFAULT_INACTIVE_ALPHA = 0x22 / 0xff;

const clampOpacity = (value) => {
if (!Number.isFinite(value)) return null;
return Math.max(0, Math.min(1, value));
};

const applyAlphaToHex = (color, opacity) => {
if (typeof color !== 'string') return color;
const match = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
if (!match) return color;

const hex =
match[1].length === 3
? match[1]
.split('')
.map((c) => c + c)
.join('')
: match[1];
const alpha = Math.round(opacity * 255)
.toString(16)
.padStart(2, '0');
return `#${hex}${alpha}`;
};

export const getHighlightColor = ({ activeThreadId, threadId, isInternal, editor }) => {
if (!editor.options.isInternal && isInternal) return 'transparent';
const pluginState = CommentsPluginKey.getState(editor.state);
const color = isInternal ? pluginState.internalColor : pluginState.externalColor;
const alpha = activeThreadId == threadId ? '44' : '22';
return `${color}${alpha}`;
const highlightColors = editor.options.comments?.highlightColors || {};
const highlightOpacity = editor.options.comments?.highlightOpacity || {};
const isActive = activeThreadId == threadId;

const baseColor = isInternal
? (highlightColors.internal ?? pluginState.internalColor)
: (highlightColors.external ?? pluginState.externalColor);

const activeOverride = isInternal ? highlightColors.activeInternal : highlightColors.activeExternal;
if (isActive && activeOverride) return activeOverride;

const resolvedOpacity = clampOpacity(isActive ? highlightOpacity.active : highlightOpacity.inactive);
const opacity = resolvedOpacity ?? (isActive ? DEFAULT_ACTIVE_ALPHA : DEFAULT_INACTIVE_ALPHA);
return applyAlphaToHex(baseColor, opacity);
};
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,11 @@ export const CommentsPlugin = Extension.create({

state: {
init() {
const highlightColors = editor.options.comments?.highlightColors || {};
return {
activeThreadId: null,
externalColor: '#B1124B',
internalColor: '#078383',
externalColor: highlightColors.external ?? '#B1124B',
internalColor: highlightColors.internal ?? '#078383',
decorations: DecorationSet.empty,
allCommentPositions: {},
allCommentIds: [],
Expand Down
58 changes: 58 additions & 0 deletions packages/super-editor/src/extensions/comment/comments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,64 @@ describe('comment helpers', () => {
const hidden = getHighlightColor({ activeThreadId: null, threadId: 'thread-3', isInternal: true, editor });
expect(hidden).toBe('transparent');
});

it('uses configured highlight colors and opacity for inactive comments', () => {
const editor = {
options: {
isInternal: false,
comments: {
highlightColors: { external: '#112233' },
highlightOpacity: { inactive: 0.25 },
},
},
state: {},
};
vi.spyOn(CommentsPluginKey, 'getState').mockReturnValue({
internalColor: '#123456',
externalColor: '#abcdef',
});

const color = getHighlightColor({ activeThreadId: 'thread-2', threadId: 'thread-1', isInternal: false, editor });
expect(color).toBe('#11223340');
});

it('uses active highlight override color when provided', () => {
const editor = {
options: {
isInternal: false,
comments: {
highlightColors: { external: '#112233', activeExternal: '#ff0000' },
},
},
state: {},
};
vi.spyOn(CommentsPluginKey, 'getState').mockReturnValue({
internalColor: '#123456',
externalColor: '#abcdef',
});

const color = getHighlightColor({ activeThreadId: 'thread-1', threadId: 'thread-1', isInternal: false, editor });
expect(color).toBe('#ff0000');
});

it('falls back to plugin colors with custom opacity', () => {
const editor = {
options: {
isInternal: false,
comments: {
highlightOpacity: { active: 0.2 },
},
},
state: {},
};
vi.spyOn(CommentsPluginKey, 'getState').mockReturnValue({
internalColor: '#123456',
externalColor: '#abcdef',
});

const color = getHighlightColor({ activeThreadId: 'thread-1', threadId: 'thread-1', isInternal: false, editor });
expect(color).toBe('#abcdef33');
});
});

describe('comments plugin commands', () => {
Expand Down
29 changes: 26 additions & 3 deletions packages/superdoc/src/SuperDoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,28 @@ const commentsModuleConfig = computed(() => {
return config;
});

const superdocStyleVars = computed(() => ({
'--sd-ui-font-family': uiFontFamily.value,
}));
const superdocStyleVars = computed(() => {
const vars = {
'--sd-ui-font-family': uiFontFamily.value,
};

const commentsConfig = proxy.$superdoc.config.modules?.comments;
if (!commentsConfig || commentsConfig === false) return vars;

if (commentsConfig.highlightHoverColor) {
vars['--sd-comment-highlight-hover'] = commentsConfig.highlightHoverColor;
}

const trackChangeColors = commentsConfig.trackChangeHighlightColors || {};
const activeTrackChangeColors = commentsConfig.trackChangeActiveHighlightColors || trackChangeColors;
if (activeTrackChangeColors.insertBorder) vars['--sd-track-insert-border'] = activeTrackChangeColors.insertBorder;
if (activeTrackChangeColors.insertBackground) vars['--sd-track-insert-bg'] = activeTrackChangeColors.insertBackground;
if (activeTrackChangeColors.deleteBorder) vars['--sd-track-delete-border'] = activeTrackChangeColors.deleteBorder;
if (activeTrackChangeColors.deleteBackground) vars['--sd-track-delete-bg'] = activeTrackChangeColors.deleteBackground;
if (activeTrackChangeColors.formatBorder) vars['--sd-track-format-border'] = activeTrackChangeColors.formatBorder;

return vars;
});

// Refs
const layers = ref(null);
Expand Down Expand Up @@ -448,6 +467,10 @@ const editorOptions = (doc) => {
isCommentsEnabled: Boolean(commentsModuleConfig.value),
isAiEnabled: proxy.$superdoc.config.modules?.ai,
slashMenuConfig: proxy.$superdoc.config.modules?.slashMenu,
comments: {
highlightColors: commentsModuleConfig.value?.highlightColors,
highlightOpacity: commentsModuleConfig.value?.highlightOpacity,
},
editorCtor: useLayoutEngine ? PresentationEditor : undefined,
onBeforeCreate: onEditorBeforeCreate,
onCreate: onEditorCreate,
Expand Down
21 changes: 21 additions & 0 deletions packages/superdoc/src/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@
* currentUser?: User | null,
* superdoc?: SuperDoc | null,
* }) => boolean | undefined} [comments.permissionResolver] Custom permission resolver for comment actions
* @property {Object} [comments.highlightColors] Comment highlight colors (internal/external and active overrides)
* @property {string} [comments.highlightColors.internal] Base highlight color for internal comments
* @property {string} [comments.highlightColors.external] Base highlight color for external comments
* @property {string} [comments.highlightColors.activeInternal] Active highlight color override for internal comments
* @property {string} [comments.highlightColors.activeExternal] Active highlight color override for external comments
* @property {Object} [comments.highlightOpacity] Comment highlight opacity values (0-1)
* @property {number} [comments.highlightOpacity.active] Opacity for active comment highlight
* @property {number} [comments.highlightOpacity.inactive] Opacity for inactive comment highlight
* @property {string} [comments.highlightHoverColor] Hover highlight color for comment marks
* @property {Object} [comments.trackChangeHighlightColors] Track change highlight colors
* @property {string} [comments.trackChangeHighlightColors.insertBorder] Border color for inserted text highlight
* @property {string} [comments.trackChangeHighlightColors.insertBackground] Background color for inserted text highlight
* @property {string} [comments.trackChangeHighlightColors.deleteBorder] Border color for deleted text highlight
* @property {string} [comments.trackChangeHighlightColors.deleteBackground] Background color for deleted text highlight
* @property {string} [comments.trackChangeHighlightColors.formatBorder] Border color for format change highlight
* @property {Object} [comments.trackChangeActiveHighlightColors] Active track change highlight colors (defaults to trackChangeHighlightColors)
* @property {string} [comments.trackChangeActiveHighlightColors.insertBorder] Active border color for inserted text highlight
* @property {string} [comments.trackChangeActiveHighlightColors.insertBackground] Active background color for inserted text highlight
* @property {string} [comments.trackChangeActiveHighlightColors.deleteBorder] Active border color for deleted text highlight
* @property {string} [comments.trackChangeActiveHighlightColors.deleteBackground] Active background color for deleted text highlight
* @property {string} [comments.trackChangeActiveHighlightColors.formatBorder] Active border color for format change highlight
* @property {Object} [ai] AI module configuration
* @property {string} [ai.apiKey] Harbour API key for AI features
* @property {string} [ai.endpoint] Custom endpoint URL for AI services
Expand Down