Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
"preview": "vite preview"
},
"dependencies": {
"pdfjs-dist": "4.3.136",
"pdfjs-dist": "^5.4.296",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"superdoc": "latest"
Comment thread
harbournick marked this conversation as resolved.
},
"devDependencies": {
"@vitejs/plugin-react": "^4.0.4",
Expand Down
37 changes: 20 additions & 17 deletions examples/advanced/grading-papers-comments-annotations/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import '@harbour-enterprises/superdoc/style.css';
import 'superdoc/style.css';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Header from './components/Header.jsx';
import AssignmentHeader from './components/AssignmentHeader.jsx';
import Drawer from './components/Drawer.jsx';
import { SuperDoc } from '@harbour-enterprises/superdoc';
import { SuperDoc } from 'superdoc';
import NickPDF from '/nick.pdf?url';
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.mjs';

// Set worker globally outside SD.
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url,).toString();

const defaultWhiteboardOpacity = 1;
const disabledWhiteboardOpacity = 0.5;
Expand All @@ -16,7 +18,7 @@ const App = () => {
const docFileRef = useRef(null);

// UI state only (do not store SuperDoc instance in state).
const [whiteboardReady, setWhiteboardReady] = useState(false);
const [whiteboardInitialized, setWhiteboardInit] = useState(false);
const [whiteboardEnabled, setWhiteboardEnabled] = useState(true);
const [activeTool, setActiveTool] = useState('select');

Expand Down Expand Up @@ -51,15 +53,15 @@ const App = () => {
const superdoc = superdocRef.current;
if (!superdoc) return;
superdoc.on('whiteboard:change', (data) => {
console.log('whiteboard:change', { data });
console.log('whiteboard:change', data);
});
superdoc.on('whiteboard:tool', (tool) => {
setActiveTool(tool);
});
}, []);

