@genesislcap/ai-assistant 15.28.1-FUI-0.7 → 15.28.1-FUI-0.8

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.
@@ -646,6 +646,24 @@ export declare class AiChatMarkdown extends GenesisElement {
646
646
  content: string;
647
647
  /** @internal */
648
648
  renderMarkdown(): string;
649
+ /**
650
+ * Tag the generated elements with shadow parts so an embedding app can style them.
651
+ *
652
+ * Runs AFTER sanitising, deliberately. It only ever *adds* attributes to DOM DOMPurify has
653
+ * already cleared, so it introduces no new sanitiser surface - reversing the order would mean
654
+ * sanitising our own output and inviting the question of whether these attributes survive it.
655
+ *
656
+ * Lists additionally carry a level: a top-level list is `md-ul md-ul-level-1`, a list nested
657
+ * inside it is `md-ul-level-2`, and an `li` takes the level of the list holding it. Chat markdown
658
+ * rarely nests, so this is a safety valve rather than a feature - without it, every list at every
659
+ * depth would answer to one selector and there would be no way to tell them apart afterwards.
660
+ * Depth comes from walking the tree rather than from marked's renderers, which are invoked
661
+ * innermost-first and so cannot count downwards.
662
+ *
663
+ * An author-supplied `part` on inline HTML is overwritten: these parts are the documented
664
+ * contract, and two sources for one attribute would make which-wins depend on the markdown.
665
+ */
666
+ private annotateParts;
649
667
  }
650
668
 
