Skip to content

(Feature) Add drag-and-drop arrangement for JPG to PDF conversion#323

Open
AB527 wants to merge 3 commits intoalam00000:mainfrom
AB527:feature/drag-and-drop-arrangement-for-jpg-to-pdf
Open

(Feature) Add drag-and-drop arrangement for JPG to PDF conversion#323
AB527 wants to merge 3 commits intoalam00000:mainfrom
AB527:feature/drag-and-drop-arrangement-for-jpg-to-pdf

Conversation

@AB527
Copy link
Copy Markdown

@AB527 AB527 commented Dec 28, 2025

Description

This change adds a drag-and-drop image reordering interface to the JPG-to-PDF conversion flow. It enables users to rearrange uploaded JPG images before generating the final PDF, ensuring correct page order.

Previously, the output PDF page order depended on upload sequence or file naming, which was inconvenient and error-prone when handling multiple images. This feature brings parity with the existing Merge PDFs workflow by reusing the same UI/UX pattern.

No external dependencies were introduced.

Fixes #320

Type of change

  • New feature (non-breaking change which adds functionality)

🧪 How Has This Been Tested?

The changes were tested manually in a local development environment by uploading multiple JPG images, rearranging them using drag-and-drop, and generating PDFs multiple times to verify that the final page order matches the arranged order.

Checklist:

  • Verified output manually
  • Tested with relevant sample documents or data
  • Wrote Vite Test Case (if applicable)

Expected Results:

  • Drag-and-drop correctly updates image order.
  • Generated PDF pages follow the arranged order with no errors.

Actual Results:

  • Images could be smoothly reordered using drag-and-drop.
  • PDF was generated successfully with pages in the correct sequence.
  • No UI glitches or console errors observed.

Checklist:

  • I have signed the Contributor License Agreement (CLA) or my organization has signed the Corporate CLA
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Summary by CodeRabbit

  • New Features
    • File reordering: Drag-and-drop file reordering added with visible drag handles so users can rearrange JPGs to set the PDF page sequence.
    • Improved UX: Reordering updates the file order immediately and preserves the new sequence for conversion.
    • UI polish: Remove controls moved alongside reorder handles and initialization tuned for smoother rendering.

@MrFreePress
Copy link
Copy Markdown

Closing as stale/conflicting. If this idea still matters, please reopen it as a fresh PR rebased on current main.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

Added drag-and-drop reordering to the JPG→PDF page by rendering per-file drag handles, creating/destroying a Sortable instance for #file-display-area, and updating the in-memory files array on drag completion to reflect new order.

Changes

Cohort / File(s) Summary
JPG→PDF UI + drag-and-drop
src/js/logic/jpg-to-pdf-page.ts
Imported Sortable, added .drag-handle per file row, deferred sortable initialization (via setTimeout), introduced initializeFileListSortable() to create/destroy a single Sortable instance, updated updateUI() to render data-index and reorder/remove controls, and splice the global files array on onEnd before calling updateUI().

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as JPG→PDF UI
    participant Sortable
    participant State as files[]
    Note over UI,Sortable: Initial setup binds Sortable to `#file-display-area`
    User->>UI: Drag file row (via .drag-handle)
    UI->>Sortable: drag event
    Sortable->>UI: onEnd(oldIndex,newIndex)
    UI->>State: splice files array (move oldIndex→newIndex)
    UI->>UI: updateUI() re-renders rows and indices
    UI->>Sortable: re-initialize / maintain instance
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 I hopped in, paws on a handle,

Thumbs up as thumbnails scramble and settle,
Pages leap, reorder with cheer,
A tidy PDF soon will appear,
Hop, click, drag — conversion is gentle. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly summarizes the main change: adding drag-and-drop arrangement functionality for JPG to PDF conversion, which aligns with the primary objective in the changeset.
Description check ✅ Passed The pull request description is comprehensive and follows the template structure with all critical sections completed: clear description with motivation, issue reference (#320), type of change marked, and testing details with expected and actual results provided.
Linked Issues check ✅ Passed The code changes directly implement all primary objectives from issue #320 [#320]: providing a drag-and-drop interface for reordering JPG images, matching the existing Merge PDFs UI/UX pattern, and allowing users to control page order before PDF generation.
Out of Scope Changes check ✅ Passed All changes in the file are directly scoped to implementing the drag-and-drop reordering feature for the JPG-to-PDF conversion flow; no extraneous modifications or unrelated functionality changes are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

@AB527
Copy link
Copy Markdown
Author

AB527 commented Mar 21, 2026

Hi, I’ve resolved the conflicts and rebased the PR on the latest main. Since the changes are small, there’s no need to open another PR.

Would really appreciate it if you could merge it soon to avoid any further conflicts. Thanks!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/js/logic/jpg-to-pdf-page.ts (2)

80-86: Inconsistent indentation.

Lines 80-86 use 4-space indentation while the rest of the file uses 2-space indentation. This should be corrected for consistency.

🔧 Suggested fix
-    document.getElementById('back-to-tools')?.addEventListener('click', () => {
-        window.location.href = import.meta.env.BASE_URL;
-    });
-
-    setTimeout(() => {
-        initializeFileListSortable();
-    }, 0);
+  document.getElementById('back-to-tools')?.addEventListener('click', () => {
+    window.location.href = import.meta.env.BASE_URL;
+  });
+
+  setTimeout(() => {
+    initializeFileListSortable();
+  }, 0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/js/logic/jpg-to-pdf-page.ts` around lines 80 - 86, The block adding the
click handler to document.getElementById('back-to-tools') and the setTimeout
call that invokes initializeFileListSortable() uses 4-space indentation while
the rest of src/js/logic/jpg-to-pdf-page.ts uses 2-space indentation; update the
indentation of that block to 2 spaces to match the file style (ensure the lines
with the click listener and the setTimeout/initializeFileListSortable() call use
2-space indents).

5-5: Unused import and type suppression.

The loadPyMuPDF import on line 5 is no longer used since the code now instantiates PyMuPDF directly. Additionally, consider installing @types/sortablejs to remove the @ts-ignore and gain type safety.

♻️ Suggested cleanup
-import { loadPyMuPDF } from '../utils/pymupdf-loader.js';
+import { PyMuPDF } from '../utils/pymupdf-loader.js';

Or if you prefer to keep using the loader utility:

-    pymupdf = new PyMuPDF(import.meta.env.BASE_URL + 'pymupdf-wasm/');
-    await pymupdf.load();
+    pymupdf = await loadPyMuPDF();

Also applies to: 11-13

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/js/logic/jpg-to-pdf-page.ts` at line 5, Remove the unused import
loadPyMuPDF from the top of the file and delete the corresponding unused
references (since you now instantiate PyMuPDF directly via the PyMuPDF symbol);
also remove the `@ts-ignore` used around Sortable usage and either install
`@types/sortablejs` or add the correct Sortable import/type annotations so the
code has proper type safety instead of suppressing errors; alternatively, if you
prefer the loader pattern, revert instantiation to use loadPyMuPDF and ensure
PyMuPDF is obtained from that loader.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/js/logic/jpg-to-pdf-page.ts`:
- Around line 209-215: The function ensurePyMuPDF references the PyMuPDF class
directly (new PyMuPDF(...) and Promise<PyMuPDF>) but PyMuPDF isn't
imported/exported and is loaded via loadPyMuPDF(), causing a runtime
ReferenceError; change ensurePyMuPDF to call the existing loadPyMuPDF() helper
to obtain and cache the instance (use the existing pymupdf variable), remove
direct uses of new PyMuPDF, and update the return type to match loadPyMuPDF's
result (or use a generic/any type) so the function returns the dynamically
loaded instance from loadPyMuPDF instead of trying to construct PyMuPDF
directly.

---

Nitpick comments:
In `@src/js/logic/jpg-to-pdf-page.ts`:
- Around line 80-86: The block adding the click handler to
document.getElementById('back-to-tools') and the setTimeout call that invokes
initializeFileListSortable() uses 4-space indentation while the rest of
src/js/logic/jpg-to-pdf-page.ts uses 2-space indentation; update the indentation
of that block to 2 spaces to match the file style (ensure the lines with the
click listener and the setTimeout/initializeFileListSortable() call use 2-space
indents).
- Line 5: Remove the unused import loadPyMuPDF from the top of the file and
delete the corresponding unused references (since you now instantiate PyMuPDF
directly via the PyMuPDF symbol); also remove the `@ts-ignore` used around
Sortable usage and either install `@types/sortablejs` or add the correct
Sortable import/type annotations so the code has proper type safety instead of
suppressing errors; alternatively, if you prefer the loader pattern, revert
instantiation to use loadPyMuPDF and ensure PyMuPDF is obtained from that
loader.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9fc79e57-8ee7-4a11-a798-d96445e7029c

📥 Commits

Reviewing files that changed from the base of the PR and between 013cc17 and 37cece9.

📒 Files selected for processing (1)
  • src/js/logic/jpg-to-pdf-page.ts

Comment thread src/js/logic/jpg-to-pdf-page.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/js/logic/jpg-to-pdf-page.ts (2)

83-85: Avoid duplicate sortable initialization paths.

Line 83-Line 85 initializes sortable via setTimeout, and Line 181 initializes again after rendering. Keeping a single init path (post-render in updateUI) will reduce create/destroy churn and simplify lifecycle behavior.

Also applies to: 181-181

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/js/logic/jpg-to-pdf-page.ts` around lines 83 - 85, There are two
initialization paths for the sortable: a delayed call via setTimeout that
invokes initializeFileListSortable() and a second initialization after rendering
inside updateUI; remove the setTimeout-based initialization (the anonymous
function calling initializeFileListSortable) so sortable is only created once
from updateUI after render, and ensure updateUI is responsible for creating (and
if needed destroying/recreating) the sortable to avoid duplicate create/destroy
churn.

182-185: Destroy Sortable when list becomes empty.

When files.length === 0, the UI is hidden but the previous sortable instance remains attached until a later re-init. Explicitly destroy and clear _sortableInstance in the empty-state branch to avoid stale listeners.

Proposed patch
   } else {
+    const sortable = (fileDisplayArea as any)._sortableInstance;
+    if (sortable) {
+      sortable.destroy();
+      (fileDisplayArea as any)._sortableInstance = null;
+    }
     fileControls.classList.add('hidden');
     optionsDiv.classList.add('hidden');
   }

Also applies to: 191-193

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/js/logic/jpg-to-pdf-page.ts` around lines 182 - 185, The empty-state
branch that hides fileControls and optionsDiv must also destroy and clear the
previous Sortable instance to remove stale listeners: when handling files.length
=== 0 (the else branch that toggles fileControls.classList.add('hidden') and
optionsDiv.classList.add('hidden')), call _sortableInstance.destroy() if
_sortableInstance exists and then set _sortableInstance = null; make the same
change in the other empty-state branch around the lines referencing the same UI
hiding logic (the second occurrence noted at 191-193) so both branches clean up
the Sortable instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/js/logic/jpg-to-pdf-page.ts`:
- Around line 163-166: The drag handle is currently a non-focusable div
(dragHandle) which prevents keyboard users from reordering; replace the div with
a semantic focusable control (create a button element with type="button") or at
minimum add tabindex="0", role="button", and keydown handlers, and give it an
accessible label (e.g., aria-label="Move page" or visually hidden text). Update
the element creation where dragHandle is constructed so it uses button (or adds
tabindex/role and implements Enter/Space keyboard activation that triggers the
same drag/reorder logic), keep the same className/styling, and ensure event
listeners that start dragging reference this button element.
- Around line 10-11: Remove the unnecessary TypeScript suppression before the
Sortable import: delete the "// `@ts-ignore`" that precedes "import Sortable from
'sortablejs';" in jpg-to-pdf-page.ts so TypeScript uses the installed
`@types/sortablejs` and esModuleInterop handling; keep the import as-is (Sortable)
and run type-check to ensure no errors, removing any other redundant `@ts-ignore`
around the same import if present.

---

Nitpick comments:
In `@src/js/logic/jpg-to-pdf-page.ts`:
- Around line 83-85: There are two initialization paths for the sortable: a
delayed call via setTimeout that invokes initializeFileListSortable() and a
second initialization after rendering inside updateUI; remove the
setTimeout-based initialization (the anonymous function calling
initializeFileListSortable) so sortable is only created once from updateUI after
render, and ensure updateUI is responsible for creating (and if needed
destroying/recreating) the sortable to avoid duplicate create/destroy churn.
- Around line 182-185: The empty-state branch that hides fileControls and
optionsDiv must also destroy and clear the previous Sortable instance to remove
stale listeners: when handling files.length === 0 (the else branch that toggles
fileControls.classList.add('hidden') and optionsDiv.classList.add('hidden')),
call _sortableInstance.destroy() if _sortableInstance exists and then set
_sortableInstance = null; make the same change in the other empty-state branch
around the lines referencing the same UI hiding logic (the second occurrence
noted at 191-193) so both branches clean up the Sortable instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9662df4e-966e-41c3-bbef-f77365785c46

📥 Commits

Reviewing files that changed from the base of the PR and between 37cece9 and 715c88e.

📒 Files selected for processing (1)
  • src/js/logic/jpg-to-pdf-page.ts

Comment thread src/js/logic/jpg-to-pdf-page.ts
Comment on lines +163 to +166
const dragHandle = document.createElement('div');
dragHandle.className =
'drag-handle cursor-move text-gray-400 hover:text-white p-1 rounded transition-colors';
dragHandle.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="5" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="19" r="1"/></svg>`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make drag handle keyboard-accessible.

The reorder control is a non-focusable div (Line 163), so keyboard-only users cannot access reordering. Use a semantic button (or add tabindex, role, and keyboard handlers) plus an accessible label.

🧰 Tools
🪛 ast-grep (0.41.1)

[warning] 165-165: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: dragHandle.innerHTML = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="5" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="19" r="1"/></svg>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 165-165: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: dragHandle.innerHTML = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="5" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="19" r="1"/></svg>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/js/logic/jpg-to-pdf-page.ts` around lines 163 - 166, The drag handle is
currently a non-focusable div (dragHandle) which prevents keyboard users from
reordering; replace the div with a semantic focusable control (create a button
element with type="button") or at minimum add tabindex="0", role="button", and
keydown handlers, and give it an accessible label (e.g., aria-label="Move page"
or visually hidden text). Update the element creation where dragHandle is
constructed so it uses button (or adds tabindex/role and implements Enter/Space
keyboard activation that triggers the same drag/reorder logic), keep the same
className/styling, and ensure event listeners that start dragging reference this
button element.

@AB527
Copy link
Copy Markdown
Author

AB527 commented Mar 23, 2026

Hi @MrFreePress, just wanted to check on the status of this PR. If possible, it would be great to get it merged soon since it’s a relatively simple feature.

Also, please let me know if any changes are required from my side

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.

(Feature) Add drag-and-drop arrangement for JPG to PDF conversion

2 participants