@aglyn/plugins-ai 1.0.0-beta.224 → 1.0.0-beta.226
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/model/ai-template-subjects.d.ts +4 -3
- package/src/lib/model/ai-template-subjects.js +5 -4
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +9 -9
- package/src/lib/providers/catalog.js +11 -11
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +12 -4
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-ai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.226",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.226",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.226",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.226",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.226",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.226",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.226",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.226",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.226",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.226",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.226",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.226",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.226",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.226",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.226",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.226",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.226",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -75,9 +75,10 @@ export declare function aiTemplateAddressTokens(definition: AiTemplateSubjectDef
|
|
|
75
75
|
/**
|
|
76
76
|
* Whether a token is filled for a machine and never read on the page: a URL
|
|
77
77
|
* segment (`{{entry.slug}}`, `{{collection.slug}}`, `{{entry.collectionSlug}}`)
|
|
78
|
-
*
|
|
79
|
-
* (`{{entry.publishedAt}}`)
|
|
80
|
-
*
|
|
78
|
+
* the ISO timestamp a Video element's publication date takes
|
|
79
|
+
* (`{{entry.publishedAt}}`), or the bare seconds its duration takes
|
|
80
|
+
* (`{{entry.coverVideoDuration}}`, AGL-3584). A block may be handed one whole;
|
|
81
|
+
* a reader shown one sees hyphens and digits (AGL-3143 §16).
|
|
81
82
|
*/
|
|
82
83
|
export declare function isAiUnreadToken(token: string): boolean;
|
|
83
84
|
/** The tokens of a subject its page shows: every one a reader reads or follows, which is all but the unread. */
|
|
@@ -114,13 +114,14 @@ export const AI_TEMPLATE_SUBJECT_DEFINITIONS = {
|
|
|
114
114
|
/**
|
|
115
115
|
* Whether a token is filled for a machine and never read on the page: a URL
|
|
116
116
|
* segment (`{{entry.slug}}`, `{{collection.slug}}`, `{{entry.collectionSlug}}`)
|
|
117
|
-
*
|
|
118
|
-
* (`{{entry.publishedAt}}`)
|
|
119
|
-
*
|
|
117
|
+
* the ISO timestamp a Video element's publication date takes
|
|
118
|
+
* (`{{entry.publishedAt}}`), or the bare seconds its duration takes
|
|
119
|
+
* (`{{entry.coverVideoDuration}}`, AGL-3584). A block may be handed one whole;
|
|
120
|
+
* a reader shown one sees hyphens and digits (AGL-3143 §16).
|
|
120
121
|
*/ export function isAiUnreadToken(token) {
|
|
121
122
|
var _token_replace_split_pop;
|
|
122
123
|
const field = (_token_replace_split_pop = token.replace(/[{}\s]/g, '').split('.').pop()) != null ? _token_replace_split_pop : '';
|
|
123
|
-
return /^(?:slug|publishedAt)$|Slug$/.test(field);
|
|
124
|
+
return /^(?:slug|publishedAt)$|Slug$|Duration$/.test(field);
|
|
124
125
|
}
|
|
125
126
|
/** The tokens of a subject its page shows: every one a reader reads or follows, which is all but the unread. */ export function aiTemplateShownTokens(definition) {
|
|
126
127
|
return definition.tokens.map((entry)=>entry.token).filter((token)=>!isAiUnreadToken(token));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-template-subjects.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 {\n AUTHOR_TOKEN_CATALOG,\n COLLECTION_TOKEN_CATALOG,\n ENTRY_TOKEN_CATALOG,\n type BindingTokenCatalogEntry,\n} from '@aglyn/aglyn/app-utils/binding-token-catalog'\n\n/**\n * What a generated page template renders for (AGL-2909): one page drawn once\n * per record the site already keeps.\n *\n * - `entry` — a content collection's entry page: a blog post, an event, a\n * case study. Picked as the collection's entry template screen.\n * - `product` — the store's product page. Picked as the product template in\n * Store settings.\n * - `author` — an author's page. Picked as the author page screen.\n *\n * Everything that differs per record is a binding token, and the tokens a\n * template may bind are the ones the platform fills on that page. For an\n * entry and an author those are the besigner's own insert-picker catalogs, so\n * what a model may bind and what an author can pick are one list; a product's\n * are the commerce product page's, which no catalog names yet (below).\n *\n * Model only: shapes, the vocabularies and the inputs reader, no runtime.\n */\n\nexport type AiTemplateSubject = 'entry' | 'product' | 'author'\n\nexport const AI_TEMPLATE_SUBJECTS: readonly AiTemplateSubject[] = ['entry', 'product', 'author']\n\n/**\n * The tokens the commerce product page fills. The commerce plugin's page\n * resolver is their definition of record; a spec reads its source and holds\n * this list to it in both directions, because a plugin never imports another\n * plugin's internals.\n */\nexport const AI_PRODUCT_TEMPLATE_TOKENS: readonly BindingTokenCatalogEntry[] = [\n { token: '{{product.name}}', label: 'Name', description: 'The product’s name.' },\n {\n token: '{{product.description}}',\n label: 'Description',\n description: 'The product’s description.',\n },\n {\n token: '{{product.price}}',\n label: 'Price',\n description: 'The price, or “From” the lowest price when variants differ.',\n },\n { token: '{{product.image}}', label: 'Image', description: 'The product’s first image.' },\n { token: '{{product.slug}}', label: 'Slug', description: 'The product’s URL segment.' },\n]\n\n/** The pager tokens, which resolve on an author's page over their own archive. */\nconst PAGINATION_TOKENS = COLLECTION_TOKEN_CATALOG.filter((entry) =>\n entry.token.startsWith('{{pagination.'),\n)\n\nexport interface AiTemplateSubjectDefinition {\n subject: AiTemplateSubject\n /** One rendered page's record, as a sentence names it. */\n noun: string\n /** The token the page's one h1 binds, so every record shows its own title. */\n titleToken: string\n /** Every token the page may bind. */\n tokens: readonly BindingTokenCatalogEntry[]\n /**\n * Palette blocks that fill themselves from this subject on its page, and\n * render nothing on another subject's. Inside a Collection Entries card each\n * fills from that card's entry instead, so a card is exempt.\n */\n blocks: readonly string[]\n /** The template's name when the plan names none. */\n defaultName: string\n /** Where a member puts the page to work, for the proposal and the docs. */\n appliedIn: string\n}\n\nexport const AI_TEMPLATE_SUBJECT_DEFINITIONS: Readonly<\n Record<AiTemplateSubject, AiTemplateSubjectDefinition>\n> = {\n entry: {\n subject: 'entry',\n noun: 'a collection entry',\n titleToken: '{{entry.title}}',\n tokens: [...ENTRY_TOKEN_CATALOG, ...COLLECTION_TOKEN_CATALOG],\n blocks: ['collectionEntryMeta', 'collectionEntryBody', 'collectionEntryAuthor', 'collectionRelated'],\n defaultName: 'Entry page template',\n appliedIn: 'the collection’s entry template page, under Template pages in Content',\n },\n product: {\n subject: 'product',\n noun: 'a product',\n titleToken: '{{product.name}}',\n tokens: AI_PRODUCT_TEMPLATE_TOKENS,\n blocks: [],\n defaultName: 'Product page template',\n appliedIn: 'the product page template in Store settings',\n },\n author: {\n subject: 'author',\n noun: 'an author',\n titleToken: '{{author.name}}',\n tokens: [...AUTHOR_TOKEN_CATALOG, ...PAGINATION_TOKENS],\n blocks: ['contentAuthorProfile'],\n defaultName: 'Author page template',\n appliedIn: 'the author page on the Authors tab',\n },\n}\n\n/** Every `{{…}}` token a string holds, spelled without inner whitespace. */\nexport function aiBindingTokensIn(value: string): string[] {\n return [...value.matchAll(/\\{\\{\\s*([^{}\\s]+)\\s*\\}\\}/g)].map((match) => `{{${match[1]}}}`)\n}\n\n/**\n * Whether a token may fill a link or media prop whole: its field is an\n * address or a picture (`{{entry.url}}`, `{{entry.coverImage}}`,\n * `{{author.image}}`), never copy.\n */\nexport function isAiAddressToken(token: string): boolean {\n const field = token.replace(/^\\{\\{|\\}\\}$/g, '').split('.').pop() ?? ''\n return /^(?:url|image)$|(?:Url|Image|Video)$/.test(field)\n}\n\n/** The tokens of a subject a link or media prop may hold. */\nexport function aiTemplateAddressTokens(definition: AiTemplateSubjectDefinition): string[] {\n return definition.tokens.map((entry) => entry.token).filter(isAiAddressToken)\n}\n\n/**\n * Whether a token is filled for a machine and never read on the page: a URL\n * segment (`{{entry.slug}}`, `{{collection.slug}}`, `{{entry.collectionSlug}}`)\n * or the ISO timestamp a Video element's publication date takes\n * (`{{entry.publishedAt}}`). A block may be handed one whole; a reader shown\n * one sees hyphens and digits (AGL-3143 §16).\n */\nexport function isAiUnreadToken(token: string): boolean {\n const field = token.replace(/[{}\\s]/g, '').split('.').pop() ?? ''\n return /^(?:slug|publishedAt)$|Slug$/.test(field)\n}\n\n/** The tokens of a subject its page shows: every one a reader reads or follows, which is all but the unread. */\nexport function aiTemplateShownTokens(definition: AiTemplateSubjectDefinition): string[] {\n return definition.tokens.map((entry) => entry.token).filter((token) => !isAiUnreadToken(token))\n}\n\n/** What a template job reads from its inputs. */\nexport interface AiTemplateJobInputs {\n subject: AiTemplateSubject\n /** The content collection an entry template is for; `null` for a product or author page. */\n collectionId: string | null\n}\n\nconst RECORD_ID = /^[A-Za-z0-9_-]{1,100}$/\n\n/** The inputs of a template job, or a sentence naming what is missing. */\nexport function parseAiTemplateJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiTemplateJobInputs | string {\n const subject = String(inputs?.['subject'] ?? '')\n if (!(AI_TEMPLATE_SUBJECTS as readonly string[]).includes(subject)) {\n return `Name what the template is for: inputs.subject is one of ${AI_TEMPLATE_SUBJECTS.join(', ')}`\n }\n if (subject !== 'entry') return { subject: subject as AiTemplateSubject, collectionId: null }\n const raw = inputs?.['collectionId']\n const collectionId = typeof raw === 'string' ? raw.trim() : ''\n if (!RECORD_ID.test(collectionId)) {\n return 'An entry page template names its content collection in inputs.collectionId'\n }\n return { subject: 'entry', collectionId }\n}\n"],"names":["AUTHOR_TOKEN_CATALOG","COLLECTION_TOKEN_CATALOG","ENTRY_TOKEN_CATALOG","AI_TEMPLATE_SUBJECTS","AI_PRODUCT_TEMPLATE_TOKENS","token","label","description","PAGINATION_TOKENS","filter","entry","startsWith","AI_TEMPLATE_SUBJECT_DEFINITIONS","subject","noun","titleToken","tokens","blocks","defaultName","appliedIn","product","author","aiBindingTokensIn","value","matchAll","map","match","isAiAddressToken","field","replace","split","pop","test","aiTemplateAddressTokens","definition","isAiUnreadToken","aiTemplateShownTokens","RECORD_ID","parseAiTemplateJobInputs","inputs","String","includes","join","collectionId","raw","trim"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-template-subjects.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 {\n AUTHOR_TOKEN_CATALOG,\n COLLECTION_TOKEN_CATALOG,\n ENTRY_TOKEN_CATALOG,\n type BindingTokenCatalogEntry,\n} from '@aglyn/aglyn/app-utils/binding-token-catalog'\n\n/**\n * What a generated page template renders for (AGL-2909): one page drawn once\n * per record the site already keeps.\n *\n * - `entry` — a content collection's entry page: a blog post, an event, a\n * case study. Picked as the collection's entry template screen.\n * - `product` — the store's product page. Picked as the product template in\n * Store settings.\n * - `author` — an author's page. Picked as the author page screen.\n *\n * Everything that differs per record is a binding token, and the tokens a\n * template may bind are the ones the platform fills on that page. For an\n * entry and an author those are the besigner's own insert-picker catalogs, so\n * what a model may bind and what an author can pick are one list; a product's\n * are the commerce product page's, which no catalog names yet (below).\n *\n * Model only: shapes, the vocabularies and the inputs reader, no runtime.\n */\n\nexport type AiTemplateSubject = 'entry' | 'product' | 'author'\n\nexport const AI_TEMPLATE_SUBJECTS: readonly AiTemplateSubject[] = ['entry', 'product', 'author']\n\n/**\n * The tokens the commerce product page fills. The commerce plugin's page\n * resolver is their definition of record; a spec reads its source and holds\n * this list to it in both directions, because a plugin never imports another\n * plugin's internals.\n */\nexport const AI_PRODUCT_TEMPLATE_TOKENS: readonly BindingTokenCatalogEntry[] = [\n { token: '{{product.name}}', label: 'Name', description: 'The product’s name.' },\n {\n token: '{{product.description}}',\n label: 'Description',\n description: 'The product’s description.',\n },\n {\n token: '{{product.price}}',\n label: 'Price',\n description: 'The price, or “From” the lowest price when variants differ.',\n },\n { token: '{{product.image}}', label: 'Image', description: 'The product’s first image.' },\n { token: '{{product.slug}}', label: 'Slug', description: 'The product’s URL segment.' },\n]\n\n/** The pager tokens, which resolve on an author's page over their own archive. */\nconst PAGINATION_TOKENS = COLLECTION_TOKEN_CATALOG.filter((entry) =>\n entry.token.startsWith('{{pagination.'),\n)\n\nexport interface AiTemplateSubjectDefinition {\n subject: AiTemplateSubject\n /** One rendered page's record, as a sentence names it. */\n noun: string\n /** The token the page's one h1 binds, so every record shows its own title. */\n titleToken: string\n /** Every token the page may bind. */\n tokens: readonly BindingTokenCatalogEntry[]\n /**\n * Palette blocks that fill themselves from this subject on its page, and\n * render nothing on another subject's. Inside a Collection Entries card each\n * fills from that card's entry instead, so a card is exempt.\n */\n blocks: readonly string[]\n /** The template's name when the plan names none. */\n defaultName: string\n /** Where a member puts the page to work, for the proposal and the docs. */\n appliedIn: string\n}\n\nexport const AI_TEMPLATE_SUBJECT_DEFINITIONS: Readonly<\n Record<AiTemplateSubject, AiTemplateSubjectDefinition>\n> = {\n entry: {\n subject: 'entry',\n noun: 'a collection entry',\n titleToken: '{{entry.title}}',\n tokens: [...ENTRY_TOKEN_CATALOG, ...COLLECTION_TOKEN_CATALOG],\n blocks: ['collectionEntryMeta', 'collectionEntryBody', 'collectionEntryAuthor', 'collectionRelated'],\n defaultName: 'Entry page template',\n appliedIn: 'the collection’s entry template page, under Template pages in Content',\n },\n product: {\n subject: 'product',\n noun: 'a product',\n titleToken: '{{product.name}}',\n tokens: AI_PRODUCT_TEMPLATE_TOKENS,\n blocks: [],\n defaultName: 'Product page template',\n appliedIn: 'the product page template in Store settings',\n },\n author: {\n subject: 'author',\n noun: 'an author',\n titleToken: '{{author.name}}',\n tokens: [...AUTHOR_TOKEN_CATALOG, ...PAGINATION_TOKENS],\n blocks: ['contentAuthorProfile'],\n defaultName: 'Author page template',\n appliedIn: 'the author page on the Authors tab',\n },\n}\n\n/** Every `{{…}}` token a string holds, spelled without inner whitespace. */\nexport function aiBindingTokensIn(value: string): string[] {\n return [...value.matchAll(/\\{\\{\\s*([^{}\\s]+)\\s*\\}\\}/g)].map((match) => `{{${match[1]}}}`)\n}\n\n/**\n * Whether a token may fill a link or media prop whole: its field is an\n * address or a picture (`{{entry.url}}`, `{{entry.coverImage}}`,\n * `{{author.image}}`), never copy.\n */\nexport function isAiAddressToken(token: string): boolean {\n const field = token.replace(/^\\{\\{|\\}\\}$/g, '').split('.').pop() ?? ''\n return /^(?:url|image)$|(?:Url|Image|Video)$/.test(field)\n}\n\n/** The tokens of a subject a link or media prop may hold. */\nexport function aiTemplateAddressTokens(definition: AiTemplateSubjectDefinition): string[] {\n return definition.tokens.map((entry) => entry.token).filter(isAiAddressToken)\n}\n\n/**\n * Whether a token is filled for a machine and never read on the page: a URL\n * segment (`{{entry.slug}}`, `{{collection.slug}}`, `{{entry.collectionSlug}}`)\n * the ISO timestamp a Video element's publication date takes\n * (`{{entry.publishedAt}}`), or the bare seconds its duration takes\n * (`{{entry.coverVideoDuration}}`, AGL-3584). A block may be handed one whole;\n * a reader shown one sees hyphens and digits (AGL-3143 §16).\n */\nexport function isAiUnreadToken(token: string): boolean {\n const field = token.replace(/[{}\\s]/g, '').split('.').pop() ?? ''\n return /^(?:slug|publishedAt)$|Slug$|Duration$/.test(field)\n}\n\n/** The tokens of a subject its page shows: every one a reader reads or follows, which is all but the unread. */\nexport function aiTemplateShownTokens(definition: AiTemplateSubjectDefinition): string[] {\n return definition.tokens.map((entry) => entry.token).filter((token) => !isAiUnreadToken(token))\n}\n\n/** What a template job reads from its inputs. */\nexport interface AiTemplateJobInputs {\n subject: AiTemplateSubject\n /** The content collection an entry template is for; `null` for a product or author page. */\n collectionId: string | null\n}\n\nconst RECORD_ID = /^[A-Za-z0-9_-]{1,100}$/\n\n/** The inputs of a template job, or a sentence naming what is missing. */\nexport function parseAiTemplateJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiTemplateJobInputs | string {\n const subject = String(inputs?.['subject'] ?? '')\n if (!(AI_TEMPLATE_SUBJECTS as readonly string[]).includes(subject)) {\n return `Name what the template is for: inputs.subject is one of ${AI_TEMPLATE_SUBJECTS.join(', ')}`\n }\n if (subject !== 'entry') return { subject: subject as AiTemplateSubject, collectionId: null }\n const raw = inputs?.['collectionId']\n const collectionId = typeof raw === 'string' ? raw.trim() : ''\n if (!RECORD_ID.test(collectionId)) {\n return 'An entry page template names its content collection in inputs.collectionId'\n }\n return { subject: 'entry', collectionId }\n}\n"],"names":["AUTHOR_TOKEN_CATALOG","COLLECTION_TOKEN_CATALOG","ENTRY_TOKEN_CATALOG","AI_TEMPLATE_SUBJECTS","AI_PRODUCT_TEMPLATE_TOKENS","token","label","description","PAGINATION_TOKENS","filter","entry","startsWith","AI_TEMPLATE_SUBJECT_DEFINITIONS","subject","noun","titleToken","tokens","blocks","defaultName","appliedIn","product","author","aiBindingTokensIn","value","matchAll","map","match","isAiAddressToken","field","replace","split","pop","test","aiTemplateAddressTokens","definition","isAiUnreadToken","aiTemplateShownTokens","RECORD_ID","parseAiTemplateJobInputs","inputs","String","includes","join","collectionId","raw","trim"],"mappings":"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"}
|
|
@@ -137,19 +137,19 @@ export interface AnthropicCrmAssistanceWording {
|
|
|
137
137
|
never: string;
|
|
138
138
|
}
|
|
139
139
|
/**
|
|
140
|
-
* CRM assistance as `/legal/subprocessors`
|
|
141
|
-
* fields of a record, with addresses and numbers in its
|
|
142
|
-
* is what production sends while
|
|
140
|
+
* CRM assistance as `/legal/subprocessors` published it until October 5,
|
|
141
|
+
* 2026: the disclosed fields of a record, with addresses and numbers in its
|
|
142
|
+
* text replaced. It is still what production sends while
|
|
143
|
+
* `release_crm_assist_whole_record` is off, which the published wording
|
|
144
|
+
* covers: the row describes what the CRM can send, not who has it on.
|
|
143
145
|
*/
|
|
144
146
|
export declare const ANTHROPIC_CRM_ASSISTANCE_DISCLOSED: AnthropicCrmAssistanceWording;
|
|
145
147
|
/**
|
|
146
148
|
* CRM assistance once the whole record is sent (AGL-3520): the open-ended
|
|
147
|
-
* wording the Subprocessors row and Privacy Policy section 2
|
|
148
|
-
* republished with
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* this takes its place and `publishedOn` moves to the change-log date
|
|
152
|
-
* (`docs/drafts/agl-3520-crm-assistance-whole-record-disclosure.md`).
|
|
149
|
+
* wording the Subprocessors row and Privacy Policy section 2 were
|
|
150
|
+
* republished with on October 5, 2026 (legal v10), before
|
|
151
|
+
* `release_crm_assist_whole_record` may be turned on. The catalog row below
|
|
152
|
+
* carries it, dated to that change-log entry.
|
|
153
153
|
*/
|
|
154
154
|
export declare const ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD: AnthropicCrmAssistanceWording;
|
|
155
155
|
/** The Anthropic row's data cell, with CRM assistance worded as `crm` words it. */
|
|
@@ -49,21 +49,21 @@ const OPENAI_COMPATIBLE = 'openai-compatible';
|
|
|
49
49
|
*/ const ANTHROPIC_DATA_RECEIVED_LEAD = "What the user submits — a question, instruction or brief, with the earlier messages of the same Assist conversation — and the content of the element, post, section or page being worked on, with the generated response. On Pro and above, the organization's name and the console route and host travel with an Assist question. For an edit the assistant proposes in the besigner, an outline of the open page, component or layout: element and component ids, layer names, shortened setting values and the selected element's styles. For a generation job, the site inventory: the names and addresses of its pages and collections, the names of its components, layouts, templates, forms and datasets with their prop and field names, and the theme's summary, colors and fonts. For a theme change, the site's current theme settings and brand colors as hex values, from the organization's brand settings, the site logo in the media library or a public page the brief links to. For features that review or write search information, the text and structure of the pages concerned. For product copy, the store's name, the product's name, type, description, tags, options and search listing, the store's category names, and the product's first media-library photo as a copy at most 768 px on its longer edge with its metadata stripped; never another media file, a price, stock, an order or a customer. For products, categories and discounts proposed from a brief, the store's name and its existing category names. For an automation drafted from a brief, which of CRM, webhooks and bookings the plan includes; for an explanation, the automation's outline (trigger, conditions, each step's text with the names of the lists, campaigns, workflows, webhooks and datasets it uses and whether each exists, and a workflow's function names and expressions) and, for a failed run, its time, steps and recorded errors, with email addresses removed and never the triggering event's data. For an insight, the figure reports available and aggregate tables: traffic with top page paths, referrers and campaign tags; form views and submissions; revenue, orders and best-selling product names; bookings by service; campaign subjects with delivery, open and click rates; A/B test and variant conversions; and, for a dataset the member can see, field names and types, record and fill counts, number ranges and totals grouped by a value at least three records share. Email addresses and phone numbers are removed, and no individual record is sent. ";
|
|
50
50
|
/** What every CRM door adds after its record sentence: drafts and imports. */ const ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS = "An email draft adds the request and the record's merge field names; an import sends the field names and types and each column's header and value kind, never a row.";
|
|
51
51
|
/**
|
|
52
|
-
* CRM assistance as `/legal/subprocessors`
|
|
53
|
-
* fields of a record, with addresses and numbers in its
|
|
54
|
-
* is what production sends while
|
|
52
|
+
* CRM assistance as `/legal/subprocessors` published it until October 5,
|
|
53
|
+
* 2026: the disclosed fields of a record, with addresses and numbers in its
|
|
54
|
+
* text replaced. It is still what production sends while
|
|
55
|
+
* `release_crm_assist_whole_record` is off, which the published wording
|
|
56
|
+
* covers: the row describes what the CRM can send, not who has it on.
|
|
55
57
|
*/ export const ANTHROPIC_CRM_ASSISTANCE_DISCLOSED = {
|
|
56
58
|
record: "For CRM assistance, the opened contact, company, deal or lead as the CRM shows it: its name and, by kind, job title, company, lifecycle stage, tags, capture history and counts, domain, industry, headcount, pipeline stages, status, amount, dates, lost reason, parties and lead status, with its notes, newest timeline entries and open tasks and deals. Email addresses and phone numbers in that text are replaced, and no email, phone or postal field, marketing consent, custom field value, team member or record id is sent.",
|
|
57
59
|
never: 'No account identifiers, email addresses or authentication tokens.'
|
|
58
60
|
};
|
|
59
61
|
/**
|
|
60
62
|
* CRM assistance once the whole record is sent (AGL-3520): the open-ended
|
|
61
|
-
* wording the Subprocessors row and Privacy Policy section 2
|
|
62
|
-
* republished with
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* this takes its place and `publishedOn` moves to the change-log date
|
|
66
|
-
* (`docs/drafts/agl-3520-crm-assistance-whole-record-disclosure.md`).
|
|
63
|
+
* wording the Subprocessors row and Privacy Policy section 2 were
|
|
64
|
+
* republished with on October 5, 2026 (legal v10), before
|
|
65
|
+
* `release_crm_assist_whole_record` may be turned on. The catalog row below
|
|
66
|
+
* carries it, dated to that change-log entry.
|
|
67
67
|
*/ export const ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD = {
|
|
68
68
|
record: 'For CRM assistance, the opened record as the CRM shows it, with all of its standard and custom fields, including contact details, notes, timeline and related records.',
|
|
69
69
|
never: 'No account identifiers or authentication tokens, and no email address outside an opened CRM record.'
|
|
@@ -84,9 +84,9 @@ const OPENAI_COMPATIBLE = 'openai-compatible';
|
|
|
84
84
|
entity: 'Anthropic, PBC',
|
|
85
85
|
region: 'United States',
|
|
86
86
|
purpose: "AI-assisted features in the console and the site editor: the Aglyn Assist helper, including changes it proposes to a page, component, or layout open in the editor; editor assistance (rewriting element copy, drafting blog bodies, generating a section layout); and AI generation, which creates drafts and proposals for a customer's site from a brief or from what the site already holds, such as copy, layouts, templates, automations, search titles and descriptions, theme changes, product descriptions and tags, draft products, and store categories and discounts; explanations of a site's automations, including why an automation's run failed; AI insights, which answer a customer's questions about its own figures, such as site traffic, sales, bookings, forms, campaigns, A/B tests and datasets, and write a weekly summary of them for members who ask for one; and AI assistance in the CRM, which summarizes a contact, company, deal or lead for a user and suggests a next step, drafts a one-to-one email for the user to review and send, and suggests how the columns of a spreadsheet the user imports match CRM fields",
|
|
87
|
-
publishedOn: '2026-
|
|
87
|
+
publishedOn: '2026-10-05',
|
|
88
88
|
reason: "Reached through the AI plugin's Anthropic adapter (`libs/plugins/ai/src/lib/providers/anthropic.ts`) by the doors that call the AI runtime. `libs/plugins/ai/src/lib/server/assist-chat.ts` is gated by `release_assist` AND the key, and a generation job's text step by `release_ai_generative`; `libs/plugins/ai/src/lib/server/ai-assist.ts` carries NO release flag, so setting `{apiKeyEnv}` in production is by itself what starts this flow. `assist-anthropic-subprocessor-gate.spec.ts` holds the per-door detail and is the deeper guard for this one vendor.",
|
|
89
|
-
dataReceived: anthropicDataReceived(
|
|
89
|
+
dataReceived: anthropicDataReceived(ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD)
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/catalog.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 { PluginSubprocessorDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-subprocessors'\nimport type { AiModelDescriptor, AiUsage } from './contract'\n\n/**\n * The model catalog (AGL-2939): every model id the plugin will route to,\n * with its provider, its display name, its capabilities and its list rates.\n * The ONE place a model id is written outside a provider adapter — the\n * routing table maps a step kind to an id through here, the meter prices\n * usage by id through here, and a settings form offers ids from here.\n *\n * ## Two rates, because one figure was answering two questions\n *\n * Every row carries a PROVIDER rate and a BILLED rate (AGL-3015):\n *\n * - `providerRates` is what the vendor charges us. It is money leaving the\n * company, so cost accounting, the margin surfaces and every staff spend\n * meter price usage through this one and no other.\n * - `billedRates` is what a customer's credits are charged at. The credit\n * meter, the band, the overage ladder and the model selector price usage\n * through this one, because it is the figure a customer's balance moves by.\n *\n * They are equal on nearly every row, and a helper writes the pair from one\n * figure so an unmarked row cannot drift apart by a typo. A row may be\n * billed ABOVE its provider rate deliberately — the markup is carried in the\n * rate rather than in the credit conversion, which keeps a credit a fixed\n * quantity of billed spend on every model and leaves the band, the ladder\n * and the invoice on the one arithmetic they were sized with. A row billed\n * BELOW its provider rate would sell tokens at a loss, and\n * `billed-rate-is-not-provider-cost.spec.ts` refuses one.\n *\n * Both are keyed by model rather than fixed at one model's rates because the\n * default is an env override: a one-line incident swap to a dearer model\n * would otherwise keep reporting the cheaper money, and per-org cost would\n * read as roughly right — the failure mode this whole meter exists to\n * prevent. An unknown id falls back to the most EXPENSIVE known tier on\n * purpose, on both rates: an estimate that errs low is worse than one that\n * errs high, whether the figure is our bill or the customer's.\n */\n\nexport interface AiTokenRates {\n inputPerToken: number\n outputPerToken: number\n cacheReadPerToken: number\n cacheWritePerToken: number\n}\n\n/** List price per MTok → per-token rates, cache read at 0.1x, write 1.25x. */\nexport function aiRatesPerMTok(inputPerMTok: number, outputPerMTok: number): AiTokenRates {\n return {\n inputPerToken: inputPerMTok / 1_000_000,\n outputPerToken: outputPerMTok / 1_000_000,\n cacheReadPerToken: (inputPerMTok * 0.1) / 1_000_000,\n cacheWritePerToken: (inputPerMTok * 1.25) / 1_000_000,\n }\n}\n\n/**\n * The two rate tables a catalog row carries. Separate fields rather than one\n * table and a multiplier: a reader that wants our cost has to name it, and\n * a reader that wants the customer's has to name that, so neither can be\n * reached by accident.\n */\nexport interface AiCatalogRates {\n /** What the provider bills us for this model. */\n providerRates: AiTokenRates\n /** What a customer's credits are charged at for this model. */\n billedRates: AiTokenRates\n}\n\n/**\n * A row billed at exactly the provider's list rate — the usual case, written\n * once so the pair cannot drift.\n */\nexport function aiRatesAtList(\n inputPerMTok: number,\n outputPerMTok: number,\n): AiCatalogRates {\n return {\n providerRates: aiRatesPerMTok(inputPerMTok, outputPerMTok),\n billedRates: aiRatesPerMTok(inputPerMTok, outputPerMTok),\n }\n}\n\nexport interface AiCatalogEntry extends AiModelDescriptor, AiCatalogRates {\n /**\n * The tier a routing decision reads: `fast` for short, constrained\n * answers; `balanced` for the assistant and briefs; `deep` for the\n * dearest, slowest class.\n */\n tier: 'fast' | 'balanced' | 'deep'\n /**\n * The shortest prefix this model's provider will cache (AGL-2937). A\n * request whose cached span falls under it is served with the markers\n * honored and NOTHING cached: no error, no warning, and a usage report\n * that reads as a permanent cache miss.\n *\n * It is a property of the model rather than of the tier or the vendor,\n * and it does not fall as a generation advances — so the number has to be\n * written beside the model, and a door that means to cache has to be held\n * to it (`aiCachedPrefixCaches` in the runtime, and the door table in\n * `runtime/ai-prompt-cache.spec.ts`). Without it, \"this block carries a\n * breakpoint\" is a claim nothing checks, and a prompt can be designed\n * around a cache it never had.\n */\n cacheMinTokens: number\n}\n\n/**\n * The sentinels a docs-only answer is metered under (AGL-2486): not\n * models, but ids the meter sees, priced at zero so a deflected turn lands\n * in the same rollup as a served one and a keyless deployment does not\n * read as the most efficient one on the platform.\n */\nexport const AI_METER_SENTINELS = {\n docsRetrieval: 'docs-retrieval',\n docsLinks: 'docs-links',\n answerCache: 'assist-cache',\n} as const\n\nconst ANTHROPIC = 'anthropic'\nconst OPENAI_COMPATIBLE = 'openai-compatible'\n\n/**\n * A provider's row on the published subprocessor list: every field but the\n * host, which the registered adapter reports as its `endpointHost`.\n * `{apiKeyEnv}` anywhere in the wording stands for the adapter's\n * `apiKeyEnv`, so a credential's name is written in its adapter alone and\n * never in this module, which the browser bundle carries.\n */\nexport type AiProviderSubprocessorWording = Omit<PluginSubprocessorDeclaration, 'host'>\n\n/** A provider the catalog carries models for. */\nexport interface AiCatalogProvider {\n id: string\n /** The name a settings form shows. */\n label: string\n /**\n * The vendor's row on the published subprocessor list, written here and\n * nowhere else: `aiSubprocessors` derives the plugin's declarations from\n * it. Absent for a provider whose endpoint an operator names, which has\n * no fixed recipient to publish.\n */\n subprocessor?: AiProviderSubprocessorWording\n}\n\n/**\n * The Anthropic row's data cell up to its CRM sentence: every door but CRM\n * assistance, worded once for both of the CRM sentences below.\n */\nconst ANTHROPIC_DATA_RECEIVED_LEAD =\n \"What the user submits — a question, instruction or brief, with the earlier messages of the same Assist conversation — and the content of the element, post, section or page being worked on, with the generated response. On Pro and above, the organization's name and the console route and host travel with an Assist question. For an edit the assistant proposes in the besigner, an outline of the open page, component or layout: element and component ids, layer names, shortened setting values and the selected element's styles. For a generation job, the site inventory: the names and addresses of its pages and collections, the names of its components, layouts, templates, forms and datasets with their prop and field names, and the theme's summary, colors and fonts. For a theme change, the site's current theme settings and brand colors as hex values, from the organization's brand settings, the site logo in the media library or a public page the brief links to. For features that review or write search information, the text and structure of the pages concerned. For product copy, the store's name, the product's name, type, description, tags, options and search listing, the store's category names, and the product's first media-library photo as a copy at most 768 px on its longer edge with its metadata stripped; never another media file, a price, stock, an order or a customer. For products, categories and discounts proposed from a brief, the store's name and its existing category names. For an automation drafted from a brief, which of CRM, webhooks and bookings the plan includes; for an explanation, the automation's outline (trigger, conditions, each step's text with the names of the lists, campaigns, workflows, webhooks and datasets it uses and whether each exists, and a workflow's function names and expressions) and, for a failed run, its time, steps and recorded errors, with email addresses removed and never the triggering event's data. For an insight, the figure reports available and aggregate tables: traffic with top page paths, referrers and campaign tags; form views and submissions; revenue, orders and best-selling product names; bookings by service; campaign subjects with delivery, open and click rates; A/B test and variant conversions; and, for a dataset the member can see, field names and types, record and fill counts, number ranges and totals grouped by a value at least three records share. Email addresses and phone numbers are removed, and no individual record is sent. \"\n\n/** What every CRM door adds after its record sentence: drafts and imports. */\nconst ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS =\n \"An email draft adds the request and the record's merge field names; an import sends the field names and types and each column's header and value kind, never a row.\"\n\n/** How the data cell words CRM assistance: what a record sends, and what never leaves. */\nexport interface AnthropicCrmAssistanceWording {\n record: string\n never: string\n}\n\n/**\n * CRM assistance as `/legal/subprocessors` publishes it today: the disclosed\n * fields of a record, with addresses and numbers in its text replaced. This\n * is what production sends while `release_crm_assist_whole_record` is off.\n */\nexport const ANTHROPIC_CRM_ASSISTANCE_DISCLOSED: AnthropicCrmAssistanceWording = {\n record:\n \"For CRM assistance, the opened contact, company, deal or lead as the CRM shows it: its name and, by kind, job title, company, lifecycle stage, tags, capture history and counts, domain, industry, headcount, pipeline stages, status, amount, dates, lost reason, parties and lead status, with its notes, newest timeline entries and open tasks and deals. Email addresses and phone numbers in that text are replaced, and no email, phone or postal field, marketing consent, custom field value, team member or record id is sent.\",\n never: 'No account identifiers, email addresses or authentication tokens.',\n}\n\n/**\n * CRM assistance once the whole record is sent (AGL-3520): the open-ended\n * wording the Subprocessors row and Privacy Policy section 2 must be\n * republished with BEFORE `release_crm_assist_whole_record` is turned on.\n * Not published: the catalog row below carries\n * {@link ANTHROPIC_CRM_ASSISTANCE_DISCLOSED} until that republication, when\n * this takes its place and `publishedOn` moves to the change-log date\n * (`docs/drafts/agl-3520-crm-assistance-whole-record-disclosure.md`).\n */\nexport const ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD: AnthropicCrmAssistanceWording = {\n record:\n 'For CRM assistance, the opened record as the CRM shows it, with all of its standard and custom fields, including contact details, notes, timeline and related records.',\n never: 'No account identifiers or authentication tokens, and no email address outside an opened CRM record.',\n}\n\n/** The Anthropic row's data cell, with CRM assistance worded as `crm` words it. */\nexport function anthropicDataReceived(crm: AnthropicCrmAssistanceWording): string {\n return `${ANTHROPIC_DATA_RECEIVED_LEAD}${crm.record} ${ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS} ${crm.never}`\n}\n\n/**\n * The providers the catalog carries models for, with the name a settings\n * form shows. A form offers providers from here for the reason it offers\n * models from here: nothing outside this folder names a vendor. For the\n * same reason a vendor's subprocessor row is written here too.\n */\nexport const AI_CATALOG_PROVIDERS: readonly AiCatalogProvider[] = [\n {\n id: ANTHROPIC,\n label: 'Anthropic',\n subprocessor: {\n entity: 'Anthropic, PBC',\n region: 'United States',\n purpose:\n \"AI-assisted features in the console and the site editor: the Aglyn Assist helper, including changes it proposes to a page, component, or layout open in the editor; editor assistance (rewriting element copy, drafting blog bodies, generating a section layout); and AI generation, which creates drafts and proposals for a customer's site from a brief or from what the site already holds, such as copy, layouts, templates, automations, search titles and descriptions, theme changes, product descriptions and tags, draft products, and store categories and discounts; explanations of a site's automations, including why an automation's run failed; AI insights, which answer a customer's questions about its own figures, such as site traffic, sales, bookings, forms, campaigns, A/B tests and datasets, and write a weekly summary of them for members who ask for one; and AI assistance in the CRM, which summarizes a contact, company, deal or lead for a user and suggests a next step, drafts a one-to-one email for the user to review and send, and suggests how the columns of a spreadsheet the user imports match CRM fields\",\n publishedOn: '2026-09-17',\n reason:\n \"Reached through the AI plugin's Anthropic adapter (`libs/plugins/ai/src/lib/providers/anthropic.ts`) by the doors that call the AI runtime. `libs/plugins/ai/src/lib/server/assist-chat.ts` is gated by `release_assist` AND the key, and a generation job's text step by `release_ai_generative`; `libs/plugins/ai/src/lib/server/ai-assist.ts` carries NO release flag, so setting `{apiKeyEnv}` in production is by itself what starts this flow. `assist-anthropic-subprocessor-gate.spec.ts` holds the per-door detail and is the deeper guard for this one vendor.\",\n dataReceived: anthropicDataReceived(ANTHROPIC_CRM_ASSISTANCE_DISCLOSED),\n },\n },\n { id: OPENAI_COMPATIBLE, label: 'OpenAI-compatible endpoint' },\n]\n\n/**\n * `vision` is written on every row rather than assumed (AGL-2916): each model\n * below reads a picture in a user turn, in the four formats\n * `AI_IMAGE_MEDIA_TYPES` names, as its vendor documents. A row added without\n * it reads as a model that does not, and is never sent one.\n */\nconst anthropicCapabilities = {\n streaming: true,\n tools: true,\n thinking: true,\n promptCache: true,\n vision: true,\n}\n\nexport const AI_MODEL_CATALOG: readonly AiCatalogEntry[] = [\n {\n id: 'claude-sonnet-5',\n provider: ANTHROPIC,\n label: 'Claude Sonnet 5',\n capabilities: anthropicCapabilities,\n /*\n * THE ONE ROW BILLED ABOVE ITS PROVIDER RATE (AGL-3015).\n *\n * The balanced tier prices most requests, so this markup is most of the\n * platform's AI margin. It is carried in the rate and not in the credit\n * conversion for the reason the module head gives: a credit stays a\n * fixed quantity of billed spend on every model, so the band, the\n * overage ladder and the invoice keep the arithmetic they were sized\n * with, and nothing below has to know which model served a request.\n *\n * Everything that reasons about what we PAY — the margin surfaces, the\n * staff spend meters, the discount guardrail's cost of goods — reads\n * `providerRates`, which is the vendor's published list.\n */\n providerRates: aiRatesPerMTok(2, 10),\n billedRates: aiRatesPerMTok(3, 15),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n id: 'claude-sonnet-4-6',\n provider: ANTHROPIC,\n label: 'Claude Sonnet 4.6',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(3, 15),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n // Rejects an explicit `thinking` setting; the runtime omits it.\n id: 'claude-haiku-4-5',\n provider: ANTHROPIC,\n label: 'Claude Haiku 4.5',\n capabilities: { ...anthropicCapabilities, thinking: false },\n ...aiRatesAtList(1, 5),\n tier: 'fast',\n // Four times the balanced tier's minimum: the cheapest model per token\n // is the hardest one to cache for, which is why a short prompt moved\n // here can cost more per request than it saved per token.\n cacheMinTokens: 4_096,\n },\n {\n id: 'claude-opus-5',\n provider: ANTHROPIC,\n label: 'Claude Opus 5',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(5, 25),\n tier: 'deep',\n cacheMinTokens: 512,\n },\n {\n id: 'claude-opus-4-8',\n provider: ANTHROPIC,\n label: 'Claude Opus 4.8',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(5, 25),\n tier: 'deep',\n cacheMinTokens: 1_024,\n },\n /**\n * The OpenAI-compatible adapter serves whatever the endpoint behind\n * `AI_OPENAI_COMPAT_BASE_URL` serves; these are the ids the routing table\n * may name on it. The figures are the vendor's public list, billed at\n * list: a deployment that reaches its own gateway at a negotiated price\n * is paying less than this says, so its margin reads low rather than\n * high — the direction a cost figure may be wrong in.\n */\n {\n id: 'gpt-5',\n provider: OPENAI_COMPATIBLE,\n label: 'GPT-5',\n capabilities: { streaming: true, tools: true, thinking: false, promptCache: true, vision: true },\n ...aiRatesAtList(1.25, 10),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n id: 'gpt-5-mini',\n provider: OPENAI_COMPATIBLE,\n label: 'GPT-5 mini',\n capabilities: { streaming: true, tools: true, thinking: false, promptCache: true, vision: true },\n ...aiRatesAtList(0.25, 2),\n tier: 'fast',\n cacheMinTokens: 1_024,\n },\n]\n\n/** The dearest known tier, used when a model id is not in the catalog. */\nexport const AI_FALLBACK_RATES: AiTokenRates = aiRatesPerMTok(10, 50)\n\n/**\n * The minimum an unknown model id is assumed to have: the largest one the\n * catalog knows. It errs the way the rate fallback errs — toward the answer\n * that costs more — so an unrecognized id reads as \"this prompt does not\n * cache\" rather than promising a saving nobody measured.\n */\nexport const AI_FALLBACK_CACHE_MIN_TOKENS = 4_096\n\n/** The shortest prefix a model's provider will cache; the dearest assumption for an unknown id. */\nexport function aiModelCacheMinTokens(modelId: string): number {\n return aiCatalogEntry(modelId)?.cacheMinTokens ?? AI_FALLBACK_CACHE_MIN_TOKENS\n}\n\nconst ZERO_RATES = aiRatesPerMTok(0, 0)\n\n/** The catalog row for a model id, or `undefined` for an unknown id. */\nexport function aiCatalogEntry(modelId: string): AiCatalogEntry | undefined {\n return AI_MODEL_CATALOG.find((entry) => entry.id === modelId)\n}\n\nconst isSentinel = (modelId: string): boolean =>\n (Object.values(AI_METER_SENTINELS) as string[]).includes(modelId)\n\n/**\n * What the PROVIDER charges for a model id — our bill. A meter sentinel is\n * free; an unknown id is priced at the dearest tier.\n *\n * The figure every cost and margin surface prices usage at. Answering the\n * customer's question from here would report a margin the platform does not\n * earn, and answering ours from `aiBilledRatesForModel` reports one it does\n * not pay; the two are separate functions so neither is reachable by\n * forgetting which one was meant.\n */\nexport function aiProviderRatesForModel(modelId: string): AiTokenRates {\n if (isSentinel(modelId)) return ZERO_RATES\n return aiCatalogEntry(modelId)?.providerRates ?? AI_FALLBACK_RATES\n}\n\n/**\n * What a CUSTOMER'S CREDITS are charged at for a model id. A meter sentinel\n * is free; an unknown id is priced at the dearest tier.\n *\n * The figure the credit meter, the band and the model selector price usage\n * at. It is at or above `aiProviderRatesForModel` on every row.\n */\nexport function aiBilledRatesForModel(modelId: string): AiTokenRates {\n if (isSentinel(modelId)) return ZERO_RATES\n return aiCatalogEntry(modelId)?.billedRates ?? AI_FALLBACK_RATES\n}\n\nfunction priceUsage(usage: AiUsage, rate: AiTokenRates): number {\n const raw =\n usage.inputTokens * rate.inputPerToken +\n usage.outputTokens * rate.outputPerToken +\n usage.cacheReadTokens * rate.cacheReadPerToken +\n usage.cacheWriteTokens * rate.cacheWritePerToken\n return Math.round(raw * 1_000_000) / 1_000_000\n}\n\n/**\n * What one exchange COST US, at the serving model's provider rates, rounded\n * to 6dp. Real money, and the only figure a margin may be taken against.\n */\nexport function estimateAiProviderCostUsd(usage: AiUsage, modelId: string): number {\n return priceUsage(usage, aiProviderRatesForModel(modelId))\n}\n\n/**\n * What one exchange DRAWS FROM A CUSTOMER, at the serving model's billed\n * rates, rounded to 6dp — the figure `assistCreditsFromUsd` turns into\n * credits. At or above `estimateAiProviderCostUsd` for the same exchange.\n */\nexport function estimateAiBilledUsd(usage: AiUsage, modelId: string): number {\n return priceUsage(usage, aiBilledRatesForModel(modelId))\n}\n\n/** Every catalog id a provider serves. */\nexport function aiModelIdsForProvider(providerId: string): string[] {\n return AI_MODEL_CATALOG.filter((entry) => entry.provider === providerId).map(\n (entry) => entry.id,\n )\n}\n\n/**\n * What a step asks for, and what the routing table answers with. Every\n * door names its kind here rather than a model, so a model swap — an\n * incident override, an org's own setting, a cheaper model for a cheap\n * step — is one row rather than a search through the doors (AGL-2937).\n */\nexport type AiStepKind =\n | 'assist.chat'\n | 'copy.element'\n | 'copy.section'\n | 'copy.blog'\n | 'generate.section'\n | 'job.component'\n | 'job.crm'\n | 'job.experiment'\n | 'job.form'\n | 'job.insight'\n | 'job.layout'\n | 'job.template'\n | 'job.page'\n | 'job.products'\n | 'job.seo'\n | 'job.text'\n | 'job.theme'\n | 'job.plan'\n | 'job.email'\n | 'job.campaign'\n | 'job.workflow'\n\n/** The tier each step kind is served from when no setting overrides it. */\nexport const AI_STEP_TIERS: Record<AiStepKind, AiCatalogEntry['tier']> = {\n 'assist.chat': 'balanced',\n 'copy.element': 'fast',\n 'copy.section': 'balanced',\n 'copy.blog': 'balanced',\n 'generate.section': 'balanced',\n // A reusable component is one structured tree with the typed props it\n // declares, and choosing what becomes a prop is the judgment the step sells.\n 'job.component': 'balanced',\n // CRM by AI (AGL-2917): a record's summary and next step, a deal's stage, a\n // lead's standing, a one-to-one email draft and an import's column matches.\n // Short answers through a strict tool, held to the facts the CRM reports\n // about the record the member opened.\n 'job.crm': 'fast',\n // A/B tests by AI (AGL-2914): variants of one piece of copy, and a\n // result put into words for a verdict code already reached. Short answers\n // through a strict tool, but the judgment a variant sells — which ONE idea\n // to vary so the result says something — is the balanced tier's.\n 'job.experiment': 'balanced',\n // A form is one small tree held to the building rules and to the contract\n // its submissions are read by; a re-ask costs more than the tier saves.\n 'job.form': 'balanced',\n // An insight (AGL-2915) chooses which figures answer a question and says\n // what they show; the trace holds every number, and the judgment of what is\n // worth saying is what the fast tier does worse.\n 'job.insight': 'balanced',\n // A layout or a page template is one structured tree held to every\n // building rule; the fast tier re-asks more than it saves.\n 'job.layout': 'balanced',\n 'job.template': 'balanced',\n // A page section (AGL-2907) is held to the whole page's building rules on\n // every pass, for the same reason.\n 'job.page': 'balanced',\n // A product's copy, a store's first products, or its categories and\n // discounts (AGL-2916): judgment about what a photo shows and what a shopper\n // needs, held to storefront claim rules a re-ask costs more to meet than the\n // tier saves.\n 'job.products': 'balanced',\n // SEO fields, alt text and an audit's fixes (AGL-2910): short answers\n // through a strict tool, held to a length and to the page's own text.\n 'job.seo': 'fast',\n 'job.text': 'balanced',\n // A theme is one structured answer over a small, fixed control set; the\n // judgment is in the color choices, which the fast tier makes worse.\n 'job.theme': 'balanced',\n 'job.plan': 'balanced',\n // An email is one tree held to every building rule, with subject lines\n // whose judgment is the point of asking; the fast tier re-asks more than it\n // saves. A campaign is the same email, drafted into a campaign.\n 'job.email': 'balanced',\n 'job.campaign': 'balanced',\n // An automation is a small structured answer whose judgment is choosing the\n // trigger and steps a description means, and an explanation reads one\n // closely; the fast tier re-asks more than it saves on both (AGL-2919).\n 'job.workflow': 'balanced',\n}\n\n/** The first catalog model of a tier on a provider, or the provider's first model. */\nexport function aiDefaultModelFor(providerId: string, tier: AiCatalogEntry['tier']): string | undefined {\n const onProvider = AI_MODEL_CATALOG.filter((entry) => entry.provider === providerId)\n return (onProvider.find((entry) => entry.tier === tier) ?? onProvider[0])?.id\n}\n"],"names":["aiRatesPerMTok","inputPerMTok","outputPerMTok","inputPerToken","outputPerToken","cacheReadPerToken","cacheWritePerToken","aiRatesAtList","providerRates","billedRates","AI_METER_SENTINELS","docsRetrieval","docsLinks","answerCache","ANTHROPIC","OPENAI_COMPATIBLE","ANTHROPIC_DATA_RECEIVED_LEAD","ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS","ANTHROPIC_CRM_ASSISTANCE_DISCLOSED","record","never","ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD","anthropicDataReceived","crm","AI_CATALOG_PROVIDERS","id","label","subprocessor","entity","region","purpose","publishedOn","reason","dataReceived","anthropicCapabilities","streaming","tools","thinking","promptCache","vision","AI_MODEL_CATALOG","provider","capabilities","tier","cacheMinTokens","AI_FALLBACK_RATES","AI_FALLBACK_CACHE_MIN_TOKENS","aiModelCacheMinTokens","modelId","aiCatalogEntry","ZERO_RATES","find","entry","isSentinel","Object","values","includes","aiProviderRatesForModel","aiBilledRatesForModel","priceUsage","usage","rate","raw","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","Math","round","estimateAiProviderCostUsd","estimateAiBilledUsd","aiModelIdsForProvider","providerId","filter","map","AI_STEP_TIERS","aiDefaultModelFor","onProvider"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAgDD,4EAA4E,GAC5E,OAAO,SAASA,eAAeC,YAAoB,EAAEC,aAAqB;IACxE,OAAO;QACLC,eAAeF,eAAe;QAC9BG,gBAAgBF,gBAAgB;QAChCG,mBAAmB,AAACJ,eAAe,MAAO;QAC1CK,oBAAoB,AAACL,eAAe,OAAQ;IAC9C;AACF;AAeA;;;CAGC,GACD,OAAO,SAASM,cACdN,YAAoB,EACpBC,aAAqB;IAErB,OAAO;QACLM,eAAeR,eAAeC,cAAcC;QAC5CO,aAAaT,eAAeC,cAAcC;IAC5C;AACF;AA0BA;;;;;CAKC,GACD,OAAO,MAAMQ,qBAAqB;IAChCC,eAAe;IACfC,WAAW;IACXC,aAAa;AACf,EAAU;AAEV,MAAMC,YAAY;AAClB,MAAMC,oBAAoB;AAyB1B;;;CAGC,GACD,MAAMC,+BACJ;AAEF,4EAA4E,GAC5E,MAAMC,iDACJ;AAQF;;;;CAIC,GACD,OAAO,MAAMC,qCAAoE;IAC/EC,QACE;IACFC,OAAO;AACT,EAAC;AAED;;;;;;;;CAQC,GACD,OAAO,MAAMC,wCAAuE;IAClFF,QACE;IACFC,OAAO;AACT,EAAC;AAED,iFAAiF,GACjF,OAAO,SAASE,sBAAsBC,GAAkC;IACtE,OAAO,GAAGP,+BAA+BO,IAAIJ,MAAM,CAAC,CAAC,EAAEF,+CAA+C,CAAC,EAAEM,IAAIH,KAAK,EAAE;AACtH;AAEA;;;;;CAKC,GACD,OAAO,MAAMI,uBAAqD;IAChE;QACEC,IAAIX;QACJY,OAAO;QACPC,cAAc;YACZC,QAAQ;YACRC,QAAQ;YACRC,SACE;YACFC,aAAa;YACbC,QACE;YACFC,cAAcX,sBAAsBJ;QACtC;IACF;IACA;QAAEO,IAAIV;QAAmBW,OAAO;IAA6B;CAC9D,CAAA;AAED;;;;;CAKC,GACD,MAAMQ,wBAAwB;IAC5BC,WAAW;IACXC,OAAO;IACPC,UAAU;IACVC,aAAa;IACbC,QAAQ;AACV;AAEA,OAAO,MAAMC,mBAA8C;IACzD;QACEf,IAAI;QACJgB,UAAU3B;QACVY,OAAO;QACPgB,cAAcR;QACd;;;;;;;;;;;;;KAaC,GACD1B,eAAeR,eAAe,GAAG;QACjCS,aAAaT,eAAe,GAAG;QAC/B2C,MAAM;QACNC,gBAAgB;IAClB;IACA;QACEnB,IAAI;QACJgB,UAAU3B;QACVY,OAAO;QACPgB,cAAcR;OACX3B,cAAc,GAAG;QACpBoC,MAAM;QACNC,gBAAgB;;IAElB;QACE,gEAAgE;QAChEnB,IAAI;QACJgB,UAAU3B;QACVY,OAAO;QACPgB,cAAc,aAAKR;YAAuBG,UAAU;;OACjD9B,cAAc,GAAG;QACpBoC,MAAM;QACN,uEAAuE;QACvE,qEAAqE;QACrE,0DAA0D;QAC1DC,gBAAgB;;IAElB;QACEnB,IAAI;QACJgB,UAAU3B;QACVY,OAAO;QACPgB,cAAcR;OACX3B,cAAc,GAAG;QACpBoC,MAAM;QACNC,gBAAgB;;IAElB;QACEnB,IAAI;QACJgB,UAAU3B;QACVY,OAAO;QACPgB,cAAcR;OACX3B,cAAc,GAAG;QACpBoC,MAAM;QACNC,gBAAgB;;IAElB;;;;;;;GAOC,GACD;QACEnB,IAAI;QACJgB,UAAU1B;QACVW,OAAO;QACPgB,cAAc;YAAEP,WAAW;YAAMC,OAAO;YAAMC,UAAU;YAAOC,aAAa;YAAMC,QAAQ;QAAK;OAC5FhC,cAAc,MAAM;QACvBoC,MAAM;QACNC,gBAAgB;;IAElB;QACEnB,IAAI;QACJgB,UAAU1B;QACVW,OAAO;QACPgB,cAAc;YAAEP,WAAW;YAAMC,OAAO;YAAMC,UAAU;YAAOC,aAAa;YAAMC,QAAQ;QAAK;OAC5FhC,cAAc,MAAM;QACvBoC,MAAM;QACNC,gBAAgB;;CAEnB,CAAA;AAED,wEAAwE,GACxE,OAAO,MAAMC,oBAAkC7C,eAAe,IAAI,IAAG;AAErE;;;;;CAKC,GACD,OAAO,MAAM8C,+BAA+B,KAAK;AAEjD,iGAAiG,GACjG,OAAO,SAASC,sBAAsBC,OAAe;;QAC5CC;IAAP,gBAAOA,kBAAAA,eAAeD,6BAAfC,gBAAyBL,cAAc,mBAAIE;AACpD;AAEA,MAAMI,aAAalD,eAAe,GAAG;AAErC,sEAAsE,GACtE,OAAO,SAASiD,eAAeD,OAAe;IAC5C,OAAOR,iBAAiBW,IAAI,CAAC,CAACC,QAAUA,MAAM3B,EAAE,KAAKuB;AACvD;AAEA,MAAMK,aAAa,CAACL,UAClB,AAACM,OAAOC,MAAM,CAAC7C,oBAAiC8C,QAAQ,CAACR;AAE3D;;;;;;;;;CASC,GACD,OAAO,SAASS,wBAAwBT,OAAe;;QAE9CC;IADP,IAAII,WAAWL,UAAU,OAAOE;IAChC,gBAAOD,kBAAAA,eAAeD,6BAAfC,gBAAyBzC,aAAa,mBAAIqC;AACnD;AAEA;;;;;;CAMC,GACD,OAAO,SAASa,sBAAsBV,OAAe;;QAE5CC;IADP,IAAII,WAAWL,UAAU,OAAOE;IAChC,gBAAOD,kBAAAA,eAAeD,6BAAfC,gBAAyBxC,WAAW,mBAAIoC;AACjD;AAEA,SAASc,WAAWC,KAAc,EAAEC,IAAkB;IACpD,MAAMC,MACJF,MAAMG,WAAW,GAAGF,KAAK1D,aAAa,GACtCyD,MAAMI,YAAY,GAAGH,KAAKzD,cAAc,GACxCwD,MAAMK,eAAe,GAAGJ,KAAKxD,iBAAiB,GAC9CuD,MAAMM,gBAAgB,GAAGL,KAAKvD,kBAAkB;IAClD,OAAO6D,KAAKC,KAAK,CAACN,MAAM,WAAa;AACvC;AAEA;;;CAGC,GACD,OAAO,SAASO,0BAA0BT,KAAc,EAAEZ,OAAe;IACvE,OAAOW,WAAWC,OAAOH,wBAAwBT;AACnD;AAEA;;;;CAIC,GACD,OAAO,SAASsB,oBAAoBV,KAAc,EAAEZ,OAAe;IACjE,OAAOW,WAAWC,OAAOF,sBAAsBV;AACjD;AAEA,wCAAwC,GACxC,OAAO,SAASuB,sBAAsBC,UAAkB;IACtD,OAAOhC,iBAAiBiC,MAAM,CAAC,CAACrB,QAAUA,MAAMX,QAAQ,KAAK+B,YAAYE,GAAG,CAC1E,CAACtB,QAAUA,MAAM3B,EAAE;AAEvB;AA+BA,yEAAyE,GACzE,OAAO,MAAMkD,gBAA4D;IACvE,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,oBAAoB;IACpB,sEAAsE;IACtE,6EAA6E;IAC7E,iBAAiB;IACjB,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE,sCAAsC;IACtC,WAAW;IACX,mEAAmE;IACnE,0EAA0E;IAC1E,2EAA2E;IAC3E,iEAAiE;IACjE,kBAAkB;IAClB,0EAA0E;IAC1E,wEAAwE;IACxE,YAAY;IACZ,yEAAyE;IACzE,4EAA4E;IAC5E,iDAAiD;IACjD,eAAe;IACf,mEAAmE;IACnE,2DAA2D;IAC3D,cAAc;IACd,gBAAgB;IAChB,0EAA0E;IAC1E,mCAAmC;IACnC,YAAY;IACZ,oEAAoE;IACpE,6EAA6E;IAC7E,6EAA6E;IAC7E,cAAc;IACd,gBAAgB;IAChB,sEAAsE;IACtE,sEAAsE;IACtE,WAAW;IACX,YAAY;IACZ,wEAAwE;IACxE,qEAAqE;IACrE,aAAa;IACb,YAAY;IACZ,uEAAuE;IACvE,4EAA4E;IAC5E,gEAAgE;IAChE,aAAa;IACb,gBAAgB;IAChB,4EAA4E;IAC5E,sEAAsE;IACtE,wEAAwE;IACxE,gBAAgB;AAClB,EAAC;AAED,oFAAoF,GACpF,OAAO,SAASC,kBAAkBJ,UAAkB,EAAE7B,IAA4B;QAExEkC;QAAAA;IADR,MAAMA,aAAarC,iBAAiBiC,MAAM,CAAC,CAACrB,QAAUA,MAAMX,QAAQ,KAAK+B;IACzE,QAAQK,SAAAA,mBAAAA,WAAW1B,IAAI,CAAC,CAACC,QAAUA,MAAMT,IAAI,KAAKA,iBAA1CkC,mBAAmDA,UAAU,CAAC,EAAE,qBAAjE,AAACA,MAAmEpD,EAAE;AAC/E"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/catalog.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 { PluginSubprocessorDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-subprocessors'\nimport type { AiModelDescriptor, AiUsage } from './contract'\n\n/**\n * The model catalog (AGL-2939): every model id the plugin will route to,\n * with its provider, its display name, its capabilities and its list rates.\n * The ONE place a model id is written outside a provider adapter — the\n * routing table maps a step kind to an id through here, the meter prices\n * usage by id through here, and a settings form offers ids from here.\n *\n * ## Two rates, because one figure was answering two questions\n *\n * Every row carries a PROVIDER rate and a BILLED rate (AGL-3015):\n *\n * - `providerRates` is what the vendor charges us. It is money leaving the\n * company, so cost accounting, the margin surfaces and every staff spend\n * meter price usage through this one and no other.\n * - `billedRates` is what a customer's credits are charged at. The credit\n * meter, the band, the overage ladder and the model selector price usage\n * through this one, because it is the figure a customer's balance moves by.\n *\n * They are equal on nearly every row, and a helper writes the pair from one\n * figure so an unmarked row cannot drift apart by a typo. A row may be\n * billed ABOVE its provider rate deliberately — the markup is carried in the\n * rate rather than in the credit conversion, which keeps a credit a fixed\n * quantity of billed spend on every model and leaves the band, the ladder\n * and the invoice on the one arithmetic they were sized with. A row billed\n * BELOW its provider rate would sell tokens at a loss, and\n * `billed-rate-is-not-provider-cost.spec.ts` refuses one.\n *\n * Both are keyed by model rather than fixed at one model's rates because the\n * default is an env override: a one-line incident swap to a dearer model\n * would otherwise keep reporting the cheaper money, and per-org cost would\n * read as roughly right — the failure mode this whole meter exists to\n * prevent. An unknown id falls back to the most EXPENSIVE known tier on\n * purpose, on both rates: an estimate that errs low is worse than one that\n * errs high, whether the figure is our bill or the customer's.\n */\n\nexport interface AiTokenRates {\n inputPerToken: number\n outputPerToken: number\n cacheReadPerToken: number\n cacheWritePerToken: number\n}\n\n/** List price per MTok → per-token rates, cache read at 0.1x, write 1.25x. */\nexport function aiRatesPerMTok(inputPerMTok: number, outputPerMTok: number): AiTokenRates {\n return {\n inputPerToken: inputPerMTok / 1_000_000,\n outputPerToken: outputPerMTok / 1_000_000,\n cacheReadPerToken: (inputPerMTok * 0.1) / 1_000_000,\n cacheWritePerToken: (inputPerMTok * 1.25) / 1_000_000,\n }\n}\n\n/**\n * The two rate tables a catalog row carries. Separate fields rather than one\n * table and a multiplier: a reader that wants our cost has to name it, and\n * a reader that wants the customer's has to name that, so neither can be\n * reached by accident.\n */\nexport interface AiCatalogRates {\n /** What the provider bills us for this model. */\n providerRates: AiTokenRates\n /** What a customer's credits are charged at for this model. */\n billedRates: AiTokenRates\n}\n\n/**\n * A row billed at exactly the provider's list rate — the usual case, written\n * once so the pair cannot drift.\n */\nexport function aiRatesAtList(\n inputPerMTok: number,\n outputPerMTok: number,\n): AiCatalogRates {\n return {\n providerRates: aiRatesPerMTok(inputPerMTok, outputPerMTok),\n billedRates: aiRatesPerMTok(inputPerMTok, outputPerMTok),\n }\n}\n\nexport interface AiCatalogEntry extends AiModelDescriptor, AiCatalogRates {\n /**\n * The tier a routing decision reads: `fast` for short, constrained\n * answers; `balanced` for the assistant and briefs; `deep` for the\n * dearest, slowest class.\n */\n tier: 'fast' | 'balanced' | 'deep'\n /**\n * The shortest prefix this model's provider will cache (AGL-2937). A\n * request whose cached span falls under it is served with the markers\n * honored and NOTHING cached: no error, no warning, and a usage report\n * that reads as a permanent cache miss.\n *\n * It is a property of the model rather than of the tier or the vendor,\n * and it does not fall as a generation advances — so the number has to be\n * written beside the model, and a door that means to cache has to be held\n * to it (`aiCachedPrefixCaches` in the runtime, and the door table in\n * `runtime/ai-prompt-cache.spec.ts`). Without it, \"this block carries a\n * breakpoint\" is a claim nothing checks, and a prompt can be designed\n * around a cache it never had.\n */\n cacheMinTokens: number\n}\n\n/**\n * The sentinels a docs-only answer is metered under (AGL-2486): not\n * models, but ids the meter sees, priced at zero so a deflected turn lands\n * in the same rollup as a served one and a keyless deployment does not\n * read as the most efficient one on the platform.\n */\nexport const AI_METER_SENTINELS = {\n docsRetrieval: 'docs-retrieval',\n docsLinks: 'docs-links',\n answerCache: 'assist-cache',\n} as const\n\nconst ANTHROPIC = 'anthropic'\nconst OPENAI_COMPATIBLE = 'openai-compatible'\n\n/**\n * A provider's row on the published subprocessor list: every field but the\n * host, which the registered adapter reports as its `endpointHost`.\n * `{apiKeyEnv}` anywhere in the wording stands for the adapter's\n * `apiKeyEnv`, so a credential's name is written in its adapter alone and\n * never in this module, which the browser bundle carries.\n */\nexport type AiProviderSubprocessorWording = Omit<PluginSubprocessorDeclaration, 'host'>\n\n/** A provider the catalog carries models for. */\nexport interface AiCatalogProvider {\n id: string\n /** The name a settings form shows. */\n label: string\n /**\n * The vendor's row on the published subprocessor list, written here and\n * nowhere else: `aiSubprocessors` derives the plugin's declarations from\n * it. Absent for a provider whose endpoint an operator names, which has\n * no fixed recipient to publish.\n */\n subprocessor?: AiProviderSubprocessorWording\n}\n\n/**\n * The Anthropic row's data cell up to its CRM sentence: every door but CRM\n * assistance, worded once for both of the CRM sentences below.\n */\nconst ANTHROPIC_DATA_RECEIVED_LEAD =\n \"What the user submits — a question, instruction or brief, with the earlier messages of the same Assist conversation — and the content of the element, post, section or page being worked on, with the generated response. On Pro and above, the organization's name and the console route and host travel with an Assist question. For an edit the assistant proposes in the besigner, an outline of the open page, component or layout: element and component ids, layer names, shortened setting values and the selected element's styles. For a generation job, the site inventory: the names and addresses of its pages and collections, the names of its components, layouts, templates, forms and datasets with their prop and field names, and the theme's summary, colors and fonts. For a theme change, the site's current theme settings and brand colors as hex values, from the organization's brand settings, the site logo in the media library or a public page the brief links to. For features that review or write search information, the text and structure of the pages concerned. For product copy, the store's name, the product's name, type, description, tags, options and search listing, the store's category names, and the product's first media-library photo as a copy at most 768 px on its longer edge with its metadata stripped; never another media file, a price, stock, an order or a customer. For products, categories and discounts proposed from a brief, the store's name and its existing category names. For an automation drafted from a brief, which of CRM, webhooks and bookings the plan includes; for an explanation, the automation's outline (trigger, conditions, each step's text with the names of the lists, campaigns, workflows, webhooks and datasets it uses and whether each exists, and a workflow's function names and expressions) and, for a failed run, its time, steps and recorded errors, with email addresses removed and never the triggering event's data. For an insight, the figure reports available and aggregate tables: traffic with top page paths, referrers and campaign tags; form views and submissions; revenue, orders and best-selling product names; bookings by service; campaign subjects with delivery, open and click rates; A/B test and variant conversions; and, for a dataset the member can see, field names and types, record and fill counts, number ranges and totals grouped by a value at least three records share. Email addresses and phone numbers are removed, and no individual record is sent. \"\n\n/** What every CRM door adds after its record sentence: drafts and imports. */\nconst ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS =\n \"An email draft adds the request and the record's merge field names; an import sends the field names and types and each column's header and value kind, never a row.\"\n\n/** How the data cell words CRM assistance: what a record sends, and what never leaves. */\nexport interface AnthropicCrmAssistanceWording {\n record: string\n never: string\n}\n\n/**\n * CRM assistance as `/legal/subprocessors` published it until October 5,\n * 2026: the disclosed fields of a record, with addresses and numbers in its\n * text replaced. It is still what production sends while\n * `release_crm_assist_whole_record` is off, which the published wording\n * covers: the row describes what the CRM can send, not who has it on.\n */\nexport const ANTHROPIC_CRM_ASSISTANCE_DISCLOSED: AnthropicCrmAssistanceWording = {\n record:\n \"For CRM assistance, the opened contact, company, deal or lead as the CRM shows it: its name and, by kind, job title, company, lifecycle stage, tags, capture history and counts, domain, industry, headcount, pipeline stages, status, amount, dates, lost reason, parties and lead status, with its notes, newest timeline entries and open tasks and deals. Email addresses and phone numbers in that text are replaced, and no email, phone or postal field, marketing consent, custom field value, team member or record id is sent.\",\n never: 'No account identifiers, email addresses or authentication tokens.',\n}\n\n/**\n * CRM assistance once the whole record is sent (AGL-3520): the open-ended\n * wording the Subprocessors row and Privacy Policy section 2 were\n * republished with on October 5, 2026 (legal v10), before\n * `release_crm_assist_whole_record` may be turned on. The catalog row below\n * carries it, dated to that change-log entry.\n */\nexport const ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD: AnthropicCrmAssistanceWording = {\n record:\n 'For CRM assistance, the opened record as the CRM shows it, with all of its standard and custom fields, including contact details, notes, timeline and related records.',\n never: 'No account identifiers or authentication tokens, and no email address outside an opened CRM record.',\n}\n\n/** The Anthropic row's data cell, with CRM assistance worded as `crm` words it. */\nexport function anthropicDataReceived(crm: AnthropicCrmAssistanceWording): string {\n return `${ANTHROPIC_DATA_RECEIVED_LEAD}${crm.record} ${ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS} ${crm.never}`\n}\n\n/**\n * The providers the catalog carries models for, with the name a settings\n * form shows. A form offers providers from here for the reason it offers\n * models from here: nothing outside this folder names a vendor. For the\n * same reason a vendor's subprocessor row is written here too.\n */\nexport const AI_CATALOG_PROVIDERS: readonly AiCatalogProvider[] = [\n {\n id: ANTHROPIC,\n label: 'Anthropic',\n subprocessor: {\n entity: 'Anthropic, PBC',\n region: 'United States',\n purpose:\n \"AI-assisted features in the console and the site editor: the Aglyn Assist helper, including changes it proposes to a page, component, or layout open in the editor; editor assistance (rewriting element copy, drafting blog bodies, generating a section layout); and AI generation, which creates drafts and proposals for a customer's site from a brief or from what the site already holds, such as copy, layouts, templates, automations, search titles and descriptions, theme changes, product descriptions and tags, draft products, and store categories and discounts; explanations of a site's automations, including why an automation's run failed; AI insights, which answer a customer's questions about its own figures, such as site traffic, sales, bookings, forms, campaigns, A/B tests and datasets, and write a weekly summary of them for members who ask for one; and AI assistance in the CRM, which summarizes a contact, company, deal or lead for a user and suggests a next step, drafts a one-to-one email for the user to review and send, and suggests how the columns of a spreadsheet the user imports match CRM fields\",\n publishedOn: '2026-10-05',\n reason:\n \"Reached through the AI plugin's Anthropic adapter (`libs/plugins/ai/src/lib/providers/anthropic.ts`) by the doors that call the AI runtime. `libs/plugins/ai/src/lib/server/assist-chat.ts` is gated by `release_assist` AND the key, and a generation job's text step by `release_ai_generative`; `libs/plugins/ai/src/lib/server/ai-assist.ts` carries NO release flag, so setting `{apiKeyEnv}` in production is by itself what starts this flow. `assist-anthropic-subprocessor-gate.spec.ts` holds the per-door detail and is the deeper guard for this one vendor.\",\n dataReceived: anthropicDataReceived(ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD),\n },\n },\n { id: OPENAI_COMPATIBLE, label: 'OpenAI-compatible endpoint' },\n]\n\n/**\n * `vision` is written on every row rather than assumed (AGL-2916): each model\n * below reads a picture in a user turn, in the four formats\n * `AI_IMAGE_MEDIA_TYPES` names, as its vendor documents. A row added without\n * it reads as a model that does not, and is never sent one.\n */\nconst anthropicCapabilities = {\n streaming: true,\n tools: true,\n thinking: true,\n promptCache: true,\n vision: true,\n}\n\nexport const AI_MODEL_CATALOG: readonly AiCatalogEntry[] = [\n {\n id: 'claude-sonnet-5',\n provider: ANTHROPIC,\n label: 'Claude Sonnet 5',\n capabilities: anthropicCapabilities,\n /*\n * THE ONE ROW BILLED ABOVE ITS PROVIDER RATE (AGL-3015).\n *\n * The balanced tier prices most requests, so this markup is most of the\n * platform's AI margin. It is carried in the rate and not in the credit\n * conversion for the reason the module head gives: a credit stays a\n * fixed quantity of billed spend on every model, so the band, the\n * overage ladder and the invoice keep the arithmetic they were sized\n * with, and nothing below has to know which model served a request.\n *\n * Everything that reasons about what we PAY — the margin surfaces, the\n * staff spend meters, the discount guardrail's cost of goods — reads\n * `providerRates`, which is the vendor's published list.\n */\n providerRates: aiRatesPerMTok(2, 10),\n billedRates: aiRatesPerMTok(3, 15),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n id: 'claude-sonnet-4-6',\n provider: ANTHROPIC,\n label: 'Claude Sonnet 4.6',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(3, 15),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n // Rejects an explicit `thinking` setting; the runtime omits it.\n id: 'claude-haiku-4-5',\n provider: ANTHROPIC,\n label: 'Claude Haiku 4.5',\n capabilities: { ...anthropicCapabilities, thinking: false },\n ...aiRatesAtList(1, 5),\n tier: 'fast',\n // Four times the balanced tier's minimum: the cheapest model per token\n // is the hardest one to cache for, which is why a short prompt moved\n // here can cost more per request than it saved per token.\n cacheMinTokens: 4_096,\n },\n {\n id: 'claude-opus-5',\n provider: ANTHROPIC,\n label: 'Claude Opus 5',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(5, 25),\n tier: 'deep',\n cacheMinTokens: 512,\n },\n {\n id: 'claude-opus-4-8',\n provider: ANTHROPIC,\n label: 'Claude Opus 4.8',\n capabilities: anthropicCapabilities,\n ...aiRatesAtList(5, 25),\n tier: 'deep',\n cacheMinTokens: 1_024,\n },\n /**\n * The OpenAI-compatible adapter serves whatever the endpoint behind\n * `AI_OPENAI_COMPAT_BASE_URL` serves; these are the ids the routing table\n * may name on it. The figures are the vendor's public list, billed at\n * list: a deployment that reaches its own gateway at a negotiated price\n * is paying less than this says, so its margin reads low rather than\n * high — the direction a cost figure may be wrong in.\n */\n {\n id: 'gpt-5',\n provider: OPENAI_COMPATIBLE,\n label: 'GPT-5',\n capabilities: { streaming: true, tools: true, thinking: false, promptCache: true, vision: true },\n ...aiRatesAtList(1.25, 10),\n tier: 'balanced',\n cacheMinTokens: 1_024,\n },\n {\n id: 'gpt-5-mini',\n provider: OPENAI_COMPATIBLE,\n label: 'GPT-5 mini',\n capabilities: { streaming: true, tools: true, thinking: false, promptCache: true, vision: true },\n ...aiRatesAtList(0.25, 2),\n tier: 'fast',\n cacheMinTokens: 1_024,\n },\n]\n\n/** The dearest known tier, used when a model id is not in the catalog. */\nexport const AI_FALLBACK_RATES: AiTokenRates = aiRatesPerMTok(10, 50)\n\n/**\n * The minimum an unknown model id is assumed to have: the largest one the\n * catalog knows. It errs the way the rate fallback errs — toward the answer\n * that costs more — so an unrecognized id reads as \"this prompt does not\n * cache\" rather than promising a saving nobody measured.\n */\nexport const AI_FALLBACK_CACHE_MIN_TOKENS = 4_096\n\n/** The shortest prefix a model's provider will cache; the dearest assumption for an unknown id. */\nexport function aiModelCacheMinTokens(modelId: string): number {\n return aiCatalogEntry(modelId)?.cacheMinTokens ?? AI_FALLBACK_CACHE_MIN_TOKENS\n}\n\nconst ZERO_RATES = aiRatesPerMTok(0, 0)\n\n/** The catalog row for a model id, or `undefined` for an unknown id. */\nexport function aiCatalogEntry(modelId: string): AiCatalogEntry | undefined {\n return AI_MODEL_CATALOG.find((entry) => entry.id === modelId)\n}\n\nconst isSentinel = (modelId: string): boolean =>\n (Object.values(AI_METER_SENTINELS) as string[]).includes(modelId)\n\n/**\n * What the PROVIDER charges for a model id — our bill. A meter sentinel is\n * free; an unknown id is priced at the dearest tier.\n *\n * The figure every cost and margin surface prices usage at. Answering the\n * customer's question from here would report a margin the platform does not\n * earn, and answering ours from `aiBilledRatesForModel` reports one it does\n * not pay; the two are separate functions so neither is reachable by\n * forgetting which one was meant.\n */\nexport function aiProviderRatesForModel(modelId: string): AiTokenRates {\n if (isSentinel(modelId)) return ZERO_RATES\n return aiCatalogEntry(modelId)?.providerRates ?? AI_FALLBACK_RATES\n}\n\n/**\n * What a CUSTOMER'S CREDITS are charged at for a model id. A meter sentinel\n * is free; an unknown id is priced at the dearest tier.\n *\n * The figure the credit meter, the band and the model selector price usage\n * at. It is at or above `aiProviderRatesForModel` on every row.\n */\nexport function aiBilledRatesForModel(modelId: string): AiTokenRates {\n if (isSentinel(modelId)) return ZERO_RATES\n return aiCatalogEntry(modelId)?.billedRates ?? AI_FALLBACK_RATES\n}\n\nfunction priceUsage(usage: AiUsage, rate: AiTokenRates): number {\n const raw =\n usage.inputTokens * rate.inputPerToken +\n usage.outputTokens * rate.outputPerToken +\n usage.cacheReadTokens * rate.cacheReadPerToken +\n usage.cacheWriteTokens * rate.cacheWritePerToken\n return Math.round(raw * 1_000_000) / 1_000_000\n}\n\n/**\n * What one exchange COST US, at the serving model's provider rates, rounded\n * to 6dp. Real money, and the only figure a margin may be taken against.\n */\nexport function estimateAiProviderCostUsd(usage: AiUsage, modelId: string): number {\n return priceUsage(usage, aiProviderRatesForModel(modelId))\n}\n\n/**\n * What one exchange DRAWS FROM A CUSTOMER, at the serving model's billed\n * rates, rounded to 6dp — the figure `assistCreditsFromUsd` turns into\n * credits. At or above `estimateAiProviderCostUsd` for the same exchange.\n */\nexport function estimateAiBilledUsd(usage: AiUsage, modelId: string): number {\n return priceUsage(usage, aiBilledRatesForModel(modelId))\n}\n\n/** Every catalog id a provider serves. */\nexport function aiModelIdsForProvider(providerId: string): string[] {\n return AI_MODEL_CATALOG.filter((entry) => entry.provider === providerId).map(\n (entry) => entry.id,\n )\n}\n\n/**\n * What a step asks for, and what the routing table answers with. Every\n * door names its kind here rather than a model, so a model swap — an\n * incident override, an org's own setting, a cheaper model for a cheap\n * step — is one row rather than a search through the doors (AGL-2937).\n */\nexport type AiStepKind =\n | 'assist.chat'\n | 'copy.element'\n | 'copy.section'\n | 'copy.blog'\n | 'generate.section'\n | 'job.component'\n | 'job.crm'\n | 'job.experiment'\n | 'job.form'\n | 'job.insight'\n | 'job.layout'\n | 'job.template'\n | 'job.page'\n | 'job.products'\n | 'job.seo'\n | 'job.text'\n | 'job.theme'\n | 'job.plan'\n | 'job.email'\n | 'job.campaign'\n | 'job.workflow'\n\n/** The tier each step kind is served from when no setting overrides it. */\nexport const AI_STEP_TIERS: Record<AiStepKind, AiCatalogEntry['tier']> = {\n 'assist.chat': 'balanced',\n 'copy.element': 'fast',\n 'copy.section': 'balanced',\n 'copy.blog': 'balanced',\n 'generate.section': 'balanced',\n // A reusable component is one structured tree with the typed props it\n // declares, and choosing what becomes a prop is the judgment the step sells.\n 'job.component': 'balanced',\n // CRM by AI (AGL-2917): a record's summary and next step, a deal's stage, a\n // lead's standing, a one-to-one email draft and an import's column matches.\n // Short answers through a strict tool, held to the facts the CRM reports\n // about the record the member opened.\n 'job.crm': 'fast',\n // A/B tests by AI (AGL-2914): variants of one piece of copy, and a\n // result put into words for a verdict code already reached. Short answers\n // through a strict tool, but the judgment a variant sells — which ONE idea\n // to vary so the result says something — is the balanced tier's.\n 'job.experiment': 'balanced',\n // A form is one small tree held to the building rules and to the contract\n // its submissions are read by; a re-ask costs more than the tier saves.\n 'job.form': 'balanced',\n // An insight (AGL-2915) chooses which figures answer a question and says\n // what they show; the trace holds every number, and the judgment of what is\n // worth saying is what the fast tier does worse.\n 'job.insight': 'balanced',\n // A layout or a page template is one structured tree held to every\n // building rule; the fast tier re-asks more than it saves.\n 'job.layout': 'balanced',\n 'job.template': 'balanced',\n // A page section (AGL-2907) is held to the whole page's building rules on\n // every pass, for the same reason.\n 'job.page': 'balanced',\n // A product's copy, a store's first products, or its categories and\n // discounts (AGL-2916): judgment about what a photo shows and what a shopper\n // needs, held to storefront claim rules a re-ask costs more to meet than the\n // tier saves.\n 'job.products': 'balanced',\n // SEO fields, alt text and an audit's fixes (AGL-2910): short answers\n // through a strict tool, held to a length and to the page's own text.\n 'job.seo': 'fast',\n 'job.text': 'balanced',\n // A theme is one structured answer over a small, fixed control set; the\n // judgment is in the color choices, which the fast tier makes worse.\n 'job.theme': 'balanced',\n 'job.plan': 'balanced',\n // An email is one tree held to every building rule, with subject lines\n // whose judgment is the point of asking; the fast tier re-asks more than it\n // saves. A campaign is the same email, drafted into a campaign.\n 'job.email': 'balanced',\n 'job.campaign': 'balanced',\n // An automation is a small structured answer whose judgment is choosing the\n // trigger and steps a description means, and an explanation reads one\n // closely; the fast tier re-asks more than it saves on both (AGL-2919).\n 'job.workflow': 'balanced',\n}\n\n/** The first catalog model of a tier on a provider, or the provider's first model. */\nexport function aiDefaultModelFor(providerId: string, tier: AiCatalogEntry['tier']): string | undefined {\n const onProvider = AI_MODEL_CATALOG.filter((entry) => entry.provider === providerId)\n return (onProvider.find((entry) => entry.tier === tier) ?? onProvider[0])?.id\n}\n"],"names":["aiRatesPerMTok","inputPerMTok","outputPerMTok","inputPerToken","outputPerToken","cacheReadPerToken","cacheWritePerToken","aiRatesAtList","providerRates","billedRates","AI_METER_SENTINELS","docsRetrieval","docsLinks","answerCache","ANTHROPIC","OPENAI_COMPATIBLE","ANTHROPIC_DATA_RECEIVED_LEAD","ANTHROPIC_DATA_RECEIVED_CRM_DRAFTS_AND_IMPORTS","ANTHROPIC_CRM_ASSISTANCE_DISCLOSED","record","never","ANTHROPIC_CRM_ASSISTANCE_WHOLE_RECORD","anthropicDataReceived","crm","AI_CATALOG_PROVIDERS","id","label","subprocessor","entity","region","purpose","publishedOn","reason","dataReceived","anthropicCapabilities","streaming","tools","thinking","promptCache","vision","AI_MODEL_CATALOG","provider","capabilities","tier","cacheMinTokens","AI_FALLBACK_RATES","AI_FALLBACK_CACHE_MIN_TOKENS","aiModelCacheMinTokens","modelId","aiCatalogEntry","ZERO_RATES","find","entry","isSentinel","Object","values","includes","aiProviderRatesForModel","aiBilledRatesForModel","priceUsage","usage","rate","raw","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","Math","round","estimateAiProviderCostUsd","estimateAiBilledUsd","aiModelIdsForProvider","providerId","filter","map","AI_STEP_TIERS","aiDefaultModelFor","onProvider"],"mappings":"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"}
|
|
@@ -2071,6 +2071,12 @@
|
|
|
2071
2071
|
'static',
|
|
2072
2072
|
'sticky'
|
|
2073
2073
|
]
|
|
2074
|
+
},
|
|
2075
|
+
shrinkOnScroll: {
|
|
2076
|
+
type: 'boolean'
|
|
2077
|
+
},
|
|
2078
|
+
hideOnScroll: {
|
|
2079
|
+
type: 'boolean'
|
|
2074
2080
|
}
|
|
2075
2081
|
},
|
|
2076
2082
|
required: [],
|
|
@@ -2083,7 +2089,9 @@
|
|
|
2083
2089
|
component: 'select',
|
|
2084
2090
|
ariaLabel: 'text-field',
|
|
2085
2091
|
color: 'select',
|
|
2086
|
-
position: 'select'
|
|
2092
|
+
position: 'select',
|
|
2093
|
+
shrinkOnScroll: 'switch',
|
|
2094
|
+
hideOnScroll: 'switch'
|
|
2087
2095
|
},
|
|
2088
2096
|
textLimits: {
|
|
2089
2097
|
ariaLabel: 200
|
|
@@ -5088,11 +5096,11 @@
|
|
|
5088
5096
|
]
|
|
5089
5097
|
};
|
|
5090
5098
|
/** The prompt catalog of each surface. */ export const AI_PALETTE_CATALOG = {
|
|
5091
|
-
screen: "Surface: screen. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack).",
|
|
5099
|
+
screen: "Surface: screen. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky, shrinkOnScroll=boolean\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack).",
|
|
5092
5100
|
email: 'Surface: email. Root: the document wrapper (componentId "div", id "_@_"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- emailButton: A call-to-action button that survives every mail client. — leaf — children=text≤40, href=url, backgroundColor=string, color=string, align=left|center|right\n- emailDivider: A horizontal rule between email sections. — leaf — color=string\n- emailImage: An email-safe image, capped at 600px wide, optionally linked. — leaf — src=media, alt=text≤200, width=text≤200, href=url, align=left|center|right\n- emailProduct: A product card resolved at send time, so it never goes stale. — leaf — buttonLabel=text≤40\n- emailSection: An email-safe band with its own background color and padding. — holds any — backgroundColor=string, padding=text≤200, align=left|center|right\n- emailSpacer: Fixed vertical whitespace between email blocks. — leaf — height=text≤200\n- emailText: A paragraph of email copy in one of the preset styles. — leaf — children=text≤2000, variant=heading|subheading|body|caption, color=string, align=left|center|right\nNamed blocks to imitate: Header (emailSection); Footer (emailSection).',
|
|
5093
5101
|
form: 'Surface: form. Root: one "form" node.\nElements (id (name): purpose — children — props; * = required):\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\nNamed blocks to imitate: Contact Form (form).',
|
|
5094
|
-
layout: "Surface: layout. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- layoutSlot: Marks where each bound page's content renders inside this layout. — leaf — caption=text≤200, component=main|div|section|article\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack).",
|
|
5095
|
-
component: "Surface: component. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- icon: A single icon from the library, at the size and color you pick. — leaf — iconId=string, size=text≤200, color=text≤200\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack)."
|
|
5102
|
+
layout: "Surface: layout. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- layoutSlot: Marks where each bound page's content renders inside this layout. — leaf — caption=text≤200, component=main|div|section|article\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky, shrinkOnScroll=boolean\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack).",
|
|
5103
|
+
component: "Surface: component. Root: the document wrapper (componentId \"div\", id \"_@_\"); every other node carries an allowed componentId.\nElements (id (name): purpose — children — props; * = required):\n- collectionCategories (Category Pills): A pill per collection category, each filtering the listing. — leaf — collectionSlug=text≤200, allLabel=text≤200\n- collectionEntries: Repeats its children once per entry in a content collection. — holds any — collectionSlug=text≤200, entriesLimit=text≤200, filterCategory=text≤200, filterTag=text≤200, searchMode=filter|suggest\n- collectionEntryAuthor (Entry Author): The author card for an entry — portrait, byline and bio. — leaf — name=text≤200, bio=text≤2000, image=media, pageUrl=url, url=url\n- collectionEntryBody (Entry Body): The current entry's markdown body, on an entry template. — leaf — markdown=text≤200\n- collectionEntryMeta (Entry Meta): The byline row for an entry — author, date, category and tags. — leaf — date=text≤200, dateFormat=default|monthYear|mediumDate|longDate|iso, author=text≤200, category=text≤200, authorPageUrl=url\n- collectionRelated (Related Posts): Other entries sharing this one's category or tags. — leaf — heading=text≤200, layout=list|cards, headingVariant=displayXl|h1|h2|h3|h4|h5|…, titleVariant=displayXl|h1|h2|h3|h4|h5|…, dateFormat=default|monthYear|mediumDate|longDate|iso\n- collectionSearch: A search box for a content collection, with a suggestions dropdown. — leaf — collectionSlug=text≤200, searchPlaceholder=text≤200\n- collectionShare (Share Bar): Share buttons for the current page, plus a copy link. — leaf — heading=text≤200\n- contentAuthorProfile (Author Profile): Who an author page is about — portrait, name, role, bio and links. — leaf — name=text≤200, bio=text≤2000, image=media, jobTitle=text≤200, url=url\n- dataTable (Table): A grid of rows and columns — a feature matrix, a spec sheet, a comparison. — leaf — headerRow=boolean, emphasizeColumn=text≤200\n- form: Collects its Form Fields and sends the answers to you, or into a dataset. — holds any — formName=text≤200, datasetName=text≤200, afterSubmit=message|redirect|reveal, redirectScreenId=screen, redirectUrl=url\n- formField: One input inside a form — text, select, radio, checkbox or rating. — leaf — fieldName=text≤200, label=text≤200, placeholder=text≤200, fieldType=text|email|textarea|select|radio|checkbox|…, options=text≤2000\n- icon: A single icon from the library, at the size and color you pick. — leaf — iconId=string, size=text≤200, color=text≤200\n- image: A picture from your media library or any URL, with fit, size and an optional link. — leaf — src=media, objectFit=cover|contain|fill|none|scale-down, loading=lazy|eager, screenId=screen, href=url\n- muiAccordion (Accordion): Header that expands to reveal its details — FAQs, long forms, anything progressive. — holds only muiAccordionSummary|muiAccordionDetails — defaultExpanded=boolean, disableGutters=boolean, disabled=boolean\n- muiAccordionDetails (Accordion Details): The region an accordion reveals when expanded. — holds any — no props\n- muiAccordionSummary (Accordion Summary): The clickable header row of an accordion. — leaf — children=text≤2000, screenId=screen\n- muiAppBar (App Bar): The bar across the top of the site — put a Toolbar Content inside it. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, color=default|inherit|transparent|primary|secondary|tertiary, position=absolute|fixed|relative|static|sticky, shrinkOnScroll=boolean\n- muiBox (Box): Generic container with no styling of its own — style it from the styles panel. — holds any — component=div|span|p|figure|figcaption|blockquote|…, colorScheme=site|light|dark\n- muiBreadcrumbs (Breadcrumbs): Trail showing where a page sits. — holds any — separator=text≤200, maxItems=text≤200, itemsBeforeCollapse=text≤200, itemsAfterCollapse=text≤200, expandText=text≤200\n- muiButton (Button): A button — or a link styled as one, if you point it at a page or URL. — leaf — children=text≤40, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large, variant=text|outlined|contained, target=_self|_blank|custom\n- muiCard (Card): Surface that groups a header, content and actions about one subject. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, variant=elevation|outlined, elevation=text≤200\n- muiCardActions (Card Actions): The button row at the bottom of a card. — holds any — disableSpacing=boolean\n- muiCardContent (Card Content): The padded body region of a card. — holds any — no props\n- muiCardHeader (Card Header): The title and subheader at the top of a card. — leaf — title=text≤200, subheader=text≤200\n- muiContainer (Container): Centers your page content and caps how wide it gets. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, fixed=boolean, maxWidth=xs|sm|md|lg|xl, colorScheme=site|light|dark\n- muiGrid (Grid): Responsive 12-column layout. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, direction=row|row-reverse, wrap=wrap|nowrap|wrap-reverse, colorScheme=site|light|dark\n- muiImageList (Image List): Dense grid of images — standard, quilted, masonry or woven. — holds only muiImageListItem — variant=standard|quilted|masonry|woven, cols=text≤200, gap=text≤200, rowHeight=text≤200\n- muiImageListItem (Image List Item): One tile of an image list, with an optional caption bar. — holds any — title=text≤200, subtitle=text≤200, barPosition=bottom|top|below, cols=text≤200, rows=text≤200\n- muiList (List): Vertical list of items, optionally under a sticky heading. — holds only muiListItem — subheader=text≤200, dense=boolean, disablePadding=boolean\n- muiListItem (List Item): One row of a list. — holds only muiListItemText — divider=boolean, alignItems=center|flex-start, dense=boolean, disableGutters=boolean, disablePadding=boolean\n- muiListItemText (List Item Text): The primary and secondary text pair inside a list row. — leaf — primary=text≤2000, secondary=text≤2000, inset=boolean\n- muiScreenLink (Page Link): Links to another page by id, so it survives a slug change. — leaf — children=text≤40, target=_self|_blank|custom, renderAs=button|link|linkButton, color=inherit|primary|secondary|tertiary|success|error|…, size=inherit|small|medium|large\n- muiStack (Stack): Lays children out in a row or column with even spacing — the workhorse for most layouts. — holds any — component=div|section|article|aside|nav|header|…, direction=column|column-reverse|row|row-reverse, justifyContent=flex-start|center|flex-end|space-between|space-around|space-evenly, alignItems=stretch|flex-start|center|flex-end|baseline, flexWrap=nowrap|wrap|wrap-reverse\n- muiToolbar (Toolbar Content): The content row inside an App Bar — brand, links, actions. — holds any — component=div|section|article|aside|nav|header|…, ariaLabel=text≤200, disableGutters=boolean, variant=dense|regular\n- muiTypography (Typography): A block of text in one of the theme's type styles. — leaf — children=text≤2000, variant=displayXl|h1|h2|h3|h4|h5|…, component=h1|h2|h3|h4|h5|h6|…, align=inherit|left|center|right|justify, noWrap=boolean\n- searchBox: A search field that opens the site's search page. — leaf — placeholder=text≤200, defaultValue=text≤200\n- section: Groups components inside a semantic HTML element (section, article, nav, …). — holds any — element=section|div|article|aside|nav|header|…, ariaLabel=text≤200, colorScheme=site|light|dark\n- socialLinks: A row of icon links to whichever social profiles you fill in. — leaf — twitter=text≤200, instagram=text≤200, facebook=text≤200, linkedin=text≤200, youtube=text≤200\n- video: Plays a video file from your media library or any URL. — leaf — src=media, poster=media\n- videoEmbed (Video): Embeds a YouTube or Vimeo player from the video's URL. — leaf — url=url\nNamed blocks to imitate: App Bar (muiAppBar); List (muiList); List Item (muiListItem); Image Gallery (muiStack); Testimonials (muiStack); Pricing Table (muiStack); FAQ (muiStack); Announcement Bar (muiStack); Nav Bar (muiAppBar); Hero (muiStack); Feature Grid (muiStack); Image + Text (muiStack); Call to Action (muiStack); Footer (section); Collection Entries (collectionEntries); Dropdown Panel (muiStack); Grid (muiGrid); Grid Cell (muiGrid); Card (muiCard); Card Outlined (muiCard); Accordion (muiAccordion); Image List (muiImageList); Image List Masonry (muiImageList); Image List Item (muiImageListItem); Breadcrumbs (muiBreadcrumbs); Contact Form (form); Contact Section (muiStack)."
|
|
5096
5104
|
};
|
|
5097
5105
|
|
|
5098
5106
|
//# sourceMappingURL=ai-palette.generated.js.map
|