Skip to content

Conversation

@tupizz
Copy link

@tupizz tupizz commented Dec 12, 2025

Summary

Add public methods to programmatically control zoom level on SuperDoc instances:

  • setZoom(percent) - Set zoom level as a percentage (e.g., 150 for 150%)
  • getZoom() - Get current zoom level as percentage
  • zoomChange event - Emitted when zoom changes

This allows developers to implement custom zoom controls (e.g., Ctrl+scroll, pinch-to-zoom, custom UI buttons) without relying on the toolbar.

Usage

const superdoc = new SuperDoc({
  selector: '#editor',
  document: file,
});

// Set zoom to 150%
superdoc.setZoom(150);

// Get current zoom
const zoom = superdoc.getZoom(); // Returns 100, 150, etc.

// Listen for zoom changes
superdoc.on('zoomChange', ({ zoom }) => {
  console.log(`Zoom changed to ${zoom}%`);
});

// Example: Ctrl+scroll zoom
window.addEventListener('wheel', (event) => {
  if (event.ctrlKey) {
    event.preventDefault();
    const currentZoom = superdoc.getZoom();
    const delta = event.deltaY > 0 ? -10 : 10;
    superdoc.setZoom(Math.max(25, Math.min(400, currentZoom + delta)));
  }
}, { passive: false });

Test plan

  • Call superdoc.setZoom(150) and verify document scales to 150%
  • Call superdoc.getZoom() and verify it returns correct percentage
  • Verify zoomChange event fires when zoom is changed
  • Test with multiple zoom levels (50%, 100%, 200%)
  • Verify invalid values are handled gracefully (negative, NaN, etc.)

Closes #928

Add public methods to programmatically control zoom level:
- `setZoom(percent)` - Set zoom level (e.g., 150 for 150%)
- `getZoom()` - Get current zoom level as percentage

This allows developers to implement custom zoom controls
without relying on the toolbar UI.

Usage:
```javascript
// Set zoom to 150%
superdoc.setZoom(150);

// Get current zoom
const zoom = superdoc.getZoom(); // Returns 100, 150, etc.

// Listen for zoom changes
superdoc.on('zoomChange', ({ zoom }) => {
  console.log(`Zoom changed to ${zoom}%`);
});
```

Closes superdoc-dev#928
Copilot AI review requested due to automatic review settings December 12, 2025 13:33
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +850 to +854
// Update all presentation editors
this.superdocStore?.documents?.forEach((doc) => {
const presentationEditor = typeof doc?.getPresentationEditor === 'function' ? doc.getPresentationEditor() : null;
if (presentationEditor && typeof presentationEditor.setZoom === 'function') {
presentationEditor.setZoom(zoomMultiplier);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge setZoom bypasses shared activeZoom state

Programmatic zoom changes only call presentationEditor.setZoom for each document (lines shown) and emit an event, but they never update superdocStore.activeZoom. The rest of the UI scales overlays and coordinates from activeZoom (e.g., CommentsLayer.vue uses it to position highlights), and SuperDoc.vue watches it to propagate zoom to PresentationEditor globally. Calling the new setZoom() API will therefore zoom the document content while overlays and hit-testing still assume the previous zoom level, causing misaligned comments/selection after programmatic zoom changes.

Useful? React with 👍 / 👎.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds programmatic zoom control to SuperDoc by exposing setZoom() and getZoom() public API methods, along with a zoomChange event. This enables developers to implement custom zoom controls (e.g., keyboard shortcuts, custom UI buttons, gesture controls) without relying on the built-in toolbar.

  • Adds getZoom() method to retrieve current zoom level as a percentage
  • Adds setZoom(percent) method to programmatically set zoom level for all documents
  • Emits zoomChange event when zoom is changed programmatically

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +819 to +860
/**
* Get the current zoom level as a percentage (e.g., 100 for 100%)
* @returns {number} The current zoom level as a percentage
* @example
* const zoom = superdoc.getZoom(); // Returns 100, 150, 200, etc.
*/
getZoom() {
const doc = this.superdocStore?.documents?.[0];
const presentationEditor = typeof doc?.getPresentationEditor === 'function' ? doc.getPresentationEditor() : null;
if (presentationEditor && typeof presentationEditor.zoom === 'number') {
return Math.round(presentationEditor.zoom * 100);
}
// Fallback to 100% if no presentation editor
return 100;
}

/**
* Set the zoom level for all documents
* @param {number} percent - The zoom level as a percentage (e.g., 100, 150, 200)
* @example
* superdoc.setZoom(150); // Set zoom to 150%
* superdoc.setZoom(50); // Set zoom to 50%
*/
setZoom(percent) {
if (typeof percent !== 'number' || !Number.isFinite(percent) || percent <= 0) {
console.warn('[SuperDoc] setZoom expects a positive number representing percentage');
return;
}

const zoomMultiplier = percent / 100;

// Update all presentation editors
this.superdocStore?.documents?.forEach((doc) => {
const presentationEditor = typeof doc?.getPresentationEditor === 'function' ? doc.getPresentationEditor() : null;
if (presentationEditor && typeof presentationEditor.setZoom === 'function') {
presentationEditor.setZoom(zoomMultiplier);
}
});

// Emit zoom change event
this.emit('zoomChange', { zoom: percent });
}
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new setZoom and getZoom methods lack test coverage. The test file at packages/superdoc/src/SuperDoc.test.js contains comprehensive tests for other SuperDoc methods, and these new public API methods should also be tested.

Consider adding tests that verify:

  1. setZoom correctly updates all presentation editors with the zoom multiplier
  2. getZoom returns the correct percentage from the presentation editor
  3. The zoomChange event is emitted with the correct payload
  4. Invalid inputs (negative, NaN, zero) are handled gracefully
  5. Behavior when no presentation editor exists

Copilot uses AI. Check for mistakes.
presentationEditor.setZoom(zoomMultiplier);
}
});

Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setZoom method emits a 'zoomChange' event but does not update the superdocStore.activeZoom property. This creates an inconsistency because:

  1. The existing onToolbarCommand method (line 678) sets this.superdocStore.activeZoom when the toolbar zoom is changed
  2. SuperDoc.vue watches activeZoom (lines 770-777) and calls PresentationEditor.setGlobalZoom when it changes
  3. By not updating activeZoom, this method bypasses the centralized state management, causing the store to be out of sync with the actual zoom level

