Skip to content

Commit b5e591a

Browse files
committed
feat: diversity pass, minimal-palette fix, border radius, privacy note
Generation quality: - Add Jaccard similarity diversity pass in useAldusGeneration — re-runs sequences with similarity > 0.8 at temperature 1.2 to keep layouts visually distinct from each other - Add temperatureOverride parameter to inferTokens() to support the diversity re-run without changing normal temperature logic Theme / renderer: - Fix aldus_pick_accent() on minimal palettes (< 4 colours) where the luminance search previously returned the same slug as the dark colour, producing no visual contrast in generated layouts - Add aldus_palette_is_minimal() and aldus_lighten_hex() utility functions - Add optional radius parameter to aldus_block_cover() and aldus_block_group() so renderers can apply border-radius without touching inline styles manually UI: - Add inline privacy note ("Your words stay private…") with lock icon in the BuildingScreen status area and matching .aldus-privacy-note SCSS rule Docs: - Rewrite README.md with cleaner, more direct copy Made-with: Cursor
1 parent 03c4709 commit b5e591a

10 files changed

Lines changed: 222 additions & 94 deletions

File tree

README.md

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,80 @@
11
# Aldus
22

3-
**You write the words. Aldus figures out the page.**
3+
**You write it. Aldus shows you every way it could look.**
44

5-
Drop in a headline, some paragraphs, an image, a quote, a button. Click **Make it happen**. In seconds, your layouts appear — each one a fully-composed, edit-ready WordPress page built from your actual content. Pick the one that feels right. Done.
6-
7-
No templates. No drag-and-drop. No theme builder subscription. No AI account. The model runs in your browser and your content never leaves your site.
5+
Add your content — a headline, some paragraphs, an image, a quote, a button. Click **Make it happen**. Aldus arranges it as a magazine spread, a cinematic landing page, a newspaper layout, a minimal essay, and more. All at once. Pick the one that fits. It becomes real WordPress blocks.
86

97
---
108

11-
## Every way to be interesting
12-
13-
Every layout proposal comes from a named personality — a distinct visual voice with its own opinion about how your content should be arranged. Same content, different results.
14-
15-
| Personality | Vibe |
16-
|---|---|
17-
| **Dispatch** | Breaking-news urgency. Dark full-bleed opener, a statement pullquote, then evidence and action. |
18-
| **Folio** | Classic magazine spread. Every section labeled left, body text right, like a feature layout. |
19-
| **Stratum** | The page as landscape. Dark, light, and accent bands stacked like geological strata. |
20-
| **Broadside** | Cinematic product-page energy. Alternating image-text panels with a punchy CTA cut-in. |
21-
| **Manifesto** | Starts quiet with a raw H1, erupts into a full-dark declaration, then triptych columns. |
22-
| **Nocturne** | Dark cover bleeds into a full-bleed image, then content surfaces into light. Maximum chiaroscuro. |
23-
| **Tribune** | Newspaper front page. Three-column opener anchored by a bold pullquote that splits the page in two. |
24-
| **Overture** | Light cinematic cover builds to a media panel, then the accent section delivers the curtain call. |
25-
| **Codex** | Typographic restraint. Display headlines, kicker labels, editorial border-inset, generous white space. |
26-
| **Dusk** | Full-height split-screen opener bleeds into a gradient section. Cinematic from the first pixel. |
27-
| **Broadsheet** | Newspaper-grid density. Four equal columns, a centered pullquote that cleaves the page. |
28-
| **Solstice** | Clean and luminous. Minimal cover, two-column rhythm, nothing that doesn't need to be there. |
29-
| **Mirage** | Gradient-drenched and lush. Split-screen cover, layered color sections, pure atmosphere. |
30-
| **Ledger** | Long-form essay structure. Two-column flow, attributed quote, editorial border-inset for the key section. |
31-
| **Mosaic** | Gallery-first. Images lead and dominate. Built for photographers and visual portfolios. |
32-
| **Prism** | Three equal columns open into a full gallery grid. Structure and imagery in dialogue. |
9+
## How it works
10+
11+
**1. Add what you've got.**
12+
A headline, some text, an image, a quote — whatever the page needs. Don't worry about layout.
13+
14+
**2. See every option at once.**
15+
Aldus arranges your content in every layout style — editorial columns, cinematic heroes, newspaper grids, minimal typography. Same words, different energy.
16+
17+
**3. Pick the one that fits.**
18+
It becomes real WordPress blocks — fully editable, rearrangeable, yours. Aldus stays available if you want to try a different look later.
3319

