@styleflow.app/flowpress-astro-core 1.0.0-beta.5 → 1.0.0-beta.6

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 (53) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +41 -3
  3. package/dist/astro/BaseLayout.astro +5 -1
  4. package/dist/astro/CompositionRenderer.astro +126 -0
  5. package/dist/astro/DefaultTemplate.astro +6 -1
  6. package/dist/astro/GutenbergRenderer.astro +62 -51
  7. package/dist/astro/ThemeRuntime.astro +24 -0
  8. package/dist/astro/blocks/CarouselBlock.astro +4 -6
  9. package/dist/astro/blocks/CarouselItemBlock.astro +2 -2
  10. package/dist/astro/blocks/ColumnBlock.astro +2 -3
  11. package/dist/astro/blocks/FormBlock.astro +5 -5
  12. package/dist/astro/blocks/GridBlock.astro +2 -2
  13. package/dist/astro/blocks/HeroBlock.astro +2 -3
  14. package/dist/astro/blocks/ImageBlock.astro +2 -3
  15. package/dist/astro/blocks/ListItemFallback.astro +4 -4
  16. package/dist/astro/blocks/NumberSpinnerBlock.astro +24 -18
  17. package/dist/astro/blocks/SemanticElement.astro +35 -0
  18. package/dist/astro/blocks/SiteLogoBlock.astro +53 -0
  19. package/dist/astro/blocks/TextBlock.astro +27 -23
  20. package/dist/astro/blocks/ThemeToggleBlock.astro +23 -0
  21. package/dist/astro/blocks/helpers.d.ts +7 -0
  22. package/dist/astro/blocks/helpers.d.ts.map +1 -1
  23. package/dist/astro/blocks/helpers.js +49 -36
  24. package/dist/astro/blocks/helpers.js.map +1 -1
  25. package/dist/astro/blocks/helpers.ts +61 -40
  26. package/dist/block-renderers.d.ts +18 -2
  27. package/dist/block-renderers.d.ts.map +1 -1
  28. package/dist/block-renderers.js.map +1 -1
  29. package/dist/client.d.ts +2 -1
  30. package/dist/client.d.ts.map +1 -1
  31. package/dist/client.js +31 -0
  32. package/dist/client.js.map +1 -1
  33. package/dist/composition.d.ts +14 -0
  34. package/dist/composition.d.ts.map +1 -0
  35. package/dist/composition.js +86 -0
  36. package/dist/composition.js.map +1 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +2 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/styleflow.d.ts +2 -0
  42. package/dist/styleflow.d.ts.map +1 -1
  43. package/dist/styleflow.js +35 -1
  44. package/dist/styleflow.js.map +1 -1
  45. package/dist/theme-runtime.d.ts +16 -0
  46. package/dist/theme-runtime.d.ts.map +1 -0
  47. package/dist/theme-runtime.js +77 -0
  48. package/dist/theme-runtime.js.map +1 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/dist/types.d.ts +73 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js.map +1 -1
  53. package/package.json +7 -3
@@ -69,8 +69,7 @@ const fetchPriority = fetchPriorityValue(attributes.fetchPriority);
69
69
  ---
70
70
 
71
71
  {src ? (
72
- <S
73
- as="div"
72
+ <S.div
74
73
  {...semanticProps(attributes, contract)}
75
74
  {...blockHtmlAttributes(attributes)}
76
75
  >
@@ -86,5 +85,5 @@ const fetchPriority = fetchPriorityValue(attributes.fetchPriority);
86
85
  decoding={decoding}
87
86
  fetchpriority={fetchPriority}
88
87
  />
89
- </S>
88
+ </S.div>
90
89
  ) : null}
@@ -22,9 +22,9 @@ const href = flowpressLinkHref(item.uri);
22
22
  />
23
23
  ) : null}
24
24
  {href ? (
25
- <S as="a" href={href}>{String(item.title ?? "")}</S>
25
+ <S.a href={href}>{String(item.title ?? "")}</S.a>
26
26
  ) : (
27
- <S as="span">{String(item.title ?? "")}</S>
27
+ <S.span>{String(item.title ?? "")}</S.span>
28
28
  )}
29
- {item.excerpt ? <S as="p">{String(item.excerpt)}</S> : null}
30
- {item.priceHtml ? <S as="div" set:html={String(item.priceHtml)} /> : null}
29
+ {item.excerpt ? <S.p>{String(item.excerpt)}</S.p> : null}
30
+ {item.priceHtml ? <S.div set:html={String(item.priceHtml)} /> : null}
@@ -12,6 +12,7 @@ import {
12
12
  semanticProps,
13
13
  textHtmlTag
14
14
  } from "./helpers.js";
15
+ import SemanticElement from "./SemanticElement.astro";
15
16
 
