@styleflow.app/flowpress-astro-core 1.0.0-beta.1 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +80 -1
  2. package/README.md +94 -3
  3. package/dist/astro/BaseLayout.astro +5 -1
  4. package/dist/astro/CompositionRenderer.astro +127 -0
  5. package/dist/astro/DefaultBlockAdapter.astro +35 -0
  6. package/dist/astro/DefaultTemplate.astro +6 -1
  7. package/dist/astro/GutenbergRenderer.astro +215 -55
  8. package/dist/astro/ThemeRuntime.astro +24 -0
  9. package/dist/astro/blocks/CarouselBlock.astro +198 -0
  10. package/dist/astro/blocks/CarouselItemBlock.astro +26 -0
  11. package/dist/astro/blocks/ColumnBlock.astro +2 -3
  12. package/dist/astro/blocks/FormBlock.astro +5 -5
  13. package/dist/astro/blocks/GridBlock.astro +2 -2
  14. package/dist/astro/blocks/HeroBlock.astro +2 -3
  15. package/dist/astro/blocks/ImageBlock.astro +2 -3
  16. package/dist/astro/blocks/ListItemFallback.astro +7 -5
  17. package/dist/astro/blocks/NumberSpinnerBlock.astro +24 -18
  18. package/dist/astro/blocks/SemanticElement.astro +35 -0
  19. package/dist/astro/blocks/SiteLogoBlock.astro +53 -0
  20. package/dist/astro/blocks/TextBlock.astro +27 -23
  21. package/dist/astro/blocks/ThemeToggleBlock.astro +23 -0
  22. package/dist/astro/blocks/helpers.d.ts +7 -0
  23. package/dist/astro/blocks/helpers.d.ts.map +1 -1
  24. package/dist/astro/blocks/helpers.js +51 -41
  25. package/dist/astro/blocks/helpers.js.map +1 -1
  26. package/dist/astro/blocks/helpers.ts +63 -45
  27. package/dist/block-renderers.d.ts +19 -2
  28. package/dist/block-renderers.d.ts.map +1 -1
  29. package/dist/block-renderers.js.map +1 -1
  30. package/dist/carousel.d.ts +22 -0
  31. package/dist/carousel.d.ts.map +1 -0
  32. package/dist/carousel.js +102 -0
  33. package/dist/carousel.js.map +1 -0
  34. package/dist/client.d.ts +2 -1
  35. package/dist/client.d.ts.map +1 -1
  36. package/dist/client.js +31 -0
  37. package/dist/client.js.map +1 -1
  38. package/dist/composition.d.ts +14 -0
  39. package/dist/composition.d.ts.map +1 -0
  40. package/dist/composition.js +124 -0
  41. package/dist/composition.js.map +1 -0
  42. package/dist/default-blocks.d.ts +9 -0
  43. package/dist/default-blocks.d.ts.map +1 -0
  44. package/dist/default-blocks.js +54 -0
  45. package/dist/default-blocks.js.map +1 -0
  46. package/dist/env.d.ts +9 -0
  47. package/dist/env.d.ts.map +1 -1
  48. package/dist/env.js +20 -0
  49. package/dist/env.js.map +1 -1
  50. package/dist/index.d.ts +6 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +6 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/links.d.ts +5 -0
  55. package/dist/links.d.ts.map +1 -0
  56. package/dist/links.js +32 -0
  57. package/dist/links.js.map +1 -0
  58. package/dist/media.d.ts +8 -0
  59. package/dist/media.d.ts.map +1 -0
  60. package/dist/media.js +121 -0
  61. package/dist/media.js.map +1 -0
  62. package/dist/styleflow.d.ts +2 -0
  63. package/dist/styleflow.d.ts.map +1 -1
  64. package/dist/styleflow.js +47 -5
  65. package/dist/styleflow.js.map +1 -1
  66. package/dist/theme-runtime.d.ts +16 -0
  67. package/dist/theme-runtime.d.ts.map +1 -0
  68. package/dist/theme-runtime.js +77 -0
  69. package/dist/theme-runtime.js.map +1 -0
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/dist/types.d.ts +84 -1
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/types.js.map +1 -1
  74. package/package.json +9 -3
package/CHANGELOG.md CHANGED
@@ -2,12 +2,91 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 1.0.0-beta.10
6
+
7
+ - Added a versioned catalog of official default block adapters and a public
8
+ source generator for editable project overrides.
9
+ - Let an official default render a block without recursively selecting the
10
+ override currently wrapping it, while preserving certified overrides for
11
+ every nested child.
12
+ - Added reusable `FLOWPRESS_MEDIA` resolution, Astro image remote patterns and
13
+ safe upload/media proxy helpers with `GET`, `HEAD`, range and cache support.
14
+ - Extended normalized block HTML props with an explicit safe attribute
15
+ allowlist; Gutenberg event handlers, inline styles and Styleflow-owned data
16
+ attributes remain blocked.
17
+
18
+ ## 1.0.0-beta.9
19
+
20
+ - Validated typography variants against the union of contract variants when
21
+ the block adapter has not yet supplied its typography type, preventing the
22
+ composition registry from rejecting valid heading-only variants early.
23
+
24
+ ## 1.0.0-beta.8
25
+
26
+ - Normalized finite numeric CMS values for enumerated Styleflow axes to their
27
+ canonical contract strings, so typography variants such as WordPress `v=0`
28
+ remain valid in strict production builds without emitting a fallback.
29
+
30
+ ## 1.0.0-beta.7
31
+
32
+ - Fixed theme-aware logo selection by placing fallback visibility variables on
33
+ the document root, allowing the nearest global or nested Styleflow theme to
34
+ override them while preserving per-block logo overrides.
35
+
36
+ ## 1.0.0-beta.6
37
+
38
+ - Added required `htmlProps`, `styleflowProps`, composition and deterministic
39
+ render context to every certified block adapter.
40
+ - Added single-emission composition stylesheets for validated WordPress block
41
+ CSS, plus theme-aware positive/negative Site Logo rendering.
42
+ - Added the no-hydration multi-theme runtime and `sf/theme-toggle` adapter.
43
+ - Made `<S.*>` the only public Astro authoring syntax in package sources.
44
+ - Added `FlowpressCompositionV1`, typed `resolvedData`,
45
+ `FlowpressClient.getComposition()` and recursive production fail-closed
46
+ rendering with exact compatibility commands in development.
47
+
48
+ ## 1.0.0-beta.5
49
+
50
+ - Updated the exact `@styleflow.app/astro` dependency to `1.0.0-beta.4`,
51
+ adopting the Styleflow contract beta.6 validator and generated runtime.
52
+ - Kept the grouped CMS coordinates and Gutenberg block format unchanged; no
53
+ legacy adapter was introduced.
54
+
55
+ ## 1.0.0-beta.4
56
+
57
+ - Exported the `FlowpressLinkValue` relational contract and centralized safe
58
+ href extraction for resolved internal, external, protocol, and fragment
59
+ destinations.
60
+ - Made failed, missing, and bare-`#` relations fail closed: buttons retain
61
+ their label and Styleflow semantics as non-interactive elements, while
62
+ cards and list fallbacks emit anchors only for a valid resolved href.
63
+ - Added renderer and unit fixtures for missing translations, root-relative
64
+ query/fragment paths, safe protocols, and invalid schemes.
65
+
66
+ ## 1.0.0-beta.3
67
+
68
+ - Deferred generic Gutenberg semantic validation until its renderer branch is
69
+ selected, preventing valid numeric heading variants from being validated as
70
+ body typography and spuriously falling back to `md`.
71
+ - Added a strict production fixture for `core/heading` with `v="5"` and verified
72
+ that the emitted Styleflow type and variant remain `heading` and `5`.
73
+
74
+ ## 1.0.0-beta.2
75
+
76
+ - Added exact `@splidejs/splide@4.1.4` support and dedicated renderers for
77
+ `sf/carousel` and `sf/carousel-item` with responsive item counts, arrows,
78
+ pagination, drag, focused keyboard control, loop and autoplay.
79
+ - Added accessible live-region integration and mandatory Play/Pause controls
80
+ for autoplay, including hover/focus pauses and reduced-motion protection.
81
+ - Added defensive CMS clamps, per-instance custom-element lifecycle cleanup,
82
+ readable no-JavaScript layouts and a diagnostic static fallback that keeps
83
+ legacy content whose direct children are not carousel items.
84
+
5
85
  ## 1.0.0-beta.1
6
86
 
7
87
  - Updated the exact `@styleflow.app/astro` dependency to `1.0.0-beta.1`,
8
88
  keeping consumer trees on one runtime while adopting deterministic CLI
9
89
  reports. Runtime behavior is unchanged.
10
-
11
90
  ## 1.0.0-beta.0
12
91
 
13
92
  - Adopted the grouped Styleflow vNext project contract and the exact Astro v1
package/README.md CHANGED
@@ -9,8 +9,8 @@ The package must not import from an application or project plugin.
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.1 \
13
- @styleflow.app/astro@1.0.0-beta.1
12
+ pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.10 \
13
+ @styleflow.app/astro@1.0.0-beta.6
14
14
  ```
15
15
 
16
16
  Prereleases are pinned exactly. The package expects Astro 7 or newer and the
@@ -54,6 +54,85 @@ visible characters. The exported
54
54
  from rendered CMS blocks so consumers can generate Tailwind
55
55
  `@source inline()` directives before compiling their frontend CSS.
56
56
 
57
+ The package exports `FlowpressLinkValue`, `flowpressLinkValue()` and
58
+ `flowpressLinkHref()` for the WordPress relational link contract. Navigation
59
+ paths are root-relative and are rendered only when resolution succeeds.
60
+ `missing_translation`, `missing_entity`, invalid schemes, and a bare `#` fail
61
+ closed. FlowPress buttons keep their authored label and semantic styling as an
62
+ `aria-disabled` non-interactive element; cards and list fallbacks omit the
63
+ anchor. Canonical, hreflang, social and media URLs remain under their separate
64
+ absolute-URL contracts.
65
+
66
+ `sf/carousel` renders an accessible horizontal Splide carousel whose direct
67
+ children must be `sf/carousel-item`; each item can recursively contain any
68
+ renderable block. Item counts inherit across `base/sm/md/lg/xl/2xl`, with
69
+ defaults `1/1/2/3/3/3`. Arrows, pagination, drag/swipe, focused keyboard
70
+ navigation, loop and autoplay follow CMS attributes. Autoplay always includes
71
+ one Play/Pause control, pauses on configured hover/focus states and starts
72
+ paused under reduced-motion preferences. Every custom element owns and
73
+ destroys exactly one Splide instance.
74
+
75
+ Stored CMS values are clamped to the public contract: item counts and movement
76
+ `1–12`, gap `0–128px`, autoplay interval `1000–60000ms`, and transition speed
77
+ `0–5000ms`. Without JavaScript, all valid items remain a readable responsive
78
+ grid and dead controls are hidden. If a legacy carousel contains any direct
79
+ child other than `sf/carousel-item`, no interactive instance is initialized:
80
+ the complete content renders statically, with a development diagnostic.
81
+
82
+ ## Site Editor composition
83
+
84
+ `FlowpressClient.getComposition(uri, locale)` reads the public
85
+ `FlowpressCompositionV1` contract. `CompositionRenderer.astro` walks its block
86
+ tree through an explicit `FlowpressBlockAdapters` registry, passes authored
87
+ attributes and `resolvedData` separately, and never renders `innerHtml` as a
88
+ fallback. Blocking WordPress diagnostics always fail the render. Missing or
89
+ draft adapters, absent block-type descriptors and schema-fingerprint drift
90
+ are visible diagnostics in development and fail closed in production with
91
+ the exact scaffold, refresh or approval command. The fingerprint comparison
92
+ is repeated by Astro and does not rely only on the CMS gate.
93
+ Before invoking an adapter, the renderer resolves the universal Styleflow
94
+ attributes through the same framework-neutral validator used by the built-in
95
+ renderer and passes them as `styleflowProps`. Project adapters can therefore
96
+ apply the authored semantic context to their real root without accepting raw
97
+ WordPress visual values.
98
+
99
+ Every adapter also receives required normalized `htmlProps` (`anchor` → `id`
100
+ and the additional classes), the full composition and a deterministic render
101
+ context. The public HTML allowlist also preserves `title`, `role`, `lang`,
102
+ `dir`, `tabindex`, `aria-*` and project `data-*` attributes. Inline styles,
103
+ event handlers and Styleflow-owned `data-*` attributes are never copied from
104
+ Gutenberg indiscriminately. WordPress-processed block custom CSS is emitted
105
+ once at the root.
106
+ `ThemeRuntime.astro` applies the persisted contract theme before paint and
107
+ progressively enhances `sf/theme-toggle` without hydrating React. The shared
108
+ Site Logo adapter renders positive and negative variants while inherited CSS
109
+ variables make the nearest nested theme authoritative.
110
+
111
+ ```astro
112
+ ---
113
+ import CompositionRenderer from "@styleflow.app/flowpress-astro-core/CompositionRenderer.astro";
114
+ import { blockAdapters } from "./generated/block-adapter-registry";
115
+ ---
116
+
117
+ <CompositionRenderer composition={composition} adapters={blockAdapters} />
118
+ ```
119
+
120
+ The package publishes a versioned `flowpressDefaultBlockCatalog` and
121
+ `DefaultBlockAdapter.astro`. A project registry can therefore resolve a block
122
+ in this order: conventional local `namespace-block.astro`, explicit manifest
123
+ renderer, official default. Calling the official default skips only the
124
+ current override and keeps the same registry for nested children, avoiding
125
+ recursion without losing child overrides. File discovery never changes a
126
+ `draft`, missing or stale fingerprint into a certified adapter.
127
+
128
+ `createFlowpressDefaultBlockAdapterSource(blockName)` returns the editable
129
+ source used by scaffolding when `--from-default` is selected. Unsupported
130
+ blocks throw instead of pretending that a semantic implementation exists.
131
+
132
+ Static adapters produce no JavaScript by default. A project opts into client
133
+ runtime only inside adapters classified as interactive; React views rendered
134
+ without `client:*` remain server-only HTML.
135
+
57
136
  Heading, rich-text, quote and matching core blocks accept Gutenberg alignment