3420
---
3521

36-
## How it works
22+
## Not a page builder. Not a template library. Not an AI writer.
3723

38-
**1. Tell Aldus what you have.**
39-
Click a content type — Headline, Paragraph, Image, Quote, Button, and more. The type buttons are the first thing you see. Once you've added at least one piece of content, style chips and the generate button appear below. You can also import your post title or existing page copy as a starting point.
24+
**Not a page builder.** You don't drag and drop. You pick.
4025

41-
**2. Aldus proposes layouts.**
42-
A small AI model downloads once to your browser — about 200 MB, cached forever after that. It looks at the *shape* of your content (how many paragraphs, whether you have an image, etc.) and proposes a different layout sequence for each personality. Your actual words never leave your site.
26+
**Not a template library.** Layouts are generated from your content, not filled in from blanks.
4327

44-
**3. You pick one and it becomes real blocks.**
45-
Browse the layout cards. Each shows a wireframe so you can scan the rhythm at a glance. Hover a card to reveal the **Use this one** button at the bottom — or click the eye icon in the top-right corner to expand a full preview first. Hit **Use this one** and Aldus replaces itself with standard, editable WordPress blocks — no lock-in, no custom markup, nothing you can't move or delete.
28+
**Not an AI writer.** Aldus never reads, rewrites, or suggests changes to your words.
4629

4730
---
4831

49-
## Tools that help you get there
32+
## The styles
33+
34+
Every layout has its own structure, mood, and rhythm. Same words — completely different energy.
35+
36+
| | | |
37+
|---|---|---|
38+
| **Dispatch** | dramatic | Dark opener, bold declaration, then the ask — urgency in three acts |
39+
| **Folio** | editorial | Label left, body right — every section reads like a magazine spread |
40+
| **Stratum** | structural | Dark, light, accent — the page as landscape |
41+
| **Broadside** | cinematic | Cinematic image-text panels, CTA cut right in the middle |
42+
| **Manifesto** | dramatic | Quiet H1, then a dark declaration, then three columns erupt |
43+
| **Nocturne** | cinematic | Dark cover bleeds into full image, then surfaces into light |
44+
| **Tribune** | structural | Newspaper front page energy, split by a bold pullquote |
45+
| **Overture** | editorial | Light cover builds to a reveal, accent section drops the curtain |
46+
| **Codex** | editorial | Typographic restraint — kicker, display headline, editorial inset |
47+
| **Dusk** | atmospheric | Split-screen opener bleeds into gradient atmosphere |
48+
| **Broadsheet** | structural | Four-column newspaper density, cleaved by a centered pullquote |
49+
| **Solstice** | minimal | Minimal cover, two-column rhythm, nothing superfluous |
50+
| **Mirage** | atmospheric | Gradient-drenched and lush — cover and color converge |
51+
| **Ledger** | editorial | Essay structure: two columns, attributed quote, editorial inset |
52+
| **Mosaic** | visual | Images lead, text stays lean — built for visual portfolios |
53+
| **Prism** | visual | Three columns open into a full gallery grid |
54+
55+
---
5056

51-
- **Style Notes** — nudge every personality in a direction before generating: image-forward, minimal, dark mood, bold CTA, magazine, and more. Appear automatically once you've added content.
52-
- **Per-card re-roll** — not quite right? Regenerate one personality without touching the rest
53-
- **Copy blocks / Try with my content** — compact icon buttons in each card footer let you copy a layout to clipboard or preview it with your own content, without leaving the results screen
54-
- **Mix & Match** — combine the best sections from different personalities into a single custom layout
55-
- **Saved Sessions** — snapshot your content and results, come back to them later
56-
- **Quick Start presets** — jump in with a sensible content structure for a blog post, landing page, feature story, product pitch, visual portfolio, or product comparison — accessible right from the empty state
57-
- **Nine sample packs** — try every personality style instantly with themed content: Roast, Meridian, Hearth, Plume, Grove, Loot, Signal, Forge, and Slim — no model download needed
58-
- **Completeness hints** — colored prompts that tell you which content type would unlock more layout options
57+
## More than a one-trick plugin.
58+
59+
- **Mix & match** — combine sections from different styles into one custom layout
60+
- **Per-card re-roll** — regenerate just one card without losing the rest
61+
- **Style direction** — steer toward image-forward, minimal, dark mood, or magazine
62+
- **Import from your page** — already have content in the editor? One click pulls it in
63+
- **Quick Peek** — preview a specific style before seeing all of them
64+
- **Saved Sessions** — bookmark your content set and reload it later
65+
- **Sample packs** — nine themed content packs show you every layout style with real copy, no model download needed
5966

