Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
144 commits
Select commit Hold shift + click to select a range
da474bd
Start iterating on a common content model
michael May 31, 2026
7f1b4c7
Remove colorset props
michael May 31, 2026
77c6f09
Compact
michael May 31, 2026
b2d62bd
Introduce DescriptiveGallery
michael May 31, 2026
9b1de8f
Add disclaimer
michael May 31, 2026
af29601
Document Hero and SupportingMedia
michael May 31, 2026
8b8a596
Add type index
michael May 31, 2026
3e7bfb0
Improve index
michael May 31, 2026
2ec6f80
Improve index
michael May 31, 2026
e88d404
Improved styling
michael May 31, 2026
f0ce3e1
LinkCollection -> DescriptiveGallery
michael Jun 1, 2026
f2f6f50
Simplify vertical block spacing
michael Jun 1, 2026
fd78344
Allow saving in static mode
michael Jun 1, 2026
71614d5
Remove intro/outro for gallery
michael Jun 1, 2026
7059174
Remove intro/outro of Gallery node
michael Jun 1, 2026
6052fa1
Fix padding
michael Jun 1, 2026
1ed908e
Linting
michael Jun 1, 2026
3e8e881
Add listing content type
michael Jun 1, 2026
e672214
Add DescriptiveList
michael Jun 1, 2026
e10fbf2
Introduce Accordion node type
michael Jun 2, 2026
133511e
Add chevron and enable text interaction
michael Jun 2, 2026
cd813e7
Polish
michael Jun 2, 2026
06197a2
UX improvements for accordion
michael Jun 2, 2026
c0e3df2
Ability to toggle accordion details with TAB in edit mode
michael Jun 2, 2026
36f6fc3
Make surface around chevron in accordion receive pointer events
michael Jun 2, 2026
498973a
Proper example content for listing and accordion
michael Jun 2, 2026
61b68d3
Better spacing
michael Jun 2, 2026
2aa334c
Dead code elimination
michael Jun 2, 2026
82e589e
Fixes
michael Jun 2, 2026
ce6cac0
Spec for MediaHero
michael Jun 2, 2026
78e8456
Implement MediaHero
michael Jun 2, 2026
4ec655c
Stars
michael Jun 3, 2026
5b63272
Add logo grid layout to Gallery
michael Jun 4, 2026
83a85f7
Render title underline in edit mode too
michael Jun 4, 2026
c1cc376
Add list node type
michael Jun 4, 2026
9ce008c
Add List to Common Content Model
michael Jun 4, 2026
e7fd703
Decoration -> SupportingMedia
michael Jun 4, 2026
e242316
Get rid of extra cursor position in lists
michael Jun 4, 2026
b8b8fb5
Cleanup
michael Jun 5, 2026
887b63e
Implement range requests for videos
michael Jun 5, 2026
3cd2573
Always use sizes="auto"
michael Jun 5, 2026
c44f8d0
Implement ButtonGroup
michael Jun 5, 2026
f557b68
Order node_types list by importance
michael Jun 6, 2026
86603fa
Introduce center-oriented layout for Prose
michael Jun 6, 2026
bff8b31
Improve MediaHero
michael Jun 6, 2026
421356d
Fix is_centered check
michael Jun 6, 2026
5c3869e
Style adjustments
michael Jun 6, 2026
0d10dc0
Add CaptionedFigure
michael Jun 6, 2026
e6b8e3a
Text sizing
michael Jun 7, 2026
5bfc17d
Upgrade Svelte+Kit and get rid of svelte.config.js
michael Jun 7, 2026
335c333
Introduce ProseGrid
michael Jun 7, 2026
2722347
Update demo_doc.js
michael Jun 7, 2026
4e93a84
Wording
michael Jun 7, 2026
1b62dbb
Fix regressions
michael Jun 7, 2026
c1191ad
Add Preformatted node type
michael Jun 7, 2026
1dfc85b
Disable the grid in .body-node-array
michael Jun 7, 2026
8856306
Fix
michael Jun 9, 2026
39ec13d
Simplify and fix create new page flow
michael Jun 9, 2026
35b287f
Explicit text types (#122)
michael Jun 15, 2026
4c8147d
Easier to use typography system using @apply (#124)
michael Jun 19, 2026
7e44ac1
Bugfix
michael Jun 19, 2026
d029252
Make layout of DescriptiveListingItem more robust
michael Jun 20, 2026
b0a9fc9
Only pulse cycle layout when pointing to same node as cycle node type
michael Jun 20, 2026
b1485ba
New layout options for Figure to allow some variation
michael Jun 20, 2026
89defde
Introduce 2 column layout for listing
michael Jun 20, 2026
cbe6eb4
Different layout for DescriptiveListing
michael Jun 20, 2026
410027b
Change Figure default aspect ratio
michael Jun 20, 2026
cd6b2cd
Introduce Accordion layouts
michael Jun 20, 2026
c49c700
Use consistent markup for all Figure layouts
michael Jun 20, 2026
8df54fd
2:1 ratio for CaptionedFigure
michael Jun 20, 2026
d881c2f
Consistent markup for Prose Layout 3
michael Jun 20, 2026
bd06be0
Get rid of full bleed Feature layouts
michael Jun 20, 2026
e591538
Tighter, more consistent spacing
michael Jun 20, 2026
44d77c9
Improve spacing
michael Jun 20, 2026
12e1419
Better layout and more balanced typography
michael Jun 20, 2026
eaef812
y-spacing
michael Jun 20, 2026
6f79b23
3 column nav (#126)
michael Jun 23, 2026
a94a443
Introduce section annotation for body node arrays
michael Jun 23, 2026
e164c0b
Use new value syntax for node_arrays everywhere
michael Jun 24, 2026
0a0ab03
Fixes
michael Jun 24, 2026
4fae1d6
Introduce ToggleSectionCommand and tool
michael Jun 24, 2026
fc9e444
Utilize new annotations API that works on nodes too, not just on text
michael Jun 24, 2026
2117d73
Use new data shape of node arrays that take annotations
michael Jun 24, 2026
93ad28f
Add additional top spacing for Prose whent it is at the start of a
michael Jun 24, 2026
450a160
Render section brackets
michael Jun 27, 2026
a4ff756
Conditional top and bottom padding for Prose
michael Jun 27, 2026
5712b20
Conditional section spacing
michael Jun 27, 2026
26a0ac2
Update headline
michael Jun 27, 2026
13245e0
Improve section marker alignment
michael Jun 27, 2026
f55c6e7
Group body nodes into sections
michael Jun 27, 2026
073f043
New node shape
michael Jun 27, 2026
506a65c
`annotated_text` is now just `text`
michael Jun 29, 2026
d6482e6
Migrate `text.text` to `text.content`
michael Jun 29, 2026
5b8e9af
session.active_annotation is now a derived value
michael Jun 30, 2026
f4f69c1
Generalize footer content
michael Jun 30, 2026
b5297f5
Style tweaks
michael Jun 30, 2026
704a760
More footer content generalization
michael Jul 1, 2026
ed22ad5
Rename FooterColumnLabel to FooterLinkCategory
michael Jul 1, 2026
b34f4fa
Split NavItem into NavButton and NavLink
michael Jul 1, 2026
1c66467
Consistent nav button padding
michael Jul 1, 2026
cf98fe6
Add support for inline code
michael Jul 1, 2026
22a2148
Layout 2 for DescriptiveGallery
michael Jul 1, 2026
e41fd09
Consistent property names
michael Jul 1, 2026
c6675e6
Update demo doc
michael Jul 2, 2026
b3027a7
Upgrade to latest Svedit API of `annotation` prop for node array child
michael Jul 3, 2026
7661bbf
Update demo_doc.js
michael Jul 4, 2026
bffd7ee
Update COMMON_CONTENT_MODEL.md
michael Jul 4, 2026
dfc5ea1
Fix inserter for Prose
michael Jul 4, 2026
7cdf318
Document all text types
michael Jul 4, 2026
5053326
Make child node definitions explicit
michael Jul 4, 2026
a0b6681
Rename to Editable
michael Jul 4, 2026
a0206b8
Cleanup annotation groups
michael Jul 4, 2026
908a560
Get rid of colorset concept and cleanup
michael Jul 4, 2026
30cf144
Expand media defaults
michael Jul 4, 2026
8cbf0e1
Fix copy in README
michael Jul 4, 2026
c8c856b
New color variables for --muted, --muted-foreground and --border
michael Jul 5, 2026
9fa24aa
Make use of shared color variables in Toolbar
michael Jul 5, 2026
7f0cae2
Consistent drop shadows
michael Jul 5, 2026
a77c568
Add dark theme example
michael Jul 5, 2026
dcf7142
Improve Feature x spacing
michael Jul 5, 2026
c97dde5
Improve page metadata display
michael Jul 5, 2026
46271ab
New footer structure
michael Jul 5, 2026
642b1a0
Use --background color for secondary button
michael Jul 5, 2026
dd085fe
Include secondary button in button group
michael Jul 5, 2026
6e9fc58
Make page metadata editor more compact
michael Jul 5, 2026
aa8366c
Upgrade to new shape with { marks, annotations, ... }
michael Jul 6, 2026
eca1de4
Update default doc copy
michael Jul 6, 2026
7a3bb12
Improve page metadata extraction
michael Jul 7, 2026
88f9729
Invalidate site metadata on save
michael Jul 7, 2026
9bee3d8
Shorter nav images
michael Jul 7, 2026
599bed1
Explicit page titles
michael Jul 7, 2026
83fb18c
Ensure Asset API endpoints are authenticated
michael Jul 7, 2026
b5aa861
Protect the single admin password against online brute force:
michael Jul 7, 2026
1f4e377
Validate document before saving
michael Jul 7, 2026
f81934a
Serve assets inert and verify content hashes on upload
michael Jul 7, 2026
250f023
Extract shared with_transaction helper
michael Jul 7, 2026
5e98baf
Consolidate media type constants and error statuses
michael Jul 7, 2026
a57f683
Renew admin sessions at most once per day
michael Jul 7, 2026
6a1c0df
Start asset grace period at dereference, not upload
michael Jul 7, 2026
6f0bee0
Update demo doc
michael Jul 8, 2026
fb3a47c
Update README.md
michael Jul 8, 2026
543bdd5
Upgrade to Svedit 0.12.0
michael Jul 8, 2026
6aea341
Upgrade package-lock.json
michael Jul 8, 2026
b327f0c
Update @sveltejs/adapter-node to fix broken deployments
michael Jul 10, 2026
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
167 changes: 100 additions & 67 deletions ARCHITECTURE.md

Large diffs are not rendered by default.

475 changes: 475 additions & 0 deletions COMMON_CONTENT_MODEL.md

Large diffs are not rendered by default.

329 changes: 323 additions & 6 deletions IMPLEMENTATION_PLAN.md

Large diffs are not rendered by default.

31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Editable Website v2
# Editable

The next generation of Editable Website — a complete rewrite designed to make Svelte developers happy.
Editable brings live on-page editing to Svelte. Site owners edit directly in the layout; developers keep full control with ordinary Svelte components.

> **NOTE:** v2 is a complete rewrite using [Svedit](https://github.com/michael/svedit). It's under active development — feel free to explore locally, but hold off on production deployments for now.

## Who is Editable Website for?
## Who is Editable for?

For anyone who enjoys creating websites using Svelte and wants to allow their clients to make edits directly in the layout as they browse their site.

Expand Down Expand Up @@ -59,14 +57,21 @@ Next, you probably want to adjust the colors and fonts in [app.css](./src/app.cs
:root {
--background: oklch(0.98 0 0);
--foreground: oklch(0 0 0);
--muted: oklch(1 0 0);
--border: oklch(0.88 0 0);
--muted-foreground: oklch(0.55 0 0);
--accent: oklch(0.21 0.034 264);
--accent-foreground: oklch(0.98 0 0);
}

@theme {
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
--font-serif: 'Libertinos Serif Display', ui-serif, Georgia, serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
monospace;
--shadow-sm: 0 2px 4px -1px oklch(0 0 0 / 0.12), 0 1px 2px -1px oklch(0 0 0 / 0.12);
--shadow-xl: 0 20px 25px -5px oklch(0 0 0 / 0.12), 0 8px 10px -6px oklch(0 0 0 / 0.12);
}
```

Expand All @@ -76,7 +81,7 @@ However, likely you'll want to customize more than that. E.g. edit [Button.svelt

## Manual

For detailed documentation on building with Editable Website see [MANUAL.md](./MANUAL.md).
For detailed documentation on building with Editable see [MANUAL.md](./MANUAL.md).

## Deploy

Expand All @@ -91,6 +96,8 @@ fly secrets set -a my-editable-website \
ADMIN_PASSWORD='change-me'
```

Optionally set `ASSET_GRACE_PERIOD_DAYS` (default 7): unreferenced asset files are kept on disk this many days after losing their last reference. This is also the safe window for rolling back a database backup against the live assets folder without ending up with dead image references.

```
fly deploy -a my-editable-website --primary-region fra --vm-size shared-cpu-1x --vm-memory 256 --volume-initial-size 1
```
Expand All @@ -117,23 +124,23 @@ All content lives in a single `data/` directory — an SQLite database (`db.sqli

### How about AI?

Editable Website is a foundational, AI-agnostic tool. That said, it makes perfect sense to utilize AI workflows to help building your custom site. Think prompts like "Create a hero block type with title + description and optional CTA buttons" and "Implement Hero.svelte with 5 distinct layout variations".
Editable is a foundational, AI-agnostic tool. That said, it makes perfect sense to utilize AI workflows to help building your custom site. Think prompts like "Create a hero block type with title + description and optional CTA buttons" and "Implement Hero.svelte with 5 distinct layout variations".

### Plugins?

Editable Website is modular and you can and should reuse code across projects. However, I purposely don't want to establish a community maintained plugin repository. I want to encourage you to own all your code, for the benefit of simplicity, safety, and control. Share code snippets, not plugins.
Editable is modular and you can and should reuse code across projects. However, I purposely don't want to establish a community maintained plugin repository. I want to encourage you to own all your code, for the benefit of simplicity, safety, and control. Share code snippets, not plugins.

### Hosting?

Editable Website runs on any VPS. All you need is Node.js and SQLite. The repository includes a `Dockerfile` and `fly.toml` for one-command deployment to [Fly.io](https://fly.io) — see [Deploying to Fly.io](#deploying-to-flyio) above. The same Dockerfile works with any platform that supports Docker.
Editable runs on any VPS. All you need is Node.js and SQLite. The repository includes a `Dockerfile` and `fly.toml` for one-command deployment to [Fly.io](https://fly.io) — see [Deploying to Fly.io](#deploying-to-flyio) above. The same Dockerfile works with any platform that supports Docker.

### Static builds?

There's no point for static builds with Editable Website. The whole idea is that users edit content live, without having to wait for a rebuild to finish. SQLite is fast. Very fast. Web-optimized images are generated client-side before upload: resizing happens in the browser via canvas and `toBlob()`, and WebP encoding is done with `@jsquash/webp`. It still makes sense to enable a proxy for images, so they can be delivered from a CDN.
There's no point for static builds with Editable. The whole idea is that users edit content live, without having to wait for a rebuild to finish. SQLite is fast. Very fast. Web-optimized images are generated client-side before upload: resizing happens in the browser via canvas and `toBlob()`, and WebP encoding is done with `@jsquash/webp`. It still makes sense to enable a proxy for images, so they can be delivered from a CDN.

### License?

Editable Website will at least be source-available. There will likely be an affordable one-time registration fee (per domain) for personal use, and a fair fee for commercial projects. I'm still working on the details. If you’re open to discussion, join the [technical preview](https://docs.google.com/forms/d/e/1FAIpQLSfkL9e9X3Lcn6oBDIG-gU4yrfSenh8fndupbIX7zkyxX3X9ZQ/viewform).
Editable will at least be source-available. There will likely be an affordable one-time registration fee (per domain) for personal use, and a fair fee for commercial projects. I'm still working on the details. If you’re open to discussion, join the [technical preview](https://docs.google.com/forms/d/e/1FAIpQLSfkL9e9X3Lcn6oBDIG-gU4yrfSenh8fndupbIX7zkyxX3X9ZQ/viewform).

## Looking for v1?

Expand Down
28 changes: 28 additions & 0 deletions add_section.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import fs from 'fs';

let content = fs.readFileSync('src/lib/demo_doc.js', 'utf-8');

const section1Str = ` section_1: {
id: 'section_1',
type: 'section'
},`;

content = content.replace('page_1: {', section1Str + '\n\t\tpage_1: {');

const page1BodyEndStr = ` 'footer_1'
],
annotations: []`;

const page1BodyEndStrRepl = ` 'footer_1'
],
annotations: [
{
start_offset: 0,
end_offset: 3,
node_id: 'section_1'
}
]`;

content = content.replace(page1BodyEndStr, page1BodyEndStrRepl);

fs.writeFileSync('src/lib/demo_doc.js', content);
14 changes: 14 additions & 0 deletions convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import fs from 'fs';

let content = fs.readFileSync('src/lib/demo_doc.js', 'utf-8');

const array_properties = ['buttons', 'content', 'body', 'gallery_items', 'items', 'blocks', 'nav_items', 'footer_items'];

for (const prop of array_properties) {
const regex = new RegExp(`\\b${prop}:\\s*\\[([^\\]]*)\\]`, 'g');
content = content.replace(regex, (match, inner) => {
return `${prop}: {\n\t\tnodes: [${inner}],\n\t\tannotations: []\n\t}`;
});
}

fs.writeFileSync('src/lib/demo_doc.js', content);
Loading