Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,99 @@ All notable changes to this project will be documented here.

The project follows [Semantic Versioning](https://semver.org/).

## 0.1.0-beta.5 - 2026-08-06

### Added

- Added a composed demo page that renders through a shared layout and reusable
components while sourcing editable values from direct JSON imports and an
Astro Content Collection entry.
- Added an optional, local-only demo-page switcher so testers can move between
the original single-file fixture and the composed-source fixture.
- Added visible structured source paths and a shared-route warning before an
affected value is queued for editing.
- Added browser coverage that reviews, commits and safely restores a JSON
property and Markdown frontmatter field in one multi-file batch.
- Added syntax-aware project source discovery for exact Astro,
Markdown/frontmatter, JSON/JSONC and YAML values, including separate
stale-checked locators for repeated Astro literals.
- Added owner-confirmed section setup that maps existing rendered containers to
contiguous Astro children or complete JSON/JSONC and YAML arrays without
requiring editor annotations in site components.
- Added policy-backed section mappings with exact source-item hashes, reviewed
policy persistence and safe refresh after source order changes.
- Added browser coverage that enables four mixed component/element blocks on
the complex fixture, reorders them, commits the exact Astro diff and restores
the source.
- Added nested region coverage that enables a component's direct text and
button children independently, reorders them alongside a parent-page move,
saves both files atomically and restores both sources.
- Added a hierarchical on-page region chooser that exposes the smallest area,
parent areas and whole page from one click, including rendered cards backed
by JSON or YAML arrays.

### Changed

- Simplified the workbench around three editing modes and one compact
**Changes (N)** tray, keeping review, undo, redo, history, discard and restore
available without presenting review as a competing editing mode.
- Made owner Settings start with matching on-page selectors for text and
sections. Full searchable text and section management now stays behind
explicit progressive-disclosure controls.
- Separated the neutral editor surface and demo-only route switcher from the
host page, reserved orange for primary save actions and limited page outlines
to the currently hovered or selected item.
- Renamed ambiguous source-control language to owner-facing save, discard and
restore actions while preserving exact diffs and collapsed technical detail.
- Made each queued-change receipt lead with the affected page, a concise
owner-readable description and visible before/after content. Exact source
paths, selectors, hashes and complete structural order remain under collapsed
technical details.
- Kept syntax-aware adapters and confirmed project mappings as the final
authority. Ambiguous, transformed or non-reversible data flow is refused
rather than guessed or globally replaced.
- Made explicitly mapped structured fields use their file and source path as a
stable preview target, so repeated component output is edited independently.
- Kept Sections mode open with a clear explanation when the current page has
no declared source-owned section region.
- Made section-region setup visible in the owner Settings screen alongside
text permissions, with current-route mappings and reviewed removal controls.
- Preserved draft section mappings when text permission rules are added or
removed, so independent setup changes cannot discard one another.
- Replaced developer-facing section and text setup with one consistent visual
picker: readable labels, on-page hover highlighting and click selection.
- Moved syntax paths and file candidates behind collapsed technical details,
automatically accepting only a uniquely scored structural source match.
- Matched parsed Astro literal nodes before considering quoted-value fallback,
preventing identical words in annotations from causing false ambiguity.
- Made the section picker describe its direct-child boundary and allow an
inner component region to be enabled separately, with readable Heading,
Text, Button, Card and Section control labels.

### Fixed

- Made delegated Astro SEO fields update unique literal route props when a
layout owns the rendered `<head>`, while refusing missing, computed or
ambiguous prop matches.
- Exposed every SEO field shown by the complex fixture through its layout, so
keywords, canonical, social metadata and robots changes all have a safe,
explicit source target at final review.
- Matched structured-array section candidates against the ordered rendered
values before enabling card reordering, preventing unrelated equal-length
arrays from being selected.
- Stopped section setup from silently accepting an unrelated source structure
merely because its container had the same number of children.
- Added composed-page regressions for delete and Undo, guarded section-source
confirmation, and one atomic JSON, Markdown frontmatter and SEO batch.
- Prevented generated section metadata from becoming persistent CSS selectors
or biasing nested source discovery toward the parent page file.
- Kept section controls outside the host element so headings, paragraphs and
buttons remain valid HTML while their source-owned region is being edited.
- Added a bounded source-preview timeout that preserves queued changes and
offers a safe retry instead of leaving the editor on “Checking source files”.
- Aligned source-discovery and save-time hashes for multiline Astro closing
tags, preventing a valid nested reorder from being rejected or malformed.

## 0.1.0-beta.4 - 2026-08-05

### Added
Expand Down
128 changes: 99 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@ a wider website project, see the <a href="https://opace.agency/services/web-desi
> **Release status:** install the current published version from npm. The
> [changelog](./CHANGELOG.md) records released changes.

| At a glance | Behaviour |
| --------------- | --------------------------------------------------------------------------------- |
| Editing surface | Astro's native development toolbar on the rendered page |
| Content | Literal Astro/Markdown text plus explicitly mapped JSON, JSONC and YAML fields |
| Page structure | Add, delete and reorder sections inside declared source-owned regions |
| Review | One mixed text, SEO and section ledger with undo, redo, commit and guarded revert |
| Safety boundary | Local development only; no editor client or write endpoint in production |
| At a glance | Behaviour |
| --------------- | ------------------------------------------------------------------------------ |
| Editing surface | Astro's native development toolbar on the rendered page |
| Content | Discovered or mapped Astro/Markdown, JSON/JSONC and YAML values |
| Page structure | Enable and reorder Astro children or complete JSON/YAML arrays safely |
| Changes | One mixed text, SEO and section tray with undo, redo, save and guarded restore |
| Safety boundary | Local development only; no editor client or write endpoint in production |

## What it does

- Click rendered text and preview a replacement in place.
- Edit title, description, keywords, canonical URL, Open Graph fields and
robots directives in one SEO form.
- Add, delete and reorder declared sections.
- Enable existing page regions, then add, delete or reorder supported sections.
- Reorder with real pointer drag-and-drop, move buttons or keyboard controls.
- Add sections from a reusable template registry.
- Queue mixed text, SEO and structural changes in one visible ledger.
Expand Down Expand Up @@ -132,25 +132,37 @@ toolbar and select **Astro Visual Editor**.
Suggested review path:

1. In **Text**, edit the hero heading and queue it without committing.
2. Undo and redo the queued preview, then inspect the Review ledger.
2. Undo and redo the queued preview, then open the **Changes** tray.
3. In **Sections**, drag a card by its handle, use a move button, insert a
template and undo the structural changes.
4. In **SEO**, change a field and inspect its queued preview.
5. Commit only when you intentionally want to modify `demo/src/pages/index.astro`;
use **Revert last commit** immediately afterwards to test safe restoration.
6. Narrow the viewport to exercise compact Pick mode and the Review sheet.
use **Restore previous save** immediately afterwards to test safe restoration.
6. Narrow the viewport to exercise compact Pick mode and the Changes sheet.
7. Use **Complex sources** in the workbench to open `/fixtures/complex`. Queue
the shared JSON hero title and the Content Collection title, review the
two-file diff, then commit and restore it from History. The fixture proves
layout, component, JSON and Markdown-frontmatter ownership in one route.

Port 4322 is only a documented demo choice; any free loopback port works. Source
writes are disabled by default when the dev server is exposed beyond loopback.

## Editor modes

### Editability Setup
### Editor Setup

Choose the settings control in the workbench to open the owner-only setup mode.
On desktop the inventory docks beside the page so the content remains visible.
It lists each visible text item with its status, plain-language reason, owning
source file and structured path where known.
On desktop the settings panel docks beside the page so the content remains visible.
It exposes matching **Sections** and **Text** on-page selectors. Full searchable
text and section inventories remain collapsed under **Manage all text** and
**Manage sections** until needed. Section mappings
map existing page containers to syntax-aware source structures for safe
reordering, while the text inventory lists each visible item with its status,
plain-language reason, owning source file and structured path where known.
Both use the same non-technical interaction: choose a readable item from the
list or select it directly on the page with hover highlighting. Source
verification runs behind that visual choice; technical paths stay collapsed
unless ambiguity requires explicit confirmation.

An owner can allow or block one stable element or an appropriate element group,
such as all `<strong>` labels on the current route. Choosing a permission opens
Expand All @@ -162,6 +174,12 @@ project-relative source file and optional structured path. Every change is
shown as an exact diff before it is written to
`astro-visual-editor.policy.json`.

Choosing **Find source and allow editing** searches supported project files with Astro,
Markdown/frontmatter, JSON/JSONC and YAML parsers. It lists every exact
candidate with its line and structured path. The owner confirms one candidate,
and repeated Astro literals remain separate stale-checked nodes instead of
being changed by a global string replacement.

An allow rule only makes an element selectable. It does not override explicit
`data-astro-edit-ignore` exclusions, unsafe nested structure, missing source
ownership or syntax-aware adapter validation.
Expand All @@ -175,8 +193,23 @@ Keyboard users can focus editable content and press `Alt+Enter`.

### Sections

Section persistence is deliberately explicit. Declare a source-owned region and
give every direct section a stable ID:
An owner can choose **Select section on page** or **Choose from list**. After
clicking page content, the editor shows every valid nesting level under that
point: the smallest reorderable area, its parent areas and the whole page.
Each level can be enabled independently, so a page's major sections and the
cards, headings, text or buttons inside them can all be maintained. The editor
automatically uses a unique structural source match and stores a project policy
mapping without adding editor attributes to the site's components. The candidate
supports contiguous Astro component/element children and complete JSON/JSONC
or YAML arrays. Structured arrays must also match the visible values in order,
and every complete item is hash-checked before reordering.

Each enabled container moves only its direct items. Parent and inner regions
can be reordered in the same reviewed multi-file save while each remains tied
to its own source.

Projects can also declare a source-owned region directly and give every section
a stable ID:

```astro
<div data-astro-edit-region="homepage-sections" data-astro-edit-file="src/pages/index.astro">
Expand All @@ -186,7 +219,7 @@ give every direct section a stable ID:
</div>
```

The editable sections must be contiguous direct children of the region. This
Declared editable sections must be contiguous direct children of the region. This
lets the Astro adapter preserve each complete source block while safely
reordering, deleting or inserting it.

Expand Down Expand Up @@ -231,20 +264,32 @@ result is compiled before writing. For `.md` and `.mdx` owners, standard YAML
frontmatter fields are updated. Length guidance is editorial and non-blocking;
canonical URLs must be complete HTTP(S) URLs.

### Review
If the route delegates metadata to a layout, expose every editable field as a
unique literal route prop: `title`, `description`, `keywords`, `canonical`,
`ogTitle`, `ogDescription` and `robots`. Missing, computed or ambiguous props
are refused rather than guessed against a shared layout.

The ledger groups all queued changes by type and source file. It supports:
### Changes

The compact **Changes (N)** tray shows the affected page first, followed by a
plain-language description of the visible change. Exact source files, fields,
selectors and complete before/after values remain available under
**Technical details**. It supports:

- remove one change;
- undo/redo the last queued operation;
- clear all;
- a single validated commit;
- idempotent retry if the response is delayed;
- a bounded source check that keeps the queue intact and offers a retry if the
development connection stops responding;
- revert the latest successful receipt while its files remain unchanged.

Structural rows use semantic summaries such as **Reordered 3 sections** or
**Added 1 section**, followed by labelled Before/After region order, so a drag
operation is reviewable before commit.
**Added 1 section**, followed by readable section names instead of internal
section identifiers. Long text edits show the changed fragment rather than two
apparently identical truncated values. The final save review still presents
the exact source diff before anything is written.

Shortcuts: `Cmd/Ctrl+S` saves, `Cmd/Ctrl+Z` undoes and
`Cmd/Ctrl+Shift+Z` or `Cmd/Ctrl+Y` redoes.
Expand Down Expand Up @@ -287,6 +332,23 @@ For Markdown frontmatter:
Unstructured MDX body edits are refused because replacing text across expression
boundaries cannot yet be proven safe.

When a project renders an explicit source annotation from a direct import or
Content Collection field, it can also declare the provenance and known shared
routes. The editor shows this context before an edit is queued:

```astro
<h1
data-astro-edit-file="src/data/homepage.json"
data-astro-edit-path="hero.title"
data-astro-edit-origin="a direct JSON import"
data-astro-edit-shared-routes="/,/pricing"
>
{homepage.hero.title}
</h1>
```

These hints explain a supported mapping; they never bypass adapter validation.

### Selector mappings

```js
Expand Down Expand Up @@ -331,7 +393,7 @@ Exclude content with:
<p data-astro-edit-ignore>Managed externally.</p>
```

The visual Editability Setup adds project-owned policy on top of these explicit
The visual Editor Setup adds project-owned policy on top of these explicit
annotations. Source exclusions remain authoritative, and saved rules are
visible in Git rather than being hidden in browser storage.

Expand Down Expand Up @@ -370,6 +432,7 @@ interface AstroVisualEditorOptions {
selectorMappings?: Record<string, string>;
allowedExtensions?: Array<'.astro' | '.md' | '.mdx' | '.json' | '.jsonc' | '.yaml' | '.yml'>;
sectionTemplates?: SectionTemplate[];
demoPages?: Array<{ id: string; label: string; path: string; description: string }>;
maxChanges?: number; // 100
maxTextLength?: number; // 10,000
maxRequestBytes?: number; // 1,000,000
Expand All @@ -384,6 +447,11 @@ interface AstroVisualEditorOptions {
}
```

`demoPages` is optional and local-only. It is intended for a project's test
fixture, not application navigation. When two or more entries are supplied,
the workbench displays a compact route switcher and warns before leaving queued
preview changes.

`allowUnsafeSourceText` permits raw markup insertion into literal Astro text
nodes. It is an expert escape hatch and remains off by default. The normal mode
HTML-escapes structural characters so they remain visible text.
Expand All @@ -400,8 +468,8 @@ even if ordinary source writes were explicitly enabled with `allowRemoteDev`.
## Mobile and accessibility

At phone widths, enabling the editor opens compact Pick mode rather than the
full workbench. Page content remains tappable, while **Review N** opens a bottom
sheet containing every mode and commit state.
full workbench. Page content remains tappable, while **Review N** opens the
Changes sheet with the complete mixed ledger and save state.

Implemented accessibility behaviour includes:

Expand Down Expand Up @@ -440,10 +508,12 @@ Astro integration (development command only)
## Current boundaries

- It is a local development editor, not an authenticated production CMS.
- Source ownership still requires annotations/mappings for component props,
shared data and non-conventional routes.
- It does not rewrite arbitrary Astro component structure; section operations
require declared, contiguous regions with stable IDs.
- Exact source values can be discovered across supported formats, but
transformed output that cannot be reversed to one source target still
requires owner confirmation or is refused.
- Section Setup maps contiguous Astro children and complete JSON/JSONC or YAML
arrays. It does not infer Markdown heading groups or filtered, merged and
transformed subsets.
- Existing sections cannot be moved across unrelated regions, and templates
cannot be dropped onto arbitrary page locations.
- It does not infer every import/data dependency or provide a full schema-aware
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Security fixes are applied to the latest published version.
## Reporting a vulnerability

Please do not open a public issue for a suspected vulnerability. Email
`security@opace.co.uk` with a minimal reproduction and impact description.
`info@opace.co.uk` with a minimal reproduction and impact description.

## Security model

Expand Down
15 changes: 15 additions & 0 deletions demo/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,25 @@ export default defineConfig({
editabilityRole: 'owner',
fileMappings: {
'/': 'src/pages/index.astro',
'/fixtures/complex': 'src/pages/fixtures/complex.astro',
},
selectorMappings: {
'[data-demo-banner]': 'src/pages/index.astro',
},
demoPages: [
{
id: 'simple',
label: 'Simple demo',
path: '/',
description: 'The original single-page Beta 4 fixture.',
},
{
id: 'complex',
label: 'Complex sources',
path: '/fixtures/complex',
description: 'A layout, components, JSON data and Content Collection fixture.',
},
],
}),
],
});
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev": "astro dev"
},
"dependencies": {
"@opacedev/astro-visual-editor": "0.1.0-beta.4",
"@opacedev/astro-visual-editor": "0.1.0-beta.5",
"astro": "^7.1.6"
},
"devDependencies": {
Expand Down
Loading