From 1cba3ce5ce9ff168c12d2051a6f0a5ecdba003af Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 27 Jan 2026 15:30:52 -0800 Subject: [PATCH 1/2] chore(release): 1.9.0-next.7 [skip ci] * **export:** prefix Relationship IDs with rId for valid xsd:ID ([#1855](https://github.com/superdoc-dev/superdoc/issues/1855)) ([11e67e1](https://github.com/superdoc-dev/superdoc/commit/11e67e1e4332976df279edf62f1aa33177004f9e)) --- packages/superdoc/CHANGELOG.md | 7 +++++++ packages/superdoc/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/superdoc/CHANGELOG.md b/packages/superdoc/CHANGELOG.md index 903826be4e..030023c701 100644 --- a/packages/superdoc/CHANGELOG.md +++ b/packages/superdoc/CHANGELOG.md @@ -1,3 +1,10 @@ +# [1.9.0-next.7](https://github.com/superdoc-dev/superdoc/compare/v1.9.0-next.6...v1.9.0-next.7) (2026-01-27) + + +### Bug Fixes + +* **export:** prefix Relationship IDs with rId for valid xsd:ID ([#1855](https://github.com/superdoc-dev/superdoc/issues/1855)) ([11e67e1](https://github.com/superdoc-dev/superdoc/commit/11e67e1e4332976df279edf62f1aa33177004f9e)) + # [1.9.0-next.5](https://github.com/superdoc-dev/superdoc/compare/v1.9.0-next.4...v1.9.0-next.5) (2026-01-24) diff --git a/packages/superdoc/package.json b/packages/superdoc/package.json index c103d6cbd1..d8e89fada8 100644 --- a/packages/superdoc/package.json +++ b/packages/superdoc/package.json @@ -1,7 +1,7 @@ { "name": "superdoc", "type": "module", - "version": "1.9.0-next.5", + "version": "1.9.0-next.7", "license": "AGPL-3.0", "repository": { "type": "git", From b3b19fc0cd90c6b22e3608939189a1dad3164a84 Mon Sep 17 00:00:00 2001 From: Matthew Connelly Date: Fri, 30 Jan 2026 12:52:28 -0500 Subject: [PATCH 2/2] feat(editor): add flag for text selection in viewing mode --- .../assets/styles/elements/prosemirror.css | 14 +++++++ .../src/core/extensions/editable.js | 38 ++++++++++++++----- .../presentation-editor/PresentationEditor.ts | 8 ++++ .../src/core/presentation-editor/types.ts | 7 ++++ .../src/core/types/EditorConfig.ts | 8 ++++ packages/super-editor/src/index.d.ts | 8 ++++ packages/superdoc/src/SuperDoc.vue | 8 ++-- packages/superdoc/src/core/SuperDoc.js | 1 + .../src/dev/components/SuperdocDev.vue | 1 + packages/superdoc/tsconfig.build.json | 5 +-- 10 files changed, 82 insertions(+), 16 deletions(-) diff --git a/packages/super-editor/src/assets/styles/elements/prosemirror.css b/packages/super-editor/src/assets/styles/elements/prosemirror.css index b56f7f533f..45a5e926f6 100644 --- a/packages/super-editor/src/assets/styles/elements/prosemirror.css +++ b/packages/super-editor/src/assets/styles/elements/prosemirror.css @@ -79,6 +79,20 @@ caret-color: transparent; } +/* Allow selection visibility in viewing mode when allowSelectionInViewMode is enabled */ +.presentation-editor--allow-selection .ProseMirror-hideselection *::selection { + background: Highlight; + background: -moz-Highlight; +} + +.presentation-editor--allow-selection .ProseMirror-hideselection *::-moz-selection { + background: Highlight; +} + +.presentation-editor--allow-selection .ProseMirror-hideselection * { + caret-color: auto; +} + /* See https://github.com/ProseMirror/prosemirror/issues/1421#issuecomment-1759320191 */ .ProseMirror [draggable][contenteditable='false'] { user-select: text; diff --git a/packages/super-editor/src/core/extensions/editable.js b/packages/super-editor/src/core/extensions/editable.js index 2f0ce45251..d5a4f38a35 100644 --- a/packages/super-editor/src/core/extensions/editable.js +++ b/packages/super-editor/src/core/extensions/editable.js @@ -6,11 +6,17 @@ import { Extension } from '../Extension.js'; * * When editable is false, all user interactions are blocked: * - Text input via beforeinput events - * - Mouse interactions via mousedown - * - Focus via automatic blur - * - Click, double-click, and triple-click events + * - Mouse interactions via mousedown (unless allowSelectionInViewMode is true) + * - Focus via automatic blur (unless allowSelectionInViewMode is true) + * - Click, double-click, and triple-click events (unless allowSelectionInViewMode is true) * - Keyboard shortcuts via handleKeyDown * - Paste and drop events + * + * When allowSelectionInViewMode is true and editable is false: + * - Mouse interactions are allowed for text selection + * - Focus is allowed + * - Click events are allowed for selection + * - But text input, keyboard shortcuts, paste, and drop remain blocked */ export const Editable = Extension.create({ name: 'editable', @@ -30,14 +36,16 @@ export const Editable = Extension.create({ return false; }, mousedown: (_view, event) => { - if (!editor.options.editable) { + // Allow mousedown for selection when allowSelectionInViewMode is enabled + if (!editor.options.editable && !editor.options.allowSelectionInViewMode) { event.preventDefault(); return true; } return false; }, focus: (view, event) => { - if (!editor.options.editable) { + // Allow focus when allowSelectionInViewMode is enabled + if (!editor.options.editable && !editor.options.allowSelectionInViewMode) { event.preventDefault(); view.dom.blur(); return true; @@ -45,10 +53,22 @@ export const Editable = Extension.create({ return false; }, }, - handleClick: () => !editor.options.editable, - handleDoubleClick: () => !editor.options.editable, - handleTripleClick: () => !editor.options.editable, - handleKeyDown: () => !editor.options.editable, + // Allow click events for selection when allowSelectionInViewMode is enabled + handleClick: () => !editor.options.editable && !editor.options.allowSelectionInViewMode, + handleDoubleClick: () => !editor.options.editable && !editor.options.allowSelectionInViewMode, + handleTripleClick: () => !editor.options.editable && !editor.options.allowSelectionInViewMode, + // Always block keyboard input, paste, and drop when not editable + handleKeyDown: (_view, event) => { + if (!editor.options.editable) { + // Allow Ctrl+C / Cmd+C for copy when allowSelectionInViewMode is enabled + if (editor.options.allowSelectionInViewMode) { + const isCopy = (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c'; + if (isCopy) return false; + } + return true; + } + return false; + }, handlePaste: () => !editor.options.editable, handleDrop: () => !editor.options.editable, }, diff --git a/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts b/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts index 98e7bdfcc4..3fb80fd797 100644 --- a/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts +++ b/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts @@ -1040,6 +1040,10 @@ export class PresentationEditor extends EventEmitter { #syncDocumentModeClass() { if (!this.#visibleHost) return; this.#visibleHost.classList.toggle('presentation-editor--viewing', this.#documentMode === 'viewing'); + this.#visibleHost.classList.toggle( + 'presentation-editor--allow-selection', + this.#documentMode === 'viewing' && !!this.#options.allowSelectionInViewMode, + ); } /** @@ -4600,12 +4604,16 @@ export class PresentationEditor extends EventEmitter { * Determines whether the current viewing mode should block edits. * When documentMode is viewing but the active editor has been toggled * back to editable (e.g. permission ranges), we treat the view as editable. + * Also returns false when allowSelectionInViewMode is enabled, allowing + * text selection while still blocking actual edits. */ #isViewLocked(): boolean { if (this.#documentMode !== 'viewing') return false; const hasPermissionOverride = !!(this.#editor as Editor & { storage?: Record })?.storage ?.permissionRanges?.hasAllowedRanges; if (hasPermissionOverride) return false; + // Allow selection visuals when allowSelectionInViewMode is enabled + if (this.#options.allowSelectionInViewMode) return false; return this.#documentMode === 'viewing'; } diff --git a/packages/super-editor/src/core/presentation-editor/types.ts b/packages/super-editor/src/core/presentation-editor/types.ts index 3b6d446d8c..21a2b9fe8a 100644 --- a/packages/super-editor/src/core/presentation-editor/types.ts +++ b/packages/super-editor/src/core/presentation-editor/types.ts @@ -140,6 +140,13 @@ export type PresentationEditorOptions = ConstructorParameters[0] * @default false */ disableContextMenu?: boolean; + /** + * Allow text selection in viewing mode. + * When true, users can select and copy text while in viewing mode, + * but editing (typing, paste, delete) remains blocked. + * @default false + */ + allowSelectionInViewMode?: boolean; }; /** diff --git a/packages/super-editor/src/core/types/EditorConfig.ts b/packages/super-editor/src/core/types/EditorConfig.ts index 2dd198243b..0c7ffac250 100644 --- a/packages/super-editor/src/core/types/EditorConfig.ts +++ b/packages/super-editor/src/core/types/EditorConfig.ts @@ -225,6 +225,14 @@ export interface EditorOptions { /** Whether the editor is editable */ editable?: boolean; + /** + * Allow text selection in viewing mode. + * When true, users can select and copy text while in viewing mode, + * but editing (typing, paste, delete) remains blocked. + * @default false + */ + allowSelectionInViewMode?: boolean; + /** Editor properties */ editorProps?: Record; diff --git a/packages/super-editor/src/index.d.ts b/packages/super-editor/src/index.d.ts index 5c3ed192a9..66d57fef40 100644 --- a/packages/super-editor/src/index.d.ts +++ b/packages/super-editor/src/index.d.ts @@ -143,6 +143,13 @@ export interface OpenOptions { isCommentsEnabled?: boolean; suppressDefaultDocxStyles?: boolean; documentMode?: 'editing' | 'viewing' | 'suggesting'; + /** + * Allow text selection in viewing mode. + * When true, users can select and copy text while in viewing mode, + * but editing (typing, paste, delete) remains blocked. + * @default false + */ + allowSelectionInViewMode?: boolean; content?: unknown; mediaFiles?: Record; fonts?: Record; @@ -166,6 +173,7 @@ export declare class Editor { content?: string | object; extensions?: any[]; editable?: boolean; + allowSelectionInViewMode?: boolean; autofocus?: boolean | 'start' | 'end' | 'all' | number; [key: string]: any; }); diff --git a/packages/superdoc/src/SuperDoc.vue b/packages/superdoc/src/SuperDoc.vue index 3c377a045a..3c33602e0f 100644 --- a/packages/superdoc/src/SuperDoc.vue +++ b/packages/superdoc/src/SuperDoc.vue @@ -88,6 +88,7 @@ const { isHighContrastMode } = useHighContrastMode(); const { uiFontFamily } = useUiFontFamily(); const isViewingMode = () => proxy?.$superdoc?.config?.documentMode === 'viewing'; +const allowSelectionInViewMode = () => !!proxy?.$superdoc?.config?.allowSelectionInViewMode; const isViewingCommentsVisible = computed( () => isViewingMode() && proxy?.$superdoc?.config?.comments?.visible === true, ); @@ -283,13 +284,13 @@ const onEditorSelectionChange = ({ editor, transaction }) => { // When comment is added selection will be equal to comment text // Should skip calculations to keep text selection for comments correct skipSelectionUpdate.value = false; - if (isViewingMode()) { + if (isViewingMode() && !allowSelectionInViewMode()) { resetSelection(); } return; } - if (isViewingMode()) { + if (isViewingMode() && !allowSelectionInViewMode()) { resetSelection(); return; } @@ -463,6 +464,7 @@ const editorOptions = (doc) => { html: doc.html, markdown: doc.markdown, documentMode: proxy.$superdoc.config.documentMode, + allowSelectionInViewMode: proxy.$superdoc.config.allowSelectionInViewMode, rulers: doc.rulers, rulerContainer: proxy.$superdoc.config.rulerContainer, isInternal: proxy.$superdoc.config.isInternal, @@ -688,7 +690,7 @@ const getSelectionPosition = computed(() => { }); const handleSelectionChange = (selection) => { - if (isViewingMode()) { + if (isViewingMode() && !allowSelectionInViewMode()) { resetSelection(); return; } diff --git a/packages/superdoc/src/core/SuperDoc.js b/packages/superdoc/src/core/SuperDoc.js index 7e3f94d642..e578659d78 100644 --- a/packages/superdoc/src/core/SuperDoc.js +++ b/packages/superdoc/src/core/SuperDoc.js @@ -59,6 +59,7 @@ export class SuperDoc extends EventEmitter { superdocId: null, selector: '#superdoc', documentMode: 'editing', + allowSelectionInViewMode: false, role: 'editor', document: {}, documents: [], diff --git a/packages/superdoc/src/dev/components/SuperdocDev.vue b/packages/superdoc/src/dev/components/SuperdocDev.vue index 54edd2080a..9d95aee887 100644 --- a/packages/superdoc/src/dev/components/SuperdocDev.vue +++ b/packages/superdoc/src/dev/components/SuperdocDev.vue @@ -186,6 +186,7 @@ const init = async () => { // format: 'docx', // html: '

Hello world

', // isDev: true, + // allowSelectionInViewMode: true, user, title: 'Test document', users: [ diff --git a/packages/superdoc/tsconfig.build.json b/packages/superdoc/tsconfig.build.json index 00fdd88f11..fc8520e737 100644 --- a/packages/superdoc/tsconfig.build.json +++ b/packages/superdoc/tsconfig.build.json @@ -1,6 +1,3 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "customConditions": [] - } + "extends": "./tsconfig.json" }