@wix/web5-core 1.63.38 → 1.63.40

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.
@@ -6,79 +6,103 @@ exports.resolveErrorTemplate = exports.DEFAULT_ERROR_TEMPLATES = void 0;
6
6
 
7
7
  /** Icon identifiers for error templates — resolved to SVG components by the rendering component */
8
8
 
9
- const retryAndContactButtons = [{
10
- text: 'Try Again',
9
+ /**
10
+ * The eleven error types collapse into five states a shopper can act on
11
+ * (B2B-4615). Each state has one job, so each has one line and at most one
12
+ * action:
13
+ *
14
+ * A · No answer — llm-error-text, llm-empty-response
15
+ * B · Interrupted — 500, generic-network-error, timeout, streaming-error,
16
+ * history-load-error
17
+ * C · Fresh start — 403, 404
18
+ * D · Partial — partial-load
19
+ * E · Quota — 429
20
+ *
21
+ * `iconType` stays on every template that had one: the new design drops icons,
22
+ * but w5-client-circana and w5-client-feature-com still read the field and
23
+ * must keep the look they have.
24
+ */
25
+
26
+ const retryButton = [{
27
+ text: 'Try again',
11
28
  action: 'retry',
12
29
  variant: 'cta'
13
- }, {
14
- text: 'Contact Us',
15
- variant: 'link'
16
30
  }];
31
+
32
+ /** A · No answer — nothing to retry, so it offers somewhere else to go. */
33
+ const noAnswerTemplate = {
34
+ title: "I can't help with that one \u2014 but I can help with these.",
35
+ showChips: true
36
+ };
37
+
38
+ /** B · Interrupted — the message is still there to send again. */
39
+ const interruptedTitle = "That didn't come through. Let's try again.";
40
+
41
+ /** C · Fresh start — the thread is gone; hand the conversation back. */
42
+ const freshStartTemplate = {
43
+ title: "We're starting fresh \u2014 earlier messages aren't saved.",
44
+ showChips: true,
45
+ chipsLabel: 'Ask anything to pick up again'
46
+ };
17
47
  const DEFAULT_ERROR_TEMPLATES = exports.DEFAULT_ERROR_TEMPLATES = {
18
- 'conversation-llm-error-text': {
19
- title: "I couldn't find information about what you asked.\nTry asking about a different topic.",
20
- showChips: true
21
- },
22
- 'conversation-llm-empty-response': {
23
- title: "I couldn't find information about what you asked.\nTry asking about a different topic.",
24
- showChips: true
25
- },
48
+ 'conversation-llm-error-text': noAnswerTemplate,
49
+ 'conversation-llm-empty-response': noAnswerTemplate,
26
50
  'conversation-500': {
27
51
  iconType: 'error-500',
28
- title: "This page isn't available right now.",
29
- buttons: retryAndContactButtons
52
+ title: interruptedTitle,
53
+ buttons: retryButton
30
54
  },
31
55
  'conversation-generic-network-error': {
32
56
  iconType: 'timeout',
33
- title: 'Something went wrong on our side.\nPlease try again in a moment.',
34
- buttons: retryAndContactButtons
57
+ title: interruptedTitle,
58
+ buttons: retryButton
35
59
  },
36
60
  'conversation-timeout': {
37
61
  iconType: 'timeout',
38
- title: 'Something went wrong on our side.\nPlease try again in a moment.',
39
- buttons: retryAndContactButtons
62
+ title: interruptedTitle,
63
+ buttons: retryButton
40
64
  },
41
65
  'conversation-streaming-error': {
42
66
  iconType: 'streaming-error',
43
- title: 'The connection dropped. Please try again.',
44
- buttons: retryAndContactButtons
67
+ title: interruptedTitle,
68
+ buttons: retryButton
45
69
  },
46
- 'conversation-404': {
47
- iconType: 'not-found',
48
- title: 'Page not found.\nIt happens! Let\u2019s get you back on track',
49
- showChips: true,
50
- chipsLabel: 'Some ideas to get you started:'
70
+ // Same state as the four above, deliberately not the same action: a history
71
+ // load fails before there is a last message, so `retry` has nothing to
72
+ // re-send and would sit there doing nothing.
73
+ 'history-load-error': {
74
+ iconType: 'streaming-error',
75
+ title: interruptedTitle,
76
+ buttons: [{
77
+ text: 'Try again',
78
+ action: 'reload',
79
+ variant: 'cta'
80
+ }]
51
81
  },
52
82
  'conversation-403': {
53
83
  iconType: 'forbidden',
54
- title: 'Welcome back.\nFor privacy, we couldn\u2019t restore previous content. Ask anything to continue.',
55
- showChips: true,
56
- chipsLabel: 'Some ideas to get you started:'
84
+ ...freshStartTemplate
57
85
  },
58
- 'conversation-429': {
59
- iconType: 'quota-exceeded',
60
- title: 'This service is currently out of quota.\nPlease try again later.',
61
- buttons: [{
62
- text: 'Contact Us',
63
- variant: 'link'
64
- }]
86
+ 'conversation-404': {
87
+ iconType: 'not-found',
88
+ ...freshStartTemplate
65
89
  },
90
+ // D · Partial — an answer DID render; some of it is missing. This one is
91
+ // appended to the answer rather than replacing it.
66
92
  'conversation-partial-load': {
67
93
  iconType: 'partial-load',
68
- title: 'Some content didn\u2019t load',
69
- buttons: retryAndContactButtons
70
- },
71
- 'history-load-error': {
72
- iconType: 'streaming-error',
73
- title: 'We couldn\u2019t load this conversation.',
94
+ title: "Some of this didn't load.",
74
95
  buttons: [{
75
- text: 'New Conversation',
76
- action: 'newConversation',
96
+ text: 'Reload',
97
+ action: 'reload',
77
98
  variant: 'cta'
78
- }, {
79
- text: 'Contact Us',
80
- variant: 'link'
81
99
  }]
100
+ },
101
+ // E · Quota — waiting is the only fix, so the copy asks for time.
102
+ 'conversation-429': {
103
+ iconType: 'quota-exceeded',
104
+ title: 'I need a minute to catch up. Try again shortly.',
105
+ buttons: retryButton
82
106
  }
83
107
  };
84
108
 
@@ -1 +1 @@
1
- {"version":3,"names":["retryAndContactButtons","text","action","variant","DEFAULT_ERROR_TEMPLATES","exports","title","showChips","iconType","buttons","chipsLabel","resolveErrorTemplate","type","overrides","templates"],"sources":["../../../src/errors/errorTemplates.ts"],"sourcesContent":["import type { ConversationErrorType } from './conversationErrorTypes';\n\n/** Action identifiers for error template buttons — resolved to callbacks by the rendering component */\nexport type ErrorActionType = 'retry' | 'newConversation' | 'reload';\n\n/** Icon identifiers for error templates — resolved to SVG components by the rendering component */\nexport type ErrorIconType =\n | 'error-500'\n | 'timeout'\n | 'streaming-error'\n | 'not-found'\n | 'forbidden'\n | 'partial-load'\n | 'quota-exceeded';\n\nexport interface ErrorTemplateButton {\n text: string;\n /** URL for navigation (renders as a link) */\n href?: string;\n /** Action identifier — component resolves to a callback */\n action?: ErrorActionType;\n variant: 'cta' | 'link';\n}\n\nexport interface ErrorTemplate {\n title: string;\n subtitle?: string;\n /** Icon identifier — component resolves to an SVG */\n iconType?: ErrorIconType;\n buttons?: ErrorTemplateButton[];\n showChips?: boolean;\n chipsLabel?: string;\n}\n\nexport type ErrorTemplateOverrides = Partial<\n Record<ConversationErrorType, ErrorTemplate>\n>;\n\nconst retryAndContactButtons: ErrorTemplateButton[] = [\n { text: 'Try Again', action: 'retry', variant: 'cta' },\n { text: 'Contact Us', variant: 'link' },\n];\n\nexport const DEFAULT_ERROR_TEMPLATES: Record<\n ConversationErrorType,\n ErrorTemplate\n> = {\n 'conversation-llm-error-text': {\n title:\n \"I couldn't find information about what you asked.\\nTry asking about a different topic.\",\n showChips: true,\n },\n 'conversation-llm-empty-response': {\n title:\n \"I couldn't find information about what you asked.\\nTry asking about a different topic.\",\n showChips: true,\n },\n 'conversation-500': {\n iconType: 'error-500',\n title: \"This page isn't available right now.\",\n buttons: retryAndContactButtons,\n },\n 'conversation-generic-network-error': {\n iconType: 'timeout',\n title: 'Something went wrong on our side.\\nPlease try again in a moment.',\n buttons: retryAndContactButtons,\n },\n 'conversation-timeout': {\n iconType: 'timeout',\n title: 'Something went wrong on our side.\\nPlease try again in a moment.',\n buttons: retryAndContactButtons,\n },\n 'conversation-streaming-error': {\n iconType: 'streaming-error',\n title: 'The connection dropped. Please try again.',\n buttons: retryAndContactButtons,\n },\n 'conversation-404': {\n iconType: 'not-found',\n title: 'Page not found.\\nIt happens! Let\\u2019s get you back on track',\n showChips: true,\n chipsLabel: 'Some ideas to get you started:',\n },\n 'conversation-403': {\n iconType: 'forbidden',\n title:\n 'Welcome back.\\nFor privacy, we couldn\\u2019t restore previous content. Ask anything to continue.',\n showChips: true,\n chipsLabel: 'Some ideas to get you started:',\n },\n 'conversation-429': {\n iconType: 'quota-exceeded',\n title: 'This service is currently out of quota.\\nPlease try again later.',\n buttons: [{ text: 'Contact Us', variant: 'link' }],\n },\n 'conversation-partial-load': {\n iconType: 'partial-load',\n title: 'Some content didn\\u2019t load',\n buttons: retryAndContactButtons,\n },\n 'history-load-error': {\n iconType: 'streaming-error',\n title: 'We couldn\\u2019t load this conversation.',\n buttons: [\n { text: 'New Conversation', action: 'newConversation', variant: 'cta' },\n { text: 'Contact Us', variant: 'link' },\n ],\n },\n};\n\n/** Resolves the error template for a given type, merging client overrides on top of defaults */\nexport const resolveErrorTemplate = (\n type: string,\n overrides?: ErrorTemplateOverrides,\n): ErrorTemplate => {\n const templates = overrides\n ? { ...DEFAULT_ERROR_TEMPLATES, ...overrides }\n : DEFAULT_ERROR_TEMPLATES;\n return (\n templates[type as ConversationErrorType] ??\n templates['conversation-llm-error-text']\n );\n};\n"],"mappings":";;;;AAEA;;AAGA;;AAiCA,MAAMA,sBAA6C,GAAG,CACpD;EAAEC,IAAI,EAAE,WAAW;EAAEC,MAAM,EAAE,OAAO;EAAEC,OAAO,EAAE;AAAM,CAAC,EACtD;EAAEF,IAAI,EAAE,YAAY;EAAEE,OAAO,EAAE;AAAO,CAAC,CACxC;AAEM,MAAMC,uBAGZ,GAAAC,OAAA,CAAAD,uBAAA,GAAG;EACF,6BAA6B,EAAE;IAC7BE,KAAK,EACH,wFAAwF;IAC1FC,SAAS,EAAE;EACb,CAAC;EACD,iCAAiC,EAAE;IACjCD,KAAK,EACH,wFAAwF;IAC1FC,SAAS,EAAE;EACb,CAAC;EACD,kBAAkB,EAAE;IAClBC,QAAQ,EAAE,WAAW;IACrBF,KAAK,EAAE,sCAAsC;IAC7CG,OAAO,EAAET;EACX,CAAC;EACD,oCAAoC,EAAE;IACpCQ,QAAQ,EAAE,SAAS;IACnBF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAET;EACX,CAAC;EACD,sBAAsB,EAAE;IACtBQ,QAAQ,EAAE,SAAS;IACnBF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAET;EACX,CAAC;EACD,8BAA8B,EAAE;IAC9BQ,QAAQ,EAAE,iBAAiB;IAC3BF,KAAK,EAAE,2CAA2C;IAClDG,OAAO,EAAET;EACX,CAAC;EACD,kBAAkB,EAAE;IAClBQ,QAAQ,EAAE,WAAW;IACrBF,KAAK,EAAE,+DAA+D;IACtEC,SAAS,EAAE,IAAI;IACfG,UAAU,EAAE;EACd,CAAC;EACD,kBAAkB,EAAE;IAClBF,QAAQ,EAAE,WAAW;IACrBF,KAAK,EACH,kGAAkG;IACpGC,SAAS,EAAE,IAAI;IACfG,UAAU,EAAE;EACd,CAAC;EACD,kBAAkB,EAAE;IAClBF,QAAQ,EAAE,gBAAgB;IAC1BF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAE,CAAC;MAAER,IAAI,EAAE,YAAY;MAAEE,OAAO,EAAE;IAAO,CAAC;EACnD,CAAC;EACD,2BAA2B,EAAE;IAC3BK,QAAQ,EAAE,cAAc;IACxBF,KAAK,EAAE,+BAA+B;IACtCG,OAAO,EAAET;EACX,CAAC;EACD,oBAAoB,EAAE;IACpBQ,QAAQ,EAAE,iBAAiB;IAC3BF,KAAK,EAAE,0CAA0C;IACjDG,OAAO,EAAE,CACP;MAAER,IAAI,EAAE,kBAAkB;MAAEC,MAAM,EAAE,iBAAiB;MAAEC,OAAO,EAAE;IAAM,CAAC,EACvE;MAAEF,IAAI,EAAE,YAAY;MAAEE,OAAO,EAAE;IAAO,CAAC;EAE3C;AACF,CAAC;;AAED;AACO,MAAMQ,oBAAoB,GAAGA,CAClCC,IAAY,EACZC,SAAkC,KAChB;EAClB,MAAMC,SAAS,GAAGD,SAAS,GACvB;IAAE,GAAGT,uBAAuB;IAAE,GAAGS;EAAU,CAAC,GAC5CT,uBAAuB;EAC3B,OACEU,SAAS,CAACF,IAAI,CAA0B,IACxCE,SAAS,CAAC,6BAA6B,CAAC;AAE5C,CAAC;AAACT,OAAA,CAAAM,oBAAA,GAAAA,oBAAA","ignoreList":[]}
1
+ {"version":3,"names":["retryButton","text","action","variant","noAnswerTemplate","title","showChips","interruptedTitle","freshStartTemplate","chipsLabel","DEFAULT_ERROR_TEMPLATES","exports","iconType","buttons","resolveErrorTemplate","type","overrides","templates"],"sources":["../../../src/errors/errorTemplates.ts"],"sourcesContent":["import type { ConversationErrorType } from './conversationErrorTypes';\n\n/** Action identifiers for error template buttons — resolved to callbacks by the rendering component */\nexport type ErrorActionType = 'retry' | 'newConversation' | 'reload';\n\n/** Icon identifiers for error templates — resolved to SVG components by the rendering component */\nexport type ErrorIconType =\n | 'error-500'\n | 'timeout'\n | 'streaming-error'\n | 'not-found'\n | 'forbidden'\n | 'partial-load'\n | 'quota-exceeded';\n\nexport interface ErrorTemplateButton {\n text: string;\n /** URL for navigation (renders as a link) */\n href?: string;\n /** Action identifier — component resolves to a callback */\n action?: ErrorActionType;\n variant: 'cta' | 'link';\n}\n\nexport interface ErrorTemplate {\n title: string;\n subtitle?: string;\n /** Icon identifier — component resolves to an SVG */\n iconType?: ErrorIconType;\n buttons?: ErrorTemplateButton[];\n showChips?: boolean;\n chipsLabel?: string;\n}\n\nexport type ErrorTemplateOverrides = Partial<\n Record<ConversationErrorType, ErrorTemplate>\n>;\n\n/**\n * The eleven error types collapse into five states a shopper can act on\n * (B2B-4615). Each state has one job, so each has one line and at most one\n * action:\n *\n * A · No answer — llm-error-text, llm-empty-response\n * B · Interrupted — 500, generic-network-error, timeout, streaming-error,\n * history-load-error\n * C · Fresh start — 403, 404\n * D · Partial — partial-load\n * E · Quota — 429\n *\n * `iconType` stays on every template that had one: the new design drops icons,\n * but w5-client-circana and w5-client-feature-com still read the field and\n * must keep the look they have.\n */\n\nconst retryButton: ErrorTemplateButton[] = [\n { text: 'Try again', action: 'retry', variant: 'cta' },\n];\n\n/** A · No answer — nothing to retry, so it offers somewhere else to go. */\nconst noAnswerTemplate: ErrorTemplate = {\n title: \"I can't help with that one \\u2014 but I can help with these.\",\n showChips: true,\n};\n\n/** B · Interrupted — the message is still there to send again. */\nconst interruptedTitle = \"That didn't come through. Let's try again.\";\n\n/** C · Fresh start — the thread is gone; hand the conversation back. */\nconst freshStartTemplate: ErrorTemplate = {\n title: \"We're starting fresh \\u2014 earlier messages aren't saved.\",\n showChips: true,\n chipsLabel: 'Ask anything to pick up again',\n};\n\nexport const DEFAULT_ERROR_TEMPLATES: Record<\n ConversationErrorType,\n ErrorTemplate\n> = {\n 'conversation-llm-error-text': noAnswerTemplate,\n 'conversation-llm-empty-response': noAnswerTemplate,\n 'conversation-500': {\n iconType: 'error-500',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-generic-network-error': {\n iconType: 'timeout',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-timeout': {\n iconType: 'timeout',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-streaming-error': {\n iconType: 'streaming-error',\n title: interruptedTitle,\n buttons: retryButton,\n },\n // Same state as the four above, deliberately not the same action: a history\n // load fails before there is a last message, so `retry` has nothing to\n // re-send and would sit there doing nothing.\n 'history-load-error': {\n iconType: 'streaming-error',\n title: interruptedTitle,\n buttons: [{ text: 'Try again', action: 'reload', variant: 'cta' }],\n },\n 'conversation-403': { iconType: 'forbidden', ...freshStartTemplate },\n 'conversation-404': { iconType: 'not-found', ...freshStartTemplate },\n // D · Partial — an answer DID render; some of it is missing. This one is\n // appended to the answer rather than replacing it.\n 'conversation-partial-load': {\n iconType: 'partial-load',\n title: \"Some of this didn't load.\",\n buttons: [{ text: 'Reload', action: 'reload', variant: 'cta' }],\n },\n // E · Quota — waiting is the only fix, so the copy asks for time.\n 'conversation-429': {\n iconType: 'quota-exceeded',\n title: 'I need a minute to catch up. Try again shortly.',\n buttons: retryButton,\n },\n};\n\n/** Resolves the error template for a given type, merging client overrides on top of defaults */\nexport const resolveErrorTemplate = (\n type: string,\n overrides?: ErrorTemplateOverrides,\n): ErrorTemplate => {\n const templates = overrides\n ? { ...DEFAULT_ERROR_TEMPLATES, ...overrides }\n : DEFAULT_ERROR_TEMPLATES;\n return (\n templates[type as ConversationErrorType] ??\n templates['conversation-llm-error-text']\n );\n};\n"],"mappings":";;;;AAEA;;AAGA;;AAiCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,MAAMA,WAAkC,GAAG,CACzC;EAAEC,IAAI,EAAE,WAAW;EAAEC,MAAM,EAAE,OAAO;EAAEC,OAAO,EAAE;AAAM,CAAC,CACvD;;AAED;AACA,MAAMC,gBAA+B,GAAG;EACtCC,KAAK,EAAE,8DAA8D;EACrEC,SAAS,EAAE;AACb,CAAC;;AAED;AACA,MAAMC,gBAAgB,GAAG,4CAA4C;;AAErE;AACA,MAAMC,kBAAiC,GAAG;EACxCH,KAAK,EAAE,4DAA4D;EACnEC,SAAS,EAAE,IAAI;EACfG,UAAU,EAAE;AACd,CAAC;AAEM,MAAMC,uBAGZ,GAAAC,OAAA,CAAAD,uBAAA,GAAG;EACF,6BAA6B,EAAEN,gBAAgB;EAC/C,iCAAiC,EAAEA,gBAAgB;EACnD,kBAAkB,EAAE;IAClBQ,QAAQ,EAAE,WAAW;IACrBP,KAAK,EAAEE,gBAAgB;IACvBM,OAAO,EAAEb;EACX,CAAC;EACD,oCAAoC,EAAE;IACpCY,QAAQ,EAAE,SAAS;IACnBP,KAAK,EAAEE,gBAAgB;IACvBM,OAAO,EAAEb;EACX,CAAC;EACD,sBAAsB,EAAE;IACtBY,QAAQ,EAAE,SAAS;IACnBP,KAAK,EAAEE,gBAAgB;IACvBM,OAAO,EAAEb;EACX,CAAC;EACD,8BAA8B,EAAE;IAC9BY,QAAQ,EAAE,iBAAiB;IAC3BP,KAAK,EAAEE,gBAAgB;IACvBM,OAAO,EAAEb;EACX,CAAC;EACD;EACA;EACA;EACA,oBAAoB,EAAE;IACpBY,QAAQ,EAAE,iBAAiB;IAC3BP,KAAK,EAAEE,gBAAgB;IACvBM,OAAO,EAAE,CAAC;MAAEZ,IAAI,EAAE,WAAW;MAAEC,MAAM,EAAE,QAAQ;MAAEC,OAAO,EAAE;IAAM,CAAC;EACnE,CAAC;EACD,kBAAkB,EAAE;IAAES,QAAQ,EAAE,WAAW;IAAE,GAAGJ;EAAmB,CAAC;EACpE,kBAAkB,EAAE;IAAEI,QAAQ,EAAE,WAAW;IAAE,GAAGJ;EAAmB,CAAC;EACpE;EACA;EACA,2BAA2B,EAAE;IAC3BI,QAAQ,EAAE,cAAc;IACxBP,KAAK,EAAE,2BAA2B;IAClCQ,OAAO,EAAE,CAAC;MAAEZ,IAAI,EAAE,QAAQ;MAAEC,MAAM,EAAE,QAAQ;MAAEC,OAAO,EAAE;IAAM,CAAC;EAChE,CAAC;EACD;EACA,kBAAkB,EAAE;IAClBS,QAAQ,EAAE,gBAAgB;IAC1BP,KAAK,EAAE,iDAAiD;IACxDQ,OAAO,EAAEb;EACX;AACF,CAAC;;AAED;AACO,MAAMc,oBAAoB,GAAGA,CAClCC,IAAY,EACZC,SAAkC,KAChB;EAClB,MAAMC,SAAS,GAAGD,SAAS,GACvB;IAAE,GAAGN,uBAAuB;IAAE,GAAGM;EAAU,CAAC,GAC5CN,uBAAuB;EAC3B,OACEO,SAAS,CAACF,IAAI,CAA0B,IACxCE,SAAS,CAAC,6BAA6B,CAAC;AAE5C,CAAC;AAACN,OAAA,CAAAG,oBAAA,GAAAA,oBAAA","ignoreList":[]}
@@ -8,7 +8,12 @@
8
8
  /* Color tokens — HSL triplets */
9
9
  --background: 0 0% 100%;
10
10
  --foreground: 0 0% 3.9%;
11
- --card: 0 0% 100%;
11
+ /* A card must not dissolve into the page it sits on. Until the seed's
12
+ * migration to `bg-card` this separation was drawn the other way round —
13
+ * section bands were `bg-muted` and cards were the pure `--background` — so
14
+ * the tint moves onto the card and `--background` goes back to meaning what
15
+ * it says: the page. Same two greys, opposite surfaces. */
16
+ --card: 0 0% 96.1%;
12
17
  --card-foreground: 0 0% 3.9%;
13
18
  --popover: 0 0% 100%;
14
19
  --popover-foreground: 0 0% 3.9%;
@@ -113,7 +118,7 @@
113
118
  .dark {
114
119
  --background: 0 0% 3.9%;
115
120
  --foreground: 0 0% 98%;
116
- --card: 0 0% 3.9%;
121
+ --card: 0 0% 14.9%;
117
122
  --card-foreground: 0 0% 98%;
118
123
  --popover: 0 0% 3.9%;
119
124
  --popover-foreground: 0 0% 98%;
@@ -71,11 +71,19 @@ exports.hostAliasFor = hostAliasFor;
71
71
  * with no name, and a label on a token nobody may set is a promise the panel
72
72
  * cannot keep. One argument, one decision.
73
73
  *
74
- * Labels are the merchant's words rather than the token's — "Buttons & links",
75
- * not "primary". Someone choosing their shop's colours is not reading a design
74
+ * Labels are the merchant's words rather than the token's — "Card background",
75
+ * not "card". Someone choosing their shop's colours is not reading a design
76
76
  * system, and `--primary-foreground` is not a colour anyone picks: it is
77
77
  * whatever stays legible ON primary, which the theme derives. Exposing the
78
78
  * derived half is how a panel produces white text on a white button.
79
+ *
80
+ * Only FOUR carry a label (product request, 2026-09-09): the page's surface and
81
+ * text, and a card's. They are the four a shop owner can point at on the page
82
+ * and name without being taught the vocabulary, and — unlike `--secondary`,
83
+ * `--accent` or `--muted`, which the importer MIXES from the anchors — they are
84
+ * independent of each other, so setting one cannot contradict the arithmetic
85
+ * that produced another. Everything else stays import-only: the store still
86
+ * fills it, the panel just does not offer it.
79
87
  */
80
88
  const brandColor = label => ({
81
89
  bucket: 'brand',
@@ -97,21 +105,21 @@ const brandColor = label => ({
97
105
  */
98
106
  const THEME_TOKEN_CONTRACT = exports.THEME_TOKEN_CONTRACT = Object.freeze({
99
107
  // ── brand · colour roles (18) ────────────────────────────────────────────
100
- '--background': brandColor('Page background'),
101
- '--foreground': brandColor('Text'),
102
- '--card': brandColor(),
103
- '--card-foreground': brandColor(),
108
+ '--background': brandColor('Background color'),
109
+ '--foreground': brandColor('Text color'),
110
+ '--card': brandColor('Card background'),
111
+ '--card-foreground': brandColor('Card text'),
104
112
  '--popover': brandColor(),
105
113
  '--popover-foreground': brandColor(),
106
- '--primary': brandColor('Buttons & links'),
114
+ '--primary': brandColor(),
107
115
  '--primary-foreground': brandColor(),
108
116
  '--secondary': brandColor(),
109
117
  '--secondary-foreground': brandColor(),
110
118
  '--muted': brandColor(),
111
119
  '--muted-foreground': brandColor(),
112
- '--accent': brandColor('Highlights'),
120
+ '--accent': brandColor(),
113
121
  '--accent-foreground': brandColor(),
114
- '--border': brandColor('Lines & borders'),
122
+ '--border': brandColor(),
115
123
  '--input': brandColor(),
116
124
  '--ring': brandColor(),
117
125
  /**
@@ -121,7 +129,7 @@ const THEME_TOKEN_CONTRACT = exports.THEME_TOKEN_CONTRACT = Object.freeze({
121
129
  * emits the other twenty. A store that states a distinct heading colour has
122
130
  * somewhere for it to go the moment an adapter learns to read one.
123
131
  */
124
- '--heading': brandColor('Headings'),
132
+ '--heading': brandColor(),
125
133
  // ── brand · type (2) ─────────────────────────────────────────────────────
126
134
  '--font-sans': {
127
135
  bucket: 'brand',
@@ -136,14 +144,15 @@ const THEME_TOKEN_CONTRACT = exports.THEME_TOKEN_CONTRACT = Object.freeze({
136
144
  // ── host · shape (1) ─────────────────────────────────────────────────────
137
145
  /**
138
146
  * The one token whose bucket changed, and the reason this contract exists.
139
- * Editable because it is the one value a store and a template are known to
140
- * disagree about in a way a merchant can see and wants to settle.
147
+ * A store's imported radius still reaches the page as `--web5-host-radius`
148
+ * and a template still beats it; what changed is that the OWNER no longer
149
+ * gets a control for it — the panel is four colours now, and shape is not
150
+ * one of them.
141
151
  */
142
152
  '--radius': {
143
153
  bucket: 'host',
144
154
  type: 'length',
145
- editable: true,
146
- label: 'Corner rounding'
155
+ editable: false
147
156
  }
148
157
  });
149
158
 
@@ -169,8 +178,9 @@ const TOKEN_NAME_PATTERN = exports.TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;
169
178
  * Derived from the contract rather than listed again beside it, so a token
170
179
  * cannot be editable in one place and absent from the other. The order is the
171
180
  * declaration order above, which reads outside-in — the page, then its text,
172
- * then the things drawn on it — and is a better first impression than
173
- * alphabetical or than the order the tokens happen to be defined for CSS.
181
+ * then the card drawn on it, then the card's text — and is a better first
182
+ * impression than alphabetical or than the order the tokens happen to be
183
+ * defined for CSS.
174
184
  */
175
185
  const EDITABLE_TOKENS = exports.EDITABLE_TOKENS = Object.freeze(Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable));
176
186
 
@@ -1 +1 @@
1
- {"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","exports","Object","freeze","BRAND_TOKENS","Set","entries","filter","entry","map","token","TOKEN_NAME_PATTERN","EDITABLE_TOKENS","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Buttons & links\",\n * not \"primary\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Page background'),\n '--foreground': brandColor('Text'),\n '--card': brandColor(),\n '--card-foreground': brandColor(),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor('Buttons & links'),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor('Highlights'),\n '--accent-foreground': brandColor(),\n '--border': brandColor('Lines & borders'),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor('Headings'),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * Editable because it is the one value a store and a template are known to\n * disagree about in a way a merchant can see and wants to settle.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: true,\n label: 'Corner rounding',\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the things drawn on it — and is a better first impression than\n * alphabetical or than the order the tokens happen to be defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","exports","Object","freeze","BRAND_TOKENS","Set","entries","filter","entry","map","token","TOKEN_NAME_PATTERN","EDITABLE_TOKENS","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Card background\",\n * not \"card\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n *\n * Only FOUR carry a label (product request, 2026-09-09): the page's surface and\n * text, and a card's. They are the four a shop owner can point at on the page\n * and name without being taught the vocabulary, and — unlike `--secondary`,\n * `--accent` or `--muted`, which the importer MIXES from the anchors — they are\n * independent of each other, so setting one cannot contradict the arithmetic\n * that produced another. Everything else stays import-only: the store still\n * fills it, the panel just does not offer it.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Background color'),\n '--foreground': brandColor('Text color'),\n '--card': brandColor('Card background'),\n '--card-foreground': brandColor('Card text'),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor(),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor(),\n '--accent-foreground': brandColor(),\n '--border': brandColor(),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor(),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * A store's imported radius still reaches the page as `--web5-host-radius`\n * and a template still beats it; what changed is that the OWNER no longer\n * gets a control for it — the panel is four colours now, and shape is not\n * one of them.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: false,\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the card drawn on it, then the card's text — and is a better first\n * impression than alphabetical or than the order the tokens happen to be\n * defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"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","ignoreList":[]}
@@ -2,79 +2,103 @@
2
2
 
3
3
  /** Icon identifiers for error templates — resolved to SVG components by the rendering component */
4
4
 
5
- const retryAndContactButtons = [{
6
- text: 'Try Again',
5
+ /**
6
+ * The eleven error types collapse into five states a shopper can act on
7
+ * (B2B-4615). Each state has one job, so each has one line and at most one
8
+ * action:
9
+ *
10
+ * A · No answer — llm-error-text, llm-empty-response
11
+ * B · Interrupted — 500, generic-network-error, timeout, streaming-error,
12
+ * history-load-error
13
+ * C · Fresh start — 403, 404
14
+ * D · Partial — partial-load
15
+ * E · Quota — 429
16
+ *
17
+ * `iconType` stays on every template that had one: the new design drops icons,
18
+ * but w5-client-circana and w5-client-feature-com still read the field and
19
+ * must keep the look they have.
20
+ */
21
+
22
+ const retryButton = [{
23
+ text: 'Try again',
7
24
  action: 'retry',
8
25
  variant: 'cta'
9
- }, {
10
- text: 'Contact Us',
11
- variant: 'link'
12
26
  }];
27
+
28
+ /** A · No answer — nothing to retry, so it offers somewhere else to go. */
29
+ const noAnswerTemplate = {
30
+ title: "I can't help with that one \u2014 but I can help with these.",
31
+ showChips: true
32
+ };
33
+
34
+ /** B · Interrupted — the message is still there to send again. */
35
+ const interruptedTitle = "That didn't come through. Let's try again.";
36
+
37
+ /** C · Fresh start — the thread is gone; hand the conversation back. */
38
+ const freshStartTemplate = {
39
+ title: "We're starting fresh \u2014 earlier messages aren't saved.",
40
+ showChips: true,
41
+ chipsLabel: 'Ask anything to pick up again'
42
+ };
13
43
  export const DEFAULT_ERROR_TEMPLATES = {
14
- 'conversation-llm-error-text': {
15
- title: "I couldn't find information about what you asked.\nTry asking about a different topic.",
16
- showChips: true
17
- },
18
- 'conversation-llm-empty-response': {
19
- title: "I couldn't find information about what you asked.\nTry asking about a different topic.",
20
- showChips: true
21
- },
44
+ 'conversation-llm-error-text': noAnswerTemplate,
45
+ 'conversation-llm-empty-response': noAnswerTemplate,
22
46
  'conversation-500': {
23
47
  iconType: 'error-500',
24
- title: "This page isn't available right now.",
25
- buttons: retryAndContactButtons
48
+ title: interruptedTitle,
49
+ buttons: retryButton
26
50
  },
27
51
  'conversation-generic-network-error': {
28
52
  iconType: 'timeout',
29
- title: 'Something went wrong on our side.\nPlease try again in a moment.',
30
- buttons: retryAndContactButtons
53
+ title: interruptedTitle,
54
+ buttons: retryButton
31
55
  },
32
56
  'conversation-timeout': {
33
57
  iconType: 'timeout',
34
- title: 'Something went wrong on our side.\nPlease try again in a moment.',
35
- buttons: retryAndContactButtons
58
+ title: interruptedTitle,
59
+ buttons: retryButton
36
60
  },
37
61
  'conversation-streaming-error': {
38
62
  iconType: 'streaming-error',
39
- title: 'The connection dropped. Please try again.',
40
- buttons: retryAndContactButtons
63
+ title: interruptedTitle,
64
+ buttons: retryButton
41
65
  },
42
- 'conversation-404': {
43
- iconType: 'not-found',
44
- title: 'Page not found.\nIt happens! Let\u2019s get you back on track',
45
- showChips: true,
46
- chipsLabel: 'Some ideas to get you started:'
66
+ // Same state as the four above, deliberately not the same action: a history
67
+ // load fails before there is a last message, so `retry` has nothing to
68
+ // re-send and would sit there doing nothing.
69
+ 'history-load-error': {
70
+ iconType: 'streaming-error',
71
+ title: interruptedTitle,
72
+ buttons: [{
73
+ text: 'Try again',
74
+ action: 'reload',
75
+ variant: 'cta'
76
+ }]
47
77
  },
48
78
  'conversation-403': {
49
79
  iconType: 'forbidden',
50
- title: 'Welcome back.\nFor privacy, we couldn\u2019t restore previous content. Ask anything to continue.',
51
- showChips: true,
52
- chipsLabel: 'Some ideas to get you started:'
80
+ ...freshStartTemplate
53
81
  },
54
- 'conversation-429': {
55
- iconType: 'quota-exceeded',
56
- title: 'This service is currently out of quota.\nPlease try again later.',
57
- buttons: [{
58
- text: 'Contact Us',
59
- variant: 'link'
60
- }]
82
+ 'conversation-404': {
83
+ iconType: 'not-found',
84
+ ...freshStartTemplate
61
85
  },
86
+ // D · Partial — an answer DID render; some of it is missing. This one is
87
+ // appended to the answer rather than replacing it.
62
88
  'conversation-partial-load': {
63
89
  iconType: 'partial-load',
64
- title: 'Some content didn\u2019t load',
65
- buttons: retryAndContactButtons
66
- },
67
- 'history-load-error': {
68
- iconType: 'streaming-error',
69
- title: 'We couldn\u2019t load this conversation.',
90
+ title: "Some of this didn't load.",
70
91
  buttons: [{
71
- text: 'New Conversation',
72
- action: 'newConversation',
92
+ text: 'Reload',
93
+ action: 'reload',
73
94
  variant: 'cta'
74
- }, {
75
- text: 'Contact Us',
76
- variant: 'link'
77
95
  }]
96
+ },
97
+ // E · Quota — waiting is the only fix, so the copy asks for time.
98
+ 'conversation-429': {
99
+ iconType: 'quota-exceeded',
100
+ title: 'I need a minute to catch up. Try again shortly.',
101
+ buttons: retryButton
78
102
  }
79
103
  };
80
104
 
@@ -1 +1 @@
1
- {"version":3,"names":["retryAndContactButtons","text","action","variant","DEFAULT_ERROR_TEMPLATES","title","showChips","iconType","buttons","chipsLabel","resolveErrorTemplate","type","overrides","templates"],"sources":["../../../src/errors/errorTemplates.ts"],"sourcesContent":["import type { ConversationErrorType } from './conversationErrorTypes';\n\n/** Action identifiers for error template buttons — resolved to callbacks by the rendering component */\nexport type ErrorActionType = 'retry' | 'newConversation' | 'reload';\n\n/** Icon identifiers for error templates — resolved to SVG components by the rendering component */\nexport type ErrorIconType =\n | 'error-500'\n | 'timeout'\n | 'streaming-error'\n | 'not-found'\n | 'forbidden'\n | 'partial-load'\n | 'quota-exceeded';\n\nexport interface ErrorTemplateButton {\n text: string;\n /** URL for navigation (renders as a link) */\n href?: string;\n /** Action identifier — component resolves to a callback */\n action?: ErrorActionType;\n variant: 'cta' | 'link';\n}\n\nexport interface ErrorTemplate {\n title: string;\n subtitle?: string;\n /** Icon identifier — component resolves to an SVG */\n iconType?: ErrorIconType;\n buttons?: ErrorTemplateButton[];\n showChips?: boolean;\n chipsLabel?: string;\n}\n\nexport type ErrorTemplateOverrides = Partial<\n Record<ConversationErrorType, ErrorTemplate>\n>;\n\nconst retryAndContactButtons: ErrorTemplateButton[] = [\n { text: 'Try Again', action: 'retry', variant: 'cta' },\n { text: 'Contact Us', variant: 'link' },\n];\n\nexport const DEFAULT_ERROR_TEMPLATES: Record<\n ConversationErrorType,\n ErrorTemplate\n> = {\n 'conversation-llm-error-text': {\n title:\n \"I couldn't find information about what you asked.\\nTry asking about a different topic.\",\n showChips: true,\n },\n 'conversation-llm-empty-response': {\n title:\n \"I couldn't find information about what you asked.\\nTry asking about a different topic.\",\n showChips: true,\n },\n 'conversation-500': {\n iconType: 'error-500',\n title: \"This page isn't available right now.\",\n buttons: retryAndContactButtons,\n },\n 'conversation-generic-network-error': {\n iconType: 'timeout',\n title: 'Something went wrong on our side.\\nPlease try again in a moment.',\n buttons: retryAndContactButtons,\n },\n 'conversation-timeout': {\n iconType: 'timeout',\n title: 'Something went wrong on our side.\\nPlease try again in a moment.',\n buttons: retryAndContactButtons,\n },\n 'conversation-streaming-error': {\n iconType: 'streaming-error',\n title: 'The connection dropped. Please try again.',\n buttons: retryAndContactButtons,\n },\n 'conversation-404': {\n iconType: 'not-found',\n title: 'Page not found.\\nIt happens! Let\\u2019s get you back on track',\n showChips: true,\n chipsLabel: 'Some ideas to get you started:',\n },\n 'conversation-403': {\n iconType: 'forbidden',\n title:\n 'Welcome back.\\nFor privacy, we couldn\\u2019t restore previous content. Ask anything to continue.',\n showChips: true,\n chipsLabel: 'Some ideas to get you started:',\n },\n 'conversation-429': {\n iconType: 'quota-exceeded',\n title: 'This service is currently out of quota.\\nPlease try again later.',\n buttons: [{ text: 'Contact Us', variant: 'link' }],\n },\n 'conversation-partial-load': {\n iconType: 'partial-load',\n title: 'Some content didn\\u2019t load',\n buttons: retryAndContactButtons,\n },\n 'history-load-error': {\n iconType: 'streaming-error',\n title: 'We couldn\\u2019t load this conversation.',\n buttons: [\n { text: 'New Conversation', action: 'newConversation', variant: 'cta' },\n { text: 'Contact Us', variant: 'link' },\n ],\n },\n};\n\n/** Resolves the error template for a given type, merging client overrides on top of defaults */\nexport const resolveErrorTemplate = (\n type: string,\n overrides?: ErrorTemplateOverrides,\n): ErrorTemplate => {\n const templates = overrides\n ? { ...DEFAULT_ERROR_TEMPLATES, ...overrides }\n : DEFAULT_ERROR_TEMPLATES;\n return (\n templates[type as ConversationErrorType] ??\n templates['conversation-llm-error-text']\n );\n};\n"],"mappings":"AAEA;;AAGA;;AAiCA,MAAMA,sBAA6C,GAAG,CACpD;EAAEC,IAAI,EAAE,WAAW;EAAEC,MAAM,EAAE,OAAO;EAAEC,OAAO,EAAE;AAAM,CAAC,EACtD;EAAEF,IAAI,EAAE,YAAY;EAAEE,OAAO,EAAE;AAAO,CAAC,CACxC;AAED,OAAO,MAAMC,uBAGZ,GAAG;EACF,6BAA6B,EAAE;IAC7BC,KAAK,EACH,wFAAwF;IAC1FC,SAAS,EAAE;EACb,CAAC;EACD,iCAAiC,EAAE;IACjCD,KAAK,EACH,wFAAwF;IAC1FC,SAAS,EAAE;EACb,CAAC;EACD,kBAAkB,EAAE;IAClBC,QAAQ,EAAE,WAAW;IACrBF,KAAK,EAAE,sCAAsC;IAC7CG,OAAO,EAAER;EACX,CAAC;EACD,oCAAoC,EAAE;IACpCO,QAAQ,EAAE,SAAS;IACnBF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAER;EACX,CAAC;EACD,sBAAsB,EAAE;IACtBO,QAAQ,EAAE,SAAS;IACnBF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAER;EACX,CAAC;EACD,8BAA8B,EAAE;IAC9BO,QAAQ,EAAE,iBAAiB;IAC3BF,KAAK,EAAE,2CAA2C;IAClDG,OAAO,EAAER;EACX,CAAC;EACD,kBAAkB,EAAE;IAClBO,QAAQ,EAAE,WAAW;IACrBF,KAAK,EAAE,+DAA+D;IACtEC,SAAS,EAAE,IAAI;IACfG,UAAU,EAAE;EACd,CAAC;EACD,kBAAkB,EAAE;IAClBF,QAAQ,EAAE,WAAW;IACrBF,KAAK,EACH,kGAAkG;IACpGC,SAAS,EAAE,IAAI;IACfG,UAAU,EAAE;EACd,CAAC;EACD,kBAAkB,EAAE;IAClBF,QAAQ,EAAE,gBAAgB;IAC1BF,KAAK,EAAE,kEAAkE;IACzEG,OAAO,EAAE,CAAC;MAAEP,IAAI,EAAE,YAAY;MAAEE,OAAO,EAAE;IAAO,CAAC;EACnD,CAAC;EACD,2BAA2B,EAAE;IAC3BI,QAAQ,EAAE,cAAc;IACxBF,KAAK,EAAE,+BAA+B;IACtCG,OAAO,EAAER;EACX,CAAC;EACD,oBAAoB,EAAE;IACpBO,QAAQ,EAAE,iBAAiB;IAC3BF,KAAK,EAAE,0CAA0C;IACjDG,OAAO,EAAE,CACP;MAAEP,IAAI,EAAE,kBAAkB;MAAEC,MAAM,EAAE,iBAAiB;MAAEC,OAAO,EAAE;IAAM,CAAC,EACvE;MAAEF,IAAI,EAAE,YAAY;MAAEE,OAAO,EAAE;IAAO,CAAC;EAE3C;AACF,CAAC;;AAED;AACA,OAAO,MAAMO,oBAAoB,GAAGA,CAClCC,IAAY,EACZC,SAAkC,KAChB;EAClB,MAAMC,SAAS,GAAGD,SAAS,GACvB;IAAE,GAAGR,uBAAuB;IAAE,GAAGQ;EAAU,CAAC,GAC5CR,uBAAuB;EAC3B,OACES,SAAS,CAACF,IAAI,CAA0B,IACxCE,SAAS,CAAC,6BAA6B,CAAC;AAE5C,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["retryButton","text","action","variant","noAnswerTemplate","title","showChips","interruptedTitle","freshStartTemplate","chipsLabel","DEFAULT_ERROR_TEMPLATES","iconType","buttons","resolveErrorTemplate","type","overrides","templates"],"sources":["../../../src/errors/errorTemplates.ts"],"sourcesContent":["import type { ConversationErrorType } from './conversationErrorTypes';\n\n/** Action identifiers for error template buttons — resolved to callbacks by the rendering component */\nexport type ErrorActionType = 'retry' | 'newConversation' | 'reload';\n\n/** Icon identifiers for error templates — resolved to SVG components by the rendering component */\nexport type ErrorIconType =\n | 'error-500'\n | 'timeout'\n | 'streaming-error'\n | 'not-found'\n | 'forbidden'\n | 'partial-load'\n | 'quota-exceeded';\n\nexport interface ErrorTemplateButton {\n text: string;\n /** URL for navigation (renders as a link) */\n href?: string;\n /** Action identifier — component resolves to a callback */\n action?: ErrorActionType;\n variant: 'cta' | 'link';\n}\n\nexport interface ErrorTemplate {\n title: string;\n subtitle?: string;\n /** Icon identifier — component resolves to an SVG */\n iconType?: ErrorIconType;\n buttons?: ErrorTemplateButton[];\n showChips?: boolean;\n chipsLabel?: string;\n}\n\nexport type ErrorTemplateOverrides = Partial<\n Record<ConversationErrorType, ErrorTemplate>\n>;\n\n/**\n * The eleven error types collapse into five states a shopper can act on\n * (B2B-4615). Each state has one job, so each has one line and at most one\n * action:\n *\n * A · No answer — llm-error-text, llm-empty-response\n * B · Interrupted — 500, generic-network-error, timeout, streaming-error,\n * history-load-error\n * C · Fresh start — 403, 404\n * D · Partial — partial-load\n * E · Quota — 429\n *\n * `iconType` stays on every template that had one: the new design drops icons,\n * but w5-client-circana and w5-client-feature-com still read the field and\n * must keep the look they have.\n */\n\nconst retryButton: ErrorTemplateButton[] = [\n { text: 'Try again', action: 'retry', variant: 'cta' },\n];\n\n/** A · No answer — nothing to retry, so it offers somewhere else to go. */\nconst noAnswerTemplate: ErrorTemplate = {\n title: \"I can't help with that one \\u2014 but I can help with these.\",\n showChips: true,\n};\n\n/** B · Interrupted — the message is still there to send again. */\nconst interruptedTitle = \"That didn't come through. Let's try again.\";\n\n/** C · Fresh start — the thread is gone; hand the conversation back. */\nconst freshStartTemplate: ErrorTemplate = {\n title: \"We're starting fresh \\u2014 earlier messages aren't saved.\",\n showChips: true,\n chipsLabel: 'Ask anything to pick up again',\n};\n\nexport const DEFAULT_ERROR_TEMPLATES: Record<\n ConversationErrorType,\n ErrorTemplate\n> = {\n 'conversation-llm-error-text': noAnswerTemplate,\n 'conversation-llm-empty-response': noAnswerTemplate,\n 'conversation-500': {\n iconType: 'error-500',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-generic-network-error': {\n iconType: 'timeout',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-timeout': {\n iconType: 'timeout',\n title: interruptedTitle,\n buttons: retryButton,\n },\n 'conversation-streaming-error': {\n iconType: 'streaming-error',\n title: interruptedTitle,\n buttons: retryButton,\n },\n // Same state as the four above, deliberately not the same action: a history\n // load fails before there is a last message, so `retry` has nothing to\n // re-send and would sit there doing nothing.\n 'history-load-error': {\n iconType: 'streaming-error',\n title: interruptedTitle,\n buttons: [{ text: 'Try again', action: 'reload', variant: 'cta' }],\n },\n 'conversation-403': { iconType: 'forbidden', ...freshStartTemplate },\n 'conversation-404': { iconType: 'not-found', ...freshStartTemplate },\n // D · Partial — an answer DID render; some of it is missing. This one is\n // appended to the answer rather than replacing it.\n 'conversation-partial-load': {\n iconType: 'partial-load',\n title: \"Some of this didn't load.\",\n buttons: [{ text: 'Reload', action: 'reload', variant: 'cta' }],\n },\n // E · Quota — waiting is the only fix, so the copy asks for time.\n 'conversation-429': {\n iconType: 'quota-exceeded',\n title: 'I need a minute to catch up. Try again shortly.',\n buttons: retryButton,\n },\n};\n\n/** Resolves the error template for a given type, merging client overrides on top of defaults */\nexport const resolveErrorTemplate = (\n type: string,\n overrides?: ErrorTemplateOverrides,\n): ErrorTemplate => {\n const templates = overrides\n ? { ...DEFAULT_ERROR_TEMPLATES, ...overrides }\n : DEFAULT_ERROR_TEMPLATES;\n return (\n templates[type as ConversationErrorType] ??\n templates['conversation-llm-error-text']\n );\n};\n"],"mappings":"AAEA;;AAGA;;AAiCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,MAAMA,WAAkC,GAAG,CACzC;EAAEC,IAAI,EAAE,WAAW;EAAEC,MAAM,EAAE,OAAO;EAAEC,OAAO,EAAE;AAAM,CAAC,CACvD;;AAED;AACA,MAAMC,gBAA+B,GAAG;EACtCC,KAAK,EAAE,8DAA8D;EACrEC,SAAS,EAAE;AACb,CAAC;;AAED;AACA,MAAMC,gBAAgB,GAAG,4CAA4C;;AAErE;AACA,MAAMC,kBAAiC,GAAG;EACxCH,KAAK,EAAE,4DAA4D;EACnEC,SAAS,EAAE,IAAI;EACfG,UAAU,EAAE;AACd,CAAC;AAED,OAAO,MAAMC,uBAGZ,GAAG;EACF,6BAA6B,EAAEN,gBAAgB;EAC/C,iCAAiC,EAAEA,gBAAgB;EACnD,kBAAkB,EAAE;IAClBO,QAAQ,EAAE,WAAW;IACrBN,KAAK,EAAEE,gBAAgB;IACvBK,OAAO,EAAEZ;EACX,CAAC;EACD,oCAAoC,EAAE;IACpCW,QAAQ,EAAE,SAAS;IACnBN,KAAK,EAAEE,gBAAgB;IACvBK,OAAO,EAAEZ;EACX,CAAC;EACD,sBAAsB,EAAE;IACtBW,QAAQ,EAAE,SAAS;IACnBN,KAAK,EAAEE,gBAAgB;IACvBK,OAAO,EAAEZ;EACX,CAAC;EACD,8BAA8B,EAAE;IAC9BW,QAAQ,EAAE,iBAAiB;IAC3BN,KAAK,EAAEE,gBAAgB;IACvBK,OAAO,EAAEZ;EACX,CAAC;EACD;EACA;EACA;EACA,oBAAoB,EAAE;IACpBW,QAAQ,EAAE,iBAAiB;IAC3BN,KAAK,EAAEE,gBAAgB;IACvBK,OAAO,EAAE,CAAC;MAAEX,IAAI,EAAE,WAAW;MAAEC,MAAM,EAAE,QAAQ;MAAEC,OAAO,EAAE;IAAM,CAAC;EACnE,CAAC;EACD,kBAAkB,EAAE;IAAEQ,QAAQ,EAAE,WAAW;IAAE,GAAGH;EAAmB,CAAC;EACpE,kBAAkB,EAAE;IAAEG,QAAQ,EAAE,WAAW;IAAE,GAAGH;EAAmB,CAAC;EACpE;EACA;EACA,2BAA2B,EAAE;IAC3BG,QAAQ,EAAE,cAAc;IACxBN,KAAK,EAAE,2BAA2B;IAClCO,OAAO,EAAE,CAAC;MAAEX,IAAI,EAAE,QAAQ;MAAEC,MAAM,EAAE,QAAQ;MAAEC,OAAO,EAAE;IAAM,CAAC;EAChE,CAAC;EACD;EACA,kBAAkB,EAAE;IAClBQ,QAAQ,EAAE,gBAAgB;IAC1BN,KAAK,EAAE,iDAAiD;IACxDO,OAAO,EAAEZ;EACX;AACF,CAAC;;AAED;AACA,OAAO,MAAMa,oBAAoB,GAAGA,CAClCC,IAAY,EACZC,SAAkC,KAChB;EAClB,MAAMC,SAAS,GAAGD,SAAS,GACvB;IAAE,GAAGL,uBAAuB;IAAE,GAAGK;EAAU,CAAC,GAC5CL,uBAAuB;EAC3B,OACEM,SAAS,CAACF,IAAI,CAA0B,IACxCE,SAAS,CAAC,6BAA6B,CAAC;AAE5C,CAAC","ignoreList":[]}
@@ -8,7 +8,12 @@
8
8
  /* Color tokens — HSL triplets */
9
9
  --background: 0 0% 100%;
10
10
  --foreground: 0 0% 3.9%;
11
- --card: 0 0% 100%;
11
+ /* A card must not dissolve into the page it sits on. Until the seed's
12
+ * migration to `bg-card` this separation was drawn the other way round —
13
+ * section bands were `bg-muted` and cards were the pure `--background` — so
14
+ * the tint moves onto the card and `--background` goes back to meaning what
15
+ * it says: the page. Same two greys, opposite surfaces. */
16
+ --card: 0 0% 96.1%;
12
17
  --card-foreground: 0 0% 3.9%;
13
18
  --popover: 0 0% 100%;
14
19
  --popover-foreground: 0 0% 3.9%;
@@ -113,7 +118,7 @@
113
118
  .dark {
114
119
  --background: 0 0% 3.9%;
115
120
  --foreground: 0 0% 98%;
116
- --card: 0 0% 3.9%;
121
+ --card: 0 0% 14.9%;
117
122
  --card-foreground: 0 0% 98%;
118
123
  --popover: 0 0% 3.9%;
119
124
  --popover-foreground: 0 0% 98%;
@@ -65,11 +65,19 @@
65
65
  * with no name, and a label on a token nobody may set is a promise the panel
66
66
  * cannot keep. One argument, one decision.
67
67
  *
68
- * Labels are the merchant's words rather than the token's — "Buttons & links",
69
- * not "primary". Someone choosing their shop's colours is not reading a design
68
+ * Labels are the merchant's words rather than the token's — "Card background",
69
+ * not "card". Someone choosing their shop's colours is not reading a design
70
70
  * system, and `--primary-foreground` is not a colour anyone picks: it is
71
71
  * whatever stays legible ON primary, which the theme derives. Exposing the
72
72
  * derived half is how a panel produces white text on a white button.
73
+ *
74
+ * Only FOUR carry a label (product request, 2026-09-09): the page's surface and
75
+ * text, and a card's. They are the four a shop owner can point at on the page
76
+ * and name without being taught the vocabulary, and — unlike `--secondary`,
77
+ * `--accent` or `--muted`, which the importer MIXES from the anchors — they are
78
+ * independent of each other, so setting one cannot contradict the arithmetic
79
+ * that produced another. Everything else stays import-only: the store still
80
+ * fills it, the panel just does not offer it.
73
81
  */
74
82
  const brandColor = label => ({
75
83
  bucket: 'brand',
@@ -91,21 +99,21 @@ const brandColor = label => ({
91
99
  */
92
100
  export const THEME_TOKEN_CONTRACT = Object.freeze({
93
101
  // ── brand · colour roles (18) ────────────────────────────────────────────
94
- '--background': brandColor('Page background'),
95
- '--foreground': brandColor('Text'),
96
- '--card': brandColor(),
97
- '--card-foreground': brandColor(),
102
+ '--background': brandColor('Background color'),
103
+ '--foreground': brandColor('Text color'),
104
+ '--card': brandColor('Card background'),
105
+ '--card-foreground': brandColor('Card text'),
98
106
  '--popover': brandColor(),
99
107
  '--popover-foreground': brandColor(),
100
- '--primary': brandColor('Buttons & links'),
108
+ '--primary': brandColor(),
101
109
  '--primary-foreground': brandColor(),
102
110
  '--secondary': brandColor(),
103
111
  '--secondary-foreground': brandColor(),
104
112
  '--muted': brandColor(),
105
113
  '--muted-foreground': brandColor(),
106
- '--accent': brandColor('Highlights'),
114
+ '--accent': brandColor(),
107
115
  '--accent-foreground': brandColor(),
108
- '--border': brandColor('Lines & borders'),
116
+ '--border': brandColor(),
109
117
  '--input': brandColor(),
110
118
  '--ring': brandColor(),
111
119
  /**
@@ -115,7 +123,7 @@ export const THEME_TOKEN_CONTRACT = Object.freeze({
115
123
  * emits the other twenty. A store that states a distinct heading colour has
116
124
  * somewhere for it to go the moment an adapter learns to read one.
117
125
  */
118
- '--heading': brandColor('Headings'),
126
+ '--heading': brandColor(),
119
127
  // ── brand · type (2) ─────────────────────────────────────────────────────
120
128
  '--font-sans': {
121
129
  bucket: 'brand',
@@ -130,14 +138,15 @@ export const THEME_TOKEN_CONTRACT = Object.freeze({
130
138
  // ── host · shape (1) ─────────────────────────────────────────────────────
131
139
  /**
132
140
  * The one token whose bucket changed, and the reason this contract exists.
133
- * Editable because it is the one value a store and a template are known to
134
- * disagree about in a way a merchant can see and wants to settle.
141
+ * A store's imported radius still reaches the page as `--web5-host-radius`
142
+ * and a template still beats it; what changed is that the OWNER no longer
143
+ * gets a control for it — the panel is four colours now, and shape is not
144
+ * one of them.
135
145
  */
136
146
  '--radius': {
137
147
  bucket: 'host',
138
148
  type: 'length',
139
- editable: true,
140
- label: 'Corner rounding'
149
+ editable: false
141
150
  }
142
151
  });
143
152
 
@@ -169,8 +178,9 @@ export const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;
169
178
  * Derived from the contract rather than listed again beside it, so a token
170
179
  * cannot be editable in one place and absent from the other. The order is the
171
180
  * declaration order above, which reads outside-in — the page, then its text,
172
- * then the things drawn on it — and is a better first impression than
173
- * alphabetical or than the order the tokens happen to be defined for CSS.
181
+ * then the card drawn on it, then the card's text — and is a better first
182
+ * impression than alphabetical or than the order the tokens happen to be
183
+ * defined for CSS.
174
184
  */
175
185
  export const EDITABLE_TOKENS = Object.freeze(Object.entries(THEME_TOKEN_CONTRACT).filter(_ref3 => {
176
186
  let [, entry] = _ref3;
@@ -1 +1 @@
1
- {"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","Object","freeze","BRAND_TOKENS","Set","entries","filter","_ref","entry","map","_ref2","token","TOKEN_NAME_PATTERN","EDITABLE_TOKENS","_ref3","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Buttons & links\",\n * not \"primary\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Page background'),\n '--foreground': brandColor('Text'),\n '--card': brandColor(),\n '--card-foreground': brandColor(),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor('Buttons & links'),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor('Highlights'),\n '--accent-foreground': brandColor(),\n '--border': brandColor('Lines & borders'),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor('Headings'),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * Editable because it is the one value a store and a template are known to\n * disagree about in a way a merchant can see and wants to settle.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: true,\n label: 'Corner rounding',\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the things drawn on it — and is a better first impression than\n * alphabetical or than the order the tokens happen to be defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","Object","freeze","BRAND_TOKENS","Set","entries","filter","_ref","entry","map","_ref2","token","TOKEN_NAME_PATTERN","EDITABLE_TOKENS","_ref3","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Card background\",\n * not \"card\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n *\n * Only FOUR carry a label (product request, 2026-09-09): the page's surface and\n * text, and a card's. They are the four a shop owner can point at on the page\n * and name without being taught the vocabulary, and — unlike `--secondary`,\n * `--accent` or `--muted`, which the importer MIXES from the anchors — they are\n * independent of each other, so setting one cannot contradict the arithmetic\n * that produced another. Everything else stays import-only: the store still\n * fills it, the panel just does not offer it.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Background color'),\n '--foreground': brandColor('Text color'),\n '--card': brandColor('Card background'),\n '--card-foreground': brandColor('Card text'),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor(),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor(),\n '--accent-foreground': brandColor(),\n '--border': brandColor(),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor(),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * A store's imported radius still reaches the page as `--web5-host-radius`\n * and a template still beats it; what changed is that the OWNER no longer\n * gets a control for it — the panel is four colours now, and shape is not\n * one of them.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: false,\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the card drawn on it, then the card's text — and is a better first\n * impression than alphabetical or than the order the tokens happen to be\n * defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAUA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMA,UAAU,GAAIC,KAAc,KAA0B;EAC1DC,MAAM,EAAE,OAAO;EACfC,IAAI,EAAE,WAAW;EACjBC,QAAQ,EAAEH,KAAK,KAAKI,SAAS;EAC7B,IAAIJ,KAAK,KAAKI,SAAS,GAAG,CAAC,CAAC,GAAG;IAAEJ;EAAM,CAAC;AAC1C,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMK,oBAAkE,GAC7EC,MAAM,CAACC,MAAM,CAAC;EACZ;EACA,cAAc,EAAER,UAAU,CAAC,kBAAkB,CAAC;EAC9C,cAAc,EAAEA,UAAU,CAAC,YAAY,CAAC;EACxC,QAAQ,EAAEA,UAAU,CAAC,iBAAiB,CAAC;EACvC,mBAAmB,EAAEA,UAAU,CAAC,WAAW,CAAC;EAC5C,WAAW,EAAEA,UAAU,CAAC,CAAC;EACzB,sBAAsB,EAAEA,UAAU,CAAC,CAAC;EACpC,WAAW,EAAEA,UAAU,CAAC,CAAC;EACzB,sBAAsB,EAAEA,UAAU,CAAC,CAAC;EACpC,aAAa,EAAEA,UAAU,CAAC,CAAC;EAC3B,wBAAwB,EAAEA,UAAU,CAAC,CAAC;EACtC,SAAS,EAAEA,UAAU,CAAC,CAAC;EACvB,oBAAoB,EAAEA,UAAU,CAAC,CAAC;EAClC,UAAU,EAAEA,UAAU,CAAC,CAAC;EACxB,qBAAqB,EAAEA,UAAU,CAAC,CAAC;EACnC,UAAU,EAAEA,UAAU,CAAC,CAAC;EACxB,SAAS,EAAEA,UAAU,CAAC,CAAC;EACvB,QAAQ,EAAEA,UAAU,CAAC,CAAC;EAEtB;AACJ;AACA;AACA;AACA;AACA;AACA;EACI,WAAW,EAAEA,UAAU,CAAC,CAAC;EAEzB;EACA,aAAa,EAAE;IAAEE,MAAM,EAAE,OAAO;IAAEC,IAAI,EAAE,YAAY;IAAEC,QAAQ,EAAE;EAAM,CAAC;EACvE,gBAAgB,EAAE;IAAEF,MAAM,EAAE,OAAO;IAAEC,IAAI,EAAE,YAAY;IAAEC,QAAQ,EAAE;EAAM,CAAC;EAE1E;EACA;AACJ;AACA;AACA;AACA;AACA;AACA;EACI,UAAU,EAAE;IACVF,MAAM,EAAE,MAAM;IACdC,IAAI,EAAE,QAAQ;IACdC,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEJ;AACA;AACA;AACA;AACA,OAAO,MAAMK,YAAiC,GAAGF,MAAM,CAACC,MAAM,CAC5D,IAAIE,GAAG,CACLH,MAAM,CAACI,OAAO,CAACL,oBAAoB,CAAC,CACjCM,MAAM,CAACC,IAAA;EAAA,IAAC,GAAGC,KAAK,CAAC,GAAAD,IAAA;EAAA,OAAKC,KAAK,CAACZ,MAAM,KAAK,OAAO;AAAA,EAAC,CAC/Ca,GAAG,CAACC,KAAA;EAAA,IAAC,CAACC,KAAK,CAAC,GAAAD,KAAA;EAAA,OAAKC,KAAK;AAAA,EAC3B,CACF,CAAwB;;AAExB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,GAAG,gBAAgB;;AAElD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,eAGT,GAAGZ,MAAM,CAACC,MAAM,CAClBD,MAAM,CAACI,OAAO,CAACL,oBAAoB,CAAC,CAACM,MAAM,CAACQ,KAAA;EAAA,IAAC,GAAGN,KAAK,CAAC,GAAAM,KAAA;EAAA,OAAKN,KAAK,CAACV,QAAQ;AAAA,EAC3E,CAAuD;;AAEvD;AACA,OAAO,SAASiB,YAAYA,CAACJ,KAAa,EAAU;EAClD,OAAO,eAAeA,KAAK,CAACK,KAAK,CAAC,CAAC,CAAC,EAAE;AACxC;;AAEA;AACA,OAAO,SAASC,QAAQA,CAACN,KAAa,EAAe;EAAA,IAAAO,qBAAA;EACnD,OAAO,EAAAA,qBAAA,GAAAlB,oBAAoB,CAACW,KAAK,CAAC,qBAA3BO,qBAAA,CAA6BtB,MAAM,KAAI,MAAM;AACtD","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"errorTemplates.d.ts","sourceRoot":"","sources":["../../../src/errors/errorTemplates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE,uGAAuG;AACvG,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAErE,mGAAmG;AACnG,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,SAAS,GACT,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC1C,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAC7C,CAAC;AAOF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,aAAa,CA+Dd,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,SACzB,MAAM,cACA,sBAAsB,KACjC,aAQF,CAAC"}
1
+ {"version":3,"file":"errorTemplates.d.ts","sourceRoot":"","sources":["../../../src/errors/errorTemplates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE,uGAAuG;AACvG,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAErE,mGAAmG;AACnG,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,SAAS,GACT,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC1C,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAC7C,CAAC;AAuCF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,aAAa,CA+Cd,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,SACzB,MAAM,cACA,sBAAsB,KACjC,aAQF,CAAC"}
@@ -102,8 +102,9 @@ export declare const TOKEN_NAME_PATTERN: RegExp;
102
102
  * Derived from the contract rather than listed again beside it, so a token
103
103
  * cannot be editable in one place and absent from the other. The order is the
104
104
  * declaration order above, which reads outside-in — the page, then its text,
105
- * then the things drawn on it — and is a better first impression than
106
- * alphabetical or than the order the tokens happen to be defined for CSS.
105
+ * then the card drawn on it, then the card's text — and is a better first
106
+ * impression than alphabetical or than the order the tokens happen to be
107
+ * defined for CSS.
107
108
  */
108
109
  export declare const EDITABLE_TOKENS: readonly (readonly [
109
110
  token: string,
@@ -1 +1 @@
1
- {"version":3,"file":"tokenContract.d.ts","sourceRoot":"","sources":["../../../src/theme/tokenContract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,uEAAuE;AACvE,MAAM,MAAM,SAAS;AACnB,oFAAoF;AAClF,WAAW;AACb,iEAAiE;GAC/D,YAAY;AACd;yEACyE;GACvE,QAAQ,CAAC;AAEb,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsBD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CA8C1E,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAMrB,CAAC;AAEzB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,CAAC,SAAS;IAC/C,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,kBAAkB;CAC1B,CAAC,EAEqD,CAAC;AAExD,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,kGAAkG;AAClG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAEnD"}
1
+ {"version":3,"file":"tokenContract.d.ts","sourceRoot":"","sources":["../../../src/theme/tokenContract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,uEAAuE;AACvE,MAAM,MAAM,SAAS;AACnB,oFAAoF;AAClF,WAAW;AACb,iEAAiE;GAC/D,YAAY;AACd;yEACyE;GACvE,QAAQ,CAAC;AAEb,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA8BD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CA+C1E,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAMrB,CAAC;AAEzB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AAEnD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,CAAC,SAAS;IAC/C,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,kBAAkB;CAC1B,CAAC,EAEqD,CAAC;AAExD,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,kGAAkG;AAClG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAEnD"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wix/web5-core",
3
3
  "license": "MIT",
4
- "version": "1.63.38",
4
+ "version": "1.63.40",
5
5
  "author": {
6
6
  "name": "tsachis",
7
7
  "email": "tsachis@wix.com"
@@ -100,5 +100,5 @@
100
100
  "wallaby": {
101
101
  "autoDetect": true
102
102
  },
103
- "falconPackageHash": "527f52ff447d5d61666f8af72e2f79ab513eea188fe4f1a640a38cb2"
103
+ "falconPackageHash": "822781481f70a468191111623bc8e82e6c573c2980eddd65f4ec07c4"
104
104
  }
@@ -8,7 +8,12 @@
8
8
  /* Color tokens — HSL triplets */
9
9
  --background: 0 0% 100%;
10
10
  --foreground: 0 0% 3.9%;
11
- --card: 0 0% 100%;
11
+ /* A card must not dissolve into the page it sits on. Until the seed's
12
+ * migration to `bg-card` this separation was drawn the other way round —
13
+ * section bands were `bg-muted` and cards were the pure `--background` — so
14
+ * the tint moves onto the card and `--background` goes back to meaning what
15
+ * it says: the page. Same two greys, opposite surfaces. */
16
+ --card: 0 0% 96.1%;
12
17
  --card-foreground: 0 0% 3.9%;
13
18
  --popover: 0 0% 100%;
14
19
  --popover-foreground: 0 0% 3.9%;
@@ -113,7 +118,7 @@
113
118
  .dark {
114
119
  --background: 0 0% 3.9%;
115
120
  --foreground: 0 0% 98%;
116
- --card: 0 0% 3.9%;
121
+ --card: 0 0% 14.9%;
117
122
  --card-foreground: 0 0% 98%;
118
123
  --popover: 0 0% 3.9%;
119
124
  --popover-foreground: 0 0% 98%;