Skip to content

Fix/remixer punchlist 3 - #979

Merged
jakeaturner merged 16 commits into
stagingfrom
fix/remixer-punchlist-3
Sep 29, 2026
Merged

jakeaturner merged 16 commits into
stagingfrom
fix/remixer-punchlist-3

Conversation

@yghaemi

@yghaemi yghaemi commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@yghaemi
yghaemi requested a review from jakeaturner September 28, 2026 19:24
jakeaturner

This comment was marked as outdated.

@jakeaturner jakeaturner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Step 2 moves an "unmoved" page under its live parent without checking whether that parent now sits below the page in the draft.
  • Live book: someone in the library moves 21 (Alpha) under 23 (Gamma).
  • Draft: the user moved 23 under 21. Page 21 keeps its number (2.1), so it doesn't count as moved in the draft.
  • Result: 21's parent becomes 23 and 23's parent becomes 21 (["23","21"]).

Both pages and their subtrees lose their link to the root, so they disappear from the tree. The rebase also refreshes the fingerprint, so the 409 check won't stop the publish, and the job then tries to move a page under its own descendant. Potential fix:

const isUnder = (id: string, ancestorId: string): boolean => {
  const byId = new Map(book.map((n) => [n["@id"], n]));
  const seen = new Set<string>();
  for (let cur = byId.get(id); cur && !seen.has(cur["@id"]); cur = byId.get(parentKey(cur))) {
    if (cur["@id"] === ancestorId) return true;
    seen.add(cur["@id"]);
  }
  return false;
};
// in the step-2 loop, before placeAtLivePosition:
if (liveParent !== "-1" && isUnder(liveParent, node["@id"])) continue;

Also should add this case to the "both changed" tests.

  1. Retrying after a failed publish always gets a 409 with the wrong explanation (RemixerDashboard.tsx:2735)
  • A job that fails partway has already created, moved or deleted pages.
  • The error branch doesn't reload anything, so the client still holds the pre-publish fingerprint.
  • The user clicks Publish again and is told "the book was changed in the library". Their own partial publish made that change.
  • Suggested fix: on job.status === "error", rebase the local draft (handleLoadSourceRef.current("local")) and say the book was partly published.
  1. The 409 message steers users toward losing edits (RemixerDashboard.tsx:2145, server/api/remixer.ts:301)
  • The check runs before the findOneAndUpdate that saves currentBook, so a refused publish saves nothing to the server.
  • The message says "Reload your draft from Load Remixer State" without naming an option.
  • Choosing Server or Server Draft loads the last saved state and drops everything since. Only the browser draft (autosaved every 500ms) has the latest edits.
  • Potential improvement: on a 409, reload the browser draft for the user with handleLoadSourceRef.current("local"). If that ends up introducing too much complexity or is too risky, I suggest just prompting the user to reload the browser draft themselves.

jakeaturner
jakeaturner previously approved these changes Sep 29, 2026

@jakeaturner jakeaturner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good! One final edge-case hardening that would be good to follow up on: A failed rebase turns the guard off right after a 409 (RemixerDashboard.tsx:653)

rebaseDraftOnLiveToc still sets liveBookFingerprintRef.current = null in its catch. Both new recovery paths call it:

  • Publish returns 409, so you know the book changed.
  • The rebase's TOC fetch fails, and the fingerprint becomes null.
  • The user ignores the "reload the page" notice and clicks Publish again.
  • The client sends no fingerprint, the server skips the check, and the publish reverts the library changes the 409 just caught.

The job-error path has the same gap. Nulling made sense on initial load, where no baseline exists. After a 409 you want the stale fingerprint kept so publishes keep failing until a rebase succeeds:

const rebaseDraftOnLiveToc = async (
book, coverPageId, libreLibrary, pathLevelFormats,
{ keepFingerprintOnFailure = false } = {},
) => {
...
} catch (error) {
if (!keepFingerprintOnFailure) liveBookFingerprintRef.current = null;
...

Suggested fix: pass { keepFingerprintOnFailure: true } from rebaseCurrentBookOnLiveToc.

@yghaemi
yghaemi dismissed jakeaturner’s stale review September 29, 2026 16:52

The merge-base changed after approval.

…el labels

Introduced a new PublishSuccessDialog component to provide user feedback upon successful publishing. Updated labels in the EditPanel for clarity, changing "Override Autonumbers" to "Override Autonumberer" and "Override URL Ending" to "Override URL". Additionally, refactored the RemixerDashboard to integrate the new dialog and manage its visibility state.
…ality

Introduced a new StartOverModal component to provide users with options to reload the original book structure while preserving settings or starting over completely. Updated the RemixerDashboard to integrate this modal, replacing the previous confirmation modal for starting over, thereby improving user experience and clarity in project management.
…hboard

Added a loading spinner to indicate when the catalog is being processed in the RemixerDashboard. Introduced a new state variable `catalogBookLoading` to manage the loading state and prevent race conditions during catalog updates. Updated the UI to display a spinner and skeleton while loading the selected book, improving user experience and feedback during data fetching.
…ation

Introduced a new CorePageDeleteModal component to confirm the deletion of core front/back matter pages and sections. Integrated this modal into the RemixerDashboard, allowing users to acknowledge the implications of deleting essential content. Updated context menu and related logic to support deletion confirmation for core matter pages, enhancing user experience and preventing accidental deletions.
…mixerDashboard

Added a `catalogLoading` prop to the LibraryActions component to manage the loading state of the catalog. Updated UI elements to reflect loading status, including disabling buttons and changing text to indicate when the catalog is being loaded. Enhanced error handling in the RemixerDashboard to ensure the catalog button is appropriately disabled if loading fails, improving user experience during catalog operations.
Implemented `_resolvePageID` to retrieve a page's numeric ID from its path, enhancing page management. Introduced `_orderMatterPages` to ensure proper ordering of front and back matter pages, improving the structure of generated books. Updated the page creation process to utilize these methods, ensuring that matter pages are correctly ordered during creation.
Removed the `sortMatterSiblings` function and updated the logic for adding and ordering matter pages in the RemixerDashboard. Enhanced the insertion logic to allow custom pages to be slotted between default matter pages, ensuring proper ordering during drag-and-drop operations. Updated related functions to reflect these changes, improving the overall structure and usability of the Remixer component.
Added a new method to rebase saved drafts against the live table of contents (TOC), allowing for accurate change tracking between drafts and published content. Introduced a reconciliation report to summarize changes, including adopted, inserted, relocated, and untracked pages. Enhanced the RemixerDashboard to utilize this functionality, improving the user experience by providing notifications about changes during draft loading. Updated related services to support the new rebase logic.
Implemented a structural fingerprinting mechanism for the live book to ensure safe publishing of drafts. This feature prevents overwriting changes made in the library since the draft was loaded by comparing the draft's fingerprint with the live book's fingerprint. Updated the RemixerDashboard and related services to handle the new fingerprinting logic, enhancing user notifications and error handling during the publishing process. Additionally, refined the API to accept the live book fingerprint during project publishing.
Added a new test case to ensure that parent cycles are not created when drafts and live books have conflicting page movements. Implemented a method to check if a node is under a specific ancestor, preventing cycles during reconciliation. Updated the RemixerDashboard to rebase the current book on the live TOC when changes are detected, improving user notifications and error handling during publishing. Enhanced API responses to clarify the implications of changes in the library on draft publishing.
Enhanced the Remixer functionality to track pages created during a publish run, even if the run fails partway. This includes adding a new `createdPages` field to the job model and implementing logic to apply these mappings in the RemixerDashboard and related services. Updated the API to return created pages in job status responses, ensuring that drafts can accurately reflect the state of pages created during failed runs. Added tests to validate the new behavior and ensure robustness in draft reconciliation.
… publish

Added functionality to discard half-created pages when a publish run fails, preventing conflicts during retries. Introduced `discardHalfCreatedPage` to handle deletion of incomplete pages and `failAfterCreate` to manage errors post-creation. Enhanced the `handleNewPage` and `handleImportedPage` methods to integrate this error handling, ensuring a smoother user experience and improved reliability in page management.
Implemented `_resolvePageID` to retrieve a page's numeric ID from its path, enhancing page management. Introduced `_orderMatterPages` to ensure proper ordering of front and back matter pages, improving the structure of generated books. Updated the page creation process to utilize these methods, ensuring that matter pages are correctly ordered during creation.
Removed the `sortMatterSiblings` function and updated the logic for adding and ordering matter pages in the RemixerDashboard. Enhanced the insertion logic to allow custom pages to be slotted between default matter pages, ensuring proper ordering during drag-and-drop operations. Updated related functions to reflect these changes, improving the overall structure and usability of the Remixer component.
Implemented a structural fingerprinting mechanism for the live book to ensure safe publishing of drafts. This feature prevents overwriting changes made in the library since the draft was loaded by comparing the draft's fingerprint with the live book's fingerprint. Updated the RemixerDashboard and related services to handle the new fingerprinting logic, enhancing user notifications and error handling during the publishing process. Additionally, refined the API to accept the live book fingerprint during project publishing.
Enhanced the Remixer functionality to track pages created during a publish run, even if the run fails partway. This includes adding a new `createdPages` field to the job model and implementing logic to apply these mappings in the RemixerDashboard and related services. Updated the API to return created pages in job status responses, ensuring that drafts can accurately reflect the state of pages created during failed runs. Added tests to validate the new behavior and ensure robustness in draft reconciliation.
@jakeaturner
jakeaturner force-pushed the fix/remixer-punchlist-3 branch from 679b519 to 83f973f Compare September 29, 2026 17:05
@jakeaturner
jakeaturner merged commit 76be528 into staging Sep 29, 2026
7 checks passed
@jakeaturner
jakeaturner deleted the fix/remixer-punchlist-3 branch September 29, 2026 17:08
@libretexts-bot

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.154.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants