Skip to content
Closed
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
2 changes: 1 addition & 1 deletion packages/super-editor/src/components/slash-menu/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function getEditorContext(editor, event) {
node = state.doc.nodeAt(pos);
}

// We need to check if we have anything in the clipboard and request permission if needed
// We need to check if we have anything in the clipboard
const clipboardContent = await readFromClipboard(state);

return {
Expand Down
1 change: 0 additions & 1 deletion packages/super-editor/src/core/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ export class Editor extends EventEmitter {
// async (file) => url;
handleImageUpload: null,

// telemetry
telemetry: null,

// Docx xml updated by User
Expand Down
78 changes: 33 additions & 45 deletions packages/super-editor/src/core/utilities/clipboardUtils.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,41 @@
// @ts-nocheck
// clipboardUtils.js

import { DOMParser } from 'prosemirror-model';
import { DOMSerializer, DOMParser } from 'prosemirror-model';

/**
* Checks if clipboard read permission is granted and handles permission prompts.
* Returns true if clipboard-read permission is granted. If state is "prompt" it will
* proactively trigger a readText() call which will surface the browser permission
* dialog to the user. Falls back gracefully in older browsers that lack the
* Permissions API.
* @returns {Promise<boolean>} Whether clipboard read permission is granted
* Serializes the current selection in the editor state to HTML and plain text for clipboard use.
* @param {EditorState} state - The ProseMirror editor state containing the current selection.
* @returns {{ htmlString: string, text: string }} An object with the HTML string and plain text of the selection.
*/
export async function ensureClipboardPermission() {
if (typeof navigator === 'undefined' || !navigator.clipboard) {
return false;
}

// Some older browsers do not expose navigator.permissions – assume granted
if (!navigator.permissions || typeof navigator.permissions.query !== 'function') {
return true;
}
export function serializeSelectionToClipboard(state) {
const { from, to } = state.selection;
const slice = state.selection.content();
const htmlContainer = document.createElement('div');
htmlContainer.appendChild(DOMSerializer.fromSchema(state.schema).serializeFragment(slice.content));
const htmlString = htmlContainer.innerHTML;
const text = state.doc.textBetween(from, to);
return { htmlString, text };
}

/**
* Writes HTML and plain text data to the system clipboard.
* Uses the Clipboard API if available, otherwise falls back to plain text.
* @param {{ htmlString: string, text: string }} param0 - The HTML and plain text to write to the clipboard.
* @returns {Promise<void>} A promise that resolves when the clipboard write is complete.
*/
export async function writeToClipboard({ htmlString, text }) {
try {
// @ts-ignore – string literal is valid at runtime; TS lib DOM typing not available in .js file
const status = await navigator.permissions.query({ name: 'clipboard-read' });

if (status.state === 'granted') {
return true;
}

if (status.state === 'prompt') {
// Trigger a readText() to make the browser show its permission prompt.
try {
await navigator.clipboard.readText();
return true;
} catch {
return false;
}
if (navigator.clipboard && window.ClipboardItem) {
const clipboardItem = new window.ClipboardItem({
'text/html': new Blob([htmlString], { type: 'text/html' }),
'text/plain': new Blob([text], { type: 'text/plain' }),
});
await navigator.clipboard.write([clipboardItem]);
} else {
await navigator.clipboard.writeText(text);
}

// If we hit this area this is state === 'denied'
return false;
} catch {
return false;
} catch (e) {
console.error('Error writing to clipboard', e);
}
}

Expand All @@ -55,9 +48,7 @@ export async function ensureClipboardPermission() {
export async function readFromClipboard(state) {
let html = '';
let text = '';
const hasPermission = await ensureClipboardPermission();

if (hasPermission && navigator.clipboard && navigator.clipboard.read) {
if (navigator.clipboard && navigator.clipboard.read) {
try {
const items = await navigator.clipboard.read();
for (const item of items) {
Expand All @@ -69,13 +60,10 @@ export async function readFromClipboard(state) {
}
}
} catch {
// Fallback to plain text read; may still fail if permission denied
try {
text = await navigator.clipboard.readText();
} catch {}
text = await navigator.clipboard.readText();
}
} else {
// permissions denied or API unavailable; leave content empty
text = await navigator.clipboard.readText();
}
let content = null;
if (html) {
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion packages/superdoc/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@harbour-enterprises/superdoc",
"type": "module",
"version": "0.16.0-next.9",
"version": "0.16.0",
"license": "AGPL-3.0",
"readme": "../../README.md",
"files": [
Expand Down
1 change: 0 additions & 1 deletion packages/superdoc/src/core/SuperDoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@

isDev: false,

// telemetry config
telemetry: null,

// Events
Expand Down Expand Up @@ -402,7 +401,7 @@
/**
* Initialize telemetry service.
*/
#initTelemetry() {

Check warning on line 404 in packages/superdoc/src/core/SuperDoc.js

View workflow job for this annotation

GitHub Actions / test / validate

'#initTelemetry' is defined but never used
this.telemetry = new Telemetry({
enabled: this.config.telemetry?.enabled ?? true,
licenseKey: this.config.telemetry?.licenseKey,
Expand Down
11 changes: 6 additions & 5 deletions packages/superdoc/src/stores/comments-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,11 +472,12 @@ export const useCommentsStore = defineStore('comments', () => {

// Create comments for tracked changes
// that do not have a corresponding comment (created in Word).
const { tr } = editor.view.state;
const { dispatch } = editor.view;

groupedChanges.forEach(({ insertedMark, deletionMark, formatMark }, index) => {
console.debug(`Create comment for track change: ${index}`);

const { dispatch } = editor.view;
const { tr } = editor.view.state;

const foundComment = commentsList.value.find(
(i) =>
i.commentId === insertedMark?.mark.attrs.id ||
Expand All @@ -488,6 +489,7 @@ export const useCommentsStore = defineStore('comments', () => {
if (foundComment) {
if (isLastIteration) {
tr.setMeta(CommentsPluginKey, { type: 'force' });
dispatch(tr);
}
return;
}
Expand All @@ -502,10 +504,9 @@ export const useCommentsStore = defineStore('comments', () => {
if (isLastIteration) tr.setMeta(CommentsPluginKey, { type: 'force' });
tr.setMeta(CommentsPluginKey, { type: 'forceTrackChanges' });
tr.setMeta(TrackChangesBasePluginKey, trackChangesPayload);
dispatch(tr);
}
});

dispatch(tr);
};

const translateCommentsForExport = () => {
Expand Down