6067
---
6168

62-
## Requirements
69+
## No subscription. No server. No catch.
70+
71+
**Free forever.** GPL licensed. No subscription, no freemium tier, no "pro" version.
72+
73+
**Private by design.** The AI runs in your browser. Nothing you write ever touches a server — not ours, not anyone's. The model sees content types ("2 paragraphs, 1 image"), never your actual words.
74+
75+
**Zero lock-in.** Every layout is standard WordPress blocks. Deactivate Aldus and nothing breaks.
6376

64-
- WordPress 6.4 or later
65-
- Chrome 113+, Edge 113+, or Safari 18+ (Firefox doesn't support WebGPU yet)
66-
- No API key. No account. No subscription.
77+
**Works offline.** After the one-time model download (~200 MB), Aldus works without internet.
6778

6879
---
6980

@@ -74,22 +85,18 @@ Browse the layout cards. Each shows a wireframe so you can scan the rhythm at a
7485
3. Upload the zip, activate
7586
4. Add an **Aldus** block to any post or page
7687

88+
**Requirements:** WordPress 6.4+, Chrome 113+, Edge 113+, or Safari 18+. No API key. No account.
89+
7790
---
7891

7992
## Documentation
8093

8194
- [Getting Started](../../wiki/Getting-Started)
8295
- [Content Types](../../wiki/Content-Types)
8396
- [Personalities](../../wiki/Personalities)
84-
- [Layout Tokens](../../wiki/Tokens)
8597
- [Features](../../wiki/Features)
86-
- [Sample Packs](../../wiki/Sample-Packs)
8798
- [FAQ & Troubleshooting](../../wiki/FAQ-and-Troubleshooting)
8899

89100
---
90101

91-
## License
92-
93-
GPL-2.0-or-later. See [LICENSE](LICENSE).
94-
95-
Named after [Aldus Manutius](https://en.wikipedia.org/wiki/Aldus_Manutius) — the 15th-century Venetian printer who invented the paperback, the italic typeface, and the idea that beautiful typography should be for everyone. He would have figured out Gutenberg by lunchtime.
102+
GPL-2.0-or-later. Named after [Aldus Manutius](https://en.wikipedia.org/wiki/Aldus_Manutius) — the 15th-century Venetian printer who invented italic type, pioneered the pocket-sized book, and defined what a printed page should look like.

includes/api.php

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,12 @@ function aldus_handle_assemble( WP_REST_Request $request ): WP_REST_Response|WP_
193193
}
194194

195195
if ( '' === trim( $markup ) ) {
196-
$fallback_markup = aldus_render_fallback_markup( $items );
197-
if ( '' !== trim( $fallback_markup ) ) {
198-
$markup = $fallback_markup;
199-
$sections = array(
200-
array(
201-
'token' => 'fallback:generic',
202-
'blocks' => $fallback_markup,
203-
),
204-
);
205-
} else {
206-
aldus_record_assembly_error( $personality );
207-
return new WP_Error( 'empty_markup', __( 'Could not build markup for this layout. Try again.', 'aldus' ), array( 'status' => 422 ) );
208-
}
196+
aldus_record_assembly_error( $personality );
197+
return new WP_Error(
198+
'empty_layout',
199+
__( 'This style couldn\'t arrange your content well. Try a different style or add more content.', 'aldus' ),
200+
array( 'status' => 422 )
201+
);
209202
}
210203

211204
/**
@@ -221,19 +214,12 @@ function aldus_handle_assemble( WP_REST_Request $request ): WP_REST_Response|WP_
221214
$markup = (string) apply_filters( 'aldus_assembled_blocks', $markup, $personality, $items );
222215
// Guard against filters that zero out the markup.
223216
if ( '' === trim( $markup ) ) {
224-
$fallback_markup = aldus_render_fallback_markup( $items );
225-
if ( '' !== trim( $fallback_markup ) ) {
226-
$markup = $fallback_markup;
227-
$sections = array(
228-
array(
229-
'token' => 'fallback:generic',
230-
'blocks' => $fallback_markup,
231-
),
232-
);
233-
} else {
234-
aldus_record_assembly_error( $personality );
235-
return new WP_Error( 'empty_markup', __( 'Could not build markup for this layout. Try again.', 'aldus' ), array( 'status' => 422 ) );
236-
}
217+
aldus_record_assembly_error( $personality );
218+
return new WP_Error(
219+
'empty_layout',
220+
__( 'This style couldn\'t arrange your content well. Try a different style or add more content.', 'aldus' ),
221+
array( 'status' => 422 )
222+
);
237223
}
238224

239225
/**

includes/render-router.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ function aldus_render_block_token(
144144
'Hero',
145145
$cv,
146146
$post_id,
147-
$ia_parallax
147+
$ia_parallax,
148+
$s_radius
148149
);
149150

150151
case 'cover:light':
@@ -159,7 +160,8 @@ function aldus_render_block_token(
159160
'Feature Cover',
160161
$cv,
161162
$post_id,
162-
$ia_parallax
163+
$ia_parallax,
164+
$s_radius
163165
);
164166

165167
case 'cover:minimal':
@@ -208,7 +210,8 @@ function aldus_render_block_token(
208210
'Dark Section',
209211
$variant3,
210212
$s_block_gap,
211-
$ia_reveal
213+
$ia_reveal,
214+
$s_radius
212215
);
213216

214217
case 'group:accent-full':
@@ -220,7 +223,8 @@ function aldus_render_block_token(
220223
'Accent Section',
221224
$variant3,
222225
$s_block_gap,
223-
$ia_reveal
226+
$ia_reveal,
227+
$s_radius
224228
);
225229

226230
case 'group:light-full':
@@ -232,7 +236,8 @@ function aldus_render_block_token(
232236
'Light Section',
233237
$variant3,
234238
$s_block_gap,
235-
$ia_reveal
239+
$ia_reveal,
240+
$s_radius
236241
);
237242

238243
case 'group:border-box':

includes/renderers/cover.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ function aldus_block_cover(
1717
string $name = '',
1818
int $variant = 0,
1919
int $post_id = 0,
20-
string $ia_attrs = ''
20+
string $ia_attrs = '',
21+
string $radius = ''
2122
): string {
2223
// Variant 2: pure backdrop — no inner content. Still needs at least an image to be meaningful.
2324
if ( $variant === 2 ) {
@@ -37,6 +38,9 @@ function aldus_block_cover(
3738
'url' => esc_url_raw( $image['url'] ),
3839
'hasParallax' => false,
3940
);
41+
if ( $radius !== '' ) {
42+
$attrs['style']['border']['radius'] = $radius;
43+
}
4044
if ( $name ) {
4145
$attrs['metadata'] = array( 'name' => $name . ' (Backdrop)' );
4246
}
@@ -110,6 +114,9 @@ function aldus_block_cover(
110114
'minHeightUnit' => 'px',
111115
'layout' => array( 'type' => 'constrained' ),
112116
);
117+
if ( $radius !== '' ) {
118+
$attrs['style']['border']['radius'] = $radius;
119+
}
113120
if ( $name ) {
114121
$attrs['metadata'] = array( 'name' => $name );
115122
}

includes/renderers/group.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ function aldus_block_group(
2727
string $name = '',
2828
int $variant = 0,
2929
string $block_gap = '',
30-
string $ia_attrs = ''
30+
string $ia_attrs = '',
31+
string $radius = ''
3132
): string {
3233
$bg_safe = sanitize_html_class( $bg_slug );
3334
$tc_safe = $text_color_slug ? sanitize_html_class( $text_color_slug ) : '';
@@ -50,6 +51,9 @@ function aldus_block_group(
5051
),
5152
'style' => array( 'spacing' => $spacing ),
5253
);
54+
if ( $radius !== '' ) {
55+
$attrs['style']['border']['radius'] = $radius;
56+
}
5357
if ( $name ) {
5458
$attrs['metadata'] = array( 'name' => $name );
5559
}

includes/theme.php

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,42 @@ function aldus_get_theme_appearance_tools(): array {
287287
return $tools;
288288
}
289289

290+
/**
291+
* Returns true when the palette has fewer than four entries.
292+
*
293+
* Themes with very small palettes (black + white only, or three colours)
294+
* need special handling because the accent picker can otherwise return the
295+
* same slug as the dark picker, producing no visual contrast.
296+
*
297+
* @param list<array{slug:string,color:string}> $palette
298+
* @return bool
299+
*/
300+
function aldus_palette_is_minimal( array $palette ): bool {
301+
return count( $palette ) < 4;
302+
}
303+
304+
/**
305+
* Lightens a six-digit hex color by the given percentage (0–100).
306+
*
307+
* Useful for generating inline style values from the theme palette when
308+
* a derived shade is needed without registering a new colour preset.
309+
* Returns the original (prefixed with #) unchanged on invalid input.
310+
*
311+
* @param string $hex Six-digit hex string with or without the leading '#'.
312+
* @param int $percent Lightening amount as a percentage of the max channel value.
313+
* @return string Lightened hex colour string including the '#' prefix.
314+
*/
315+
function aldus_lighten_hex( string $hex, int $percent ): string {
316+
$hex = ltrim( $hex, '#' );
317+
if ( strlen( $hex ) !== 6 ) {
318+
return '#' . $hex;
319+
}
320+
$r = min( 255, (int) ( hexdec( substr( $hex, 0, 2 ) ) + 255 * $percent / 100 ) );
321+
$g = min( 255, (int) ( hexdec( substr( $hex, 2, 2 ) ) + 255 * $percent / 100 ) );
322+
$b = min( 255, (int) ( hexdec( substr( $hex, 4, 2 ) ) + 255 * $percent / 100 ) );
323+
return sprintf( '#%02x%02x%02x', $r, $g, $b );
324+
}
325+
290326
/**
291327
* Calculates relative luminance of a hex color string (for palette sorting).
292328
*
@@ -330,6 +366,10 @@ function aldus_pick_light( array $palette ): string {
330366
* Returns a mid-range palette slug to use as an accent.
331367
* Picks the entry closest to 40% luminance.
332368
*
369+
* On minimal palettes (< 4 colours) the luminance target often resolves to the
370+
* same slug as the dark colour, leaving no visual contrast. When that happens,
371+
* fall back to the lightest slug so the accent is always visually distinct.
372+
*
333373
* @param list<array{slug:string,color:string}> $palette
334374
*/
335375
function aldus_pick_accent( array $palette ): string {
@@ -346,7 +386,16 @@ function aldus_pick_accent( array $palette ): string {
346386
$best_d = $d;
347387
}
348388
}
349-
return sanitize_html_class( $best['slug'] ?? 'primary' );
389+
$accent = sanitize_html_class( $best['slug'] ?? 'primary' );
390+
391+
// On minimal palettes the luminance search often picks the darkest entry,
392+
// leaving accent === dark and no visible contrast in generated layouts.
393+
// Fall back to the lightest available slug to guarantee differentiation.
394+
if ( aldus_palette_is_minimal( $palette ) && $accent === aldus_pick_dark( $palette ) ) {
395+
return aldus_pick_light( $palette );
396+
}
397+
398+
return $accent;
350399
}
351400

352401
/**

src/components/BuildScreen.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { useSelect } from '@wordpress/data';
2424
import apiFetch from '@wordpress/api-fetch';
2525
import { parse as parseBlocks } from '@wordpress/blocks';
2626
import { __, sprintf, _n } from '@wordpress/i18n';
27-
import { close, plus } from '@wordpress/icons';
27+
import { close, lock, plus } from '@wordpress/icons';
2828
import { ACTIVE_PERSONALITIES } from '../data/personalities.js';
2929
import {
3030
HINT_TYPE_LABELS,
@@ -543,6 +543,13 @@ export function BuildingScreen( {
543543
) }
544544
</span>
545545
) }
546+
<span className="aldus-privacy-note">
547+
<Icon icon={ lock } size={ 14 } />
548+
{ __(
549+
'Your words stay private — the AI only sees content types, never your text.',
550+
'aldus'
551+
) }
552+
</span>
546553
</div>
547554
</OnboardingTooltip>
548555
) }

0 commit comments

Comments
 (0)