@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239

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 (172) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
  3. package/src/lib/components/ai-credits-prompt.component.js +84 -34
  4. package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
  5. package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
  6. package/src/lib/components/ai-job-cancel.component.js +156 -0
  7. package/src/lib/components/ai-job-cancel.component.js.map +1 -0
  8. package/src/lib/components/ai-job-links.js +8 -0
  9. package/src/lib/components/ai-job-links.js.map +1 -1
  10. package/src/lib/components/ai-job-plan.component.js +12 -1
  11. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  12. package/src/lib/components/ai-jobs-list-page.component.js +141 -72
  13. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  14. package/src/lib/components/ai-jobs-store.d.ts +12 -0
  15. package/src/lib/components/ai-jobs-store.js +15 -0
  16. package/src/lib/components/ai-jobs-store.js.map +1 -1
  17. package/src/lib/components/ai-site-build-page.component.js +22 -2
  18. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  19. package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
  20. package/src/lib/components/ai-usage-meter-refresh.js +63 -0
  21. package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
  22. package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
  23. package/src/lib/components/ai-usage-strip.component.js +40 -9
  24. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  25. package/src/lib/components/assist-jobs-drawer.component.js +12 -16
  26. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  27. package/src/lib/components/assist-panel.component.js +91 -51
  28. package/src/lib/components/assist-panel.component.js.map +1 -1
  29. package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
  30. package/src/lib/components/use-ai-usage-meter.js +8 -3
  31. package/src/lib/components/use-ai-usage-meter.js.map +1 -1
  32. package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
  33. package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
  34. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  35. package/src/lib/jobs/ai-job-build-step.js +33 -3
  36. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
  38. package/src/lib/jobs/ai-job-cancel.js +102 -0
  39. package/src/lib/jobs/ai-job-cancel.js.map +1 -0
  40. package/src/lib/jobs/ai-job-component-step.js +4 -1
  41. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  42. package/src/lib/jobs/ai-job-draft-ids.js +7 -2
  43. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  44. package/src/lib/jobs/ai-job-drafts.js +2 -0
  45. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  46. package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
  47. package/src/lib/jobs/ai-job-form-dataset.js +161 -0
  48. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
  49. package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
  50. package/src/lib/jobs/ai-job-form-step.js +41 -10
  51. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  52. package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
  53. package/src/lib/jobs/ai-job-layout-language.js +13 -1
  54. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  55. package/src/lib/jobs/ai-job-layout-step.js +6 -1
  56. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  57. package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
  58. package/src/lib/jobs/ai-job-page-language.js +45 -2
  59. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  60. package/src/lib/jobs/ai-job-page-step.js +12 -1
  61. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  62. package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
  63. package/src/lib/jobs/ai-job-plan-step.js +90 -13
  64. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  65. package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
  66. package/src/lib/jobs/ai-job-site-content.js +66 -7
  67. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  68. package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
  69. package/src/lib/jobs/ai-job-site-datasets.js +499 -0
  70. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
  71. package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
  72. package/src/lib/jobs/ai-job-site-step.js +152 -30
  73. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  74. package/src/lib/jobs/ai-job-template-step.js +4 -1
  75. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  76. package/src/lib/jobs/ai-jobs.d.ts +37 -12
  77. package/src/lib/jobs/ai-jobs.js +184 -28
  78. package/src/lib/jobs/ai-jobs.js.map +1 -1
  79. package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
  80. package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
  81. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  82. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
  83. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
  84. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
  85. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
  86. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
  87. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
  88. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
  89. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
  90. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
  91. package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
  92. package/src/lib/layout-language/ai-layout-compiler.js +397 -9
  93. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  94. package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
  95. package/src/lib/layout-language/ai-layout-design.js +5 -3
  96. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  97. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  98. package/src/lib/layout-language/ai-layout-frame.js +58 -3
  99. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  100. package/src/lib/layout-language/ai-layout-gaps.js +17 -1
  101. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  102. package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
  103. package/src/lib/layout-language/ai-layout-listings.js +118 -50
  104. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  105. package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
  106. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  107. package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
  108. package/src/lib/layout-language/ai-layout-storefront.js +223 -0
  109. package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
  110. package/src/lib/model/ai-build-job.d.ts +11 -1
  111. package/src/lib/model/ai-build-job.js +35 -6
  112. package/src/lib/model/ai-build-job.js.map +1 -1
  113. package/src/lib/model/ai-build-plan.d.ts +23 -3
  114. package/src/lib/model/ai-build-plan.js +47 -6
  115. package/src/lib/model/ai-build-plan.js.map +1 -1
  116. package/src/lib/model/ai-build-progress.d.ts +2 -2
  117. package/src/lib/model/ai-build-progress.js +6 -4
  118. package/src/lib/model/ai-build-progress.js.map +1 -1
  119. package/src/lib/model/ai-credit-estimate.d.ts +69 -2
  120. package/src/lib/model/ai-credit-estimate.js +91 -3
  121. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  122. package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
  123. package/src/lib/model/ai-job-cancel-copy.js +53 -0
  124. package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
  125. package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
  126. package/src/lib/model/ai-job-failure-copy.js +8 -3
  127. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  128. package/src/lib/model/ai-jobs.types.d.ts +20 -0
  129. package/src/lib/model/ai-jobs.types.js.map +1 -1
  130. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  131. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  132. package/src/lib/model/ai-site-build-progress.js +7 -4
  133. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  134. package/src/lib/model/ai-site-job.d.ts +38 -4
  135. package/src/lib/model/ai-site-job.js +50 -14
  136. package/src/lib/model/ai-site-job.js.map +1 -1
  137. package/src/lib/model/ai-site-kinds.d.ts +4 -1
  138. package/src/lib/model/ai-site-kinds.js +34 -6
  139. package/src/lib/model/ai-site-kinds.js.map +1 -1
  140. package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
  141. package/src/lib/runtime/ai-dataset-generation.js +127 -0
  142. package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
  143. package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
  144. package/src/lib/runtime/ai-doctrine-validators.js +280 -6
  145. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  146. package/src/lib/runtime/ai-doctrine.js +10 -4
  147. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  148. package/src/lib/runtime/ai-node-tree.d.ts +2 -0
  149. package/src/lib/runtime/ai-node-tree.js +16 -0
  150. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  151. package/src/lib/runtime/ai-palette.generated.js +53 -7
  152. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  153. package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
  154. package/src/lib/server/ai-jobs-cancel.js +27 -9
  155. package/src/lib/server/ai-jobs-cancel.js.map +1 -1
  156. package/src/lib/server/ai-jobs-gate.d.ts +12 -2
  157. package/src/lib/server/ai-jobs-gate.js +20 -9
  158. package/src/lib/server/ai-jobs-gate.js.map +1 -1
  159. package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
  160. package/src/lib/tools/ai-dataset-tool.js +231 -0
  161. package/src/lib/tools/ai-dataset-tool.js.map +1 -0
  162. package/src/lib/usage/ai-usage-meter.d.ts +2 -1
  163. package/src/lib/usage/ai-usage-meter.js +17 -1
  164. package/src/lib/usage/ai-usage-meter.js.map +1 -1
  165. package/src/lib/usage/ai-usage-wire.d.ts +6 -0
  166. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  167. package/src/lib/usage/assist-usage.d.ts +7 -0
  168. package/src/lib/usage/assist-usage.js +14 -4
  169. package/src/lib/usage/assist-usage.js.map +1 -1
  170. package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
  171. package/src/lib/usage/free-ai-credits-left.js +30 -4
  172. package/src/lib/usage/free-ai-credits-left.js.map +1 -1