58
137
  from either `style.typography.textAlign` or the FlowPress `textAlign` field.
59
138
  Only `left`, `center`, `right`, `justify`, `start` and `end` reach CSS.
@@ -122,6 +201,18 @@ assistive technology and without JavaScript; `prefers-reduced-motion: reduce`
122
201
  skips the animation entirely. Browsers without viewport observation keep the
123
202
  server-rendered final value instead of guessing when to start.
124
203
 
204
+ ## Media
205
+
206
+ `resolveFlowpressMediaUrl()` reads `FLOWPRESS_MEDIA`, falling back to
207
+ `BACKEND_URL`, while `flowpressMediaRemotePattern()` returns the matching Astro
208
+ image pattern. URLs with credentials, query strings or fragments are rejected.
209
+
210
+ `proxyFlowpressUpload()` and `proxyFlowpressMedia()` are shared request
211
+ handlers for server output and static Pages Functions. They accept only
212
+ `GET`/`HEAD`, preserve conditional and Range requests plus cache metadata, and
213
+ use explicit request/response header allowlists so cookies, authorization and
214
+ other sensitive headers never cross the proxy boundary.
215
+
125
216
  ## Release
126
217
 
127
218
  ```bash
@@ -142,7 +233,7 @@ The TypeScript template-hierarchy suite is self-contained. Its additional PHP
142
233
  parity test runs when the sibling `flowpress-wordpress-core` checkout is
143
234
  available locally and is skipped in a standalone package checkout.
144
235
 
145
- Publish v1 betas on `next` only after Components and WordPress Core are
236
+ Publish `1.0.0-beta.10` on `next` only after the exact Styleflow prerelease is
146
237
  available. Inspect `npm view`, `gitHead`, integrity and the downloaded tarball.
147
238
  Promote to `latest` only after the DMO canary and rollback gate.
148
239
 
@@ -1,19 +1,22 @@
1
1
  ---
2
2
  import type { SfSeo } from "../types.js";
3
3
  import S from "@styleflow.app/astro/S.astro";
4
+ import ThemeRuntime from "./ThemeRuntime.astro";
4
5
 
5
6
  interface Props {
6
7
  lang?: string;
7
8
  seo?: SfSeo | null;
8
9
  siteName?: string;
9
10
  theme?: string;
11
+ themes?: string[];
10
12
  }
11
13
 
12
14
  const {
13
15
  lang = "it",
14
16
  seo = null,
15
17
  siteName = import.meta.env.PROJECT_NAME ?? "FlowPress",
16
- theme
18
+ theme,
19
+ themes = theme ? [theme] : []
17
20
  } = Astro.props;
18
21
  const title = seo?.title || siteName;
19
22
  const description = seo?.description || "";
@@ -26,6 +29,7 @@ const jsonLd = seo?.jsonLd ? JSON.stringify(seo.jsonLd) : null;
26
29
  <head>
27
30
  <meta charset="UTF-8" />
28
31
  <meta name="viewport" content="width=device-width" />
32
+ {theme && themes.length ? <ThemeRuntime themes={themes} defaultTheme={theme} /> : null}
29
33
  <title>{title}</title>
30
34
  {description && <meta name="description" content={description} />}
31
35
  {robots && <meta name="robots" content={robots} />}
@@ -0,0 +1,127 @@
1
+ ---
2
+ import type { FlowpressBlockAdapters } from "../block-renderers.js";
3
+ import {
4
+ flowpressAdapterCompatibilityIssue,
5
+ flowpressBlockHtmlProps,
6
+ flowpressCompositionLogoStyles,
7
+ flowpressBlockTypeDescriptors
8
+ } from "../composition.js";
9
+ import {
10
+ resolveFlowpressBlockStyleflowProps,
11
+ type StyleflowContract
12
+ } from "../styleflow.js";
13
+ import type { FlowpressBlock, FlowpressCompositionV1 } from "../types.js";
14
+
15
+ interface Props {
16
+ composition: FlowpressCompositionV1;
17
+ adapters: FlowpressBlockAdapters;
18
+ blocks?: FlowpressBlock[];
19
+ contract?: StyleflowContract;
20
+ parentBlockName?: string;
21
+ production?: boolean;
22
+ depth?: number;
23
+ }
24
+
25
+ const {
26
+ composition,
27
+ adapters,
28
+ blocks = composition.blocks,
29
+ contract,
30
+ parentBlockName,
31
+ production = import.meta.env.PROD,
32
+ depth = 0
33
+ } = Astro.props;
34
+
35
+ const blocking = composition.diagnostics.filter((diagnostic) => diagnostic.blocking);
36
+ if (blocking.length > 0 && production) {
37
+ throw new Error(
38
+ `FlowPress composition ${composition.sourceDigest} is not renderable:\n`
39
+ + blocking.map(({ code, message }) => `- ${code}: ${message}`).join("\n")
40
+ );
41
+ }
42
+ const descriptors = flowpressBlockTypeDescriptors(composition);
43
+ const logoStyles = parentBlockName === undefined
44
+ ? flowpressCompositionLogoStyles(composition)
45
+ : "";
46
+ ---
47
+
48
+ {parentBlockName === undefined && composition.stylesheets.map((stylesheet) => (
49
+ <style data-flowpress-composition-style={stylesheet.id} set:html={stylesheet.css} />
50
+ ))}
51
+ {logoStyles ? <style data-flowpress-logo-styles set:html={logoStyles} /> : null}
52
+
53
+ {blocking.length > 0 && !production && parentBlockName === undefined ? (
54
+ <aside data-flowpress-composition-diagnostics role="status">
55
+ <strong>FlowPress composition diagnostics</strong>
56
+ <ul>
57
+ {blocking.map(({ code, message, command }) => (
58
+ <li data-diagnostic-code={code}>
59
+ {message}
60
+ {command ? <code>{command}</code> : null}
61
+ </li>
62
+ ))}
63
+ </ul>
64
+ </aside>
65
+ ) : null}
66
+
67
+ {blocks.map((block) => {
68
+ const adapter = adapters[block.name];
69
+ const issue = flowpressAdapterCompatibilityIssue(block.name, descriptors, adapters);
70
+ if (issue && production) throw new Error(issue.message);
71
+ if (!adapter) {
72
+ return (
73
+ <aside data-flowpress-compatibility={issue?.code ?? "missing-adapter"} data-block-name={block.name} role="status">
74
+ <strong>Missing block adapter: {block.name}</strong>
75
+ <code>{issue?.command}</code>
76
+ </aside>
77
+ );
78
+ }
79
+ const Adapter = adapter.renderer;
80
+ const children = block.innerBlocks ?? [];
81
+ const styleflowProps = resolveFlowpressBlockStyleflowProps(
82
+ block.attributes,
83
+ contract,
84
+ {},
85
+ {
86
+ strict: production,
87
+ onDiagnostic: (diagnostic) => console.warn(
88
+ `[${diagnostic.code}] ${diagnostic.message}`
89
+ )
90
+ }
91
+ );
92
+ const htmlProps = flowpressBlockHtmlProps(block.attributes);
93
+ return (
94
+ <>
95
+ {issue && (
96
+ <aside data-flowpress-compatibility={issue.code} data-block-name={block.name} role="status">
97
+ <strong>{issue.message}</strong>
98
+ <code>{issue.command}</code>
99
+ </aside>
100
+ )}
101
+ <Adapter
102
+ block={block}
103
+ parentBlockName={parentBlockName}
104
+ contract={contract}
105
+ composition={composition}
106
+ styleflowProps={styleflowProps}
107
+ htmlProps={htmlProps}
108
+ context={{
109
+ ...(parentBlockName ? { parentBlockName } : {}),
110
+ depth,
111
+ production
112
+ }}
113
+ adapters={adapters}
114
+ >
115
+ <Astro.self
116
+ composition={composition}
117
+ adapters={adapters}
118
+ blocks={children}
119
+ contract={contract}
120
+ parentBlockName={block.name}
121
+ production={production}
122
+ depth={depth + 1}
123
+ />
124
+ </Adapter>
125
+ </>
126
+ );
127
+ })}
@@ -0,0 +1,35 @@
1
+ ---
2
+ import type {
3
+ FlowpressBlockRendererProps,
4
+ FlowpressBlockRenderers
5
+ } from "../block-renderers.js";
6
+ import GutenbergRenderer from "./GutenbergRenderer.astro";
7
+
8
+ const {
9
+ block,
10
+ contract,
11
+ styleflowProps,
12
+ htmlProps,
13
+ composition,
14
+ adapters,
15
+ context
16
+ } = Astro.props as FlowpressBlockRendererProps;
17
+ const blockRenderers = Object.fromEntries(
18
+ Object.entries(adapters).map(([name, definition]) => [name, definition.renderer])
19
+ ) as FlowpressBlockRenderers;
20
+ ---
21
+
22
+ <GutenbergRenderer
23
+ blocks={[block]}
24
+ contract={contract}
25
+ blockRenderers={blockRenderers}
26
+ adapterDefinitions={adapters}
27
+ composition={composition}
28
+ rootHtmlProps={htmlProps}
29
+ rootStyleflowProps={styleflowProps}
30
+ parentBlockName={context.parentBlockName}
31
+ production={context.production}
32
+ depth={context.depth}
33
+ skipCustomRendererFor={block.name}
34
+ requireRegisteredAdapters
35
+ />
@@ -11,7 +11,12 @@ const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.cont
11
11
  //TODO SET GLOBAL LOCAL ON ENV
12
12
  ---
13
13
 
14
- <BaseLayout lang={route.locale || "it"} seo={route.seo} theme={styleflowContract?.defaultTheme}>
14
+ <BaseLayout
15
+ lang={route.locale || "it"}
16
+ seo={route.seo}
17
+ theme={styleflowContract?.defaultTheme}
18
+ themes={styleflowContract?.semantic.themes}
19
+ >
15
20
 
16
21
  {hasGutembergBlocks ? (
17
22
  <GutenbergRenderer