diff --git a/apps/docs/editor/built-in-ui/toolbar.mdx b/apps/docs/editor/built-in-ui/toolbar.mdx index d9f5e92427..7ea46deab0 100644 --- a/apps/docs/editor/built-in-ui/toolbar.mdx +++ b/apps/docs/editor/built-in-ui/toolbar.mdx @@ -72,6 +72,10 @@ const superdoc = new SuperDoc({ Custom button definitions. See [Custom Buttons](#custom-buttons). + + Show the formatting marks (pilcrow) button in the toolbar. Off by default. Distinct from `layoutEngineOptions.showFormattingMarks`, which controls whether the marks render in the document. + + ## Available buttons Use button names with `excludeItems`, `groups`, and `icons` configuration. @@ -190,6 +194,10 @@ Use button names with `excludeItems`, `groups`, and `icons` configuration. Toggle document ruler + + Toggle formatting marks (pilcrow) display. Hidden by default; enable with `modules.toolbar.showFormattingMarksButton: true`. + + Switch between editing/viewing/suggesting modes diff --git a/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.js b/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.js index 2da53e57f8..8c02a20ade 100644 --- a/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.js +++ b/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.js @@ -1066,6 +1066,7 @@ export const makeDefaultItems = ({ const itemsToHideXL = ['linkedStyles', 'clearFormatting', 'copyFormat', 'ruler', 'formattingMarks']; const itemsToHideSM = ['zoom', 'fontFamily', 'fontSize', 'redo']; const shouldUseLgCompactStyles = availableWidth <= RESPONSIVE_BREAKPOINTS.lg; + const shouldIncludeFormattingMarks = superToolbar.config?.showFormattingMarksButton === true; if (shouldUseLgCompactStyles) { documentMode.attributes.value = { @@ -1114,7 +1115,7 @@ export const makeDefaultItems = ({ linkedStyles, separator, ruler, - formattingMarks, + ...(shouldIncludeFormattingMarks ? [formattingMarks] : []), copyFormat, clearFormatting, aiButton, @@ -1136,7 +1137,7 @@ export const makeDefaultItems = ({ const getLinkedStylesIndex = toolbarItems.findIndex((item) => item.name.value === 'linkedStyles'); toolbarItems.splice(getLinkedStylesIndex - 1, 2); - const filterItems = ['ruler', 'formattingMarks', 'zoom', 'undo', 'redo']; + const filterItems = ['ruler', 'zoom', 'undo', 'redo']; toolbarItems = toolbarItems.filter((item) => !filterItems.includes(item.name.value)); } diff --git a/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.test.js b/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.test.js index 58922d7fab..5fff4d6a98 100644 --- a/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.test.js +++ b/packages/super-editor/src/editors/v1/components/toolbar/defaultItems.test.js @@ -20,9 +20,22 @@ function getItemNames(list) { return list.map((item) => item.name.value); } -function buildItems(availableWidth) { +function getItem(defaultItems, overflowItems, name) { + return [...defaultItems, ...overflowItems].find((item) => item.name.value === name); +} + +function buildItems(availableWidth, superToolbarOverrides = {}) { + const toolbar = { + ...superToolbar, + ...superToolbarOverrides, + config: { + ...superToolbar.config, + ...superToolbarOverrides.config, + }, + }; + return makeDefaultItems({ - superToolbar, + superToolbar: toolbar, toolbarIcons: stubProxy, toolbarTexts: stubProxy, toolbarFonts: [], @@ -31,6 +44,32 @@ function buildItems(availableWidth) { }); } +describe('makeDefaultItems formatting marks button opt-in', () => { + it('does not include formattingMarks in the default toolbar items', () => { + const { defaultItems, overflowItems } = buildItems(2000); + expect(getItem(defaultItems, overflowItems, 'formattingMarks')).toBeUndefined(); + }); + + it('includes formattingMarks when showFormattingMarksButton is true', () => { + const { defaultItems, overflowItems } = buildItems(2000, { + config: { showFormattingMarksButton: true }, + }); + const formattingMarks = getItem(defaultItems, overflowItems, 'formattingMarks'); + + expect(formattingMarks).toBeDefined(); + expect(formattingMarks.command).toBe('toggleFormattingMarks'); + }); + + it('includes formattingMarks in non-docx mode when showFormattingMarksButton is true', () => { + const { defaultItems, overflowItems } = buildItems(2000, { + config: { mode: 'html', showFormattingMarksButton: true }, + }); + const formattingMarks = getItem(defaultItems, overflowItems, 'formattingMarks'); + + expect(formattingMarks).toBeDefined(); + }); +}); + describe('makeDefaultItems XL overflow boundary (SD-2328)', () => { const XL_OVERFLOW_SAFETY_BUFFER = 20; const XL_CUTOFF = RESPONSIVE_BREAKPOINTS.xl + XL_OVERFLOW_SAFETY_BUFFER; diff --git a/packages/super-editor/src/editors/v1/components/toolbar/super-toolbar.js b/packages/super-editor/src/editors/v1/components/toolbar/super-toolbar.js index f88f77bb3a..b5105768db 100644 --- a/packages/super-editor/src/editors/v1/components/toolbar/super-toolbar.js +++ b/packages/super-editor/src/editors/v1/components/toolbar/super-toolbar.js @@ -47,6 +47,7 @@ import { markerTextToBulletStyle } from '@helpers/list-numbering-helpers.js'; * @property {string} [aiApiKey=null] - API key for AI integration * @property {string} [aiEndpoint=null] - Endpoint for AI integration * @property {ToolbarItem[]} [customButtons=[]] - Custom buttons to add to the toolbar + * @property {boolean} [showFormattingMarksButton=false] - Show the formatting marks (pilcrow) button in the toolbar. Distinct from `layoutEngineOptions.showFormattingMarks`, which controls whether the marks render in the document. */ /** @@ -180,6 +181,7 @@ export class SuperToolbar extends EventEmitter { aiApiKey: null, aiEndpoint: null, customButtons: [], + showFormattingMarksButton: false, }; /** diff --git a/packages/superdoc/src/core/types/index.ts b/packages/superdoc/src/core/types/index.ts index e50eb08694..eb90b31ee9 100644 --- a/packages/superdoc/src/core/types/index.ts +++ b/packages/superdoc/src/core/types/index.ts @@ -1095,6 +1095,12 @@ export interface Modules { * already pass through `modules.toolbar.customButtons`. */ customButtons?: Array>; + /** + * Show the formatting marks (pilcrow) button in the toolbar. Off by + * default. Distinct from `layoutEngineOptions.showFormattingMarks`, which + * controls whether the marks render in the document. + */ + showFormattingMarksButton?: boolean; } & Record; /** Link click popover configuration. */ links?: {