@aglyn/plugins-ai 1.0.0-beta.291 → 1.0.0-beta.293
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.
- package/package.json +17 -17
- package/src/lib/components/ai-site-import-job.component.js +5 -4
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.js +3 -3
- package/src/lib/components/ai-site-import-start.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +129 -29
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
- package/src/lib/components/ai-site-start-steps.component.js +42 -25
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.d.ts +17 -0
- package/src/lib/components/ai-site-style-previews.js +26 -95
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/declarations.server.js +22 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
- package/src/lib/jobs/ai-job-import-step.js +179 -49
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
- package/src/lib/jobs/ai-job-layout-language.js +156 -113
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +56 -17
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
- package/src/lib/jobs/ai-job-page-language.js +120 -66
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +27 -7
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-datasets.js +36 -16
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +6 -2
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
- package/src/lib/jobs/ai-site-import-compile.js +1143 -0
- package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
- package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
- package/src/lib/jobs/ai-site-import-document.js +23 -489
- package/src/lib/jobs/ai-site-import-document.js.map +1 -1
- package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
- package/src/lib/jobs/ai-site-import-look.js +163 -52
- package/src/lib/jobs/ai-site-import-look.js.map +1 -1
- package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
- package/src/lib/jobs/ai-site-import-publish.js +19 -5
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.js +4 -2
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +10 -1
- package/src/lib/jobs/ai-site-publish.js +8 -3
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
- package/src/lib/layout-language/ai-layout-compiler.js +677 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
- package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
- package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
- package/src/lib/layout-language/ai-layout-frame.js +121 -29
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
- package/src/lib/layout-language/ai-layout-language.js +85 -120
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +11 -7
- package/src/lib/model/ai-credit-estimate.js +11 -7
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +307 -13
- package/src/lib/model/ai-site-import.js +227 -19
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +65 -1
- package/src/lib/model/ai-site-look.js +141 -23
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-photos.d.ts +2 -0
- package/src/lib/model/ai-site-photos.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +4 -3
- package/src/lib/model/ai-site-start-steps.js +11 -3
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +23 -2
- package/src/lib/model/ai-site-start.js +25 -3
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/providers/routing.d.ts +11 -0
- package/src/lib/providers/routing.js +23 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
- package/src/lib/runtime/ai-doctrine-validators.js +7 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +4 -0
- package/src/lib/runtime/ai-icon-library.js +4 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +5 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +2 -0
- package/src/lib/runtime/ai-palette.generated.js +19 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +2 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +18 -1
- package/src/lib/site-import/site-import-crawl.js +70 -10
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-css.d.ts +110 -0
- package/src/lib/site-import/site-import-css.js +826 -0
- package/src/lib/site-import/site-import-css.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +116 -1
- package/src/lib/site-import/site-import-html.js +538 -46
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-layout.d.ts +79 -0
- package/src/lib/site-import/site-import-layout.js +1608 -0
- package/src/lib/site-import/site-import-layout.js.map +1 -0
- package/src/lib/site-import/site-import-nav.d.ts +40 -0
- package/src/lib/site-import/site-import-nav.js +437 -0
- package/src/lib/site-import/site-import-nav.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +8 -1
- package/src/lib/site-import/site-import-page.js +24 -2
- package/src/lib/site-import/site-import-page.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/site-import/site-import-review.d.ts +0 -40
- package/src/lib/site-import/site-import-review.js +0 -149
- package/src/lib/site-import/site-import-review.js.map +0 -1
|
@@ -119,6 +119,31 @@ export const AI_LAYOUT_STYLES = [
|
|
|
119
119
|
* list typed out by hand, which belongs in a dataset.
|
|
120
120
|
*/ export const AI_LAYOUT_MAX_ITEMS = 7;
|
|
121
121
|
/** The most quotes a group shows: each is a gap the owner fills with a real one. */ export const AI_LAYOUT_MAX_QUOTES = 3;
|
|
122
|
+
/**
|
|
123
|
+
* The block kinds only a page brought in draws (AGL-4107), never in a tool
|
|
124
|
+
* schema: a TABLE of rows of cells, TABS whose panels hold blocks, and a
|
|
125
|
+
* strip of LOGOS — pictures, or short words as chips.
|
|
126
|
+
*/ export const AI_LAYOUT_IMPORT_BLOCK_KINDS = [
|
|
127
|
+
'table',
|
|
128
|
+
'tabs',
|
|
129
|
+
'logos'
|
|
130
|
+
];
|
|
131
|
+
/** How a group of a page brought in is drawn (AGL-4107): the compiler's group variants a source can show. */ export const AI_LAYOUT_GROUP_STYLES = [
|
|
132
|
+
'cards',
|
|
133
|
+
'ruled',
|
|
134
|
+
'icons',
|
|
135
|
+
'pictures',
|
|
136
|
+
'articles'
|
|
137
|
+
];
|
|
138
|
+
/** The heroes a page brought in may open with (AGL-4107): each lays the source's own picture beside, behind or under its words. */ export const AI_LAYOUT_IMPORT_HEROES = [
|
|
139
|
+
'split',
|
|
140
|
+
'bleed',
|
|
141
|
+
'cover',
|
|
142
|
+
'poster',
|
|
143
|
+
'column',
|
|
144
|
+
'editorial',
|
|
145
|
+
'panel'
|
|
146
|
+
];
|
|
122
147
|
const ITEM_SCHEMA = {
|
|
123
148
|
type: 'object',
|
|
124
149
|
additionalProperties: false,
|
|
@@ -544,126 +569,92 @@ const wordsOf = (text)=>{
|
|
|
544
569
|
blocks: []
|
|
545
570
|
});
|
|
546
571
|
}
|
|
547
|
-
// ──
|
|
572
|
+
// ── The design of a page brought in (AGL-4107) ───────────────────────────
|
|
548
573
|
/**
|
|
549
|
-
* A page brought in from its owner's own website is
|
|
550
|
-
* (AGL-
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
'note',
|
|
561
|
-
'list',
|
|
562
|
-
'button',
|
|
563
|
-
'image',
|
|
574
|
+
* A page brought in from its owner's own website is laid out as its source
|
|
575
|
+
* lays it out (AGL-4107): the reader reads its sections, bands, rows, groups,
|
|
576
|
+
* pictures and icons (`site-import/site-import-layout.ts`), and code compiles
|
|
577
|
+
* that structure with every word as written (`jobs/ai-site-import-compile.ts`).
|
|
578
|
+
* The one question left for a model is the page's DESIGN, over the structure
|
|
579
|
+
* it is shown: which hero the opening becomes around its picture, how each
|
|
580
|
+
* group is drawn, a band where the source gave none, the kind of a group the
|
|
581
|
+
* reader could not tell, and an alignment. The schema below names only those
|
|
582
|
+
* choices, so no word of copy, no line and no structure has anywhere to go.
|
|
583
|
+
*/ /** A section's choice where the source's own stands: its band, its alignment, a group's kind. */ export const AI_LAYOUT_DESIGN_SOURCE = 'source';
|
|
584
|
+
/** The kinds a group the reader could not tell may be read as. */ export const AI_LAYOUT_DESIGN_GROUP_KINDS = [
|
|
564
585
|
'cards',
|
|
565
586
|
'steps',
|
|
566
587
|
'stats',
|
|
567
|
-
'
|
|
588
|
+
'list'
|
|
568
589
|
];
|
|
569
|
-
/** The
|
|
570
|
-
'cards',
|
|
571
|
-
'steps',
|
|
572
|
-
'stats',
|
|
573
|
-
'faq'
|
|
574
|
-
]);
|
|
575
|
-
/** The bands an arranged section may take: a brand band's words need a contrast the source never chose. */ export const AI_LAYOUT_ARRANGE_BANDS = [
|
|
590
|
+
/** The bands a design answer may give a section the source drew on no band of its own. */ export const AI_LAYOUT_DESIGN_BANDS = [
|
|
576
591
|
'plain',
|
|
577
592
|
'soft',
|
|
578
593
|
'dark'
|
|
579
594
|
];
|
|
580
|
-
const
|
|
581
|
-
type: 'array',
|
|
582
|
-
items: {
|
|
583
|
-
type: 'integer'
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
const ARRANGE_ITEM_SCHEMA = {
|
|
587
|
-
type: 'object',
|
|
588
|
-
additionalProperties: false,
|
|
589
|
-
required: [
|
|
590
|
-
'from'
|
|
591
|
-
],
|
|
592
|
-
properties: {
|
|
593
|
-
from: LINE_LIST_SCHEMA
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
const ARRANGE_BLOCK_SCHEMA = {
|
|
595
|
+
const DESIGN_GROUP_SCHEMA = {
|
|
597
596
|
type: 'object',
|
|
598
597
|
additionalProperties: false,
|
|
599
598
|
required: [
|
|
600
599
|
'kind',
|
|
601
|
-
'
|
|
602
|
-
'from',
|
|
603
|
-
'style',
|
|
604
|
-
'items'
|
|
600
|
+
'style'
|
|
605
601
|
],
|
|
606
602
|
properties: {
|
|
607
603
|
kind: {
|
|
608
604
|
type: 'string',
|
|
609
605
|
enum: [
|
|
610
|
-
|
|
606
|
+
AI_LAYOUT_DESIGN_SOURCE,
|
|
607
|
+
...AI_LAYOUT_DESIGN_GROUP_KINDS
|
|
611
608
|
]
|
|
612
609
|
},
|
|
613
|
-
col: {
|
|
614
|
-
type: 'integer'
|
|
615
|
-
},
|
|
616
|
-
from: LINE_LIST_SCHEMA,
|
|
617
610
|
style: {
|
|
618
611
|
type: 'string',
|
|
619
612
|
enum: [
|
|
620
|
-
...
|
|
621
|
-
'none'
|
|
613
|
+
...AI_LAYOUT_GROUP_STYLES
|
|
622
614
|
]
|
|
623
|
-
},
|
|
624
|
-
items: {
|
|
625
|
-
type: 'array',
|
|
626
|
-
items: ARRANGE_ITEM_SCHEMA
|
|
627
615
|
}
|
|
628
616
|
}
|
|
629
617
|
};
|
|
630
|
-
const
|
|
618
|
+
const DESIGN_SECTION_SCHEMA = {
|
|
631
619
|
type: 'object',
|
|
632
620
|
additionalProperties: false,
|
|
633
621
|
required: [
|
|
622
|
+
'hero',
|
|
634
623
|
'band',
|
|
635
|
-
'cols',
|
|
636
624
|
'align',
|
|
637
|
-
'
|
|
625
|
+
'groups'
|
|
638
626
|
],
|
|
639
627
|
properties: {
|
|
640
|
-
|
|
628
|
+
hero: {
|
|
641
629
|
type: 'string',
|
|
642
630
|
enum: [
|
|
643
|
-
|
|
631
|
+
'none',
|
|
632
|
+
...AI_LAYOUT_IMPORT_HEROES
|
|
644
633
|
]
|
|
645
634
|
},
|
|
646
|
-
|
|
647
|
-
type: '
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
635
|
+
band: {
|
|
636
|
+
type: 'string',
|
|
637
|
+
enum: [
|
|
638
|
+
AI_LAYOUT_DESIGN_SOURCE,
|
|
639
|
+
...AI_LAYOUT_DESIGN_BANDS
|
|
640
|
+
]
|
|
651
641
|
},
|
|
652
642
|
align: {
|
|
653
643
|
type: 'string',
|
|
654
644
|
enum: [
|
|
645
|
+
AI_LAYOUT_DESIGN_SOURCE,
|
|
655
646
|
...AI_LAYOUT_ALIGNS
|
|
656
647
|
]
|
|
657
648
|
},
|
|
658
|
-
|
|
649
|
+
groups: {
|
|
659
650
|
type: 'array',
|
|
660
|
-
items:
|
|
651
|
+
items: DESIGN_GROUP_SCHEMA
|
|
661
652
|
}
|
|
662
653
|
}
|
|
663
654
|
};
|
|
664
|
-
/** The
|
|
665
|
-
name: '
|
|
666
|
-
description: "Submit the page's
|
|
655
|
+
/** The design tool: one entry per section of a page brought in, in order, naming design choices only. */ export const AI_LAYOUT_DESIGN_TOOL = {
|
|
656
|
+
name: 'submit_design',
|
|
657
|
+
description: "Submit the page's design: one entry in sections for each section of the page, in order, with one entry in groups for each of its groups.",
|
|
667
658
|
strict: true,
|
|
668
659
|
inputSchema: {
|
|
669
660
|
type: 'object',
|
|
@@ -674,73 +665,47 @@ const ARRANGE_SECTION_SCHEMA = {
|
|
|
674
665
|
properties: {
|
|
675
666
|
sections: {
|
|
676
667
|
type: 'array',
|
|
677
|
-
items:
|
|
668
|
+
items: DESIGN_SECTION_SCHEMA
|
|
678
669
|
}
|
|
679
670
|
}
|
|
680
671
|
}
|
|
681
672
|
};
|
|
682
|
-
|
|
683
|
-
if (!Array.isArray(value)) return [];
|
|
684
|
-
const out = [];
|
|
685
|
-
for (const entry of value){
|
|
686
|
-
if (typeof entry === 'number' && Number.isInteger(entry) && entry > 0 && !out.includes(entry)) out.push(entry);
|
|
687
|
-
}
|
|
688
|
-
return out;
|
|
689
|
-
}
|
|
690
|
-
function readArrangedBlock(raw) {
|
|
691
|
-
var _block_items;
|
|
692
|
-
if (!isRecord(raw)) return null;
|
|
693
|
-
const kind = oneOf(raw['kind'], AI_LAYOUT_ARRANGE_KINDS);
|
|
694
|
-
if (!kind) return null;
|
|
695
|
-
// Only line numbers are read: any words an answer carried are ignored.
|
|
696
|
-
const block = {
|
|
697
|
-
kind,
|
|
698
|
-
from: lineNumbers(raw['from'])
|
|
699
|
-
};
|
|
700
|
-
if (typeof raw['col'] === 'number' && Number.isInteger(raw['col']) && raw['col'] >= 0) block.col = raw['col'];
|
|
701
|
-
const style = oneOf(raw['style'], AI_LAYOUT_STYLES);
|
|
702
|
-
if (style) block.style = style;
|
|
703
|
-
if (AI_LAYOUT_ARRANGE_ITEM_KINDS.has(kind)) {
|
|
704
|
-
const items = (Array.isArray(raw['items']) ? raw['items'] : []).map((item)=>isRecord(item) ? lineNumbers(item['from']) : []).filter((lines)=>lines.length > 0);
|
|
705
|
-
if (items.length) block.items = items;
|
|
706
|
-
}
|
|
707
|
-
// A block that names no line is no block.
|
|
708
|
-
return block.from.length || ((_block_items = block.items) == null ? void 0 : _block_items.length) ? block : null;
|
|
709
|
-
}
|
|
710
|
-
function readArrangedSection(raw) {
|
|
673
|
+
function readDesignSection(raw) {
|
|
711
674
|
if (!isRecord(raw)) return null;
|
|
712
675
|
const section = {
|
|
713
|
-
|
|
676
|
+
groups: []
|
|
714
677
|
};
|
|
715
|
-
const
|
|
678
|
+
const hero = oneOf(raw['hero'], [
|
|
679
|
+
'none',
|
|
680
|
+
...AI_LAYOUT_IMPORT_HEROES
|
|
681
|
+
]);
|
|
682
|
+
if (hero) section.hero = hero;
|
|
683
|
+
const band = oneOf(raw['band'], AI_LAYOUT_DESIGN_BANDS);
|
|
716
684
|
if (band) section.band = band;
|
|
717
685
|
const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS);
|
|
718
686
|
if (align) section.align = align;
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
if (
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (block.col !== undefined && (!section.cols || block.col >= section.cols.length)) {
|
|
727
|
-
if (section.cols) block.col = section.cols.length - 1;
|
|
728
|
-
else delete block.col;
|
|
687
|
+
for (const entry of Array.isArray(raw['groups']) ? raw['groups'] : []){
|
|
688
|
+
const group = {};
|
|
689
|
+
if (isRecord(entry)) {
|
|
690
|
+
const kind = oneOf(entry['kind'], AI_LAYOUT_DESIGN_GROUP_KINDS);
|
|
691
|
+
if (kind) group.kind = kind;
|
|
692
|
+
const style = oneOf(entry['style'], AI_LAYOUT_GROUP_STYLES);
|
|
693
|
+
if (style) group.style = style;
|
|
729
694
|
}
|
|
730
|
-
section.
|
|
695
|
+
section.groups.push(group);
|
|
731
696
|
}
|
|
732
|
-
return section
|
|
697
|
+
return section;
|
|
733
698
|
}
|
|
734
699
|
/**
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
*/ export function
|
|
700
|
+
* A design answer read (AGL-4107): each section's choices by position, and
|
|
701
|
+
* `null` for one it gave nothing usable for, which is then drawn as its
|
|
702
|
+
* source has it. An answer carries no words, and none would be read.
|
|
703
|
+
*/ export function aiReadLayoutDesign(raw, sections) {
|
|
739
704
|
const given = isRecord(raw) && Array.isArray(raw['sections']) ? raw['sections'] : [];
|
|
740
705
|
return {
|
|
741
706
|
sections: Array.from({
|
|
742
707
|
length: Math.max(0, sections)
|
|
743
|
-
}, (_, index)=>
|
|
708
|
+
}, (_, index)=>readDesignSection(given[index]))
|
|
744
709
|
};
|
|
745
710
|
}
|
|
746
711
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-language.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiTool } from '../providers/contract'\nimport { AI_ICON_WORDS } from '../runtime/ai-icon-library'\n\n/**\n * The compact layout language (AGL-3660): what a model designs a page, and a\n * site's header and footer, in — and the one place its shape is declared.\n *\n * ── Why a language, not a tree ───────────────────────────────────────────\n *\n * A page used to be asked for as a raw element tree, packed as escaped JSON\n * text inside a tool call and held to the doctrine's building rules: Grid\n * items sized as strings, every child attached, one h1, a destination on\n * every button, headings under their ceiling. A real model kept about half\n * of a page's sections on the first try, and a Free section was cut off at\n * its token ceiling. The founder chose a language over a library of section\n * templates (10/7): every page is designed for its own request, so no two\n * sites come out alike, and the rules hold because CODE writes the tree.\n *\n * So the model says WHAT each section is and how it is arranged, in a few\n * words of structure, and writes every word a visitor reads; the compiler\n * (`ai-layout-compiler.ts`) turns that into the element tree with the\n * palette's elements and the theme's tokens, by construction.\n *\n * ── The shape ────────────────────────────────────────────────────────────\n *\n * A page is its plan's sections, in order. A SECTION is\n * - `band` — its background: `plain` (the page), `soft` (a paper band),\n * `brand` (the brand color) or `dark` (always dark);\n * - `cols` — a row of columns by relative width, as `[7, 5]` or `[1, 1, 1]`,\n * up to four; absent, one column;\n * - `align` — `start` or `center`;\n * - `blocks` — what it shows, top to bottom.\n *\n * A BLOCK is one thing a visitor reads or uses. Its `kind` is one of\n * `AI_LAYOUT_BLOCK_KINDS`; `col` places it in a column of the row (0 first),\n * and a block with no `col` spans the section — above the row when it comes\n * before the first placed block, below it when it comes after. `text` is its\n * words, `to` where it goes, `style` its emphasis and `icon` a word from the\n * icon library. A group — `list`, `cards`, `steps`, `stats`, `quotes`,\n * `faq` — carries its `items`, each a `title`, a `text`, and optionally its\n * own `to` and `icon`.\n *\n * Where a block goes (`to`): `page:<id>` for a page of this site (its label\n * works too), `#<n>` for section n of this page, `form` for the site's form,\n * `phone` or `email` for the phone number or email the brief gives (a link\n * that calls or writes, AGL-3979), a form's or a component's id for a `form`\n * or `component` block, or an `https:` address the brief gave.\n *\n * A site's header and footer are two sections in the same language\n * (`AI_LAYOUT_FRAME_TOOL`): the header's brand and navigation are the\n * site's own and written by the compiler, so the model gives the header its\n * band, its alignment and an optional call-to-action button; the footer is\n * a section like any other.\n *\n * ── Cheap by shape ───────────────────────────────────────────────────────\n *\n * The schema is a structured object in the tool input (never JSON text in a\n * string), small enough for a strict tool (`tool-schema-limits.spec.ts`), and\n * the language's instructions ride in the cached prefix. A section is a few\n * dozen words of structure beside its copy, so a whole page is one answer.\n */\n\n/** Every kind of block, in the order the schema lists them. */\nexport const AI_LAYOUT_BLOCK_KINDS = [\n 'eyebrow',\n 'heading',\n 'lede',\n 'text',\n 'note',\n 'list',\n 'button',\n 'image',\n 'cards',\n 'steps',\n 'stats',\n 'quotes',\n 'faq',\n 'form',\n 'component',\n] as const\n\nexport type AiLayoutBlockKind = (typeof AI_LAYOUT_BLOCK_KINDS)[number]\n\n/** The kinds that carry `items`. */\nexport const AI_LAYOUT_GROUP_KINDS: ReadonlySet<AiLayoutBlockKind> = new Set([\n 'list',\n 'cards',\n 'steps',\n 'stats',\n 'quotes',\n 'faq',\n])\n\nexport const AI_LAYOUT_BANDS = ['plain', 'soft', 'brand', 'dark'] as const\nexport type AiLayoutBand = (typeof AI_LAYOUT_BANDS)[number]\n\nexport const AI_LAYOUT_ALIGNS = ['start', 'center'] as const\nexport type AiLayoutAlign = (typeof AI_LAYOUT_ALIGNS)[number]\n\nexport const AI_LAYOUT_STYLES = [\n 'primary',\n 'secondary',\n 'quiet',\n 'large',\n] as const\nexport type AiLayoutStyle = (typeof AI_LAYOUT_STYLES)[number]\n\n/** The most columns a row holds; a fifth is folded into the fourth. */\nexport const AI_LAYOUT_MAX_COLS = 4\n\n/**\n * The most items a group shows: one under the eight copies rule 8 reads as a\n * list typed out by hand, which belongs in a dataset.\n */\nexport const AI_LAYOUT_MAX_ITEMS = 7\n\n/** The most quotes a group shows: each is a gap the owner fills with a real one. */\nexport const AI_LAYOUT_MAX_QUOTES = 3\n\nexport interface AiLayoutItem {\n title: string\n text: string\n to?: string\n icon?: string\n}\n\nexport interface AiLayoutBlock {\n kind: AiLayoutBlockKind\n col?: number\n text?: string\n to?: string\n style?: AiLayoutStyle\n icon?: string\n items?: AiLayoutItem[]\n}\n\nexport interface AiLayoutSection {\n band?: AiLayoutBand\n cols?: number[]\n align?: AiLayoutAlign\n blocks: AiLayoutBlock[]\n}\n\n/** A page: its plan's sections, in order. */\nexport interface AiLayoutPageDoc {\n sections: AiLayoutSection[]\n}\n\n/** A site's frame: the header and the footer every page renders between. */\nexport interface AiLayoutFrameDoc {\n header: AiLayoutSection\n footer: AiLayoutSection\n}\n\nconst ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['title', 'text', 'to', 'icon'],\n properties: {\n title: { type: 'string' },\n text: { type: 'string' },\n to: { type: 'string' },\n icon: { type: 'string' },\n },\n} as const\n\nconst BLOCK_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'col', 'text', 'to', 'style', 'icon', 'items'],\n properties: {\n kind: { type: 'string', enum: [...AI_LAYOUT_BLOCK_KINDS] },\n col: { type: 'integer' },\n text: { type: 'string' },\n to: { type: 'string' },\n style: { type: 'string', enum: [...AI_LAYOUT_STYLES, 'none'] },\n icon: { type: 'string' },\n items: { type: 'array', items: ITEM_SCHEMA },\n },\n} as const\n\nconst SECTION_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['band', 'cols', 'align', 'blocks'],\n properties: {\n band: { type: 'string', enum: [...AI_LAYOUT_BANDS] },\n cols: { type: 'array', items: { type: 'integer' } },\n align: { type: 'string', enum: [...AI_LAYOUT_ALIGNS] },\n blocks: { type: 'array', items: BLOCK_SCHEMA },\n },\n} as const\n\n/** The page tool: every section of one page, in plan order, in one answer. */\nexport const AI_LAYOUT_PAGE_TOOL: AiTool = {\n name: 'submit_page',\n description:\n 'Submit the page: one entry in sections for each planned section, in order, in the layout language.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['sections'],\n properties: { sections: { type: 'array', items: SECTION_SCHEMA } },\n },\n}\n\n/** The frame tool: the site's header and footer, in the same language. */\nexport const AI_LAYOUT_FRAME_TOOL: AiTool = {\n name: 'submit_frame',\n description:\n \"Submit the site's header and footer, each as a section in the layout language.\",\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['header', 'footer'],\n properties: { header: SECTION_SCHEMA, footer: SECTION_SCHEMA },\n },\n}\n\n/**\n * The language as a model is taught it: the cached instructions both doors\n * share. Every word of copy is the model's; this says only how a design is\n * written down and what each word becomes.\n */\nexport const AI_LAYOUT_LANGUAGE_TEXT = [\n \"You design web pages in a compact layout language. You decide the structure and write every word; the platform turns your design into the page, with the site's theme, spacing and type, so never describe styling beyond the words below.\",\n '',\n 'A page is a list of sections, one for each section the plan names, in that order. A section has:',\n '- band: plain (the page background), soft (a light band), brand (the brand color) or dark (always dark). Vary the bands down a page so neighboring sections read apart; keep brand and dark to one or two sections a page.',\n '- cols: relative column widths for a row, such as [1, 1] for halves, [7, 5] for a wide and a narrow column, or [1, 1, 1]; at most 4. [] for one column.',\n '- align: start or center.',\n '- blocks: what the section shows, top to bottom. A block with col 0 or more goes in that column of the row (0 is the first); a block with col -1 spans the whole section, above the row when it comes first and below it when it comes last.',\n 'Every field is always written: a block that does not use one writes \"\" (text, to, icon), \"none\" (style), -1 (col) or [] (items); an item that does not use one writes \"\".',\n '',\n 'Block kinds:',\n \"- eyebrow: a short label above a heading. heading: a heading; the first heading of the page's first section is the page's title, and the first heading of every other section is that section's heading. lede: the larger sentence under a heading. text: a paragraph. note: small print.\",\n '- button: text is the label and to says where it goes; style primary, secondary or quiet. Two buttons in a row read as a pair.',\n '- image: text describes the picture to place, which becomes its alt text; the owner adds the picture. icon names a drawing to show until then. to lightbox opens it large when pressed.',\n '- list: items are the lines (title only; text may be empty).',\n \"- cards, steps, stats, faq: items, each with a title and a text. cards are features or services; steps are numbered; stats are a figure (title) with its label (text), only for figures the brief gives; faq items are a question (title) and its answer (text). Use no quotes block: the site has no customer's words to show.\",\n \"- form: places a saved form by its id in to. component: places a reusable component by its id in to; its items fill the component's props, title the prop name and text the value.\",\n '- style large on a heading makes the page title display-sized. icon (on a block or an item) is one of: ' +\n AI_ICON_WORDS.join(', ') +\n '.',\n '',\n \"Where a link goes (to): page:<page id> for a page of this site; #<n> for section n of this page (counted from 1); form for the site's form; phone or email to call or write to the phone number or email the brief gives; or an https: address the brief gives. A button the page has nowhere to send is left out. A to value is never written into words: in text, name a page by its title.\",\n '',\n \"Writing: plain, specific copy in the site's voice, from the brief, the business and its audience, never filler. Keep a page title under 70 characters and a section heading under 80. The site is published exactly as you write it, so where the brief leaves out a fact such as a price, a figure, a name, a phone number, an email, an address or opening hours, leave it out: write the sentence without it, and make no list line, card or contact row for it. Never write a placeholder or a gap in square brackets, and never invent the fact. Never invent a customer, a review, an award or a statistic.\",\n '',\n 'Design each page for this business and this request: choose the bands, the rows and the blocks that tell its story best, so no two sites read alike.',\n].join('\\n')\n\n// ── Reading an answer ────────────────────────────────────────────────────\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\nfunction oneOf<T extends string>(\n value: unknown,\n values: readonly T[],\n): T | undefined {\n return typeof value === 'string' &&\n (values as readonly string[]).includes(value)\n ? (value as T)\n : undefined\n}\n\n/** What the reader settled on its own, for the job's record and the specs. */\nexport interface AiLayoutSettlement {\n /** Where: `sections[2].blocks[1]`, `header`. */\n at: string\n what: string\n}\n\nfunction readItem(raw: unknown): AiLayoutItem | null {\n if (!isRecord(raw)) return null\n const item: AiLayoutItem = {\n title: typeof raw['title'] === 'string' ? raw['title'] : '',\n text: typeof raw['text'] === 'string' ? raw['text'] : '',\n }\n if (!item.title.trim() && !item.text.trim()) return null\n const to = text(raw['to'])\n const icon = text(raw['icon'])\n return { ...item, ...(to ? { to } : {}), ...(icon ? { icon } : {}) }\n}\n\nfunction readBlock(\n raw: unknown,\n at: string,\n settled: AiLayoutSettlement[],\n): AiLayoutBlock | null {\n if (!isRecord(raw)) {\n settled.push({ at, what: 'not a block; left out' })\n return null\n }\n const kind = oneOf(raw['kind'], AI_LAYOUT_BLOCK_KINDS)\n if (!kind) {\n settled.push({\n at,\n what: `unknown kind ${JSON.stringify(raw['kind'] ?? null)}; left out`,\n })\n return null\n }\n const block: AiLayoutBlock = { kind }\n if (\n typeof raw['col'] === 'number' &&\n Number.isInteger(raw['col']) &&\n raw['col'] >= 0\n )\n block.col = raw['col']\n const words = text(raw['text'])\n if (words) block.text = words\n const to = text(raw['to'])\n if (to) block.to = to.trim()\n const style = oneOf(raw['style'], AI_LAYOUT_STYLES)\n if (style) block.style = style\n const icon = text(raw['icon'])\n if (icon) block.icon = icon.trim().toLowerCase()\n if (AI_LAYOUT_GROUP_KINDS.has(kind) || kind === 'component') {\n const items = (Array.isArray(raw['items']) ? raw['items'] : [])\n .map(readItem)\n .filter((item): item is AiLayoutItem => !!item)\n const most =\n kind === 'quotes'\n ? AI_LAYOUT_MAX_QUOTES\n : kind === 'component'\n ? Infinity\n : AI_LAYOUT_MAX_ITEMS\n if (items.length > most)\n settled.push({\n at,\n what: `${items.length} items; the first ${most} kept`,\n })\n if (items.length) block.items = items.slice(0, most)\n }\n // A block with nothing to show is no block: a group with no items, words\n // with no words. A form or a component may name its record alone.\n const empty = AI_LAYOUT_GROUP_KINDS.has(kind)\n ? !block.items?.length\n : kind === 'form' || kind === 'component'\n ? false\n : !block.text\n if (empty) {\n settled.push({ at, what: `an empty ${kind}; left out` })\n return null\n }\n return block\n}\n\n/** A section read from an answer, or `null` with why it could not be. */\nexport function aiReadLayoutSection(\n raw: unknown,\n at: string,\n settled: AiLayoutSettlement[],\n): AiLayoutSection | null {\n if (!isRecord(raw)) return null\n const section: AiLayoutSection = { blocks: [] }\n const band = oneOf(raw['band'], AI_LAYOUT_BANDS)\n if (band) section.band = band\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n if (align) section.align = align\n if (Array.isArray(raw['cols'])) {\n const cols = raw['cols'].filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0,\n )\n if (cols.length > AI_LAYOUT_MAX_COLS) {\n settled.push({\n at: `${at}.cols`,\n what: `${cols.length} columns; the first ${AI_LAYOUT_MAX_COLS} kept`,\n })\n }\n if (cols.length >= 2)\n section.cols = cols\n .slice(0, AI_LAYOUT_MAX_COLS)\n .map((value) => Math.min(12, Math.max(1, Math.round(value))))\n }\n const blocks = Array.isArray(raw['blocks']) ? raw['blocks'] : []\n blocks.forEach((entry, index) => {\n const block = readBlock(entry, `${at}.blocks[${index}]`, settled)\n if (!block) return\n if (\n block.col !== undefined &&\n section.cols &&\n block.col >= section.cols.length\n ) {\n settled.push({\n at: `${at}.blocks[${index}]`,\n what: `column ${block.col} of ${section.cols.length}; placed in the last`,\n })\n block.col = section.cols.length - 1\n }\n if (block.col !== undefined && !section.cols) delete block.col\n section.blocks.push(block)\n })\n return section.blocks.length ? section : null\n}\n\n/** A page answer read: the sections it gave, by plan position, and the positions it could not. */\nexport interface AiLayoutPageReading {\n /** One entry per planned section; `null` where the answer gave nothing usable. */\n sections: Array<AiLayoutSection | null>\n settled: AiLayoutSettlement[]\n}\n\n/** What a planned section is, as the reader matches an answer's sections to it. */\nexport interface AiLayoutPlannedSection {\n name: string\n items: number\n /** What the plan places in it: a form it binds there is one of these. */\n uses?: readonly string[]\n}\n\n/** A planned section's name that asks for a form: a contact, an inquiry, a booking. */\nconst FORM_SECTION =\n /\\b(form|contact|get in touch|reach|write|message|inquir(?:y|ies|e)|enquir(?:y|ies|e)|book(?:ing|ings)?|reserv(?:e|ation|ations)|quote|appointments?|rsvp|sign ?up|subscribe|newsletter|apply|application|register)\\b/i\n\n/** Whether a planned section is one a form may fill: it names one, or the plan binds a form there. */\nfunction takesForm(planned: AiLayoutPlannedSection): boolean {\n return FORM_SECTION.test(planned.name) || (planned.uses ?? []).some((ref) => /\\bform\\b/i.test(ref))\n}\n\n/** What a form's section scores against a planned section that asks for no form: never chosen while another fits. */\nconst AI_LAYOUT_FORM_MISFIT = -100\n\n/**\n * A word as a heading and a plan's name are compared: letters in any script,\n * with their accents (AGL-3791). As ASCII, \"Menú\" was \"men\", which every\n * \"mensajes\" began with, and \"galería\" was \"galer\" and \"a\".\n */\nconst WORD = /[\\p{L}\\p{M}\\p{N}]+/gu\nconst STOP = new Set(['a', 'an', 'and', 'the', 'of', 'for', 'to', 'our', 'your', 'with', 'in', 'on', 'we', 'us', 'you'])\nconst wordsOf = (text: string) => new Set((text.toLowerCase().match(WORD) ?? []).filter((word) => !STOP.has(word)))\n\n/** The kinds that show a section's repeated items. */\nconst ITEM_KINDS = new Set(['list', 'cards', 'steps', 'stats', 'quotes', 'faq', 'component'])\n\n/**\n * How well an answer's section fits a planned one: the words its headings\n * share with the plan's name, and whether it shows items where the plan\n * asked for them and none where it asked for none.\n *\n * A shared heading word outweighs the items (AGL-3660): a live portfolio's\n * plan gave \"Works Gallery\" no items, and its \"Selected works\" cards lost the\n * place to an inquiry form that shared no word with it. A section holding a\n * form fills only a planned section that asks for one — by its name, or a\n * form the plan binds there — so the form is never a page's stand-in.\n */\nfunction fit(raw: unknown, planned: AiLayoutPlannedSection): number {\n const blocks = isRecord(raw) && Array.isArray(raw['blocks']) ? raw['blocks'].filter(isRecord) : []\n const said = wordsOf(\n blocks\n .filter((block) => block['kind'] === 'heading' || block['kind'] === 'eyebrow')\n .map((block) => String(block['text'] ?? ''))\n .join(' '),\n )\n const named = wordsOf(planned.name)\n let shared = 0\n for (const word of named) if (said.has(word) || [...said].some((other) => other.startsWith(word) || word.startsWith(other))) shared += 1\n const showsItems = blocks.some((block) => ITEM_KINDS.has(String(block['kind'])) && (block['kind'] === 'component' || (Array.isArray(block['items']) && block['items'].length > 0)))\n const wantsItems = planned.items > 0\n const holdsForm = blocks.some((block) => block['kind'] === 'form')\n if (holdsForm && !takesForm(planned)) return AI_LAYOUT_FORM_MISFIT\n return shared * 2 + (wantsItems === showsItems ? 0.5 : -0.5)\n}\n\n/**\n * The answer's sections that best match the plan, in order, where it gave\n * more than the plan names (AGL-3660): a model that opened a page with a\n * hero of its own, or closed it with a call to action, shifted every planned\n * section by one, and the cut fell on the plan's last sections — the ones\n * that carried its items. Each planned section takes the answer's section\n * that fits it best, keeping the answer's order.\n */\nfunction alignSections(given: readonly unknown[], planned: readonly AiLayoutPlannedSection[]): number[] {\n const n = given.length\n const m = planned.length\n // best[i][j]: the best score placing plan sections j.. on answer sections i..\n const best: number[][] = Array.from({ length: n + 1 }, () => Array(m + 1).fill(-Infinity))\n for (let i = 0; i <= n; i += 1) best[i][m] = 0\n for (let i = n - 1; i >= 0; i -= 1) {\n for (let j = m - 1; j >= 0; j -= 1) {\n if (n - i < m - j) continue\n best[i][j] = Math.max(best[i + 1][j], fit(given[i], planned[j]) + best[i + 1][j + 1])\n }\n }\n const picked: number[] = []\n let i = 0\n for (let j = 0; j < m; j += 1) {\n while (i < n && best[i][j] !== fit(given[i], planned[j]) + best[i + 1][j + 1]) i += 1\n picked.push(i)\n i += 1\n }\n return picked\n}\n\n/**\n * The page an answer describes, against the sections its plan names: each\n * planned section takes the answer's section at its position — or, where the\n * answer gave more sections than the plan names and the plan's sections are\n * given, the one that fits it best (`alignSections`) — an answer's extra\n * sections are left out, and a planned section the answer left empty or out\n * is `null` — the one thing a re-ask asks for again.\n */\nexport function aiReadLayoutPage(\n raw: unknown,\n planned: number,\n plan?: readonly AiLayoutPlannedSection[],\n): AiLayoutPageReading {\n const settled: AiLayoutSettlement[] = []\n const given =\n isRecord(raw) && Array.isArray(raw['sections']) ? raw['sections'] : []\n const aligned =\n given.length > planned && plan && plan.length === planned\n ? alignSections(given, plan)\n : Array.from({ length: planned }, (_, index) => index)\n if (given.length > planned) {\n settled.push({\n at: 'sections',\n what: `${given.length} sections for a plan of ${planned}; ${\n aligned.every((at, index) => at === index) ? 'the extra left out' : `sections ${aligned.map((at) => at + 1).join(', ')} kept as the plan's`\n }`,\n })\n }\n const sections = aligned.map((at, index) =>\n aiReadLayoutSection(given[at], `sections[${index}]`, settled),\n )\n return { sections, settled }\n}\n\n/** A frame answer read: a header and a footer, either `null` where it was not usable. */\nexport function aiReadLayoutFrame(raw: unknown): {\n header: AiLayoutSection | null\n footer: AiLayoutSection | null\n settled: AiLayoutSettlement[]\n} {\n const settled: AiLayoutSettlement[] = []\n const record = isRecord(raw) ? raw : {}\n // A header needs no block of its own: its brand and navigation are the\n // site's, so an empty one is a plain bar.\n const header = isRecord(record['header'])\n ? (aiReadLayoutSection(record['header'], 'header', settled) ??\n readBare(record['header']))\n : null\n const footer = aiReadLayoutSection(record['footer'], 'footer', settled)\n return { header, footer, settled }\n}\n\n/** A section with no usable block, keeping only its band and alignment. */\nfunction readBare(raw: Record<string, unknown>): AiLayoutSection {\n const band = oneOf(raw['band'], AI_LAYOUT_BANDS)\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n return { ...(band ? { band } : {}), ...(align ? { align } : {}), blocks: [] }\n}\n\n// ── Arranging a page brought in (AGL-4084) ───────────────────────────────\n\n/**\n * A page brought in from its owner's own website is ARRANGED, never written\n * (AGL-4084): its words are the source's, numbered line by line, and the\n * answer says only how each section is laid out and which numbered lines go\n * in which kind of block. Code substitutes every line exactly as the source\n * wrote it (`jobs/ai-site-import-document.ts`), so the schema below has no\n * field a model could put a word of copy in: a rewording has nowhere to go.\n */\n\n/** The kinds of block an arranged line may go in. */\nexport const AI_LAYOUT_ARRANGE_KINDS = [\n 'eyebrow',\n 'heading',\n 'lede',\n 'text',\n 'note',\n 'list',\n 'button',\n 'image',\n 'cards',\n 'steps',\n 'stats',\n 'faq',\n] as const\n\nexport type AiLayoutArrangeKind = (typeof AI_LAYOUT_ARRANGE_KINDS)[number]\n\n/** The arranged kinds whose lines are items: each item's `from` is its title line, then its text lines. */\nexport const AI_LAYOUT_ARRANGE_ITEM_KINDS: ReadonlySet<AiLayoutArrangeKind> = new Set(['cards', 'steps', 'stats', 'faq'])\n\n/** The bands an arranged section may take: a brand band's words need a contrast the source never chose. */\nexport const AI_LAYOUT_ARRANGE_BANDS = ['plain', 'soft', 'dark'] as const\nexport type AiLayoutArrangeBand = (typeof AI_LAYOUT_ARRANGE_BANDS)[number]\n\nexport interface AiLayoutArrangedBlock {\n kind: AiLayoutArrangeKind\n col?: number\n /** The numbered source lines it shows, in order. */\n from: number[]\n style?: AiLayoutStyle\n /** For a group: each item's lines, its title line first. */\n items?: number[][]\n}\n\nexport interface AiLayoutArrangedSection {\n band?: AiLayoutArrangeBand\n cols?: number[]\n align?: AiLayoutAlign\n blocks: AiLayoutArrangedBlock[]\n}\n\n/** An arrangement answer read: one entry per source section, `null` where it gave none. */\nexport interface AiLayoutArrangement {\n sections: Array<AiLayoutArrangedSection | null>\n}\n\nconst LINE_LIST_SCHEMA = { type: 'array', items: { type: 'integer' } } as const\n\nconst ARRANGE_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['from'],\n properties: { from: LINE_LIST_SCHEMA },\n} as const\n\nconst ARRANGE_BLOCK_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'col', 'from', 'style', 'items'],\n properties: {\n kind: { type: 'string', enum: [...AI_LAYOUT_ARRANGE_KINDS] },\n col: { type: 'integer' },\n from: LINE_LIST_SCHEMA,\n style: { type: 'string', enum: [...AI_LAYOUT_STYLES, 'none'] },\n items: { type: 'array', items: ARRANGE_ITEM_SCHEMA },\n },\n} as const\n\nconst ARRANGE_SECTION_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['band', 'cols', 'align', 'blocks'],\n properties: {\n band: { type: 'string', enum: [...AI_LAYOUT_ARRANGE_BANDS] },\n cols: { type: 'array', items: { type: 'integer' } },\n align: { type: 'string', enum: [...AI_LAYOUT_ALIGNS] },\n blocks: { type: 'array', items: ARRANGE_BLOCK_SCHEMA },\n },\n} as const\n\n/** The arrangement tool: every section of a page brought in, in order, by its numbered lines. */\nexport const AI_LAYOUT_ARRANGE_TOOL: AiTool = {\n name: 'submit_arrangement',\n description:\n \"Submit the page's arrangement: one entry in sections for each section of the page, in order, placing its numbered lines in blocks by from.\",\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['sections'],\n properties: { sections: { type: 'array', items: ARRANGE_SECTION_SCHEMA } },\n },\n}\n\n/** Line numbers as an answer gives them: whole, positive, each once. */\nfunction lineNumbers(value: unknown): number[] {\n if (!Array.isArray(value)) return []\n const out: number[] = []\n for (const entry of value) {\n if (typeof entry === 'number' && Number.isInteger(entry) && entry > 0 && !out.includes(entry)) out.push(entry)\n }\n return out\n}\n\nfunction readArrangedBlock(raw: unknown): AiLayoutArrangedBlock | null {\n if (!isRecord(raw)) return null\n const kind = oneOf(raw['kind'], AI_LAYOUT_ARRANGE_KINDS)\n if (!kind) return null\n // Only line numbers are read: any words an answer carried are ignored.\n const block: AiLayoutArrangedBlock = { kind, from: lineNumbers(raw['from']) }\n if (typeof raw['col'] === 'number' && Number.isInteger(raw['col']) && raw['col'] >= 0) block.col = raw['col']\n const style = oneOf(raw['style'], AI_LAYOUT_STYLES)\n if (style) block.style = style\n if (AI_LAYOUT_ARRANGE_ITEM_KINDS.has(kind)) {\n const items = (Array.isArray(raw['items']) ? raw['items'] : [])\n .map((item) => (isRecord(item) ? lineNumbers(item['from']) : []))\n .filter((lines) => lines.length > 0)\n if (items.length) block.items = items\n }\n // A block that names no line is no block.\n return block.from.length || block.items?.length ? block : null\n}\n\nfunction readArrangedSection(raw: unknown): AiLayoutArrangedSection | null {\n if (!isRecord(raw)) return null\n const section: AiLayoutArrangedSection = { blocks: [] }\n const band = oneOf(raw['band'], AI_LAYOUT_ARRANGE_BANDS)\n if (band) section.band = band\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n if (align) section.align = align\n if (Array.isArray(raw['cols'])) {\n const cols = raw['cols'].filter((value): value is number => typeof value === 'number' && Number.isFinite(value) && value > 0)\n if (cols.length >= 2) section.cols = cols.slice(0, AI_LAYOUT_MAX_COLS).map((value) => Math.min(12, Math.max(1, Math.round(value))))\n }\n for (const entry of Array.isArray(raw['blocks']) ? raw['blocks'] : []) {\n const block = readArrangedBlock(entry)\n if (!block) continue\n if (block.col !== undefined && (!section.cols || block.col >= section.cols.length)) {\n if (section.cols) block.col = section.cols.length - 1\n else delete block.col\n }\n section.blocks.push(block)\n }\n return section.blocks.length ? section : null\n}\n\n/**\n * An arrangement answer read (AGL-4084): each source section's layout, by\n * position, and `null` for one it gave nothing usable for — that section is\n * then laid out as the source has it. Words are never read from an answer.\n */\nexport function aiReadLayoutArrangement(raw: unknown, sections: number): AiLayoutArrangement {\n const given = isRecord(raw) && Array.isArray(raw['sections']) ? raw['sections'] : []\n return { sections: Array.from({ length: Math.max(0, sections) }, (_, index) => readArrangedSection(given[index])) }\n}\n"],"names":["AI_ICON_WORDS","AI_LAYOUT_BLOCK_KINDS","AI_LAYOUT_GROUP_KINDS","Set","AI_LAYOUT_BANDS","AI_LAYOUT_ALIGNS","AI_LAYOUT_STYLES","AI_LAYOUT_MAX_COLS","AI_LAYOUT_MAX_ITEMS","AI_LAYOUT_MAX_QUOTES","ITEM_SCHEMA","type","additionalProperties","required","properties","title","text","to","icon","BLOCK_SCHEMA","kind","enum","col","style","items","SECTION_SCHEMA","band","cols","align","blocks","AI_LAYOUT_PAGE_TOOL","name","description","strict","inputSchema","sections","AI_LAYOUT_FRAME_TOOL","header","footer","AI_LAYOUT_LANGUAGE_TEXT","join","isRecord","value","Array","isArray","trim","undefined","oneOf","values","includes","readItem","raw","item","readBlock","at","settled","block","push","what","JSON","stringify","Number","isInteger","words","toLowerCase","has","map","filter","most","Infinity","length","slice","empty","aiReadLayoutSection","section","isFinite","Math","min","max","round","forEach","entry","index","FORM_SECTION","takesForm","planned","test","uses","some","ref","AI_LAYOUT_FORM_MISFIT","WORD","STOP","wordsOf","match","word","ITEM_KINDS","fit","said","String","named","shared","other","startsWith","showsItems","wantsItems","holdsForm","alignSections","given","n","m","best","from","fill","i","j","picked","aiReadLayoutPage","plan","aligned","_","every","aiReadLayoutFrame","record","readBare","AI_LAYOUT_ARRANGE_KINDS","AI_LAYOUT_ARRANGE_ITEM_KINDS","AI_LAYOUT_ARRANGE_BANDS","LINE_LIST_SCHEMA","ARRANGE_ITEM_SCHEMA","ARRANGE_BLOCK_SCHEMA","ARRANGE_SECTION_SCHEMA","AI_LAYOUT_ARRANGE_TOOL","lineNumbers","out","readArrangedBlock","lines","readArrangedSection","aiReadLayoutArrangement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-language.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiTool } from '../providers/contract'\nimport { AI_ICON_WORDS } from '../runtime/ai-icon-library'\n\n/**\n * The compact layout language (AGL-3660): what a model designs a page, and a\n * site's header and footer, in — and the one place its shape is declared.\n *\n * ── Why a language, not a tree ───────────────────────────────────────────\n *\n * A page used to be asked for as a raw element tree, packed as escaped JSON\n * text inside a tool call and held to the doctrine's building rules: Grid\n * items sized as strings, every child attached, one h1, a destination on\n * every button, headings under their ceiling. A real model kept about half\n * of a page's sections on the first try, and a Free section was cut off at\n * its token ceiling. The founder chose a language over a library of section\n * templates (10/7): every page is designed for its own request, so no two\n * sites come out alike, and the rules hold because CODE writes the tree.\n *\n * So the model says WHAT each section is and how it is arranged, in a few\n * words of structure, and writes every word a visitor reads; the compiler\n * (`ai-layout-compiler.ts`) turns that into the element tree with the\n * palette's elements and the theme's tokens, by construction.\n *\n * ── The shape ────────────────────────────────────────────────────────────\n *\n * A page is its plan's sections, in order. A SECTION is\n * - `band` — its background: `plain` (the page), `soft` (a paper band),\n * `brand` (the brand color) or `dark` (always dark);\n * - `cols` — a row of columns by relative width, as `[7, 5]` or `[1, 1, 1]`,\n * up to four; absent, one column;\n * - `align` — `start` or `center`;\n * - `blocks` — what it shows, top to bottom.\n *\n * A BLOCK is one thing a visitor reads or uses. Its `kind` is one of\n * `AI_LAYOUT_BLOCK_KINDS`; `col` places it in a column of the row (0 first),\n * and a block with no `col` spans the section — above the row when it comes\n * before the first placed block, below it when it comes after. `text` is its\n * words, `to` where it goes, `style` its emphasis and `icon` a word from the\n * icon library. A group — `list`, `cards`, `steps`, `stats`, `quotes`,\n * `faq` — carries its `items`, each a `title`, a `text`, and optionally its\n * own `to` and `icon`.\n *\n * Where a block goes (`to`): `page:<id>` for a page of this site (its label\n * works too), `#<n>` for section n of this page, `form` for the site's form,\n * `phone` or `email` for the phone number or email the brief gives (a link\n * that calls or writes, AGL-3979), a form's or a component's id for a `form`\n * or `component` block, or an `https:` address the brief gave.\n *\n * A site's header and footer are two sections in the same language\n * (`AI_LAYOUT_FRAME_TOOL`): the header's brand and navigation are the\n * site's own and written by the compiler, so the model gives the header its\n * band, its alignment and an optional call-to-action button; the footer is\n * a section like any other.\n *\n * ── Cheap by shape ───────────────────────────────────────────────────────\n *\n * The schema is a structured object in the tool input (never JSON text in a\n * string), small enough for a strict tool (`tool-schema-limits.spec.ts`), and\n * the language's instructions ride in the cached prefix. A section is a few\n * dozen words of structure beside its copy, so a whole page is one answer.\n */\n\n/** Every kind of block, in the order the schema lists them. */\nexport const AI_LAYOUT_BLOCK_KINDS = [\n 'eyebrow',\n 'heading',\n 'lede',\n 'text',\n 'note',\n 'list',\n 'button',\n 'image',\n 'cards',\n 'steps',\n 'stats',\n 'quotes',\n 'faq',\n 'form',\n 'component',\n] as const\n\nexport type AiLayoutBlockKind = (typeof AI_LAYOUT_BLOCK_KINDS)[number]\n\n/** The kinds that carry `items`. */\nexport const AI_LAYOUT_GROUP_KINDS: ReadonlySet<string> = new Set<AiLayoutBlockKind>([\n 'list',\n 'cards',\n 'steps',\n 'stats',\n 'quotes',\n 'faq',\n])\n\nexport const AI_LAYOUT_BANDS = ['plain', 'soft', 'brand', 'dark'] as const\nexport type AiLayoutBand = (typeof AI_LAYOUT_BANDS)[number]\n\nexport const AI_LAYOUT_ALIGNS = ['start', 'center'] as const\nexport type AiLayoutAlign = (typeof AI_LAYOUT_ALIGNS)[number]\n\nexport const AI_LAYOUT_STYLES = [\n 'primary',\n 'secondary',\n 'quiet',\n 'large',\n] as const\nexport type AiLayoutStyle = (typeof AI_LAYOUT_STYLES)[number]\n\n/** The most columns a row holds; a fifth is folded into the fourth. */\nexport const AI_LAYOUT_MAX_COLS = 4\n\n/**\n * The most items a group shows: one under the eight copies rule 8 reads as a\n * list typed out by hand, which belongs in a dataset.\n */\nexport const AI_LAYOUT_MAX_ITEMS = 7\n\n/** The most quotes a group shows: each is a gap the owner fills with a real one. */\nexport const AI_LAYOUT_MAX_QUOTES = 3\n\nexport interface AiLayoutItem {\n title: string\n text: string\n to?: string\n icon?: string\n // ── A page brought in (AGL-4107): what the source's item shows beside its\n // title and text, every word as written. No model writes these: no tool\n // schema names them, and the compiler draws them only on a verbatim page.\n /** Its own icon, the source's drawing as a path on the 24-unit square. */\n iconPath?: string\n /** A figure that leads it: a step's number, a plan's price. */\n figure?: string\n /** Its further paragraphs, each its own line. */\n more?: string[]\n /** Its bullet points (a plan's features). */\n list?: string[]\n /** The mark each bullet carries, as a path on the 24-unit square. */\n listIconPath?: string\n /** Its own calls to action, last in it. */\n links?: AiLayoutLink[]\n /** The picture over it, from the site's library. */\n picture?: AiLayoutPicture\n}\n\n/** A link of a page brought in (AGL-4107): its words and where it goes, `page:<id>`, `https:`, `mailto:` or `tel:`. */\nexport interface AiLayoutLink {\n text: string\n to?: string\n style?: AiLayoutStyle\n}\n\n/** A picture of a page brought in (AGL-4107): the library's copy of the source's own. */\nexport interface AiLayoutPicture {\n src: string\n alt: string\n /** Its width over its height, where the library knows them. */\n ratio?: number\n /** Where pressing it goes. */\n to?: string\n /** A byline's portrait beside a few words, drawn small. */\n small?: boolean\n}\n\n/**\n * The block kinds only a page brought in draws (AGL-4107), never in a tool\n * schema: a TABLE of rows of cells, TABS whose panels hold blocks, and a\n * strip of LOGOS — pictures, or short words as chips.\n */\nexport const AI_LAYOUT_IMPORT_BLOCK_KINDS = ['table', 'tabs', 'logos'] as const\nexport type AiLayoutImportBlockKind = (typeof AI_LAYOUT_IMPORT_BLOCK_KINDS)[number]\n\n/** A tab of a page brought in: its label and the blocks its panel shows. */\nexport interface AiLayoutTab {\n label: string\n /** The tab the source shows open. */\n selected?: boolean\n blocks: AiLayoutBlock[]\n}\n\nexport interface AiLayoutBlock {\n kind: AiLayoutBlockKind | AiLayoutImportBlockKind\n col?: number\n text?: string\n to?: string\n style?: AiLayoutStyle\n icon?: string\n items?: AiLayoutItem[]\n // ── A page brought in (AGL-4107), never in a tool schema ──\n /** An image block's picture, the source's own. */\n picture?: AiLayoutPicture\n /** A table's rows, each its cells' words; `header` when its first row names the columns. */\n rows?: Array<{ cells: string[] }>\n header?: boolean\n /** A tab strip's tabs. */\n tabs?: AiLayoutTab[]\n /** How a group is drawn, as the page's design answer chose (`submit_design`). */\n variant?: AiLayoutGroupStyle\n /** The columns the source lays a group's items out in on a wide screen. */\n columns?: number\n}\n\n/** How a group of a page brought in is drawn (AGL-4107): the compiler's group variants a source can show. */\nexport const AI_LAYOUT_GROUP_STYLES = ['cards', 'ruled', 'icons', 'pictures', 'articles'] as const\nexport type AiLayoutGroupStyle = (typeof AI_LAYOUT_GROUP_STYLES)[number]\n\n/** The heroes a page brought in may open with (AGL-4107): each lays the source's own picture beside, behind or under its words. */\nexport const AI_LAYOUT_IMPORT_HEROES = ['split', 'bleed', 'cover', 'poster', 'column', 'editorial', 'panel'] as const\nexport type AiLayoutImportHero = (typeof AI_LAYOUT_IMPORT_HEROES)[number]\n\nexport interface AiLayoutSection {\n band?: AiLayoutBand\n cols?: number[]\n align?: AiLayoutAlign\n blocks: AiLayoutBlock[]\n /** A page brought in (AGL-4107): the hero its first section opens as, around the source's picture. */\n hero?: AiLayoutImportHero\n}\n\n/** A page: its plan's sections, in order. */\nexport interface AiLayoutPageDoc {\n sections: AiLayoutSection[]\n}\n\n/** A site's frame: the header and the footer every page renders between. */\nexport interface AiLayoutFrameDoc {\n header: AiLayoutSection\n footer: AiLayoutSection\n}\n\nconst ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['title', 'text', 'to', 'icon'],\n properties: {\n title: { type: 'string' },\n text: { type: 'string' },\n to: { type: 'string' },\n icon: { type: 'string' },\n },\n} as const\n\nconst BLOCK_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'col', 'text', 'to', 'style', 'icon', 'items'],\n properties: {\n kind: { type: 'string', enum: [...AI_LAYOUT_BLOCK_KINDS] },\n col: { type: 'integer' },\n text: { type: 'string' },\n to: { type: 'string' },\n style: { type: 'string', enum: [...AI_LAYOUT_STYLES, 'none'] },\n icon: { type: 'string' },\n items: { type: 'array', items: ITEM_SCHEMA },\n },\n} as const\n\nconst SECTION_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['band', 'cols', 'align', 'blocks'],\n properties: {\n band: { type: 'string', enum: [...AI_LAYOUT_BANDS] },\n cols: { type: 'array', items: { type: 'integer' } },\n align: { type: 'string', enum: [...AI_LAYOUT_ALIGNS] },\n blocks: { type: 'array', items: BLOCK_SCHEMA },\n },\n} as const\n\n/** The page tool: every section of one page, in plan order, in one answer. */\nexport const AI_LAYOUT_PAGE_TOOL: AiTool = {\n name: 'submit_page',\n description:\n 'Submit the page: one entry in sections for each planned section, in order, in the layout language.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['sections'],\n properties: { sections: { type: 'array', items: SECTION_SCHEMA } },\n },\n}\n\n/** The frame tool: the site's header and footer, in the same language. */\nexport const AI_LAYOUT_FRAME_TOOL: AiTool = {\n name: 'submit_frame',\n description:\n \"Submit the site's header and footer, each as a section in the layout language.\",\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['header', 'footer'],\n properties: { header: SECTION_SCHEMA, footer: SECTION_SCHEMA },\n },\n}\n\n/**\n * The language as a model is taught it: the cached instructions both doors\n * share. Every word of copy is the model's; this says only how a design is\n * written down and what each word becomes.\n */\nexport const AI_LAYOUT_LANGUAGE_TEXT = [\n \"You design web pages in a compact layout language. You decide the structure and write every word; the platform turns your design into the page, with the site's theme, spacing and type, so never describe styling beyond the words below.\",\n '',\n 'A page is a list of sections, one for each section the plan names, in that order. A section has:',\n '- band: plain (the page background), soft (a light band), brand (the brand color) or dark (always dark). Vary the bands down a page so neighboring sections read apart; keep brand and dark to one or two sections a page.',\n '- cols: relative column widths for a row, such as [1, 1] for halves, [7, 5] for a wide and a narrow column, or [1, 1, 1]; at most 4. [] for one column.',\n '- align: start or center.',\n '- blocks: what the section shows, top to bottom. A block with col 0 or more goes in that column of the row (0 is the first); a block with col -1 spans the whole section, above the row when it comes first and below it when it comes last.',\n 'Every field is always written: a block that does not use one writes \"\" (text, to, icon), \"none\" (style), -1 (col) or [] (items); an item that does not use one writes \"\".',\n '',\n 'Block kinds:',\n \"- eyebrow: a short label above a heading. heading: a heading; the first heading of the page's first section is the page's title, and the first heading of every other section is that section's heading. lede: the larger sentence under a heading. text: a paragraph. note: small print.\",\n '- button: text is the label and to says where it goes; style primary, secondary or quiet. Two buttons in a row read as a pair.',\n '- image: text describes the picture to place, which becomes its alt text; the owner adds the picture. icon names a drawing to show until then. to lightbox opens it large when pressed.',\n '- list: items are the lines (title only; text may be empty).',\n \"- cards, steps, stats, faq: items, each with a title and a text. cards are features or services; steps are numbered; stats are a figure (title) with its label (text), only for figures the brief gives; faq items are a question (title) and its answer (text). Use no quotes block: the site has no customer's words to show.\",\n \"- form: places a saved form by its id in to. component: places a reusable component by its id in to; its items fill the component's props, title the prop name and text the value.\",\n '- style large on a heading makes the page title display-sized. icon (on a block or an item) is one of: ' +\n AI_ICON_WORDS.join(', ') +\n '.',\n '',\n \"Where a link goes (to): page:<page id> for a page of this site; #<n> for section n of this page (counted from 1); form for the site's form; phone or email to call or write to the phone number or email the brief gives; or an https: address the brief gives. A button the page has nowhere to send is left out. A to value is never written into words: in text, name a page by its title.\",\n '',\n \"Writing: plain, specific copy in the site's voice, from the brief, the business and its audience, never filler. Keep a page title under 70 characters and a section heading under 80. The site is published exactly as you write it, so where the brief leaves out a fact such as a price, a figure, a name, a phone number, an email, an address or opening hours, leave it out: write the sentence without it, and make no list line, card or contact row for it. Never write a placeholder or a gap in square brackets, and never invent the fact. Never invent a customer, a review, an award or a statistic.\",\n '',\n 'Design each page for this business and this request: choose the bands, the rows and the blocks that tell its story best, so no two sites read alike.',\n].join('\\n')\n\n// ── Reading an answer ────────────────────────────────────────────────────\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\nfunction oneOf<T extends string>(\n value: unknown,\n values: readonly T[],\n): T | undefined {\n return typeof value === 'string' &&\n (values as readonly string[]).includes(value)\n ? (value as T)\n : undefined\n}\n\n/** What the reader settled on its own, for the job's record and the specs. */\nexport interface AiLayoutSettlement {\n /** Where: `sections[2].blocks[1]`, `header`. */\n at: string\n what: string\n}\n\nfunction readItem(raw: unknown): AiLayoutItem | null {\n if (!isRecord(raw)) return null\n const item: AiLayoutItem = {\n title: typeof raw['title'] === 'string' ? raw['title'] : '',\n text: typeof raw['text'] === 'string' ? raw['text'] : '',\n }\n if (!item.title.trim() && !item.text.trim()) return null\n const to = text(raw['to'])\n const icon = text(raw['icon'])\n return { ...item, ...(to ? { to } : {}), ...(icon ? { icon } : {}) }\n}\n\nfunction readBlock(\n raw: unknown,\n at: string,\n settled: AiLayoutSettlement[],\n): AiLayoutBlock | null {\n if (!isRecord(raw)) {\n settled.push({ at, what: 'not a block; left out' })\n return null\n }\n const kind = oneOf(raw['kind'], AI_LAYOUT_BLOCK_KINDS)\n if (!kind) {\n settled.push({\n at,\n what: `unknown kind ${JSON.stringify(raw['kind'] ?? null)}; left out`,\n })\n return null\n }\n const block: AiLayoutBlock = { kind }\n if (\n typeof raw['col'] === 'number' &&\n Number.isInteger(raw['col']) &&\n raw['col'] >= 0\n )\n block.col = raw['col']\n const words = text(raw['text'])\n if (words) block.text = words\n const to = text(raw['to'])\n if (to) block.to = to.trim()\n const style = oneOf(raw['style'], AI_LAYOUT_STYLES)\n if (style) block.style = style\n const icon = text(raw['icon'])\n if (icon) block.icon = icon.trim().toLowerCase()\n if (AI_LAYOUT_GROUP_KINDS.has(kind) || kind === 'component') {\n const items = (Array.isArray(raw['items']) ? raw['items'] : [])\n .map(readItem)\n .filter((item): item is AiLayoutItem => !!item)\n const most =\n kind === 'quotes'\n ? AI_LAYOUT_MAX_QUOTES\n : kind === 'component'\n ? Infinity\n : AI_LAYOUT_MAX_ITEMS\n if (items.length > most)\n settled.push({\n at,\n what: `${items.length} items; the first ${most} kept`,\n })\n if (items.length) block.items = items.slice(0, most)\n }\n // A block with nothing to show is no block: a group with no items, words\n // with no words. A form or a component may name its record alone.\n const empty = AI_LAYOUT_GROUP_KINDS.has(kind)\n ? !block.items?.length\n : kind === 'form' || kind === 'component'\n ? false\n : !block.text\n if (empty) {\n settled.push({ at, what: `an empty ${kind}; left out` })\n return null\n }\n return block\n}\n\n/** A section read from an answer, or `null` with why it could not be. */\nexport function aiReadLayoutSection(\n raw: unknown,\n at: string,\n settled: AiLayoutSettlement[],\n): AiLayoutSection | null {\n if (!isRecord(raw)) return null\n const section: AiLayoutSection = { blocks: [] }\n const band = oneOf(raw['band'], AI_LAYOUT_BANDS)\n if (band) section.band = band\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n if (align) section.align = align\n if (Array.isArray(raw['cols'])) {\n const cols = raw['cols'].filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0,\n )\n if (cols.length > AI_LAYOUT_MAX_COLS) {\n settled.push({\n at: `${at}.cols`,\n what: `${cols.length} columns; the first ${AI_LAYOUT_MAX_COLS} kept`,\n })\n }\n if (cols.length >= 2)\n section.cols = cols\n .slice(0, AI_LAYOUT_MAX_COLS)\n .map((value) => Math.min(12, Math.max(1, Math.round(value))))\n }\n const blocks = Array.isArray(raw['blocks']) ? raw['blocks'] : []\n blocks.forEach((entry, index) => {\n const block = readBlock(entry, `${at}.blocks[${index}]`, settled)\n if (!block) return\n if (\n block.col !== undefined &&\n section.cols &&\n block.col >= section.cols.length\n ) {\n settled.push({\n at: `${at}.blocks[${index}]`,\n what: `column ${block.col} of ${section.cols.length}; placed in the last`,\n })\n block.col = section.cols.length - 1\n }\n if (block.col !== undefined && !section.cols) delete block.col\n section.blocks.push(block)\n })\n return section.blocks.length ? section : null\n}\n\n/** A page answer read: the sections it gave, by plan position, and the positions it could not. */\nexport interface AiLayoutPageReading {\n /** One entry per planned section; `null` where the answer gave nothing usable. */\n sections: Array<AiLayoutSection | null>\n settled: AiLayoutSettlement[]\n}\n\n/** What a planned section is, as the reader matches an answer's sections to it. */\nexport interface AiLayoutPlannedSection {\n name: string\n items: number\n /** What the plan places in it: a form it binds there is one of these. */\n uses?: readonly string[]\n}\n\n/** A planned section's name that asks for a form: a contact, an inquiry, a booking. */\nconst FORM_SECTION =\n /\\b(form|contact|get in touch|reach|write|message|inquir(?:y|ies|e)|enquir(?:y|ies|e)|book(?:ing|ings)?|reserv(?:e|ation|ations)|quote|appointments?|rsvp|sign ?up|subscribe|newsletter|apply|application|register)\\b/i\n\n/** Whether a planned section is one a form may fill: it names one, or the plan binds a form there. */\nfunction takesForm(planned: AiLayoutPlannedSection): boolean {\n return FORM_SECTION.test(planned.name) || (planned.uses ?? []).some((ref) => /\\bform\\b/i.test(ref))\n}\n\n/** What a form's section scores against a planned section that asks for no form: never chosen while another fits. */\nconst AI_LAYOUT_FORM_MISFIT = -100\n\n/**\n * A word as a heading and a plan's name are compared: letters in any script,\n * with their accents (AGL-3791). As ASCII, \"Menú\" was \"men\", which every\n * \"mensajes\" began with, and \"galería\" was \"galer\" and \"a\".\n */\nconst WORD = /[\\p{L}\\p{M}\\p{N}]+/gu\nconst STOP = new Set(['a', 'an', 'and', 'the', 'of', 'for', 'to', 'our', 'your', 'with', 'in', 'on', 'we', 'us', 'you'])\nconst wordsOf = (text: string) => new Set((text.toLowerCase().match(WORD) ?? []).filter((word) => !STOP.has(word)))\n\n/** The kinds that show a section's repeated items. */\nconst ITEM_KINDS = new Set(['list', 'cards', 'steps', 'stats', 'quotes', 'faq', 'component'])\n\n/**\n * How well an answer's section fits a planned one: the words its headings\n * share with the plan's name, and whether it shows items where the plan\n * asked for them and none where it asked for none.\n *\n * A shared heading word outweighs the items (AGL-3660): a live portfolio's\n * plan gave \"Works Gallery\" no items, and its \"Selected works\" cards lost the\n * place to an inquiry form that shared no word with it. A section holding a\n * form fills only a planned section that asks for one — by its name, or a\n * form the plan binds there — so the form is never a page's stand-in.\n */\nfunction fit(raw: unknown, planned: AiLayoutPlannedSection): number {\n const blocks = isRecord(raw) && Array.isArray(raw['blocks']) ? raw['blocks'].filter(isRecord) : []\n const said = wordsOf(\n blocks\n .filter((block) => block['kind'] === 'heading' || block['kind'] === 'eyebrow')\n .map((block) => String(block['text'] ?? ''))\n .join(' '),\n )\n const named = wordsOf(planned.name)\n let shared = 0\n for (const word of named) if (said.has(word) || [...said].some((other) => other.startsWith(word) || word.startsWith(other))) shared += 1\n const showsItems = blocks.some((block) => ITEM_KINDS.has(String(block['kind'])) && (block['kind'] === 'component' || (Array.isArray(block['items']) && block['items'].length > 0)))\n const wantsItems = planned.items > 0\n const holdsForm = blocks.some((block) => block['kind'] === 'form')\n if (holdsForm && !takesForm(planned)) return AI_LAYOUT_FORM_MISFIT\n return shared * 2 + (wantsItems === showsItems ? 0.5 : -0.5)\n}\n\n/**\n * The answer's sections that best match the plan, in order, where it gave\n * more than the plan names (AGL-3660): a model that opened a page with a\n * hero of its own, or closed it with a call to action, shifted every planned\n * section by one, and the cut fell on the plan's last sections — the ones\n * that carried its items. Each planned section takes the answer's section\n * that fits it best, keeping the answer's order.\n */\nfunction alignSections(given: readonly unknown[], planned: readonly AiLayoutPlannedSection[]): number[] {\n const n = given.length\n const m = planned.length\n // best[i][j]: the best score placing plan sections j.. on answer sections i..\n const best: number[][] = Array.from({ length: n + 1 }, () => Array(m + 1).fill(-Infinity))\n for (let i = 0; i <= n; i += 1) best[i][m] = 0\n for (let i = n - 1; i >= 0; i -= 1) {\n for (let j = m - 1; j >= 0; j -= 1) {\n if (n - i < m - j) continue\n best[i][j] = Math.max(best[i + 1][j], fit(given[i], planned[j]) + best[i + 1][j + 1])\n }\n }\n const picked: number[] = []\n let i = 0\n for (let j = 0; j < m; j += 1) {\n while (i < n && best[i][j] !== fit(given[i], planned[j]) + best[i + 1][j + 1]) i += 1\n picked.push(i)\n i += 1\n }\n return picked\n}\n\n/**\n * The page an answer describes, against the sections its plan names: each\n * planned section takes the answer's section at its position — or, where the\n * answer gave more sections than the plan names and the plan's sections are\n * given, the one that fits it best (`alignSections`) — an answer's extra\n * sections are left out, and a planned section the answer left empty or out\n * is `null` — the one thing a re-ask asks for again.\n */\nexport function aiReadLayoutPage(\n raw: unknown,\n planned: number,\n plan?: readonly AiLayoutPlannedSection[],\n): AiLayoutPageReading {\n const settled: AiLayoutSettlement[] = []\n const given =\n isRecord(raw) && Array.isArray(raw['sections']) ? raw['sections'] : []\n const aligned =\n given.length > planned && plan && plan.length === planned\n ? alignSections(given, plan)\n : Array.from({ length: planned }, (_, index) => index)\n if (given.length > planned) {\n settled.push({\n at: 'sections',\n what: `${given.length} sections for a plan of ${planned}; ${\n aligned.every((at, index) => at === index) ? 'the extra left out' : `sections ${aligned.map((at) => at + 1).join(', ')} kept as the plan's`\n }`,\n })\n }\n const sections = aligned.map((at, index) =>\n aiReadLayoutSection(given[at], `sections[${index}]`, settled),\n )\n return { sections, settled }\n}\n\n/** A frame answer read: a header and a footer, either `null` where it was not usable. */\nexport function aiReadLayoutFrame(raw: unknown): {\n header: AiLayoutSection | null\n footer: AiLayoutSection | null\n settled: AiLayoutSettlement[]\n} {\n const settled: AiLayoutSettlement[] = []\n const record = isRecord(raw) ? raw : {}\n // A header needs no block of its own: its brand and navigation are the\n // site's, so an empty one is a plain bar.\n const header = isRecord(record['header'])\n ? (aiReadLayoutSection(record['header'], 'header', settled) ??\n readBare(record['header']))\n : null\n const footer = aiReadLayoutSection(record['footer'], 'footer', settled)\n return { header, footer, settled }\n}\n\n/** A section with no usable block, keeping only its band and alignment. */\nfunction readBare(raw: Record<string, unknown>): AiLayoutSection {\n const band = oneOf(raw['band'], AI_LAYOUT_BANDS)\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n return { ...(band ? { band } : {}), ...(align ? { align } : {}), blocks: [] }\n}\n\n// ── The design of a page brought in (AGL-4107) ───────────────────────────\n\n/**\n * A page brought in from its owner's own website is laid out as its source\n * lays it out (AGL-4107): the reader reads its sections, bands, rows, groups,\n * pictures and icons (`site-import/site-import-layout.ts`), and code compiles\n * that structure with every word as written (`jobs/ai-site-import-compile.ts`).\n * The one question left for a model is the page's DESIGN, over the structure\n * it is shown: which hero the opening becomes around its picture, how each\n * group is drawn, a band where the source gave none, the kind of a group the\n * reader could not tell, and an alignment. The schema below names only those\n * choices, so no word of copy, no line and no structure has anywhere to go.\n */\n\n/** A section's choice where the source's own stands: its band, its alignment, a group's kind. */\nexport const AI_LAYOUT_DESIGN_SOURCE = 'source'\n\n/** The kinds a group the reader could not tell may be read as. */\nexport const AI_LAYOUT_DESIGN_GROUP_KINDS = ['cards', 'steps', 'stats', 'list'] as const\nexport type AiLayoutDesignGroupKind = (typeof AI_LAYOUT_DESIGN_GROUP_KINDS)[number]\n\n/** The bands a design answer may give a section the source drew on no band of its own. */\nexport const AI_LAYOUT_DESIGN_BANDS = ['plain', 'soft', 'dark'] as const\n\nexport interface AiLayoutDesignGroup {\n /** The group's kind, where the answer reads it otherwise than the reader did. */\n kind?: AiLayoutDesignGroupKind\n style?: AiLayoutGroupStyle\n}\n\nexport interface AiLayoutDesignSection {\n /** The hero the opening becomes, or `none` to keep it as the source lays it out. */\n hero?: AiLayoutImportHero | 'none'\n band?: (typeof AI_LAYOUT_DESIGN_BANDS)[number]\n align?: AiLayoutAlign\n /** One for each group of the section, in order. */\n groups: AiLayoutDesignGroup[]\n}\n\n/** A design answer read: one entry per section of the page, `null` where it gave none. */\nexport interface AiLayoutDesignAnswer {\n sections: Array<AiLayoutDesignSection | null>\n}\n\nconst DESIGN_GROUP_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'style'],\n properties: {\n kind: { type: 'string', enum: [AI_LAYOUT_DESIGN_SOURCE, ...AI_LAYOUT_DESIGN_GROUP_KINDS] },\n style: { type: 'string', enum: [...AI_LAYOUT_GROUP_STYLES] },\n },\n} as const\n\nconst DESIGN_SECTION_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['hero', 'band', 'align', 'groups'],\n properties: {\n hero: { type: 'string', enum: ['none', ...AI_LAYOUT_IMPORT_HEROES] },\n band: { type: 'string', enum: [AI_LAYOUT_DESIGN_SOURCE, ...AI_LAYOUT_DESIGN_BANDS] },\n align: { type: 'string', enum: [AI_LAYOUT_DESIGN_SOURCE, ...AI_LAYOUT_ALIGNS] },\n groups: { type: 'array', items: DESIGN_GROUP_SCHEMA },\n },\n} as const\n\n/** The design tool: one entry per section of a page brought in, in order, naming design choices only. */\nexport const AI_LAYOUT_DESIGN_TOOL: AiTool = {\n name: 'submit_design',\n description: \"Submit the page's design: one entry in sections for each section of the page, in order, with one entry in groups for each of its groups.\",\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['sections'],\n properties: { sections: { type: 'array', items: DESIGN_SECTION_SCHEMA } },\n },\n}\n\nfunction readDesignSection(raw: unknown): AiLayoutDesignSection | null {\n if (!isRecord(raw)) return null\n const section: AiLayoutDesignSection = { groups: [] }\n const hero = oneOf(raw['hero'], ['none', ...AI_LAYOUT_IMPORT_HEROES] as const)\n if (hero) section.hero = hero\n const band = oneOf(raw['band'], AI_LAYOUT_DESIGN_BANDS)\n if (band) section.band = band\n const align = oneOf(raw['align'], AI_LAYOUT_ALIGNS)\n if (align) section.align = align\n for (const entry of Array.isArray(raw['groups']) ? raw['groups'] : []) {\n const group: AiLayoutDesignGroup = {}\n if (isRecord(entry)) {\n const kind = oneOf(entry['kind'], AI_LAYOUT_DESIGN_GROUP_KINDS)\n if (kind) group.kind = kind\n const style = oneOf(entry['style'], AI_LAYOUT_GROUP_STYLES)\n if (style) group.style = style\n }\n section.groups.push(group)\n }\n return section\n}\n\n/**\n * A design answer read (AGL-4107): each section's choices by position, and\n * `null` for one it gave nothing usable for, which is then drawn as its\n * source has it. An answer carries no words, and none would be read.\n */\nexport function aiReadLayoutDesign(raw: unknown, sections: number): AiLayoutDesignAnswer {\n const given = isRecord(raw) && Array.isArray(raw['sections']) ? raw['sections'] : []\n return { sections: Array.from({ length: Math.max(0, sections) }, (_, index) => readDesignSection(given[index])) }\n}\n"],"names":["AI_ICON_WORDS","AI_LAYOUT_BLOCK_KINDS","AI_LAYOUT_GROUP_KINDS","Set","AI_LAYOUT_BANDS","AI_LAYOUT_ALIGNS","AI_LAYOUT_STYLES","AI_LAYOUT_MAX_COLS","AI_LAYOUT_MAX_ITEMS","AI_LAYOUT_MAX_QUOTES","AI_LAYOUT_IMPORT_BLOCK_KINDS","AI_LAYOUT_GROUP_STYLES","AI_LAYOUT_IMPORT_HEROES","ITEM_SCHEMA","type","additionalProperties","required","properties","title","text","to","icon","BLOCK_SCHEMA","kind","enum","col","style","items","SECTION_SCHEMA","band","cols","align","blocks","AI_LAYOUT_PAGE_TOOL","name","description","strict","inputSchema","sections","AI_LAYOUT_FRAME_TOOL","header","footer","AI_LAYOUT_LANGUAGE_TEXT","join","isRecord","value","Array","isArray","trim","undefined","oneOf","values","includes","readItem","raw","item","readBlock","at","settled","block","push","what","JSON","stringify","Number","isInteger","words","toLowerCase","has","map","filter","most","Infinity","length","slice","empty","aiReadLayoutSection","section","isFinite","Math","min","max","round","forEach","entry","index","FORM_SECTION","takesForm","planned","test","uses","some","ref","AI_LAYOUT_FORM_MISFIT","WORD","STOP","wordsOf","match","word","ITEM_KINDS","fit","said","String","named","shared","other","startsWith","showsItems","wantsItems","holdsForm","alignSections","given","n","m","best","from","fill","i","j","picked","aiReadLayoutPage","plan","aligned","_","every","aiReadLayoutFrame","record","readBare","AI_LAYOUT_DESIGN_SOURCE","AI_LAYOUT_DESIGN_GROUP_KINDS","AI_LAYOUT_DESIGN_BANDS","DESIGN_GROUP_SCHEMA","DESIGN_SECTION_SCHEMA","hero","groups","AI_LAYOUT_DESIGN_TOOL","readDesignSection","group","aiReadLayoutDesign"],"mappings":"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"}
|
|
@@ -160,16 +160,20 @@ export declare const AI_MEASURED_IMPORT_CREDITS: {
|
|
|
160
160
|
readonly p90: 29;
|
|
161
161
|
};
|
|
162
162
|
/**
|
|
163
|
-
* A main page Aglyn AI
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
163
|
+
* A main page whose design Aglyn AI answers (AGL-4107): one small answer on
|
|
164
|
+
* the balanced tier over the page's read structure — about 1,000 to 1,300
|
|
165
|
+
* tokens in, two thirds of them its cached instructions, and 500 to 900 out
|
|
166
|
+
* with its thinking, $0.009–$0.017 at Sonnet 5.5's billed rates over the
|
|
167
|
+
* recorded aglyn.com pages — and no listing, the source's own being kept.
|
|
168
|
+
* The 10/29 run spent 36–58 a page on a smaller model for a line-by-line
|
|
169
|
+
* answer twenty times the size. PROVISIONAL until the next live import
|
|
170
|
+
* measures it.
|
|
167
171
|
*/
|
|
168
172
|
readonly arranged: {
|
|
169
|
-
readonly median:
|
|
170
|
-
readonly p90:
|
|
173
|
+
readonly median: 16;
|
|
174
|
+
readonly p90: 30;
|
|
171
175
|
};
|
|
172
|
-
/** A page copied as it is (AGL-4083):
|
|
176
|
+
/** A page copied as it is (AGL-4083): no model, and a listing only where the source has none. Provisional, as `arranged`. */
|
|
173
177
|
readonly copied: {
|
|
174
178
|
readonly median: 1;
|
|
175
179
|
readonly p90: 2;
|
|
@@ -149,15 +149,19 @@
|
|
|
149
149
|
page: AI_MEASURED_PAID_SITE_CREDITS.page,
|
|
150
150
|
frame: AI_MEASURED_PAID_SITE_CREDITS.layout,
|
|
151
151
|
/**
|
|
152
|
-
* A main page Aglyn AI
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
152
|
+
* A main page whose design Aglyn AI answers (AGL-4107): one small answer on
|
|
153
|
+
* the balanced tier over the page's read structure — about 1,000 to 1,300
|
|
154
|
+
* tokens in, two thirds of them its cached instructions, and 500 to 900 out
|
|
155
|
+
* with its thinking, $0.009–$0.017 at Sonnet 5.5's billed rates over the
|
|
156
|
+
* recorded aglyn.com pages — and no listing, the source's own being kept.
|
|
157
|
+
* The 10/29 run spent 36–58 a page on a smaller model for a line-by-line
|
|
158
|
+
* answer twenty times the size. PROVISIONAL until the next live import
|
|
159
|
+
* measures it.
|
|
156
160
|
*/ arranged: {
|
|
157
|
-
median:
|
|
158
|
-
p90:
|
|
161
|
+
median: 16,
|
|
162
|
+
p90: 30
|
|
159
163
|
},
|
|
160
|
-
/** A page copied as it is (AGL-4083):
|
|
164
|
+
/** A page copied as it is (AGL-4083): no model, and a listing only where the source has none. Provisional, as `arranged`. */ copied: {
|
|
161
165
|
median: 1,
|
|
162
166
|
p90: 2
|
|
163
167
|
}
|