651
669
  /**
@@ -4929,48 +4929,6 @@
4929
4929
  "declarations": [],
4930
4930
  "exports": []
4931
4931
  },
4932
- {
4933
- "kind": "javascript-module",
4934
- "path": "src/provider/ai-provider-switcher.ts",
4935
- "declarations": [
4936
- {
4937
- "kind": "variable",
4938
- "name": "AIProviderSwitcher",
4939
- "description": "The DI token for the (AIProviderSwitcher:interface). Resolves a noop until the app\nregisters one."
4940
- }
4941
- ],
4942
- "exports": [
4943
- {
4944
- "kind": "js",
4945
- "name": "AIProviderSwitcher",
4946
- "declaration": {
4947
- "name": "AIProviderSwitcher",
4948
- "module": "src/provider/ai-provider-switcher.ts"
4949
- }
4950
- }
4951
- ]
4952
- },
4953
- {
4954
- "kind": "javascript-module",
4955
- "path": "src/provider/assistant-app-settings.ts",
4956
- "declarations": [
4957
- {
4958
- "kind": "variable",
4959
- "name": "AssistantAppSettingsProvider",
4960
- "description": "The DI token for the (AssistantAppSettingsProvider:interface). Resolves a noop\nuntil the host registers one."
4961
- }
4962
- ],
4963
- "exports": [
4964
- {
4965
- "kind": "js",
4966
- "name": "AssistantAppSettingsProvider",
4967
- "declaration": {
4968
- "name": "AssistantAppSettingsProvider",
4969
- "module": "src/provider/assistant-app-settings.ts"
4970
- }
4971
- }
4972
- ]
4973
- },
4974
4932
  {
4975
4933
  "kind": "javascript-module",
4976
4934
  "path": "src/state/ai-assistant-slice.ts",
@@ -5661,6 +5619,48 @@
5661
5619
  }
5662
5620
  ]
5663
5621
  },
5622
+ {
5623
+ "kind": "javascript-module",
5624
+ "path": "src/provider/ai-provider-switcher.ts",
5625
+ "declarations": [
5626
+ {
5627
+ "kind": "variable",
5628
+ "name": "AIProviderSwitcher",
5629
+ "description": "The DI token for the (AIProviderSwitcher:interface). Resolves a noop until the app\nregisters one."
5630
+ }
5631
+ ],
5632
+ "exports": [
5633
+ {
5634
+ "kind": "js",
5635
+ "name": "AIProviderSwitcher",
5636
+ "declaration": {
5637
+ "name": "AIProviderSwitcher",
5638
+ "module": "src/provider/ai-provider-switcher.ts"
5639
+ }
5640
+ }
5641
+ ]
5642
+ },
5643
+ {
5644
+ "kind": "javascript-module",
5645
+ "path": "src/provider/assistant-app-settings.ts",
5646
+ "declarations": [
5647
+ {
5648
+ "kind": "variable",
5649
+ "name": "AssistantAppSettingsProvider",
5650
+ "description": "The DI token for the (AssistantAppSettingsProvider:interface). Resolves a noop\nuntil the host registers one."
5651
+ }
5652
+ ],
5653
+ "exports": [
5654
+ {
5655
+ "kind": "js",
5656
+ "name": "AssistantAppSettingsProvider",
5657
+ "declaration": {
5658
+ "name": "AssistantAppSettingsProvider",
5659
+ "module": "src/provider/assistant-app-settings.ts"
5660
+ }
5661
+ }
5662
+ ]
5663
+ },
5664
5664
  {
5665
5665
  "kind": "javascript-module",
5666
5666
  "path": "src/suggestions/chat-suggestions.ts",
@@ -11939,6 +11939,25 @@
11939
11939
  },
11940
11940
  "default": "''",
11941
11941
  "description": "The markdown string to render."
11942
+ },
11943
+ {
11944
+ "kind": "method",
11945
+ "name": "annotateParts",
11946
+ "privacy": "private",
11947
+ "return": {
11948
+ "type": {
11949
+ "text": "string"
11950
+ }
11951
+ },
11952
+ "parameters": [
11953
+ {
11954
+ "name": "html",
11955
+ "type": {
11956
+ "text": "string"
11957
+ }
11958
+ }
11959
+ ],
11960
+ "description": "Tag the generated elements with shadow parts so an embedding app can style them.\n\nRuns AFTER sanitising, deliberately. It only ever *adds* attributes to DOM DOMPurify has\nalready cleared, so it introduces no new sanitiser surface - reversing the order would mean\nsanitising our own output and inviting the question of whether these attributes survive it.\n\nLists additionally carry a level: a top-level list is `md-ul md-ul-level-1`, a list nested\ninside it is `md-ul-level-2`, and an `li` takes the level of the list holding it. Chat markdown\nrarely nests, so this is a safety valve rather than a feature - without it, every list at every\ndepth would answer to one selector and there would be no way to tell them apart afterwards.\nDepth comes from walking the tree rather than from marked's renderers, which are invoked\ninnermost-first and so cannot count downwards.\n\nAn author-supplied `part` on inline HTML is overwritten: these parts are the documented\ncontract, and two sources for one attribute would make which-wins depend on the markdown."
11942
11961
  }
11943
11962
  ],
11944
11963
  "superclass": {
@@ -4,5 +4,23 @@ export declare class AiChatMarkdown extends GenesisElement {
4
4
  content: string;
5
5
  /** @internal */
6
6
  renderMarkdown(): string;
7
+ /**
8
+ * Tag the generated elements with shadow parts so an embedding app can style them.
9
+ *
10
+ * Runs AFTER sanitising, deliberately. It only ever *adds* attributes to DOM DOMPurify has
11
+ * already cleared, so it introduces no new sanitiser surface - reversing the order would mean
12
+ * sanitising our own output and inviting the question of whether these attributes survive it.
13
+ *
14
+ * Lists additionally carry a level: a top-level list is `md-ul md-ul-level-1`, a list nested
15
+ * inside it is `md-ul-level-2`, and an `li` takes the level of the list holding it. Chat markdown
16
+ * rarely nests, so this is a safety valve rather than a feature - without it, every list at every
17
+ * depth would answer to one selector and there would be no way to tell them apart afterwards.
18
+ * Depth comes from walking the tree rather than from marked's renderers, which are invoked
19
+ * innermost-first and so cannot count downwards.
20
+ *
21
+ * An author-supplied `part` on inline HTML is overwritten: these parts are the documented
22
+ * contract, and two sources for one attribute would make which-wins depend on the markdown.
23
+ */
24
+ private annotateParts;
7
25
  }
8
26
  //# sourceMappingURL=chat-markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-markdown/chat-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AAuE7F,qBAOa,cAAe,SAAQ,cAAc;IAChD,qCAAqC;IACzB,OAAO,EAAE,MAAM,CAAM;IAEjC,gBAAgB;IAChB,cAAc;CAKf"}