The setZoom method should update this.superdocStore.activeZoom = percent before emitting the event, to ensure consistency with the existing zoom management pattern.

Suggested change
// Update centralized zoom state before emitting event
if (this.superdocStore) {
this.superdocStore.activeZoom = percent;
}

Copilot uses AI. Check for mistakes.
});

// Emit zoom change event
this.emit('zoomChange', { zoom: percent });
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event name 'zoomChange' is inconsistent with the codebase's event naming convention. Other events in this file use kebab-case (e.g., 'pdf-document-ready', 'sidebar-toggle', 'editor-create', 'editor-destroy'), not camelCase.

This event should be renamed to 'zoom-change' to maintain consistency with the existing event naming pattern throughout the SuperDoc class.

Suggested change
this.emit('zoomChange', { zoom: percent });
this.emit('zoom-change', { zoom: percent });

Copilot uses AI. Check for mistakes.
Comment on lines +819 to +833
/**
* Get the current zoom level as a percentage (e.g., 100 for 100%)
* @returns {number} The current zoom level as a percentage
* @example
* const zoom = superdoc.getZoom(); // Returns 100, 150, 200, etc.
*/
getZoom() {
const doc = this.superdocStore?.documents?.[0];
const presentationEditor = typeof doc?.getPresentationEditor === 'function' ? doc.getPresentationEditor() : null;
if (presentationEditor && typeof presentationEditor.zoom === 'number') {
return Math.round(presentationEditor.zoom * 100);
}
// Fallback to 100% if no presentation editor
return 100;
}
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getZoom method only reads from the first document (documents[0]), but setZoom updates all documents. This creates an asymmetry that could cause confusion:

  • If you have multiple documents with different zoom levels, getZoom will only return the zoom of the first document
  • After calling setZoom, all documents will have the same zoom level

This behavior should be documented in the JSDoc comment, or the implementation should be reconsidered to ensure consistent behavior. Consider adding a note like: "Note: When multiple documents exist, this returns the zoom level of the first document. Use setZoom to apply a zoom level to all documents."

Copilot uses AI. Check for mistakes.
Add 8 test cases covering:
- getZoom returns 100 when no presentation editor is available
- getZoom returns correct percentage from presentation editor
- getZoom rounds to nearest integer
- setZoom calls presentation editor with correct multiplier
- setZoom emits zoomChange event
- setZoom updates all editors in multi-document mode
- setZoom handles invalid values gracefully
- setZoom handles missing presentation editor
@tupizz tupizz force-pushed the feat/expose-zoom-api branch from 476c981 to 84f915c Compare December 12, 2025 13:39
@tupizz
Copy link
Author

tupizz commented Dec 12, 2025

@edoversb 🙏🏻

@harbournick harbournick force-pushed the main branch 2 times, most recently from 84f8ea5 to 2a443d8 Compare December 23, 2025 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How to control the doc's scale by code but not UI

1 participant