Understand exactly what will change before publishing a Neos workspace.
This package is an experiment focused on exploring the workspace review UI. It is not optimized for code quality or production readiness.
The package extends the workspace review with navigation, local review progress and a dedicated visual preview. Publishing, discarding and the workspace module path keep their existing behavior. The preview requires backend access and checks read access to private workspaces.
- See changes on the page. A view switch shows either the change list or a visual compare: the page rendered as it will look, with created, changed, moved, hidden and deleted elements marked in place and the changed words highlighted in the text. Deleted elements are taken from the published page and shown with a red overlay.
- Track review progress. Mark a page as reviewed to collapse it; the sidebar counts reviewed pages and remembers them per workspace, and flags a page that changed after it was reviewed.
- Review from the keyboard. J/K move between pages, [ / ] step through changes, V toggles the reviewed mark and D switches views. ? opens the shortcut overview.
- Jump between changed pages. A sticky left sidebar lists the changed pages as a tree styled like the backend page tree, with their content dimension. The current page stays highlighted as you scroll, and each page is a separate block in the review.
- Read changes in page order. Within each page, cards follow the content tree's order, with containers before their children. Each card explains the individual change directly.
- Configurations become visible. Select boxes, toggles and references use translated editor labels instead of raw stored values.
- Word-level diffs reduce noise. Reviewers see the changed words instead of comparing two complete paragraphs. Long unchanged passages collapse to an ellipsis. Deleted words are struck through and added words underlined, so the diff does not rely on colour alone.
- Status and position are instantly clear. Created, deleted, moved and hidden elements receive explicit badges. Positions use readable sibling numbers instead of sorting indexes.
- Every change has an explanation. Visibility changes, reverted edits and internal updates no longer produce unexplained empty rows.
- Links and formatting become visible. Retargeted links, window behavior, linked text and formatting changes are detected even when the wording remains unchanged.
- Visual compare keeps the changes in context. Text changes can be reviewed in a page preview, and created, edited and deleted nodes are highlighted (still experimental). Clicking the info button jumps into the compare list view for details
The package lives in DistributionPackages and is installed through the distribution's path repository:
composer require codeq/workspace-review
./flow flow:cache:flushEvery changed node becomes one card with one entry for each effect publishing would have. Cards are grouped beneath their page header, without a separate per-page change summary. When no renderable property changed, the card explains why instead of staying empty.
| Entry | Appears when | Reads like |
|---|---|---|
| Text | the wording changed | … startet am ~~15.~~ 1. September, Karten kosten ~~12~~ 14 Euro … |
| Link | a link points elsewhere, opens differently, or words were linked or unlinked | Link "Anfahrt und Kontakt" · Text Hero → Image Hero |
| Formatting | the same words carry different formatting | "18 Uhr," · no formatting → bold |
| Value | a property changed, shown with its editor labels | Abstand unten · Groß → Klein |
| Position | a node was reordered among its siblings | Position · 3 of 3 → 1 of 3 |
| Image, Asset | media was replaced | the published and the new file side by side |
| Visibility | a node was hidden or made visible | Element was hidden |
| Note | nothing renderable changed | an explanation of the remaining difference |
The card header names the element type next to the node label, because the label is usually the element’s own text ("Minimalismus" alone does not say that a headline was hidden), and shows whether the node was created, deleted, moved or hidden. Only changed words are marked in the card body.
The sidebar shows the changed pages in the order and nesting of the page tree, with the node type icons, indentation and colours of the backend page tree; changed pages carry its orange "unpublished changes" edge. An unchanged page between the site and a changed subpage is listed greyed and without a link, so the structure stays readable. Click a page to jump to its changes, or scroll the review normally with the mouse wheel. The sidebar follows the current page without moving keyboard focus. On smaller screens the index appears above the review.
Each content-dimension variant has its own destination. Links also work as ordinary anchors when JavaScript is disabled.
Within a page, the change list follows the content tree's sorting order. A changed container appears before its changed children, and those children stay together before the next sibling. This makes the list easier to follow alongside the page. Custom layouts can render content in a different visual order.
Every page header has a Reviewed toggle. Marking a page collapses its changes, greys its title, shows a green check in the sidebar and advances the progress bar in the sidebar header ("2/7 reviewed"). The chevron still expands a reviewed page for a second look without clearing the mark.
Reviewed marks are stored in the browser per workspace, together with a signature of the page's changes (which nodes, last modified when). If that signature changes after review, the mark is dropped on the next load and the page carries a "Changed since your review" badge. Hovering or focusing the badge explains what happened.
These marks are personal progress notes in that browser, not a shared approval workflow. They do not select changes for publishing or discarding, and they do not prevent either action. If browser storage is unavailable, progress lasts only for the current page view.
Use the switch above the review stream, or press D, to toggle between the change list and visual compare shown in the second screenshot. Visual compare renders the reviewed workspace in the site's own layout and adds status labels and outlines to changed elements.
| Marker | Meaning |
|---|---|
| Green | Created element |
| Orange | Changed element |
| Blue | Moved element |
| Hatched grey | Hidden element that is present in the preview |
| Red overlay | Deleted element, restored from the published rendering for review |
Text edits appear in place when the new wording can be matched to a text element on the rendered page. Deleted words are struck through and additions underlined, using the same word diff as the change list. The preview combines the workspace rendering with review annotations, so deleted words and restored deleted elements will not appear on the published page.
Deleted elements are placed next to a surviving neighbour from the published page. If no neighbour survives, they appear in a separate section at the end of the page. New pages have a green banner. Deleted pages have a red banner and show their currently published content.
Click a marker's status label to switch to the corresponding change card, which receives focus and is briefly highlighted. Changes with no visible matching element, such as document properties, custom-rendered content or content inside a closed accordion, are listed below the frame with links to their cards. The change list also provides the full details for configuration, links and formatting.
Each preview scrolls independently. Link navigation and form submission are blocked inside it. Pages load as they approach the visible area, and the browser remembers the selected view.
Press ? anywhere in the module, or use the button at the bottom of the sidebar, for an overview of all shortcuts. Shortcuts apply while nothing or a page, change or sidebar entry has focus; they stay off inside form controls and buttons, and modifier combinations are left to the browser.
| Keys | Action |
|---|---|
| ↓ / J, ↑ / K | next / previous page |
| Home / End | first / last page |
| Enter | focus the page in the review stream (from the sidebar) |
| ] / [ | next / previous change on the page; [ on the first change returns to the page heading. In the visual compare the keys walk the markers on the page. |
| V | mark the page as reviewed / not reviewed |
| D | switch between change list and visual compare |
| Esc | from a change back to the page heading, from the page back to its sidebar entry, or close the overlay |
Without a focused element the keys act on the page currently shown at the top of the stream.
Text is compared word by word. An unchanged run longer than two dozen words collapses to the words surrounding the edit plus an ellipsis.
Deleted words are struck through and added words underlined in addition to their red and green colouring, so the kind of change stays readable with a colour vision deficiency, in greyscale and in forced-colours mode. Screen readers hear “deleted:” or “added:” before each edited run, because <del> and <ins> alone are not reliably announced.
HTML entities are decoded before comparison and in labels, so titles show & instead of the literal entity &.
Select boxes and toggles use translated editorOptions.values labels, such as “Kein Abstand” instead of none. Booleans render as Yes or No, and references render as node labels.
A property changed back to its NodeType default is still reported instead of being silently omitted.
A text comparison cannot see markup-only edits. Rich text is therefore compared a second time to detect links and formatting while keeping the findings conservative.
- A link points somewhere else. Internal
node://andasset://targets resolve to page and asset names instead of exposing UUIDs. - A link behaves differently. A new-window change reads
Same tab → New tab. - Words were linked or unlinked. This is reported when the surrounding wording is unchanged. When the text also changed, the text diff owns the explanation.
- Formatting changed. Bold, italic, underline, strikethrough, subscript, superscript, code, highlight and heading-level changes are reported per passage.
When two targets resolve to the same label, their raw URIs remain visible so the result never claims X → X.
A moved node shows its place among siblings, such as 3 of 3 → 1 of 3, instead of an internal sorting index such as 100 → 150.
An index changed only by sibling renumbering is identified as internal re-sorting. Created, deleted, moved and hidden nodes receive explicit, correctly translated badges.
The review uses three distinct notes when it cannot show a normal value comparison:
- “Changed – the wording is unchanged, please check details in the preview.” The stored value differs, but the visible words do not. The property name tells the reviewer where to look.
- “Edited, but matching the published version again – no content differences found.” The node was changed and then restored. Publishing it has no effect.
- “No visible changes (internal update).” The node differs, but the change cannot be attributed to a renderable field.
These distinctions help reviewers decide whether they need to open the preview or can safely discard a reverted change.
Configuration/Settings.yamlreplaces the controller ofmanagement/workspaceswithCodeQ\WorkspaceReview\Controller\Module\Management\WorkspacesController.- The controller extends the Neos core controller, builds readable diffs, groups documents in page-tree order and sorts each document's changes by their content-tree positions.
Resources/Public/JavaScript/PageNavigation.jshandles sidebar navigation, keyboard shortcuts, browser-local review progress and visual compare. Review signatures combine node paths with modification timestamps.Classes/Controller/PreviewController.phprenders pages throughneos/workspace-review/preview. The route requires backend access, checks read access to private workspaces, sendsCache-Control: no-storeand disables the Fusion content cache.Resources/Private/Fusion/Root.fusionadds node identifiers toNeos.Neos:ContentComponentandNeos.Neos:Contentin the review preview and suppresses Neos.Ui editing scripts there. Custom rendering that bypasses these prototypes has no marker and is listed below the preview when it cannot be located. Public page rendering is unaffected.Classes/Diff/RichTextDiffer.phpparses markup with\DOMDocument, pairs links by identity and label, and compares formatting word by word.- The rich-text differ has no injected dependencies or content-repository knowledge. The controller resolves labels, references and translations.
- The controller points the view at this package's templates and at the Neos layouts in
initializeView(). Flow applies only the single heaviest-matchingViews.yamlentry rather than merging them, so another package overriding the same module could otherwise leave the module without a layout root. Configuration/Policy.yamlgrants the inherited controller actions after the controller replacement. Without it, the module's method-based privilege matching would return 403.- Only
Show.html,ContentChangeDiff.htmlandDocumentBreadcrumb.htmloverride core templates.
ddev exec bin/phpunit --configuration Build/BuildEssentials/PhpUnit/UnitTests.xml \
DistributionPackages/CodeQ.WorkspaceReview/Tests/Unit