1
+ {"version":3,"file":"chat-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-markdown/chat-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AAkH7F,qBAOa,cAAe,SAAQ,cAAc;IAChD,qCAAqC;IACzB,OAAO,EAAE,MAAM,CAAM;IAEjC,gBAAgB;IAChB,cAAc;IAMd;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,aAAa;CAsBtB"}
@@ -68,6 +68,47 @@ const styles = css `
68
68
  font-weight: 600;
69
69
  }
70
70
  `;
71
+ /**
72
+ * Element tag -> the shadow part it is exposed as.
73
+ *
74
+ * Markdown is rendered from a string at runtime, so there is no template on which to author
75
+ * `part=` attributes - which is why this component previously exposed nothing at all and an
76
+ * embedding app could only reach it through whichever global design tokens the rules below happen
77
+ * to use. Annotating the generated DOM gives an app ordinary CSS over each element type
78
+ * (`ai-chat-markdown::part(md-li) { ... }`) instead of a per-property token API that would have to
79
+ * grow every time someone wants to style something new.
80
+ *
81
+ * The vocabulary is deliberately a fixed list of element kinds rather than anything derived from
82
+ * the content: it is a public API surface, so it should change only when this map does.
83
+ */
84
+ const MARKDOWN_PARTS = {
85
+ P: 'md-p',
86
+ UL: 'md-ul',
87
+ OL: 'md-ol',
88
+ LI: 'md-li',
89
+ PRE: 'md-pre',
90
+ CODE: 'md-code',
91
+ A: 'md-link',
92
+ BLOCKQUOTE: 'md-quote',
93
+ TABLE: 'md-table',
94
+ THEAD: 'md-thead',
95
+ TBODY: 'md-tbody',
96
+ TR: 'md-tr',
97
+ TH: 'md-th',
98
+ TD: 'md-td',
99
+ HR: 'md-hr',
100
+ IMG: 'md-img',
101
+ STRONG: 'md-strong',
102
+ EM: 'md-em',
103
+ H1: 'md-h1',
104
+ H2: 'md-h2',
105
+ H3: 'md-h3',
106
+ H4: 'md-h4',
107
+ H5: 'md-h5',
108
+ H6: 'md-h6',
109
+ };
110
+ /** Tags whose part is also emitted with a nesting level (see {@link annotateParts}). */
111
+ const DEPTH_TAGGED = new Set(['UL', 'OL', 'LI']);
71
112
  let AiChatMarkdown = class AiChatMarkdown extends GenesisElement {
72
113
  constructor() {
73
114
  super(...arguments);
@@ -79,7 +120,41 @@ let AiChatMarkdown = class AiChatMarkdown extends GenesisElement {
79
120
  if (!this.content)
80
121
  return '';
81
122
  const rawHtml = marked.parse(this.content);
82
- return DOMPurify.sanitize(rawHtml);
123
+ return this.annotateParts(DOMPurify.sanitize(rawHtml));
124
+ }
125
+ /**
126
+ * Tag the generated elements with shadow parts so an embedding app can style them.
127
+ *
128
+ * Runs AFTER sanitising, deliberately. It only ever *adds* attributes to DOM DOMPurify has
129
+ * already cleared, so it introduces no new sanitiser surface - reversing the order would mean
130
+ * sanitising our own output and inviting the question of whether these attributes survive it.
131
+ *
132
+ * Lists additionally carry a level: a top-level list is `md-ul md-ul-level-1`, a list nested
133
+ * inside it is `md-ul-level-2`, and an `li` takes the level of the list holding it. Chat markdown
134
+ * rarely nests, so this is a safety valve rather than a feature - without it, every list at every
135
+ * depth would answer to one selector and there would be no way to tell them apart afterwards.
136
+ * Depth comes from walking the tree rather than from marked's renderers, which are invoked
137
+ * innermost-first and so cannot count downwards.
138
+ *
139
+ * An author-supplied `part` on inline HTML is overwritten: these parts are the documented
140
+ * contract, and two sources for one attribute would make which-wins depend on the markdown.
141
+ */
142
+ annotateParts(html) {
143
+ const template = document.createElement('template');
144
+ template.innerHTML = html;
145
+ const walk = (parent, listDepth) => {
146
+ for (const el of Array.from(parent.children)) {
147
+ const base = MARKDOWN_PARTS[el.tagName];
148
+ // A list opens the next level; everything else sits at its parent list's level.
149
+ const depth = el.tagName === 'UL' || el.tagName === 'OL' ? listDepth + 1 : listDepth;
150
+ if (base) {
151
+ el.setAttribute('part', DEPTH_TAGGED.has(el.tagName) ? `${base} ${base}-level-${depth}` : base);
152
+ }
153
+ walk(el, depth);
154
+ }
155
+ };
156
+ walk(template.content, 0);
157
+ return template.innerHTML;
83
158
  }
84
159
  };
85
160
  __decorate([
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/ai-assistant",
3
3
  "description": "Genesis AI Assistant micro-frontend",
4
- "version": "15.28.1-FUI-0.7",
4
+ "version": "15.28.1-FUI-0.8",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -74,26 +74,26 @@
74
74
  }
75
75
  },
76
76
  "devDependencies": {
77
- "@genesislcap/foundation-testing": "15.28.1-FUI-0.7",
78
- "@genesislcap/genx": "15.28.1-FUI-0.7",
79
- "@genesislcap/rollup-builder": "15.28.1-FUI-0.7",
80
- "@genesislcap/ts-builder": "15.28.1-FUI-0.7",
81
- "@genesislcap/uvu-playwright-builder": "15.28.1-FUI-0.7",
82
- "@genesislcap/vite-builder": "15.28.1-FUI-0.7",
83
- "@genesislcap/webpack-builder": "15.28.1-FUI-0.7",
77
+ "@genesislcap/foundation-testing": "15.28.1-FUI-0.8",
78
+ "@genesislcap/genx": "15.28.1-FUI-0.8",
79
+ "@genesislcap/rollup-builder": "15.28.1-FUI-0.8",
80
+ "@genesislcap/ts-builder": "15.28.1-FUI-0.8",
81
+ "@genesislcap/uvu-playwright-builder": "15.28.1-FUI-0.8",
82
+ "@genesislcap/vite-builder": "15.28.1-FUI-0.8",
83
+ "@genesislcap/webpack-builder": "15.28.1-FUI-0.8",
84
84
  "@types/dompurify": "^3.0.5",
85
85
  "@types/marked": "^5.0.2",
86
86
  "esbuild": "0.25.12"
87
87
  },
88
88
  "dependencies": {
89
- "@genesislcap/foundation-ai": "15.28.1-FUI-0.7",
90
- "@genesislcap/foundation-logger": "15.28.1-FUI-0.7",
91
- "@genesislcap/foundation-notifications": "15.28.1-FUI-0.7",
92
- "@genesislcap/foundation-redux": "15.28.1-FUI-0.7",
93
- "@genesislcap/foundation-ui": "15.28.1-FUI-0.7",
94
- "@genesislcap/foundation-utils": "15.28.1-FUI-0.7",
95
- "@genesislcap/rapid-design-system": "15.28.1-FUI-0.7",
96
- "@genesislcap/web-core": "15.28.1-FUI-0.7",
89
+ "@genesislcap/foundation-ai": "15.28.1-FUI-0.8",
90
+ "@genesislcap/foundation-logger": "15.28.1-FUI-0.8",
91
+ "@genesislcap/foundation-notifications": "15.28.1-FUI-0.8",
92
+ "@genesislcap/foundation-redux": "15.28.1-FUI-0.8",
93
+ "@genesislcap/foundation-ui": "15.28.1-FUI-0.8",
94
+ "@genesislcap/foundation-utils": "15.28.1-FUI-0.8",
95
+ "@genesislcap/rapid-design-system": "15.28.1-FUI-0.8",
96
+ "@genesislcap/web-core": "15.28.1-FUI-0.8",
97
97
  "dompurify": "^3.3.1",
98
98
  "marked": "^17.0.3"
99
99
  },