16
17
  interface Props {
17
18
  block: FlowpressBlock;
@@ -56,8 +57,7 @@ const numberTag = textHtmlTag(attributes.numberTag);
56
57
  const suffixTag = textHtmlTag(attributes.suffixTag);
57
58
  ---
58
59
 
59
- <S
60
- as="div"
60
+ <S.div
61
61
  {...semantic}
62
62
  {...htmlAttributes}
63
63
  data-flowpress-number-spinner
@@ -67,18 +67,22 @@ const suffixTag = textHtmlTag(attributes.suffixTag);
67
67
  data-spinner-locale={locale || undefined}
68
68
  >
69
69
  {prefix ? (
70
- <S
71
- as={prefixTag}
72
- class="flowpress-number-spinner__segment"
73
- {...prefixSemantic}
70
+ <SemanticElement
71
+ tag={prefixTag}
72
+ attributes={{
73
+ class: "flowpress-number-spinner__segment",
74
+ ...prefixSemantic
75
+ }}
74
76
  >
75
77
  {prefix}
76
- </S>
78
+ </SemanticElement>
77
79
  ) : null}
78
- <S
79
- as={numberTag}
80
- class="flowpress-number-spinner__segment flowpress-number-spinner__number"
81
- {...numberSemantic}
80
+ <SemanticElement
81
+ tag={numberTag}
82
+ attributes={{
83
+ class: "flowpress-number-spinner__segment flowpress-number-spinner__number",
84
+ ...numberSemantic
85
+ }}
82
86
  >
83
87
  <span class="flowpress-number-spinner__accessible-value">
84
88
  {numberText}
@@ -89,17 +93,19 @@ const suffixTag = textHtmlTag(attributes.suffixTag);
89
93
  >
90
94
  {numberText}
91
95
  </span>
92
- </S>
96
+ </SemanticElement>
93
97
  {suffix ? (
94
- <S
95
- as={suffixTag}
96
- class="flowpress-number-spinner__segment"
97
- {...suffixSemantic}
98
+ <SemanticElement
99
+ tag={suffixTag}
100
+ attributes={{
101
+ class: "flowpress-number-spinner__segment",
102
+ ...suffixSemantic
103
+ }}
98
104
  >
99
105
  {suffix}
100
- </S>
106
+ </SemanticElement>
101
107
  ) : null}
102
- </S>
108
+ </S.div>
103
109
 
104
110
  <script>
105
111
  import { initializeNumberSpinners } from "../../number-spinner.js";
@@ -0,0 +1,35 @@
1
+ ---
2
+ import S from "@styleflow.app/astro/S.astro";
3
+
4
+ type SemanticElementTag =
5
+ | "div" | "span" | "p" | "a" | "li" | "ol" | "ul" | "blockquote"
6
+ | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "strong" | "em"
7
+ | "small" | "mark";
8
+
9
+ interface Props {
10
+ tag: SemanticElementTag;
11
+ attributes?: Record<string, unknown>;
12
+ html?: string;
13
+ }
14
+
15
+ const { tag, attributes = {}, html } = Astro.props;
16
+ ---
17
+
18
+ {tag === "div" ? <S.div {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.div>
19
+ : tag === "span" ? <S.span {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.span>
20
+ : tag === "p" ? <S.p {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.p>
21
+ : tag === "a" ? <S.a {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.a>
22
+ : tag === "li" ? <S.li {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.li>
23
+ : tag === "ol" ? <S.ol {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.ol>
24
+ : tag === "ul" ? <S.ul {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.ul>
25
+ : tag === "blockquote" ? <S.blockquote {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.blockquote>
26
+ : tag === "h1" ? <S.h1 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h1>
27
+ : tag === "h2" ? <S.h2 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h2>
28
+ : tag === "h3" ? <S.h3 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h3>
29
+ : tag === "h4" ? <S.h4 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h4>
30
+ : tag === "h5" ? <S.h5 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h5>
31
+ : tag === "h6" ? <S.h6 {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.h6>
32
+ : tag === "strong" ? <S.strong {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.strong>
33
+ : tag === "em" ? <S.em {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.em>
34
+ : tag === "small" ? <S.small {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.small>
35
+ : <S.mark {...attributes}>{html === undefined ? <slot /> : <Fragment set:html={html} />}</S.mark>}
@@ -0,0 +1,53 @@
1
+ ---
2
+ import S from "@styleflow.app/astro/S.astro";
3
+ import type { FlowpressBlockRendererProps } from "../../block-renderers.js";
4
+
5
+ type LogoImage = { url?: string; width?: number; height?: number; alt?: string };
6
+ type LogoData = {
7
+ images?: { positive?: LogoImage; negative?: LogoImage };
8
+ homeUrl?: string;
9
+ override?: "auto" | "positive" | "negative";
10
+ };
11
+
12
+ const { block, styleflowProps, htmlProps } = Astro.props as FlowpressBlockRendererProps;
13
+ const data = (block.resolvedData ?? {}) as LogoData;
14
+ const positive = data.images?.positive ?? {};
15
+ const negative = data.images?.negative ?? {};
16
+ const homeUrl = typeof data.homeUrl === "string" && data.homeUrl ? data.homeUrl : "/";
17
+ const override = data.override === "positive" || data.override === "negative"
18
+ ? data.override
19
+ : "auto";
20
+ const rootHtmlProps = {
21
+ ...htmlProps,
22
+ class: ["flowpress-site-logo", htmlProps.class].filter(Boolean).join(" ")
23
+ };
24
+ ---
25
+
26
+ {positive.url || negative.url ? (
27
+ <S.a
28
+ {...rootHtmlProps}
29
+ {...styleflowProps}
30
+ href={homeUrl}
31
+ data-flowpress-logo-override={override === "auto" ? undefined : override}
32
+ aria-label="Home"
33
+ >
34
+ {positive.url ? (
35
+ <img
36
+ class="flowpress-site-logo__image flowpress-site-logo__image--positive"
37
+ src={positive.url}
38
+ width={positive.width || undefined}
39
+ height={positive.height || undefined}
40
+ alt={positive.alt ?? ""}
41
+ />
42
+ ) : null}
43
+ {negative.url ? (
44
+ <img
45
+ class="flowpress-site-logo__image flowpress-site-logo__image--negative"
46
+ src={negative.url}
47
+ width={negative.width || undefined}
48
+ height={negative.height || undefined}
49
+ alt={negative.alt ?? ""}
50
+ />
51
+ ) : null}
52
+ </S.a>
53
+ ) : null}
@@ -7,10 +7,12 @@ import {
7
7
  preserveEmptyTextBlock,
8
8
  preserveRichTextSpacing,
9
9
  semanticProps,
10
+ textBlockSemanticDefaults,
10
11
  textAlignmentStyle,
11
12
  textValue,
12
13
  unwrapCoreHtml
13
14
  } from "./helpers.js";
15
+ import SemanticElement from "./SemanticElement.astro";
14
16
 
15
17
  interface Props {
16
18
  block: FlowpressBlock;
@@ -24,19 +26,7 @@ const attributes = block.attributes;
24
26
  const heading = name === "heading" || name === "core/heading";
25
27
  const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));
26
28
  const headingTag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
27
- const defaults = heading
28
- ? {
29
- ty: "heading",
30
- v: String(attributes.v ?? attributes.variant ?? level),
31
- w: String(attributes.w ?? attributes.weight ?? "strong"),
32
- foreground: "primary"
33
- }
34
- : {
35
- ty: "body",
36
- v: String(attributes.v ?? (name.includes("quote") ? "lg" : "md")),
37
- w: String(attributes.w ?? "default"),
38
- foreground: "primary"
39
- };
29
+ const defaults = textBlockSemanticDefaults(name, attributes, contract);
40
30
  const semantic = semanticProps(attributes, contract, defaults);
41
31
  const htmlAttributes = blockHtmlAttributes(attributes);
42
32
  const alignmentStyle = textAlignmentStyle(attributes);
@@ -60,24 +50,38 @@ const listTag = Boolean(attributes.ordered) ? "ol" : "ul";
60
50
  ---
61
51
 
62
52
  {heading ? (
63
- <S as={headingTag} {...semantic} {...htmlAttributes} style={alignmentStyle} set:html={html} />
53
+ <SemanticElement
54
+ tag={headingTag}
55
+ attributes={{ ...semantic, ...htmlAttributes, style: alignmentStyle }}
56
+ html={html}
57
+ />
64
58
  ) : name === "core/list" ? (
65
- <S as={listTag} {...semantic} {...htmlAttributes} style={alignmentStyle}>
66
- {hasChildren ? <slot /> : <Fragment set:html={html} />}
67
- </S>
59
+ <SemanticElement
60
+ tag={listTag}
61
+ attributes={{ ...semantic, ...htmlAttributes, style: alignmentStyle }}
62
+ html={hasChildren ? undefined : html}
63
+ >
64
+ {hasChildren ? <slot /> : null}
65
+ </SemanticElement>
68
66
  ) : name === "core/list-item" ? (
69
- <S as="li" {...semantic} {...htmlAttributes} style={alignmentStyle}>
67
+ <S.li {...semantic} {...htmlAttributes} style={alignmentStyle}>
70
68
  <span set:html={html}></span>
71
69
  {hasChildren ? <slot /> : null}
72
- </S>
70
+ </S.li>
73
71
  ) : name === "quote" || name === "core/quote" ? (
74
- <S as="blockquote" {...semantic} {...htmlAttributes} style={alignmentStyle}>
72
+ <S.blockquote {...semantic} {...htmlAttributes} style={alignmentStyle}>
75
73
  {hasChildren ? <slot /> : <div set:html={html}></div>}
76
74
  {attributes.citation ? <cite>{String(attributes.citation)}</cite> : null}
77
- </S>
75
+ </S.blockquote>
76
+ ) : name === "core/paragraph" ? (
77
+ <S.p
78
+ {...semantic}
79
+ {...htmlAttributes}
80
+ style={alignmentStyle}
81
+ set:html={html}
82
+ />
78
83
  ) : (
79
- <S
80
- as={name === "core/paragraph" ? "p" : "div"}
84
+ <S.div
81
85
  {...semantic}
82
86
  {...htmlAttributes}
83
87
  style={alignmentStyle}
@@ -0,0 +1,23 @@
1
+ ---
2
+ import S from "@styleflow.app/astro/S.astro";
3
+ import type { FlowpressBlockRendererProps } from "../../block-renderers.js";
4
+
5
+ const { block, styleflowProps, htmlProps, composition } = Astro.props as FlowpressBlockRendererProps;
6
+ const label = typeof block.attributes.label === "string" && block.attributes.label.trim()
7
+ ? block.attributes.label.trim()
8
+ : "Change color theme";
9
+ const showThemeName = block.attributes.showThemeName !== false;
10
+ const themeName = "Theme";
11
+ ---
12
+
13
+ <S.button
14
+ {...htmlProps}
15
+ {...styleflowProps}
16
+ type="button"
17
+ aria-label={label}
18
+ data-styleflow-theme-toggle
19
+ >
20
+ <span data-styleflow-theme-label aria-hidden="true">
21
+ {showThemeName ? String(themeName) : "Theme"}
22
+ </span>
23
+ </S.button>
@@ -13,6 +13,13 @@ export declare function blockHtmlAttributes(attributes: Record<string, unknown>,
13
13
  };
14
14
  export declare const TEXT_HTML_TAGS: readonly ["span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "small", "mark"];
15
15
  export type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];
16
+ /**
17
+ * Derive a variant only when Gutenberg omitted it. The selected typography
18
+ * type can differ from the block's native type, so a paragraph styled as a
19
+ * heading must not inherit the paragraph-only `md` variant.
20
+ */
21
+ export declare function compatibleTypographyVariant(type: string, preferred: string, contract?: StyleflowContract): string;
22
+ export declare function textBlockSemanticDefaults(blockName: string, attributes: Record<string, unknown>, contract?: StyleflowContract): Record<string, unknown>;
16
23
  export declare function textHtmlTag(value: unknown): TextHtmlTag;
17
24
  export declare function semanticProps(attributes: Record<string, unknown>, contract?: StyleflowContract, defaults?: Record<string, unknown>): Record<string, unknown>;
18
25
  /** Resolve only Gutenberg's supported text-alignment values to inline CSS. */
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,eAAO,MAAM,sBAAsB;;;;CAIlC,CAAC;AAEF,eAAO,MAAM,4BAA4B,YAAY,CAAC;AAEtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBzD;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,SAAS,SAAK,GACb;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBjC;AAsBD,eAAO,MAAM,cAAc,6FAajB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAKvD;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAA0B,GACzD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAWD,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAErE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB5D;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAErE;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,SAAS,GAAG,MAAM,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,MAAM,CAAC,CAe5D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,eAAO,MAAM,sBAAsB;;;;CAIlC,CAAC;AAEF,eAAO,MAAM,4BAA4B,YAAY,CAAC;AAEtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBzD;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,SAAS,SAAK,GACb;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBjC;AAED,eAAO,MAAM,cAAc,6FAajB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAQ1D;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,MAAM,CAcR;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAKvD;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAA0B,GACzD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAWD,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAErE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB5D;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAErE;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,SAAS,GAAG,MAAM,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,MAAM,CAAC,CAe5D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD"}
@@ -1,4 +1,4 @@
1
- import { resolveDynamicStyleflowProps } from "../../styleflow.js";
1
+ import { resolveFlowpressBlockStyleflowProps } from "../../styleflow.js";
2
2
  import { flowpressLinkHref } from "../../links.js";
3
3
  export const DEFAULT_SEMANTIC_PROPS = {
4
4
  surface: "default",
@@ -36,25 +36,6 @@ export function blockHtmlAttributes(attributes, baseClass = "") {
36
36
  ...(className ? { class: className } : {})
37
37
  };
38
38
  }
39
- const SEMANTIC_KEYS = [
40
- "theme",
41
- "tone",
42
- "intensity",
43
- "surface",
44
- "layoutRole",
45
- "density",
46
- "ty",
47
- "v",
48
- "w",
49
- "foreground",
50
- "interactivePriority",
51
- "noBackground",
52
- "noBorder",
53
- "noPadding",
54
- "noGap",
55
- "noRadius",
56
- "noForeground"
57
- ];
58
39
  export const TEXT_HTML_TAGS = [
59
40
  "span",
60
41
  "p",
@@ -69,6 +50,50 @@ export const TEXT_HTML_TAGS = [
69
50
  "small",
70
51
  "mark"
71
52
  ];
53
+ function preferredTypographyVariant(type) {
54
+ if (type === "heading")
55
+ return "2";
56
+ if (type === "body" || type === "label" || type === "code")
57
+ return "md";
58
+ return undefined;
59
+ }
60
+ /**
61
+ * Derive a variant only when Gutenberg omitted it. The selected typography
62
+ * type can differ from the block's native type, so a paragraph styled as a
63
+ * heading must not inherit the paragraph-only `md` variant.
64
+ */
65
+ export function compatibleTypographyVariant(type, preferred, contract) {
66
+ const variants = contract?.semantic.axes.typography.variantsByType[type];
67
+ if (!variants?.length)
68
+ return preferred;
69
+ return [
70
+ preferredTypographyVariant(type),
71
+ preferred,
72
+ contract?.semantic.agentPolicy.defaults.typography.ty === type
73
+ ? contract.semantic.agentPolicy.defaults.typography.v
74
+ : undefined,
75
+ variants[0]
76
+ ].find((candidate) => (typeof candidate === "string" && variants.includes(candidate))) ?? preferred;
77
+ }
78
+ export function textBlockSemanticDefaults(blockName, attributes, contract) {
79
+ const name = blockName.replace(/^sf\//, "");
80
+ const heading = name === "heading" || name === "core/heading";
81
+ const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));
82
+ const defaultType = heading ? "heading" : "body";
83
+ const activeType = String(attributes.ty ?? defaultType);
84
+ const nativeVariant = heading
85
+ ? String(level)
86
+ : name.includes("quote") ? "lg" : "md";
87
+ const authoredVariant = attributes.v ?? attributes.variant;
88
+ return {
89
+ ty: defaultType,
90
+ v: authoredVariant === undefined
91
+ ? compatibleTypographyVariant(activeType, nativeVariant, contract)
92
+ : String(authoredVariant),
93
+ w: String(attributes.w ?? attributes.weight ?? (heading ? "strong" : "default")),
94
+ foreground: "primary"
95
+ };
96
+ }
72
97
  export function textHtmlTag(value) {
73
98
  const candidate = String(value ?? "span");
74
99
  return TEXT_HTML_TAGS.includes(candidate)
@@ -76,22 +101,10 @@ export function textHtmlTag(value) {
76
101
  : "span";
77
102
  }
78
103
  export function semanticProps(attributes, contract, defaults = DEFAULT_SEMANTIC_PROPS) {
79
- const input = Object.fromEntries(SEMANTIC_KEYS
80
- .filter((key) => attributes[key] !== undefined)
81
- .map((key) => [key, attributes[key]]));
82
- if (contract) {
83
- return resolveDynamicStyleflowProps(input, defaults, contract, {
84
- strict: process.env.NODE_ENV === "production" || process.env.STYLEFLOW_STRICT === "1",
85
- onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)
86
- });
87
- }
88
- const resolved = { ...defaults, ...input };
89
- for (const key of ["tone", "intensity", "foreground"]) {
90
- if (input[key] === "inherit") {
91
- delete resolved[key];
92
- }
93
- }
94
- return resolved;
104
+ return resolveFlowpressBlockStyleflowProps(attributes, contract, defaults, {
105
+ strict: process.env.NODE_ENV === "production" || process.env.STYLEFLOW_STRICT === "1",
106
+ onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)
107
+ });
95
108
  }
96
109
  const TEXT_ALIGNMENTS = new Set([
97
110
  "left",
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveDynamicStyleflowProps,\n type StyleflowContract\n} from \"../../styleflow.js\";\nimport type { FlowpressBlock } from \"../../types.js\";\nimport { flowpressLinkHref } from \"../../links.js\";\n\nexport const DEFAULT_SEMANTIC_PROPS = {\n surface: \"default\",\n layoutRole: \"stack\",\n density: \"regular\"\n};\n\nexport const DEFAULT_HERO_OBJECT_POSITION = \"50% 50%\";\n\nexport function heroObjectPosition(value: unknown): string {\n if (typeof value !== \"string\") {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n const match = value.trim().match(\n /^(\\d+(?:\\.\\d+)?|\\.\\d+)%\\s+(\\d+(?:\\.\\d+)?|\\.\\d+)%$/\n );\n if (\n !match\n || [match[1], match[2]].some((coordinate) => {\n const numeric = Number(coordinate);\n return !Number.isFinite(numeric) || numeric < 0 || numeric > 100;\n })\n ) {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n return `${match[1]}% ${match[2]}%`;\n}\n\nexport function blockHtmlAttributes(\n attributes: Record<string, unknown>,\n baseClass = \"\"\n): { id?: string; class?: string } {\n const anchor = typeof attributes.anchor === \"string\"\n ? attributes.anchor.trim()\n : \"\";\n const customClass = typeof attributes.className === \"string\"\n ? attributes.className.trim()\n : \"\";\n const className = [baseClass.trim(), customClass]\n .filter(Boolean)\n .join(\" \")\n .replace(/\\s+/g, \" \");\n\n return {\n ...(anchor ? { id: anchor } : {}),\n ...(className ? { class: className } : {})\n };\n}\n\nconst SEMANTIC_KEYS = [\n \"theme\",\n \"tone\",\n \"intensity\",\n \"surface\",\n \"layoutRole\",\n \"density\",\n \"ty\",\n \"v\",\n \"w\",\n \"foreground\",\n \"interactivePriority\",\n \"noBackground\",\n \"noBorder\",\n \"noPadding\",\n \"noGap\",\n \"noRadius\",\n \"noForeground\"\n] as const;\n\nexport const TEXT_HTML_TAGS = [\n \"span\",\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"small\",\n \"mark\"\n] as const;\n\nexport type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];\n\nexport function textHtmlTag(value: unknown): TextHtmlTag {\n const candidate = String(value ?? \"span\");\n return TEXT_HTML_TAGS.includes(candidate as TextHtmlTag)\n ? candidate as TextHtmlTag\n : \"span\";\n}\n\nexport function semanticProps(\n attributes: Record<string, unknown>,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS\n): Record<string, unknown> {\n const input = Object.fromEntries(\n SEMANTIC_KEYS\n .filter((key) => attributes[key] !== undefined)\n .map((key) => [key, attributes[key]])\n );\n if (contract) {\n return resolveDynamicStyleflowProps(input, defaults, contract, {\n strict: process.env.NODE_ENV === \"production\" || process.env.STYLEFLOW_STRICT === \"1\",\n onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)\n });\n }\n\n const resolved = { ...defaults, ...input };\n for (const key of [\"tone\", \"intensity\", \"foreground\"]) {\n if (input[key] === \"inherit\") {\n delete resolved[key];\n }\n }\n return resolved;\n}\n\nconst TEXT_ALIGNMENTS = new Set([\n \"left\",\n \"center\",\n \"right\",\n \"justify\",\n \"start\",\n \"end\"\n]);\n\n/** Resolve only Gutenberg's supported text-alignment values to inline CSS. */\nexport function textAlignmentStyle(\n attributes: Record<string, unknown>\n): string | undefined {\n const style = attributes.style && typeof attributes.style === \"object\"\n && !Array.isArray(attributes.style)\n ? attributes.style as Record<string, unknown>\n : {};\n const typography = style.typography && typeof style.typography === \"object\"\n && !Array.isArray(style.typography)\n ? style.typography as Record<string, unknown>\n : {};\n const candidate = typography.textAlign ?? attributes.textAlign;\n const alignment = String(candidate ?? \"\");\n return TEXT_ALIGNMENTS.has(alignment) ? `text-align: ${alignment};` : undefined;\n}\n\nexport function prefixedTextSemanticProps(\n attributes: Record<string, unknown>,\n prefix: string,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = {}\n): Record<string, unknown> {\n const attributeName = (name: string) =>\n `${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}`;\n return semanticProps({\n ty: attributes[attributeName(\"ty\")],\n v: attributes[attributeName(\"v\")],\n w: attributes[attributeName(\"w\")],\n foreground: attributes[attributeName(\"foreground\")]\n }, contract, defaults);\n}\n\nexport function objectValue(\n block: FlowpressBlock,\n key: string\n): Record<string, unknown> {\n const value = block.attributes[key];\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n}\n\nexport function textValue(block: FlowpressBlock, key = \"text\"): string {\n return String(block.attributes[key] ?? \"\");\n}\n\n/**\n * Keep intentional blank lines visible across current and legacy RichText\n * serializations. Gutenberg can represent them either as repeated `<br>` tags,\n * empty paragraph elements, or literal newlines in an attribute value.\n */\nexport function preserveRichTextSpacing(html: string): string {\n const visibleEmptyBlocks = html\n .replace(/\\r\\n?/g, \"\\n\")\n .replace(\n /<(p|div)(\\s[^>]*)?>(?:\\s|&nbsp;|&#160;)*<\\/\\1>/gi,\n (_match, tag: string, attributes = \"\") =>\n `<${tag}${attributes}>&nbsp;</${tag}>`\n );\n\n return visibleEmptyBlocks\n .split(/(<[^>]+>)/g)\n .map((part) => {\n if (part.startsWith(\"<\") || part.trim() === \"\") {\n return part;\n }\n return part.replace(/\\n(?:[ \\t]*\\n)+/g, (newlines) =>\n \"<br>\".repeat((newlines.match(/\\n/g) || []).length)\n );\n })\n .join(\"\");\n}\n\n/** Keep an empty text element at its typographic line height without visible text. */\nexport function preserveEmptyTextBlock(html: string): string {\n return html.trim() === \"\" ? \"&nbsp;\" : html;\n}\n\nexport function linkValue(block: FlowpressBlock, key = \"link\"): string {\n return flowpressLinkHref(block.attributes[key]);\n}\n\nexport function responsiveValues(\n attributes: Record<string, unknown>,\n prefix: \"columns\" | \"span\",\n fallback: number\n): Record<\"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", number> {\n const keys = [\"base\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\"] as const;\n const result = {} as Record<(typeof keys)[number], number>;\n let inherited = fallback;\n for (const key of keys) {\n const suffix = key === \"base\"\n ? \"Base\"\n : key === \"2xl\" ? \"2xl\" : key.charAt(0).toUpperCase() + key.slice(1);\n const candidate = Number(attributes[`${prefix}${suffix}`]);\n if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {\n inherited = candidate;\n }\n result[key] = inherited;\n }\n return result;\n}\n\nexport function unwrapCoreHtml(html: string): string {\n const match = html.trim().match(\n /^<(p|h[1-6]|ul|ol|li|blockquote)(?:\\s[^>]*)?>([\\s\\S]*)<\\/\\1>$/\n );\n return match?.[2] ?? html;\n}\n"]}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveFlowpressBlockStyleflowProps,\n type StyleflowContract\n} from \"../../styleflow.js\";\nimport type { FlowpressBlock } from \"../../types.js\";\nimport { flowpressLinkHref } from \"../../links.js\";\n\nexport const DEFAULT_SEMANTIC_PROPS = {\n surface: \"default\",\n layoutRole: \"stack\",\n density: \"regular\"\n};\n\nexport const DEFAULT_HERO_OBJECT_POSITION = \"50% 50%\";\n\nexport function heroObjectPosition(value: unknown): string {\n if (typeof value !== \"string\") {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n const match = value.trim().match(\n /^(\\d+(?:\\.\\d+)?|\\.\\d+)%\\s+(\\d+(?:\\.\\d+)?|\\.\\d+)%$/\n );\n if (\n !match\n || [match[1], match[2]].some((coordinate) => {\n const numeric = Number(coordinate);\n return !Number.isFinite(numeric) || numeric < 0 || numeric > 100;\n })\n ) {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n return `${match[1]}% ${match[2]}%`;\n}\n\nexport function blockHtmlAttributes(\n attributes: Record<string, unknown>,\n baseClass = \"\"\n): { id?: string; class?: string } {\n const anchor = typeof attributes.anchor === \"string\"\n ? attributes.anchor.trim()\n : \"\";\n const customClass = typeof attributes.className === \"string\"\n ? attributes.className.trim()\n : \"\";\n const className = [baseClass.trim(), customClass]\n .filter(Boolean)\n .join(\" \")\n .replace(/\\s+/g, \" \");\n\n return {\n ...(anchor ? { id: anchor } : {}),\n ...(className ? { class: className } : {})\n };\n}\n\nexport const TEXT_HTML_TAGS = [\n \"span\",\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"small\",\n \"mark\"\n] as const;\n\nexport type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];\n\nfunction preferredTypographyVariant(type: string): string | undefined {\n if (type === \"heading\") return \"2\";\n if (type === \"body\" || type === \"label\" || type === \"code\") return \"md\";\n return undefined;\n}\n\n/**\n * Derive a variant only when Gutenberg omitted it. The selected typography\n * type can differ from the block's native type, so a paragraph styled as a\n * heading must not inherit the paragraph-only `md` variant.\n */\nexport function compatibleTypographyVariant(\n type: string,\n preferred: string,\n contract?: StyleflowContract\n): string {\n const variants = contract?.semantic.axes.typography.variantsByType[type];\n if (!variants?.length) return preferred;\n\n return [\n preferredTypographyVariant(type),\n preferred,\n contract?.semantic.agentPolicy.defaults.typography.ty === type\n ? contract.semantic.agentPolicy.defaults.typography.v\n : undefined,\n variants[0]\n ].find((candidate): candidate is string => (\n typeof candidate === \"string\" && variants.includes(candidate)\n )) ?? preferred;\n}\n\nexport function textBlockSemanticDefaults(\n blockName: string,\n attributes: Record<string, unknown>,\n contract?: StyleflowContract\n): Record<string, unknown> {\n const name = blockName.replace(/^sf\\//, \"\");\n const heading = name === \"heading\" || name === \"core/heading\";\n const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));\n const defaultType = heading ? \"heading\" : \"body\";\n const activeType = String(attributes.ty ?? defaultType);\n const nativeVariant = heading\n ? String(level)\n : name.includes(\"quote\") ? \"lg\" : \"md\";\n const authoredVariant = attributes.v ?? attributes.variant;\n\n return {\n ty: defaultType,\n v: authoredVariant === undefined\n ? compatibleTypographyVariant(activeType, nativeVariant, contract)\n : String(authoredVariant),\n w: String(attributes.w ?? attributes.weight ?? (heading ? \"strong\" : \"default\")),\n foreground: \"primary\"\n };\n}\n\nexport function textHtmlTag(value: unknown): TextHtmlTag {\n const candidate = String(value ?? \"span\");\n return TEXT_HTML_TAGS.includes(candidate as TextHtmlTag)\n ? candidate as TextHtmlTag\n : \"span\";\n}\n\nexport function semanticProps(\n attributes: Record<string, unknown>,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS\n): Record<string, unknown> {\n return resolveFlowpressBlockStyleflowProps(attributes, contract, defaults, {\n strict: process.env.NODE_ENV === \"production\" || process.env.STYLEFLOW_STRICT === \"1\",\n onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)\n });\n}\n\nconst TEXT_ALIGNMENTS = new Set([\n \"left\",\n \"center\",\n \"right\",\n \"justify\",\n \"start\",\n \"end\"\n]);\n\n/** Resolve only Gutenberg's supported text-alignment values to inline CSS. */\nexport function textAlignmentStyle(\n attributes: Record<string, unknown>\n): string | undefined {\n const style = attributes.style && typeof attributes.style === \"object\"\n && !Array.isArray(attributes.style)\n ? attributes.style as Record<string, unknown>\n : {};\n const typography = style.typography && typeof style.typography === \"object\"\n && !Array.isArray(style.typography)\n ? style.typography as Record<string, unknown>\n : {};\n const candidate = typography.textAlign ?? attributes.textAlign;\n const alignment = String(candidate ?? \"\");\n return TEXT_ALIGNMENTS.has(alignment) ? `text-align: ${alignment};` : undefined;\n}\n\nexport function prefixedTextSemanticProps(\n attributes: Record<string, unknown>,\n prefix: string,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = {}\n): Record<string, unknown> {\n const attributeName = (name: string) =>\n `${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}`;\n return semanticProps({\n ty: attributes[attributeName(\"ty\")],\n v: attributes[attributeName(\"v\")],\n w: attributes[attributeName(\"w\")],\n foreground: attributes[attributeName(\"foreground\")]\n }, contract, defaults);\n}\n\nexport function objectValue(\n block: FlowpressBlock,\n key: string\n): Record<string, unknown> {\n const value = block.attributes[key];\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n}\n\nexport function textValue(block: FlowpressBlock, key = \"text\"): string {\n return String(block.attributes[key] ?? \"\");\n}\n\n/**\n * Keep intentional blank lines visible across current and legacy RichText\n * serializations. Gutenberg can represent them either as repeated `<br>` tags,\n * empty paragraph elements, or literal newlines in an attribute value.\n */\nexport function preserveRichTextSpacing(html: string): string {\n const visibleEmptyBlocks = html\n .replace(/\\r\\n?/g, \"\\n\")\n .replace(\n /<(p|div)(\\s[^>]*)?>(?:\\s|&nbsp;|&#160;)*<\\/\\1>/gi,\n (_match, tag: string, attributes = \"\") =>\n `<${tag}${attributes}>&nbsp;</${tag}>`\n );\n\n return visibleEmptyBlocks\n .split(/(<[^>]+>)/g)\n .map((part) => {\n if (part.startsWith(\"<\") || part.trim() === \"\") {\n return part;\n }\n return part.replace(/\\n(?:[ \\t]*\\n)+/g, (newlines) =>\n \"<br>\".repeat((newlines.match(/\\n/g) || []).length)\n );\n })\n .join(\"\");\n}\n\n/** Keep an empty text element at its typographic line height without visible text. */\nexport function preserveEmptyTextBlock(html: string): string {\n return html.trim() === \"\" ? \"&nbsp;\" : html;\n}\n\nexport function linkValue(block: FlowpressBlock, key = \"link\"): string {\n return flowpressLinkHref(block.attributes[key]);\n}\n\nexport function responsiveValues(\n attributes: Record<string, unknown>,\n prefix: \"columns\" | \"span\",\n fallback: number\n): Record<\"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", number> {\n const keys = [\"base\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\"] as const;\n const result = {} as Record<(typeof keys)[number], number>;\n let inherited = fallback;\n for (const key of keys) {\n const suffix = key === \"base\"\n ? \"Base\"\n : key === \"2xl\" ? \"2xl\" : key.charAt(0).toUpperCase() + key.slice(1);\n const candidate = Number(attributes[`${prefix}${suffix}`]);\n if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {\n inherited = candidate;\n }\n result[key] = inherited;\n }\n return result;\n}\n\nexport function unwrapCoreHtml(html: string): string {\n const match = html.trim().match(\n /^<(p|h[1-6]|ul|ol|li|blockquote)(?:\\s[^>]*)?>([\\s\\S]*)<\\/\\1>$/\n );\n return match?.[2] ?? html;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import {
2
- resolveDynamicStyleflowProps,
2
+ resolveFlowpressBlockStyleflowProps,
3
3
  type StyleflowContract
4
4
  } from "../../styleflow.js";
5
5
  import type { FlowpressBlock } from "../../types.js";
@@ -55,26 +55,6 @@ export function blockHtmlAttributes(
55
55
  };
56
56
  }
57
57
 
58
- const SEMANTIC_KEYS = [
59
- "theme",
60
- "tone",
61
- "intensity",
62
- "surface",
63
- "layoutRole",
64
- "density",
65
- "ty",
66
- "v",
67
- "w",
68
- "foreground",
69
- "interactivePriority",
70
- "noBackground",
71
- "noBorder",
72
- "noPadding",
73
- "noGap",
74
- "noRadius",
75
- "noForeground"
76
- ] as const;
77
-
78
58
  export const TEXT_HTML_TAGS = [
79
59
  "span",
80
60
  "p",
@@ -92,6 +72,62 @@ export const TEXT_HTML_TAGS = [
92
72
 
93
73
  export type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];
94
74
 
75
+ function preferredTypographyVariant(type: string): string | undefined {
76
+ if (type === "heading") return "2";
77
+ if (type === "body" || type === "label" || type === "code") return "md";
78
+ return undefined;
79
+ }
80
+
81
+ /**
82
+ * Derive a variant only when Gutenberg omitted it. The selected typography
83
+ * type can differ from the block's native type, so a paragraph styled as a
84
+ * heading must not inherit the paragraph-only `md` variant.
85
+ */
86
+ export function compatibleTypographyVariant(
87
+ type: string,
88
+ preferred: string,
89
+ contract?: StyleflowContract
90
+ ): string {
91
+ const variants = contract?.semantic.axes.typography.variantsByType[type];
92
+ if (!variants?.length) return preferred;
93
+
94
+ return [
95
+ preferredTypographyVariant(type),
96
+ preferred,
97
+ contract?.semantic.agentPolicy.defaults.typography.ty === type
98
+ ? contract.semantic.agentPolicy.defaults.typography.v
99
+ : undefined,
100
+ variants[0]
101
+ ].find((candidate): candidate is string => (
102
+ typeof candidate === "string" && variants.includes(candidate)
103
+ )) ?? preferred;
104
+ }
105
+
106
+ export function textBlockSemanticDefaults(
107
+ blockName: string,
108
+ attributes: Record<string, unknown>,
109
+ contract?: StyleflowContract
110
+ ): Record<string, unknown> {
111
+ const name = blockName.replace(/^sf\//, "");
112
+ const heading = name === "heading" || name === "core/heading";
113
+ const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));
114
+ const defaultType = heading ? "heading" : "body";
115
+ const activeType = String(attributes.ty ?? defaultType);
116
+ const nativeVariant = heading
117
+ ? String(level)
118
+ : name.includes("quote") ? "lg" : "md";
119
+ const authoredVariant = attributes.v ?? attributes.variant;
120
+
121
+ return {
122
+ ty: defaultType,
123
+ v: authoredVariant === undefined
124
+ ? compatibleTypographyVariant(activeType, nativeVariant, contract)
125
+ : String(authoredVariant),
126
+ w: String(attributes.w ?? attributes.weight ?? (heading ? "strong" : "default")),
127
+ foreground: "primary"
128
+ };
129
+ }
130
+
95
131
  export function textHtmlTag(value: unknown): TextHtmlTag {
96
132
  const candidate = String(value ?? "span");
97
133
  return TEXT_HTML_TAGS.includes(candidate as TextHtmlTag)
@@ -104,25 +140,10 @@ export function semanticProps(
104
140
  contract?: StyleflowContract,
105
141
  defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS
106
142
  ): Record<string, unknown> {
107
- const input = Object.fromEntries(
108
- SEMANTIC_KEYS
109
- .filter((key) => attributes[key] !== undefined)
110
- .map((key) => [key, attributes[key]])
111
- );
112
- if (contract) {
113
- return resolveDynamicStyleflowProps(input, defaults, contract, {
114
- strict: process.env.NODE_ENV === "production" || process.env.STYLEFLOW_STRICT === "1",
115
- onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)
116
- });
117
- }
118
-
119
- const resolved = { ...defaults, ...input };
120
- for (const key of ["tone", "intensity", "foreground"]) {
121
- if (input[key] === "inherit") {
122
- delete resolved[key];
123
- }
124
- }
125
- return resolved;
143
+ return resolveFlowpressBlockStyleflowProps(attributes, contract, defaults, {
144
+ strict: process.env.NODE_ENV === "production" || process.env.STYLEFLOW_STRICT === "1",
145
+ onDiagnostic: (diagnostic) => console.warn(`[${diagnostic.code}] ${diagnostic.message}`)
146
+ });
126
147
  }
127
148
 
128
149
  const TEXT_ALIGNMENTS = new Set([