const onWhiteboardReady = useCallback((whiteboard) => {
setWhiteboardReady(true);
const onWhiteboardInit = useCallback((whiteboard) => {
setWhiteboardInit(true);
setActiveTool(whiteboard?.getTool?.() ?? 'select');
registerStickers();
registerComments();
Expand All @@ -75,7 +77,7 @@ const App = () => {

const superdocInstance = new SuperDoc({
selector: '#superdoc',
document: { data: docFileRef.current },
document: docFileRef.current,
toolbar: 'superdoc-toolbar',
licenseKey: 'public_license_key_superdocinternal_ad7035140c4b',
telemetry: { enabled: false },
Expand All @@ -87,15 +89,16 @@ const App = () => {
excludeItems: [
'acceptTrackedChangeBySelection',
'rejectTrackedChangeOnSelection',
'zoom',
// 'zoom',
'documentMode',
],
},
pdf: {
pdfLib: pdfjsLib,
pdfViewer: pdfjsViewer,
setWorker: true,
textLayerMode: 0,
setWorker: false,
// workerSrc: '',
textLayer: true,
// outputScale: 1.5,
},
whiteboard: {
enabled: whiteboardEnabled,
Expand All @@ -113,10 +116,10 @@ const App = () => {
superdocRef.current = superdocInstance;
window.superdoc = superdocInstance;

superdocInstance.on('whiteboard:ready', ({ whiteboard }) => {
onWhiteboardReady(whiteboard);
superdocInstance.on('whiteboard:init', ({ whiteboard }) => {
onWhiteboardInit(whiteboard);
});
}, [onWhiteboardReady]);
}, [onWhiteboardInit]);

// Load selected file and boot SuperDoc.
const handleNewFile = useCallback(async (fileName) => {
Expand Down Expand Up @@ -163,7 +166,7 @@ const App = () => {
const exportWhiteboard = useCallback(() => {
const data = superdocRef.current?.whiteboard?.getWhiteboardData();
if (!data) return;
console.log('[Whiteboard] export', { data });
console.log('[Whiteboard] export', data);
console.log('[Whiteboard] export json:', JSON.stringify(data, null, 2));
}, []);

Expand Down Expand Up @@ -202,7 +205,7 @@ const App = () => {
<div className="document-viewer">
<div className="viewer-header">
<h3>Document Viewer</h3>
{whiteboardReady && (
{whiteboardInitialized && (
<div className="whiteboard-toolbar">
<div className="whiteboard-tools">
{toolButtons.map((tool) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,9 +696,29 @@ body {
font-size: 12px;
}

.superdoc-pdf-viewer-container {
.sd-pdf-viewer {
border: 1px solid #DBDBDB;
border-radius: 8px;
overflow: hidden;
}

.sd-pdf-viewer-page {
border-bottom: 1px solid #DBDBDB;
}

.sd-pdf-viewer-page {
border-top: 1px solid #dfdfdf;
border-bottom: 1px solid #dfdfdf;
}

.sd-pdf-viewer-page:first-child {
border-radius: 16px 16px 0 0;
border-top: none;
}

.sd-pdf-viewer-page:last-child {
border-radius: 0 0 16px 16px;
border-bottom: none;
}

.hiddenCanvasElement{
Expand Down
2 changes: 1 addition & 1 deletion packages/superdoc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
},
"peerDependencies": {
"@hocuspocus/provider": "^2.13.6",
"pdfjs-dist": ">=4.3.136 <=4.6.82",
"pdfjs-dist": "^5.4.296",
"y-prosemirror": "catalog:",
"yjs": "catalog:"
},
Expand Down
10 changes: 0 additions & 10 deletions packages/superdoc/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
// packages/superdoc/src/components/PdfViewer/PdfViewer.vue
const PDF_VIEWER_CLASS = '.superdoc-pdf-viewer';

export default {
plugins: [
(await import('postcss-nested')).default,
// https://github.com/dbtedman/postcss-prefixwrap
// This is necessary for pdf.js style scoping.
(await import('postcss-prefixwrap')).default(PDF_VIEWER_CLASS, {
whitelist: ['pdf-viewer.css'],
ignoredSelectors: [],
prefixRootTags: false,
}),
]
}
5 changes: 0 additions & 5 deletions packages/superdoc/src/SuperDoc.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ const FloatingCommentsStub = stubComponent('FloatingComments');
const CommentsLayerStub = stubComponent('CommentsLayer');
const HrbrFieldsLayerStub = stubComponent('HrbrFieldsLayer');
const AiLayerStub = stubComponent('AiLayer');
const PdfViewerStub = stubComponent('PdfViewer');
const HtmlViewerStub = stubComponent('HtmlViewer');

// Mock @superdoc/super-editor with stubs and PresentationEditor class
Expand All @@ -118,10 +117,6 @@ vi.mock('@superdoc/super-editor', () => ({
},
}));

vi.mock('./components/PdfViewer/PdfViewer.vue', () => ({
default: PdfViewerStub,
}));

vi.mock('./components/HtmlViewer/HtmlViewer.vue', () => ({
default: HtmlViewerStub,
}));
Expand Down
62 changes: 49 additions & 13 deletions packages/superdoc/src/SuperDoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ const {
} = storeToRefs(superdocStore);
const { handlePageReady, modules, user, getDocument } = superdocStore;

/*
NOTE: new PdfViewer does not emit page-loaded. Hrbr fields/annotations
rely on handlePageReady; revisit when wiring fields for PDF.

From the old code:
const containerBounds = container.getBoundingClientRect();
containerBounds.originalWidth = width;
containerBounds.originalHeight = height;
emit('page-loaded', documentId, index, containerBounds);
*/

//prettier-ignore
const {
getConfig,
Expand Down Expand Up @@ -135,6 +146,7 @@ const superdocStyleVars = computed(() => {

// Refs
const layers = ref(null);
const pdfViewerRef = ref(null);

// Comments layer
const commentsLayer = ref(null);
Expand Down Expand Up @@ -687,10 +699,12 @@ const getSelectionPosition = computed(() => {
return { x: null, y: null };
}

const top = selectionPosition.value.top;
const left = selectionPosition.value.left;
const right = selectionPosition.value.right;
const bottom = selectionPosition.value.bottom;
const isPdf = selectionPosition.value.source === 'pdf';
const zoom = isPdf ? (activeZoom.value ?? 100) / 100 : 1;
const top = selectionPosition.value.top * zoom;
const left = selectionPosition.value.left * zoom;
const right = selectionPosition.value.right * zoom;
const bottom = selectionPosition.value.bottom * zoom;
const style = {
zIndex: 500,
borderRadius: '4px',
Expand Down Expand Up @@ -733,7 +747,9 @@ const handleSelectionChange = (selection) => {
activeSelection.value = selection;

// Place the tools menu at the level of the selection
let top = selection.selectionBounds.top;
const isPdf = selection.source === 'pdf' || selection.source?.value === 'pdf';
const zoom = isPdf ? (activeZoom.value ?? 100) / 100 : 1;
const top = selection.selectionBounds.top * zoom;
toolsMenuPosition.top = top + 'px';
toolsMenuPosition.right = isMobileView ? '0' : '-25px';
};
Expand Down Expand Up @@ -795,7 +811,7 @@ const getPdfPageNumberFromEvent = (event) => {
const y = event?.clientY;
if (typeof x !== 'number' || typeof y !== 'number') return null;
const elements = document.elementsFromPoint(x, y);
const pageEl = elements.find((el) => el?.classList?.contains?.('pdf-page'));
const pageEl = elements.find((el) => el?.dataset?.pdfPage != null);
if (pageEl) {
const pageNumber = Number(pageEl.dataset?.pageNumber);
return Number.isFinite(pageNumber) ? pageNumber : null;
Expand All @@ -821,7 +837,7 @@ const handleSelectionStart = (e) => {
const zoom = activeZoom.value / 100;
const x = (e.clientX - layerBounds.left) / zoom;
const y = (e.clientY - layerBounds.top) / zoom;
updateSelection({ startX: x, startY: y, page: pageNumber });
updateSelection({ startX: x, startY: y, page: pageNumber, source: 'pdf' });
selectionLayer.value.addEventListener('mousemove', handleDragMove);
});
};
Expand Down Expand Up @@ -850,6 +866,7 @@ const handleDragEnd = (e) => {
},
page: pageNumber ?? 1,
documentId: documents.value[0].id,
source: 'pdf',
});

handleSelectionChange(selection);
Expand All @@ -873,12 +890,27 @@ const handlePdfClick = (e) => {
handleSelectionStart(e);
};

const handlePdfSelectionRaw = ({ selectionBounds, documentId, page }) => {
if (!selectionBounds || !documentId) return;
const selection = useSelection({
selectionBounds,
documentId,
page,
source: 'pdf',
});
handleSelectionChange(selection);
};

watch(
() => activeZoom.value,
(zoom) => {
if (proxy.$superdoc.config.useLayoutEngine !== false) {
PresentationEditor.setGlobalZoom((zoom ?? 100) / 100);
}

const pdfViewer = getPDFViewer();
pdfViewer?.updateScale((zoom ?? 100) / 100);

nextTick(() => {
updateWhiteboardPageSizes();
updateWhiteboardPageOffsets();
Expand Down Expand Up @@ -910,6 +942,10 @@ const {
documents,
modules,
});

const getPDFViewer = () => {
return Array.isArray(pdfViewerRef.value) ? pdfViewerRef.value[0] : pdfViewerRef.value;
};
</script>

<template>
Expand Down Expand Up @@ -998,16 +1034,16 @@ const {

<div class="superdoc__sub-document sub-document" v-for="doc in documents" :key="doc.id">
<!-- PDF renderer -->

<PdfViewer
v-if="doc.type === PDF"
:document-data="doc"
:file="doc.data"
:file-id="doc.id"
:config="pdfConfig"
@selection-change="handleSelectionChange"
@ready="handleDocumentReady"
@page-loaded="handlePageReady"
@page-ready="handleWhiteboardPageReady"
@selection-raw="handlePdfSelectionRaw"
@bypass-selection="handlePdfClick"
@page-rendered="handleWhiteboardPageReady"
@document-ready="({ documentId, viewerContainer }) => handleDocumentReady(documentId, viewerContainer)"
ref="pdfViewerRef"
/>

<n-message-provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const {
editorCommentPositions,
isCommentHighlighted,
} = storeToRefs(commentsStore);
const { activeZoom } = storeToRefs(superdocStore);

const isInternal = ref(true);
const commentInput = ref(null);
Expand Down Expand Up @@ -319,7 +320,10 @@ const getSidebarCommentStyle = computed(() => {
}

if (pendingComment.value && pendingComment.value.commentId === props.comment.commentId) {
const top = Math.max(96, pendingComment.value.selection?.selectionBounds.top - 50);
const source = pendingComment.value.selection?.source;
const isPdf = source === 'pdf' || source?.value === 'pdf';
const zoom = isPdf ? (activeZoom.value ?? 100) / 100 : 1;
const top = Math.max(96, pendingComment.value.selection?.selectionBounds.top * zoom - 50);
style.position = 'absolute';
style.top = top + 'px';
}
Expand Down
12 changes: 6 additions & 6 deletions packages/superdoc/src/components/CommentsLayer/CommentsLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ const getStyle = (conversation) => {
const { selection, commentId } = conversation;
const containerBounds = selection.getContainerLocation(props.parent);
const placement = conversation.selection.selectionBounds;
const top = (parseFloat(placement.top) + containerBounds.top) * activeZoom.value;

const isPdf = selection?.source === 'pdf';
const zoom = isPdf ? (activeZoom.value ?? 100) / 100 : 1;
const internalHighlightColor = '#078383';
const externalHighlightColor = '#B1124B';

Expand All @@ -72,10 +72,10 @@ const getStyle = (conversation) => {

return {
position: 'absolute',
top: parseFloat(placement.top) + 'px',
left: placement.left + 'px',
width: placement.right - placement.left + 'px',
height: placement.bottom - placement.top + 'px',
top: parseFloat(placement.top) * zoom + 'px',
left: placement.left * zoom + 'px',
width: (placement.right - placement.left) * zoom + 'px',
height: (placement.bottom - placement.top) * zoom + 'px',
backgroundColor: fillColor,
pointerEvents: conversation.suppressClick ? 'none' : 'auto',
};
Expand Down
Loading
Loading