@@ -22,8 +22,9 @@ import { AI_INSTANCE_REF_PROP } from "../runtime/ai-node-tree.js";
22
22
  import { aiPageScrollInteraction } from "../runtime/ai-page-links.js";
23
23
  import { aiLayoutFitText, aiLayoutQuoteGap, aiLayoutWords } from "./ai-layout-copy.js";
24
24
  import { AI_LAYOUT_GROUP_KINDS, AI_LAYOUT_MAX_ITEMS } from "./ai-layout-language.js";
25
- import { aiLayoutDesignChoices, aiLayoutGroupVariant } from "./ai-layout-design.js";
25
+ import { aiLayoutDesignChoices, aiLayoutFamily, aiLayoutGroupVariant } from "./ai-layout-design.js";
26
26
  import { AI_LAYOUT_FEATURED_RECORDS, AI_LAYOUT_LISTING_ELEMENTS, aiLayoutListingAt } from "./ai-layout-listings.js";
27
+ import { aiStorefrontItemIcons } from "./ai-layout-storefront.js";
27
28
  import { aiLayoutRenamedLabel, aiLayoutResolveLink } from "./ai-layout-links.js";
28
29
  import { AI_LAYOUT_FRAMES, AiLayoutTreeBuilder } from "./ai-layout-tree.js";
29
30
  /**
@@ -166,6 +167,9 @@ function compileSection(page, raw, index) {
166
167
  if (raw.blocks.some((block)=>block.kind === 'quotes') && !blocks.some((block)=>AI_LAYOUT_GROUP_KINDS.has(block.kind) || block.kind === 'component')) {
167
168
  page.quotesOnly.push(index);
168
169
  }
170
+ // A store's Shop page opens on its grid (AGL-3676): its first section is a
171
+ // short title bar, never a hero, so the products start above the fold.
172
+ if (index === 0 && shopGridAt(page) > 0) return shopTitle(scope, blocks);
169
173
  // A section the site's records fill — its catalog, its blog — shows them
170
174
  // through the element that keeps them, around the words the design gave it (AGL-3676).
171
175
  const listed = aiLayoutListingAt((_page_targets_listings = page.targets.listings) != null ? _page_targets_listings : [], page.targets.pageId, index);
@@ -385,7 +389,11 @@ function compileSection(page, raw, index) {
385
389
  }
386
390
  /** A band the page has room for: past two brand or two dark bands, a soft one instead. */ function settleBand(page, band, at) {
387
391
  if (band !== 'brand' && band !== 'dark') return band;
388
- if (page.strong[band] >= AI_LAYOUT_MAX_STRONG_BANDS) {
392
+ // A storefront keeps one dark band at most (AGL-3676): the beta.237 Willow
393
+ // Wick home ran its text bands dark, and read as big empty dark slabs
394
+ // between the products.
395
+ const most = band === 'dark' && page.design && aiLayoutFamily(page.design.input.kind) === 'retail' ? 1 : AI_LAYOUT_MAX_STRONG_BANDS;
396
+ if (page.strong[band] >= most) {
389
397
  page.settled.push({
390
398
  at: `${at}.band`,
391
399
  what: `a third ${band} band drawn soft`
@@ -1054,11 +1062,21 @@ function group(scope, block, room) {
1054
1062
  const placed = componentOf(scope.page.targets, block.to);
1055
1063
  if (placed) return instances(scope, placed, items, perRow);
1056
1064
  const design = scope.page.design;
1065
+ const retail = !!design && aiLayoutFamily(design.input.kind) === 'retail';
1066
+ // A store's making, shown: its steps as pictures over their words (AGL-3676),
1067
+ // where the page has the pictures for them.
1068
+ if (retail && block.kind === 'steps' && room === 'full' && items.length >= 2 && items.length <= 4 && picturesLeft(scope.page) >= items.length) {
1069
+ const drawn = designedGroup(scope, 'articles', items, room);
1070
+ if (drawn) return drawn;
1071
+ }
1057
1072
  if (rule1(scope)) {
1058
1073
  var _scope_words;
1059
1074
  // Picture cards the design draws are the compiler's, not a block typed out
1060
- // by hand (`repeatsCompiled`, AGL-3660); every other repeat stays compact.
1075
+ // by hand (`repeatsCompiled`, AGL-3660), and so are a storefront's icon
1076
+ // columns (AGL-3676): the beta.237 Willow Wick home, on a paid plan, drew
1077
+ // every other group compact — four bands of a title and a sentence.
1061
1078
  const variant = design && block.kind === 'cards' && block.style !== 'quiet' ? aiLayoutGroupVariant(design.input, design.choices, (_scope_words = scope.words) != null ? _scope_words : '') : null;
1079
+ if (variant === 'icons') return iconColumns(scope, items, room);
1062
1080
  const pictured = (variant === 'pictures' || variant === 'articles') && room === 'full' && picturesLeft(scope.page) >= items.length;
1063
1081
  return pictured && designedGroup(scope, variant, items, room) || compactGroup(scope, block, items, perRow);
1064
1082
  }
@@ -1457,6 +1475,7 @@ function instances(scope, placed, items, perRow) {
1457
1475
  flow[lead] = design.choices.hero === 'split' ? title : _extends({}, title, {
1458
1476
  style: 'large'
1459
1477
  });
1478
+ sellingHero(scope, flow);
1460
1479
  const given = images[0] ? aiLayoutFitText(images[0].text, 'alt') : '';
1461
1480
  return hero(scope, raw, flow, given ? {
1462
1481
  alt: given,
@@ -1859,6 +1878,7 @@ function instances(scope, placed, items, perRow) {
1859
1878
  variant: type
1860
1879
  }, muted(scope), null, 'text') : null;
1861
1880
  if (variant === 'cards') return null;
1881
+ if (variant === 'icons') return iconColumns(scope, items, room);
1862
1882
  const pictured = variant === 'pictures' || variant === 'articles';
1863
1883
  if (variant === 'ruled' || pictured && (room !== 'full' || picturesLeft(page) < items.length)) {
1864
1884
  const perRow = across(items.length, room, 'cards');
@@ -1944,12 +1964,16 @@ function instances(scope, placed, items, perRow) {
1944
1964
  // A portfolio's or a photographer's gallery opens in a lightbox, its
1945
1965
  // pictures one gallery with the item's title as each caption (AGL-3717).
1946
1966
  const opens = variant === 'pictures' && aiOpensGalleriesInLightbox(page);
1967
+ // A store's collection tiles open its Shop page (AGL-3676).
1968
+ const shop = variant === 'pictures' && !opens && aiLayoutFamily(design.input.kind) === 'retail' ? shopPageOf(page) : null;
1947
1969
  const photo = designImage(scope, {
1948
1970
  alt: standInAlt(scope, item.title || item.text),
1949
1971
  given: false
1950
1972
  }, _extends({
1951
1973
  width: '100%'
1952
- }, opens ? lightboxProps(scope, item.title) : {}), {
1974
+ }, opens ? lightboxProps(scope, item.title) : {}, shop ? {
1975
+ screenId: shop
1976
+ } : {}), {
1953
1977
  aspectRatio: {
1954
1978
  xs: '4 / 3',
1955
1979
  md: aspect
@@ -2016,9 +2040,10 @@ function instances(scope, placed, items, perRow) {
2016
2040
  } : null;
2017
2041
  const index = listing.placements.find((placement)=>placement.role === 'index' && placement.screenId !== page.targets.pageId);
2018
2042
  if (!index) return null;
2043
+ const text = listing.kind === 'tracks' ? 'All music' : listing.kind === 'records' ? 'See all' : 'Shop all';
2019
2044
  return {
2020
2045
  kind: 'button',
2021
- text: listing.kind === 'tracks' ? 'All music' : 'Shop all',
2046
+ text,
2022
2047
  to: `page:${index.screenId}`,
2023
2048
  style: 'secondary'
2024
2049
  };
@@ -2028,6 +2053,11 @@ function listingSection(scope, blocks, placed) {
2028
2053
  var _page_plan_sections_index_name, _page_plan_sections_index, _page_options_sectionIds;
2029
2054
  const { page, index, at } = scope;
2030
2055
  const { listing, role } = placed;
2056
+ if (listing.kind === 'reviews') return reviewsSection(scope, blocks);
2057
+ if (listing.kind === 'signup') return signupSection(scope, blocks);
2058
+ // The grid straight under a Shop page's title bar opens on the products:
2059
+ // the title bar already heads the page (AGL-3676).
2060
+ const underTitle = listing.kind === 'products' && role === 'index' && index === 1 && shopGridAt(page) === 1;
2031
2061
  const left = blocks.filter((block)=>LISTING_REPLACES.has(block.kind));
2032
2062
  if (left.length) {
2033
2063
  page.settled.push({
@@ -2035,7 +2065,7 @@ function listingSection(scope, blocks, placed) {
2035
2065
  what: `${left.map((block)=>block.kind).join(', ')} left out: the section lists the site's own ${listing.kind}`
2036
2066
  });
2037
2067
  }
2038
- const kept = blocks.filter((block)=>!LISTING_REPLACES.has(block.kind)).map((_0)=>{
2068
+ const kept = underTitle ? [] : blocks.filter((block)=>!LISTING_REPLACES.has(block.kind)).map((_0)=>{
2039
2069
  let { col: _col } = _0, block = _object_without_properties_loose(_0, [
2040
2070
  "col"
2041
2071
  ]);
@@ -2049,7 +2079,7 @@ function listingSection(scope, blocks, placed) {
2049
2079
  all
2050
2080
  ];
2051
2081
  }
2052
- if (!words.some((block)=>speaksHeading(scope, block))) {
2082
+ if (!underTitle && !words.some((block)=>speaksHeading(scope, block))) {
2053
2083
  var _page_plan_sections_index1;
2054
2084
  // Every band of records is headed: the plan's name for it, or on a first section the page's title.
2055
2085
  words.unshift({
@@ -2102,7 +2132,13 @@ function listingSection(scope, blocks, placed) {
2102
2132
  } : {}), null, parts, 'content');
2103
2133
  const container = tree.add('muiContainer', {
2104
2134
  maxWidth: 'lg'
2105
- }, {
2135
+ }, underTitle ? {
2136
+ pt: {
2137
+ xs: 2,
2138
+ md: 3
2139
+ },
2140
+ pb: _extends({}, SECTION_PADDING)
2141
+ } : {
2106
2142
  py: index === 0 ? _extends({}, HERO_PADDING) : _extends({}, SECTION_PADDING)
2107
2143
  }, [
2108
2144
  content
@@ -2113,10 +2149,268 @@ function listingSection(scope, blocks, placed) {
2113
2149
  ariaLabel: sectionLabel(name, index)
2114
2150
  }, scope.band === 'dark' ? {
2115
2151
  colorScheme: 'dark'
2116
- } : {}), bandSx(scope.band), [
2152
+ } : {}), underTitle ? null : bandSx(scope.band), [
2117
2153
  container
2118
2154
  ], 'section', (_ref = (_page_options_sectionIds = page.options.sectionIds) == null ? void 0 : _page_options_sectionIds[index]) != null ? _ref : sectionIdOf(index));
2119
2155
  }
2156
+ /** What a store's reviews are headed with where neither the design nor the plan says. */ export const AI_LAYOUT_REVIEWS_HEADING = 'What our customers say';
2157
+ /**
2158
+ * A store's own customer reviews (AGL-3676): the commerce plugin's Product
2159
+ * reviews in its store-wide scope, carrying the section's heading itself.
2160
+ * It lists approved reviews with their star ratings, and on a published page
2161
+ * with none — every store that just opened — it shows nothing, heading and
2162
+ * all, so the band takes no room until a shopper has written one. No review
2163
+ * is ever written here: the design's quotes, if it gave any, were left out.
2164
+ */ function reviewsSection(scope, blocks) {
2165
+ var _page_plan_sections_index;
2166
+ const { page, index, at } = scope;
2167
+ const tree = page.tree;
2168
+ const given = blocks.find((block)=>speaksHeading(scope, block));
2169
+ const heading = aiLayoutWords(given == null ? void 0 : given.text, 'heading', page.targets.facts) || aiLayoutFitText((_page_plan_sections_index = page.plan.sections[index]) == null ? void 0 : _page_plan_sections_index.name, 'heading') || AI_LAYOUT_REVIEWS_HEADING;
2170
+ page.settled.push({
2171
+ at,
2172
+ what: "the store's own reviews placed: shown once shoppers leave them, never written for them"
2173
+ });
2174
+ const id = tree.add(AI_LAYOUT_LISTING_ELEMENTS.reviews, {
2175
+ scope: 'store',
2176
+ heading,
2177
+ maxItems: String(AI_LAYOUT_FEATURED_RECORDS.reviews)
2178
+ }, // The padding is the element's, so a store with no reviews yet keeps no empty band.
2179
+ {
2180
+ py: _extends({}, SECTION_PADDING)
2181
+ }, null, 'reviews');
2182
+ // The reviews are what the section shows: its shoppers' to write.
2183
+ noted(scope, [
2184
+ id,
2185
+ id
2186
+ ]);
2187
+ const container = tree.add('muiContainer', {
2188
+ maxWidth: 'lg'
2189
+ }, null, [
2190
+ id
2191
+ ], 'container');
2192
+ return designedRoot(scope, [
2193
+ container
2194
+ ], bandSx(scope.band), scope.band === 'dark');
2195
+ }
2196
+ /** What a store's sign-up is headed with where neither the design nor the plan says. */ export const AI_LAYOUT_SIGNUP_HEADING = 'Join our newsletter';
2197
+ /**
2198
+ * A store's newsletter sign-up (AGL-3676): the design's heading and words
2199
+ * beside the commerce plugin's Newsletter signup field, or over it on a
2200
+ * centered band. Any form or button the design drew for it is the field's.
2201
+ */ function signupSection(scope, blocks) {
2202
+ const { page, index, at } = scope;
2203
+ const tree = page.tree;
2204
+ const flow = blocks.filter((block)=>WORD_KINDS.has(block.kind) && block.kind !== 'button').map((_0)=>{
2205
+ let { col: _col } = _0, block = _object_without_properties_loose(_0, [
2206
+ "col"
2207
+ ]);
2208
+ return block;
2209
+ });
2210
+ if (!flow.some((block)=>speaksHeading(scope, block))) {
2211
+ var _page_plan_sections_index;
2212
+ flow.unshift({
2213
+ kind: 'heading',
2214
+ text: ((_page_plan_sections_index = page.plan.sections[index]) == null ? void 0 : _page_plan_sections_index.name) || AI_LAYOUT_SIGNUP_HEADING
2215
+ });
2216
+ }
2217
+ page.settled.push({
2218
+ at,
2219
+ what: "the store's newsletter sign-up placed"
2220
+ });
2221
+ const field = tree.add(AI_LAYOUT_LISTING_ELEMENTS.signup, {
2222
+ buttonLabel: 'Subscribe'
2223
+ }, null, null, 'signup');
2224
+ noted(scope, [
2225
+ field,
2226
+ field
2227
+ ]);
2228
+ const words = compileFlow(scope, flow, scope.centered ? 'full' : 'half');
2229
+ const body = scope.centered ? tree.add('muiStack', {
2230
+ spacing: '4',
2231
+ alignItems: 'center'
2232
+ }, null, [
2233
+ words,
2234
+ tree.add('muiContainer', {
2235
+ maxWidth: 'sm',
2236
+ disableGutters: true
2237
+ }, null, [
2238
+ field
2239
+ ], 'measure')
2240
+ ], 'content') : designRow(scope, [
2241
+ ...words ? [
2242
+ {
2243
+ id: words,
2244
+ size: 'xs:12 md:6'
2245
+ }
2246
+ ] : [],
2247
+ {
2248
+ id: field,
2249
+ size: 'xs:12 md:6'
2250
+ }
2251
+ ], '6');
2252
+ const container = tree.add('muiContainer', {
2253
+ maxWidth: 'lg'
2254
+ }, {
2255
+ py: _extends({}, SECTION_PADDING)
2256
+ }, [
2257
+ body
2258
+ ], 'container');
2259
+ return designedRoot(scope, [
2260
+ container
2261
+ ], bandSx(scope.band), scope.band === 'dark');
2262
+ }
2263
+ /** A store's Shop page's grid section on this page, by plan index; -1 where this page is not its Shop page. */ function shopGridAt(page) {
2264
+ var _page_targets_listings, _ref;
2265
+ var _products_placements_find;
2266
+ const products = ((_page_targets_listings = page.targets.listings) != null ? _page_targets_listings : []).find((listing)=>listing.kind === 'products');
2267
+ return (_ref = products == null ? void 0 : (_products_placements_find = products.placements.find((placement)=>placement.role === 'index' && placement.screenId === page.targets.pageId)) == null ? void 0 : _products_placements_find.section) != null ? _ref : -1;
2268
+ }
2269
+ /** A store's Shop page, by its id, where this page is another page of the store; `null` otherwise. */ function shopPageOf(page) {
2270
+ var _page_targets_listings;
2271
+ var _products_placements_find;
2272
+ const products = ((_page_targets_listings = page.targets.listings) != null ? _page_targets_listings : []).find((listing)=>listing.kind === 'products');
2273
+ const shop = products == null ? void 0 : (_products_placements_find = products.placements.find((placement)=>placement.role === 'index')) == null ? void 0 : _products_placements_find.screenId;
2274
+ return shop && shop !== page.targets.pageId && page.targets.pages.some((entry)=>entry.id === shop) ? shop : null;
2275
+ }
2276
+ /**
2277
+ * A Shop page's opening, as a storefront's collection page opens (AGL-3676):
2278
+ * the page's title and one line under it, on the page's own ground, with
2279
+ * little room around them — no photo and no buttons — so the grid that
2280
+ * follows starts above the fold. The beta.237 Willow Wick /shop opened on a
2281
+ * tall candle photo and its words, the products below the fold.
2282
+ */ function shopTitle(scope, blocks) {
2283
+ var _ref;
2284
+ const { page, at } = scope;
2285
+ const left = blocks.filter((block)=>![
2286
+ 'eyebrow',
2287
+ 'heading',
2288
+ 'lede',
2289
+ 'text'
2290
+ ].includes(block.kind));
2291
+ if (left.length) {
2292
+ page.settled.push({
2293
+ at,
2294
+ what: `${left.map((block)=>block.kind).join(', ')} left out: a Shop page opens on its title and its grid`
2295
+ });
2296
+ }
2297
+ const heading = blocks.find((block)=>speaksHeading(scope, block));
2298
+ const line = blocks.find((block)=>block.kind === 'lede' || block.kind === 'text');
2299
+ const eyebrow = blocks.find((block)=>block.kind === 'eyebrow');
2300
+ const flow = [
2301
+ ...eyebrow ? [
2302
+ eyebrow
2303
+ ] : [],
2304
+ {
2305
+ kind: 'heading',
2306
+ text: (_ref = heading == null ? void 0 : heading.text) != null ? _ref : page.plan.title
2307
+ },
2308
+ ...line ? [
2309
+ {
2310
+ kind: 'lede',
2311
+ text: line.text
2312
+ }
2313
+ ] : []
2314
+ ];
2315
+ scope.band = 'plain';
2316
+ scope.centered = false;
2317
+ page.settled.push({
2318
+ at,
2319
+ what: "drawn as the Shop page's title bar, the grid under it"
2320
+ });
2321
+ const words = compileFlow(scope, flow, 'half');
2322
+ const container = page.tree.add('muiContainer', {
2323
+ maxWidth: 'lg'
2324
+ }, {
2325
+ pt: {
2326
+ xs: 5,
2327
+ md: 7
2328
+ },
2329
+ pb: {
2330
+ xs: 1,
2331
+ md: 2
2332
+ }
2333
+ }, [
2334
+ words
2335
+ ], 'container');
2336
+ return designedRoot(scope, [
2337
+ container
2338
+ ], null, false);
2339
+ }
2340
+ /**
2341
+ * A storefront's home hero sells (AGL-3676): it opens the shop. Where the
2342
+ * design gave it no button to the Shop page, "Shop all" leads its buttons,
2343
+ * and any other primary button steps back to secondary beside it.
2344
+ */ function sellingHero(scope, flow) {
2345
+ const design = scope.page.design;
2346
+ if (!(design == null ? void 0 : design.input.home) || aiLayoutFamily(design.input.kind) !== 'retail') return;
2347
+ const shop = shopPageOf(scope.page);
2348
+ if (!shop || flow.some((block)=>block.kind === 'button' && block.to === `page:${shop}`)) return;
2349
+ const firstButton = flow.findIndex((block)=>block.kind === 'button');
2350
+ for (const [at, block] of flow.entries()){
2351
+ var _block_style;
2352
+ if (block.kind === 'button' && ((_block_style = block.style) != null ? _block_style : 'primary') === 'primary') flow[at] = _extends({}, block, {
2353
+ style: 'secondary'
2354
+ });
2355
+ }
2356
+ const shopAll = {
2357
+ kind: 'button',
2358
+ text: AI_LAYOUT_SHOP_ALL,
2359
+ to: `page:${shop}`,
2360
+ style: 'primary'
2361
+ };
2362
+ if (firstButton === -1) flow.push(shopAll);
2363
+ else flow.splice(firstButton, 0, shopAll);
2364
+ scope.page.settled.push({
2365
+ at: scope.at,
2366
+ what: `the hero opens the shop: "${AI_LAYOUT_SHOP_ALL}" leads its buttons`
2367
+ });
2368
+ }
2369
+ /** The button a storefront's hero opens its shop with. */ export const AI_LAYOUT_SHOP_ALL = 'Shop all';
2370
+ /**
2371
+ * A storefront's reasons to buy as open columns, each led by an icon over its
2372
+ * title and words (AGL-3676): the icon the design named for it, else one its
2373
+ * words suggest (shipping a truck, returns the arrows, small batch the hand),
2374
+ * never the same twice in a row. One style key an element, so a row repeats
2375
+ * no multi-key inline style (rule 16).
2376
+ */ function iconColumns(scope, items, room) {
2377
+ const { page } = scope;
2378
+ const tree = page.tree;
2379
+ const facts = page.targets.facts;
2380
+ const iconWords = aiStorefrontItemIcons(items, (word)=>!!aiIconOfWord(word));
2381
+ const perRow = across(items.length, room, 'cards');
2382
+ const textAlign = scope.centered ? {
2383
+ align: 'center'
2384
+ } : {};
2385
+ page.settled.push({
2386
+ at: scope.at,
2387
+ what: `${items.length} items drawn as icon columns`
2388
+ });
2389
+ const ids = items.map((item, position)=>{
2390
+ var _aiIconOfWord;
2391
+ const icon = (_aiIconOfWord = aiIconOfWord(iconWords[position])) != null ? _aiIconOfWord : AI_ICON_LIBRARY.star;
2392
+ return tree.add('muiStack', _extends({
2393
+ spacing: '1.5'
2394
+ }, scope.centered ? {
2395
+ alignItems: 'center'
2396
+ } : {}), null, [
2397
+ tree.add(AI_ICON_COMPONENT_ID, {
2398
+ iconId: icon.id,
2399
+ size: '32'
2400
+ }, accent(scope), null, 'icon'),
2401
+ item.title.trim() ? tree.add('muiTypography', _extends({
2402
+ children: aiLayoutWords(item.title, 'itemTitle', facts),
2403
+ variant: 'h6',
2404
+ component: itemElement(scope)
2405
+ }, textAlign), null, null, 'title') : null,
2406
+ item.text.trim() ? tree.add('muiTypography', _extends({
2407
+ children: aiLayoutWords(item.text, 'itemText', facts),
2408
+ variant: 'body2'
2409
+ }, textAlign), muted(scope), null, 'text') : null
2410
+ ], 'iconItem');
2411
+ });
2412
+ return layOut(scope, noted(scope, ids), perRow, '5', 'icons');
2413
+ }
2120
2414
  /**
2121
2415
  * The element that lists the records, bound to them: the Product grid over
2122
2416
  * the site's catalog, or Collection Entries over its blog with one post's
@@ -2157,6 +2451,9 @@ function listingSection(scope, blocks, placed) {
2157
2451
  showCategories: true
2158
2452
  }, {
2159
2453
  cardStyle: 'photo',
2454
+ // A storefront's cards sell from the grid (AGL-3676): Add to cart on a
2455
+ // product with one variant, Choose options on one with several.
2456
+ quickAdd: true,
2160
2457
  // A store that opens before its first products are in says so, with a
2161
2458
  // way to hear when they land (AGL-3676): never placeholder products.
2162
2459
  emptyTitle: AI_LAYOUT_STORE_EMPTY.title,
@@ -2165,6 +2462,8 @@ function listingSection(scope, blocks, placed) {
2165
2462
  emptyActionLabel: listing.emptyAction.label,
2166
2463
  emptyActionHref: listing.emptyAction.href
2167
2464
  } : {}), null, null, 'products');
2465
+ } else if (listing.kind === 'records') {
2466
+ id = recordsElement(scope, listing, featured ? shown : null);
2168
2467
  } else {
2169
2468
  id = postsElement(scope, listing, featured ? shown : COLLECTION_INDEX_POSTS);
2170
2469
  }
@@ -2281,6 +2580,95 @@ function listingSection(scope, blocks, placed) {
2281
2580
  card
2282
2581
  ], 'posts');
2283
2582
  }
2583
+ // ── A dataset's records (AGL-3616) ───────────────────────────────────────
2584
+ /** A field whose name says it is a record's kicker: its category, role, time or place. */ const RECORD_KICKER = /\b(category|categories|type|kind|role|title|position|section|course|date|dates|time|times|when|day|days|location|place|where|venue|city|duration|level|tag|tags)\b/i;
2585
+ /** A field whose name says it is a record's words: its description, bio or answer. */ const RECORD_BODY = /\b(description|summary|details?|about|bio|biography|answer|text|notes?|body|blurb|overview|excerpt|ingredients|includes)\b/i;
2586
+ /** A dataset of questions and answers, listed as a ruled list rather than cards. */ const RECORD_QUESTIONS = /\b(faqs?|questions?|q ?& ?a)\b/i;
2587
+ /** The fields a record's card shows: its name, a kicker line and its words, each by id. */ export function aiLayoutRecordCardFields(fields) {
2588
+ var _ref, _ref1, _ref2, _ref3;
2589
+ var _texts_find, _rest_find, _rest_find1, _rest_find2;
2590
+ const texts = fields.filter((field)=>field.type === 'text' || field.type === 'sorted');
2591
+ const title = (_ref = (_texts_find = texts.find((field)=>field.type === 'text')) == null ? void 0 : _texts_find.id) != null ? _ref : null;
2592
+ const rest = texts.filter((field)=>field.id !== title);
2593
+ const body = (_ref1 = (_rest_find = rest.find((field)=>field.type === 'text' && RECORD_BODY.test(field.name))) == null ? void 0 : _rest_find.id) != null ? _ref1 : null;
2594
+ const kicker = (_ref2 = (_rest_find1 = rest.find((field)=>field.id !== body && RECORD_KICKER.test(field.name))) == null ? void 0 : _rest_find1.id) != null ? _ref2 : null;
2595
+ // Failing a named one, the next text field is the record's words.
2596
+ const words = (_ref3 = body != null ? body : (_rest_find2 = rest.find((field)=>field.type === 'text' && field.id !== kicker)) == null ? void 0 : _rest_find2.id) != null ? _ref3 : null;
2597
+ return {
2598
+ title,
2599
+ kicker,
2600
+ body: words
2601
+ };
2602
+ }
2603
+ /**
2604
+ * A dataset's records, as the site's design lists them: a Box that repeats
2605
+ * its one card over the dataset — the platform's own repeat, which the page
2606
+ * renders once per record — or, for questions and answers, a ruled list.
2607
+ * The card binds the record's fields as `{{item.<field>}}`: its name as the
2608
+ * item's title, a kicker over it (its category, role or time), and its words.
2609
+ * A featured band shows the first few; a page of its own, every record.
2610
+ */ function recordsElement(scope, listing, limit) {
2611
+ var _listing_fields;
2612
+ const { page } = scope;
2613
+ const tree = page.tree;
2614
+ const fields = aiLayoutRecordCardFields((_listing_fields = listing.fields) != null ? _listing_fields : []);
2615
+ const questions = RECORD_QUESTIONS.test(listing.name);
2616
+ const textAlign = scope.centered && !questions ? {
2617
+ align: 'center'
2618
+ } : {};
2619
+ const kicker = fields.kicker ? tree.add('muiTypography', _extends({
2620
+ children: `{{item.${fields.kicker}}}`,
2621
+ variant: 'overline',
2622
+ component: 'p'
2623
+ }, textAlign), accent(scope), null, 'recordKicker') : null;
2624
+ const title = fields.title ? tree.add('muiTypography', _extends({
2625
+ children: `{{item.${fields.title}}}`,
2626
+ variant: questions ? 'h6' : 'h5',
2627
+ component: itemElement(scope)
2628
+ }, textAlign), null, null, 'recordTitle') : null;
2629
+ const body = fields.body ? tree.add('muiTypography', _extends({
2630
+ children: `{{item.${fields.body}}}`,
2631
+ variant: 'body2'
2632
+ }, textAlign), muted(scope), null, 'recordBody') : null;
2633
+ page.settled.push({
2634
+ at: scope.at,
2635
+ what: `${listing.name} listed from its dataset${questions ? ' as a ruled list' : ' as cards'}`
2636
+ });
2637
+ const card = questions ? tree.add('muiStack', {
2638
+ spacing: '1'
2639
+ }, {
2640
+ borderTop: 1,
2641
+ pt: 3
2642
+ }, [
2643
+ title,
2644
+ body
2645
+ ], 'record') : tree.add('muiStack', _extends({
2646
+ spacing: '1'
2647
+ }, scope.centered ? {
2648
+ alignItems: 'center'
2649
+ } : {}), null, [
2650
+ kicker,
2651
+ title,
2652
+ body
2653
+ ], 'record');
2654
+ return tree.add(AI_LAYOUT_LISTING_ELEMENTS.records, _extends({
2655
+ repeatDataset: listing.datasetId
2656
+ }, limit ? {
2657
+ repeatLimit: String(limit)
2658
+ } : {}), questions ? {
2659
+ display: 'grid',
2660
+ gap: 3
2661
+ } : {
2662
+ display: 'grid',
2663
+ gridTemplateColumns: {
2664
+ xs: '1fr',
2665
+ md: 'repeat(3, 1fr)'
2666
+ },
2667
+ gap: 5
2668
+ }, [
2669
+ card
2670
+ ], 'records');
2671
+ }
2284
2672
  /** The id the compiled page's root is stored under. */ export const AI_LAYOUT_PAGE_ROOT_ID = CANVAS_ROOT_ELEMENT_ID;
2285
2673
 
2286
2674
  //# sourceMappingURL=ai-layout-compiler.js.map