Merge pull request #928 from genesis-ai-dev/main#929
Open
Luke-Bilhorn wants to merge 25 commits into
Open
Conversation
Introduce a dedicated subdivision model that lets milestones describe custom rendering breaks, while preserving today's arithmetic 50-cell chunking as the default fallback. Pagination, subsection lookup, and subsection progress now all go through a single resolver, so future writes to `metadata.data.subdivisions` immediately change what the editor renders without touching any other code path. - Types: add `MilestoneSubdivisionPlacement`, `SubdivisionInfo`, and `metadata.data.subdivisions` / `metadata.data.subdivisionNames` on milestone cells; extend `MilestoneInfo` with resolved `subdivisions` and add `updateMilestoneSubdivisions` / `updateMilestoneSubdivisionName` editor messages for upcoming writers. - Resolver (`utils/subdivisionUtils.ts`): pure function that returns ordered, non-overlapping subdivisions for a milestone. Handles arithmetic fallback, stable ordering, anchor pruning on deleted cells, duplicate collapse, and target-side name overrides via `FIRST_SUBDIVISION_KEY`. - CodexCellDocument: `buildMilestoneIndex`, `getCellsForMilestone`, `getSubsectionCountForMilestone`, `findMilestoneAndSubsectionForCell`, and `calculateSubsectionProgress` now share the resolver. Behavior is byte-identical for documents without custom subdivisions. - Tests: add unit + integration coverage for the resolver and the document APIs (custom breaks, slicing, stale anchor pruning, legacy 50-cell behavior). Made-with: Cursor
Wire up `updateMilestoneSubdivisions` and `updateMilestoneSubdivisionName` editor messages so the UI can now persist custom subdivision breaks and names. Placements are source-authoritative — edits from a `.codex` document are rejected and mirrored onto the paired source's target only after a root-cell-ID sanity check confirms the two milestones line up. Names live on a separate `subdivisionNames` map per document so source and target can be renamed independently. - Message handlers: validate anchors against current root content cells, strip names on mirror (target uses its own override map), recover gracefully when the paired document diverges, and refresh the webview via the existing milestone-refresh path. - Provider helpers: `getPairedNotebookUri` and `getOrOpenDocumentForUri` keep the paired-document plumbing localized to the provider and reuse any already-open `CodexCellDocument`. - Document: expose `getRootContentCellIdsForMilestone` for anchor validation / pairing checks, and invalidate the milestone-index cache whenever `subdivisions` or `subdivisionNames` change so renames and break edits appear on the next render. - Tests: cover anchor-set extraction (including paratext / child / deleted exclusions), cache invalidation on both subdivisions and name overrides, and the full round-trip from placements → arithmetic fallback when placements are cleared. Made-with: Cursor
The editor now prefers provider-computed subdivisions when building the UI's `Subsection` list, so custom milestone breaks and their names flow through to navigation the moment the resolver emits them. The prior arithmetic calculation remains as a pure fallback for the brief window before the first `milestoneIndex` lands (or for any stale payloads missing `subdivisions`), which preserves today's behaviour exactly for notebooks without custom breaks. - Extract the subsection-building logic into `CodexCellEditor/utils/subdivisionUtils.ts` so it can be unit-tested independently and reused by future tail-append / renaming paths. - Extend the `Subsection` type with `name`, `key`, `startCellId`, and `source` so downstream UI (accordion, header) can display names, tell custom breaks from auto ones, and echo the stable key back to the provider on renames. - Add unit tests covering empty milestones, subdivision preference, arithmetic fallback parity, ID stability, and edge-case page sizes. Made-with: Cursor
Each keyed subsection in the milestone accordion now carries a hover "rename" affordance. Editing is inline, works on both source and target documents (names are stored per-document in `subdivisionNames`), and an optimistic local cache keeps the new name on screen while the provider refreshes. Numeric ranges remain visible alongside the name so users never lose track of where a subdivision starts and ends. - Skip the affordance entirely for legacy subsections without a `key` (no resolver payload, nothing to persist) so the UI degrades cleanly. - Clearing the input posts an empty `newName`, which the provider handler interprets as "remove override" and falls back to the numeric range. - Pressing Enter saves, Escape cancels; cancel never posts a message. - Add focused tests covering affordance gating, display behaviour, the save/clear/no-op paths, and cancel. Made-with: Cursor
Source-side authors can now remove an individual custom subdivision break and, if they want to start over, reset all custom breaks in a milestone back to the default arithmetic chunking. Both actions post `updateMilestoneSubdivisions` with a recomputed placement list derived from the resolver's subdivisions (custom-index- >0 with a stable startCellId). The provider already mirrors the new list to the paired target document, so source and target stay aligned. - The × button only appears for subdivisions whose `source === "custom"` and that carry a `startCellId`, so legacy payloads and the implicit first subdivision are untouched. - "Reset to default breaks" uses an in-place two-click confirmation (arm-then-commit, auto-disarming after 3s) to avoid accidental loss without pulling in a modal. The accessible label flips between "Reset Subdivisions" and "Confirm Reset Subdivisions" so assistive tech announces the armed state. - Neither control is rendered on target documents; placements are source-authoritative and the provider rejects writes from target URIs anyway — the UI just omits the affordance up front. - Extend the lucide-react test mock with the new X/Undo2 icons and add focused coverage for the control visibility and both post shapes. Made-with: Cursor
Extract the save/mirror/refresh pipeline from updateMilestoneSubdivisions into commitMilestoneSubdivisionPlacements so a second entry point — a source-only add-break command that takes a 1-based cellNumber — can reuse the same source-authoritative guarantees (root-cell validation, target mirroring with names stripped, divergence-skip on root mismatch). Tests exercise the new handler via a test-only export of the handler map, covering the happy path, the preserve-names-on-append path, out-of-range rejection, idempotence on re-add, and the non-source rejection guard. Made-with: Cursor
…nly) Source-side users can now split a milestone by typing the target cell number directly into the accordion footer instead of having to touch the underlying data. The form validates the number against the milestone's root-cell range before posting addMilestoneSubdivisionAnchor, surfaces an inline error on out-of-range input, and respects Escape/Cancel to close without committing. Hidden entirely on target documents and on milestones with fewer than 2 cells. Made-with: Cursor
…bels Introduces codex-editor-extension.useSubdivisionNumberLabels (default false). When enabled, the milestone accordion always shows the numeric cell range instead of a user-assigned subdivision name. The provider broadcasts the current value on initial pagination load and whenever the setting changes. Made-with: Cursor
…ings Adds a Pagination & Subdivisions section to the Interface Settings webview so users can adjust the default milestone page size and toggle whether subdivisions render as numeric ranges instead of their names. The panel listens for configuration changes and rebroadcasts initial data when either setting is updated from elsewhere. Made-with: Cursor
… edits When a user adds, removes, or resets subdivision breaks on a source document, the placement mirror was already being persisted to the paired target file but the open target webview was never told to re-render — users had to close and reopen the target tab to see the change. This threads the existing milestone-refresh path through to the target panel so the change appears immediately. The cost per source-side break edit is one extra postMessage plus a milestone-index rebuild on the target (cached and invalidated by updateCellData), which is negligible even on older hardware. When no target webview is open the refresh is skipped silently and the target picks up the change on next load via the persisted file. Subdivision name edits remain per-side by design and don't trigger a target refresh. Made-with: Cursor
The milestone accordion now opens in a read-only baseline: the only edit-related control in the header is a gear icon. Clicking the gear reveals the title pencil, surfaces the per-subsection rename pencils (always visible — no more hover-only reveal), and unhides the source- only "Add break…" / "Reset" footers. Re-clicking the gear collapses the affordances back, and reopening the dropdown always starts from the read-only baseline so the gear-on state is never sticky across sessions. The "Add break at cell" placeholder also drops its compact "2–N" range hint in favor of a single illustrative number, matching the underlying input semantics (one cell number, not a range). Tests opt back into settings mode by default via a new `initialSettingsMode` prop so the existing edit/rename/break test suites keep exercising those flows without first having to click the gear; three new tests cover the gear toggle itself. Made-with: Cursor
…istence - resolveSubdivisions now sub-chunks long stretches between user-defined breaks by cellsPerPage, so adding a break at cell 433 in a 700-cell milestone still produces [1-50]…[401-432],[433-482]…[683-700] rather than two huge pages. - New workspace setting codex-editor-extension.maxSubdivisionLength (0 = off): when positive, stretches between user breaks shorter than this value are left intact (e.g. a 103-cell logical page survives with threshold=120), while longer stretches continue to be sub-chunked. maxSubdivisionLength is threaded through buildMilestoneIndex, getCells- ForMilestone, findMilestoneAndSubsectionForCell, and the config-change listener so all callers stay in sync. - Source subdivision names are now the default for the paired target document. Names ride on the placement object and are mirrored into subdivisionNamesFromSource; resolveSubdivisions picks them up as a fallback when the target has no local override. A target translator can still set their own independent name. - Naming an auto-generated chunk (on the source side) promotes it to a persistent placement so that changes to cellsPerPage or maxSubdivision- Length cannot displace or orphan the name. Clearing a name leaves the placement intact; the implicit first subdivision is never promoted. - Cache-invalidation fix: updateCellData now also busts the milestone index when subdivisionNamesFromSource changes. - 24 new unit / integration tests covering all of the above. Made-with: Cursor
- New "Maximum subdivision length" toggle + numeric input in the Pagination & Subdivisions section. When enabled, defaults to cellsPerPage × 2. Wired to the codex-editor-extension.maxSubdivision- Length workspace setting with validation (clamped 1-5000). - Temporarily hides the "Always show subdivision number ranges" toggle (the underlying state and message wiring are preserved); names are always shown since numeric ranges are already visible in lighter text. - Removes the "(between 5 and 200)" parenthetical from the Cells per page description. - Updates the Maximum subdivision length description to: "Pagination allows ranges between user added subdivisions up to this length". Made-with: Cursor
- Replace the per-subdivision X remove button and the milestone-level reset button icons with Trash2 (lucide), tinted vscode-errorForeground. - Rows that cannot be deleted (auto-chunks, target side, first subdivision) now render an invisible aria-hidden phantom span the same size as the trash-can button (28×28 px, visibility:hidden) so the rename pencil icons stay vertically aligned across all rows. Made-with: Cursor
This keeps row spacing consistent with the active delete button in milestone settings mode.
Adds a touch more separation between row controls and progress dots.
Ensure the clicked milestone expands before entering edit mode, and move the rename affordance into the milestone row.
Test 2, hitting big green merge button
… of https://github.com/genesis-ai-dev/codex-editor into 867-make-milestone-subdivisions-for-rendering-editable
Contributor
Author
|
/build |
Contributor
Author
|
/build |
The /build CI was failing because the webview test suite couldn't load
MilestoneAccordion: the lucide-react mock explicitly listed icons and
silently broke the moment the component imported a new one (Trash2).
Even after fixing the mock, ~30 assertions still pointed at the old
single-pencil "Edit Milestone" label that no longer exists, and the new
per-row "Rename Milestone" pencils caused getByLabelText to throw on
multi-element matches.
Two coupled fixes, one outcome:
1. Standardize rename-flow aria-labels around the user-facing nouns:
- Milestone rename: "Rename Milestone" / "Save Milestone Rename" /
"Cancel Milestone Rename" (was "Edit/Save Milestone" + "Revert
Changes").
- Milestone subdivision rename: "Rename/Save/Cancel Milestone
Subdivision Rename" (was "Rename/Save/Cancel Subsection [Name]").
Non-rename actions (gear, add/remove break, reset) keep their
existing names.
2. Make the test queries match the new UI:
- vi.mock("lucide-react") now uses importOriginal so newly imported
icons don't silently break the suite again.
- Per-milestone rename pencils are looked up via a small helper that
scopes within the row's data-testid="accordion-item-{idx}", so
tests read as "Chapter 1's pencil" rather than "the first
getByLabelText match".
- Describe blocks renamed Edit Mode -> Milestone Rename and
Subsection Rename -> Milestone Subdivision Rename to match the
user-facing language.
Contributor
Author
|
/build |
|
Pre-release: 0.27.0-pr929-9e452c2 https://github.com/genesis-ai-dev/codex-editor/releases/tag/0.27.0-pr929-9e452c2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.