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?: {