@threadplane/chat 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/fesm2022/threadplane-chat.mjs +316 -40
- package/fesm2022/threadplane-chat.mjs.map +1 -1
- package/package.json +9 -3
- package/types/threadplane-chat.d.ts +68 -3
package/README.md
CHANGED
|
@@ -173,6 +173,17 @@ Agents can emit surface specs via `buildA2uiActionMessage(...)`. Actions from ca
|
|
|
173
173
|
|
|
174
174
|
The built-in catalog ships via `a2uiBasicCatalog`. Compose a custom catalog with `withViews()` and pass it to the surface.
|
|
175
175
|
|
|
176
|
+
**Icons.** The catalog `Icon` component renders [Material Symbols](https://fonts.google.com/icons) by name (the A2UI canonical icon set — e.g. `check`, `trending_up`, `star`). For glyphs to render, include the Material Symbols Outlined stylesheet in your app's `<head>` (the library does not inject any web font):
|
|
177
|
+
|
|
178
|
+
```html
|
|
179
|
+
<link
|
|
180
|
+
rel="stylesheet"
|
|
181
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Without the font, the icon name falls back to plain text. Icons inherit `currentColor` and size via the `size` prop.
|
|
186
|
+
|
|
176
187
|
### Streaming markdown
|
|
177
188
|
|
|
178
189
|
`<chat-streaming-md>` renders markdown token-by-token as the agent streams. The `cacheplaneMarkdownViews` registry maps each CommonMark node type to an Angular component.
|
|
@@ -196,6 +207,17 @@ Per-instance, bind the registry on `<chat-streaming-md [viewRegistry]="…" />`
|
|
|
196
207
|
|
|
197
208
|
The `renderMarkdown(md, options?)` function produces a parse tree for use outside streaming contexts.
|
|
198
209
|
|
|
210
|
+
#### Math (KaTeX)
|
|
211
|
+
|
|
212
|
+
LaTeX math — inline `$…$` / `\(…\)` and display `$$…$$` / `\[…\]` — renders via [KaTeX](https://katex.org), an **optional** peer dependency loaded lazily only when a message actually contains math (so non-math chats carry zero extra bundle weight). To enable styled math, install `katex` and import its stylesheet once in your app:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// e.g. in your global styles or app bootstrap
|
|
216
|
+
import 'katex/dist/katex.min.css';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Without `katex` installed, or without the stylesheet, math degrades gracefully — the raw `$…$` source is shown rather than breaking. Currency like `$5` is not treated as math.
|
|
220
|
+
|
|
199
221
|
### Theming
|
|
200
222
|
|
|
201
223
|
`<a2ui-surface>` declares ~50 `--a2ui-*` CSS custom properties at `:host` with dark-theme defaults covering color, spacing, typography, shape radius, focus ring, motion, and elevation. Catalog components consume them via `var(--a2ui-*)`.
|
|
@@ -4,6 +4,7 @@ import { NgTemplateOutlet, NgComponentOutlet, KeyValuePipe } from '@angular/comm
|
|
|
4
4
|
import { createPartialMarkdownParser, materialize } from '@cacheplane/partial-markdown';
|
|
5
5
|
import { views, RenderSpecComponent, toRenderRegistry, signalStateStore, withViews, RenderElementComponent, injectRenderHost } from '@threadplane/render';
|
|
6
6
|
export { toRenderRegistry, views, withViews, withoutViews } from '@threadplane/render';
|
|
7
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
7
8
|
import { runLicenseCheck, inferNoncommercial, LICENSE_PUBLIC_KEY } from '@threadplane/licensing';
|
|
8
9
|
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
9
10
|
import { Router, NavigationEnd } from '@angular/router';
|
|
@@ -1794,6 +1795,11 @@ const CHAT_MARKDOWN_STYLES = `
|
|
|
1794
1795
|
}
|
|
1795
1796
|
chat-streaming-md .chat-md-image__icon { font-size: 1em; line-height: 1; }
|
|
1796
1797
|
chat-streaming-md .chat-md-image__alt { font-style: italic; }
|
|
1798
|
+
/* Math (KaTeX). Display math is a centered block that scrolls horizontally
|
|
1799
|
+
on overflow; the raw fallback (KaTeX missing or invalid LaTeX) reads as
|
|
1800
|
+
monospace so the source delimiters look intentional. */
|
|
1801
|
+
chat-streaming-md .chat-md-math--display { display: block; margin: 0.5em 0; overflow-x: auto; }
|
|
1802
|
+
chat-streaming-md .chat-md-math--raw { font-family: var(--ngaf-chat-font-mono, ui-monospace, monospace); }
|
|
1797
1803
|
`;
|
|
1798
1804
|
|
|
1799
1805
|
// libs/chat/src/lib/markdown/markdown-view-registry.ts
|
|
@@ -2143,6 +2149,125 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
2143
2149
|
}]
|
|
2144
2150
|
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }] } });
|
|
2145
2151
|
|
|
2152
|
+
// libs/chat/src/lib/markdown/katex-loader.ts
|
|
2153
|
+
// SPDX-License-Identifier: MIT
|
|
2154
|
+
/**
|
|
2155
|
+
* Lazy KaTeX integration for the markdown math view. Mirrors the lazy `marked`
|
|
2156
|
+
* loader in ../streaming/markdown-render.ts: `katex` is an optional peer
|
|
2157
|
+
* dependency, dynamically imported on module load, so chats that never contain
|
|
2158
|
+
* math pay zero base-bundle cost. `renderMath` returns the KaTeX HTML string,
|
|
2159
|
+
* or null when KaTeX is unavailable or the LaTeX is invalid — the view then
|
|
2160
|
+
* renders the raw `$…$` source instead.
|
|
2161
|
+
*
|
|
2162
|
+
* CSS: the KaTeX stylesheet (and its woff2 fonts) is the CONSUMER's
|
|
2163
|
+
* responsibility — this lib injects nothing at runtime (no third-party CDN
|
|
2164
|
+
* fetch). Apps import `katex/dist/katex.min.css` themselves; math still renders
|
|
2165
|
+
* without it, just unstyled.
|
|
2166
|
+
*/
|
|
2167
|
+
let katexRender = null;
|
|
2168
|
+
/**
|
|
2169
|
+
* Flips to true once KaTeX has loaded. The math view reads it as a signal
|
|
2170
|
+
* dependency so any math that fell back to raw source while KaTeX was still
|
|
2171
|
+
* loading re-renders once it becomes available.
|
|
2172
|
+
*/
|
|
2173
|
+
const katexReady = signal(false, ...(ngDevMode ? [{ debugName: "katexReady" }] : []));
|
|
2174
|
+
/** Resolves once the KaTeX import has settled (success or failure). Tests await this. */
|
|
2175
|
+
const katexLoaded = import('katex')
|
|
2176
|
+
.then((m) => {
|
|
2177
|
+
const katex = (m.default ?? m);
|
|
2178
|
+
katexRender = (latex, displayMode) =>
|
|
2179
|
+
// throwOnError:true so invalid LaTeX throws → we catch → raw-source
|
|
2180
|
+
// fallback, instead of KaTeX emitting its own red error markup.
|
|
2181
|
+
katex.renderToString(latex, { displayMode, throwOnError: true });
|
|
2182
|
+
katexReady.set(true);
|
|
2183
|
+
})
|
|
2184
|
+
.catch(() => {
|
|
2185
|
+
katexRender = null;
|
|
2186
|
+
});
|
|
2187
|
+
/**
|
|
2188
|
+
* Render LaTeX to a KaTeX HTML string, or null if KaTeX is unavailable or the
|
|
2189
|
+
* input throws (invalid LaTeX) — the caller then renders the raw `$…$` source.
|
|
2190
|
+
*/
|
|
2191
|
+
function renderMath(latex, displayMode) {
|
|
2192
|
+
if (!katexRender)
|
|
2193
|
+
return null;
|
|
2194
|
+
try {
|
|
2195
|
+
return katexRender(latex, displayMode);
|
|
2196
|
+
}
|
|
2197
|
+
catch {
|
|
2198
|
+
return null;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
// libs/chat/src/lib/markdown/views/markdown-math.component.ts
|
|
2203
|
+
// SPDX-License-Identifier: MIT
|
|
2204
|
+
/** Opener/closer source text per delimiter, used for the raw fallback. */
|
|
2205
|
+
const DELIMITERS = {
|
|
2206
|
+
$: ['$', '$'],
|
|
2207
|
+
'$$': ['$$', '$$'],
|
|
2208
|
+
'\\(\\)': ['\\(', '\\)'],
|
|
2209
|
+
'\\[\\]': ['\\[', '\\]'],
|
|
2210
|
+
};
|
|
2211
|
+
/**
|
|
2212
|
+
* Renders a `math-inline` / `math-display` markdown node as KaTeX. KaTeX is
|
|
2213
|
+
* lazy-loaded (see katex-loader); until it resolves, or if the LaTeX is
|
|
2214
|
+
* invalid, the raw `$…$` source is shown — never blank, never a crash.
|
|
2215
|
+
*/
|
|
2216
|
+
class MarkdownMathComponent {
|
|
2217
|
+
node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
|
|
2218
|
+
sanitizer = inject(DomSanitizer);
|
|
2219
|
+
display = computed(() => this.node().type === 'math-display', ...(ngDevMode ? [{ debugName: "display" }] : []));
|
|
2220
|
+
raw = computed(() => {
|
|
2221
|
+
const n = this.node();
|
|
2222
|
+
const [open, close] = DELIMITERS[n.delimiter];
|
|
2223
|
+
return `${open}${n.text}${close}`;
|
|
2224
|
+
}, ...(ngDevMode ? [{ debugName: "raw" }] : []));
|
|
2225
|
+
html = computed(() => {
|
|
2226
|
+
katexReady(); // re-render once KaTeX finishes loading
|
|
2227
|
+
const n = this.node();
|
|
2228
|
+
const out = renderMath(n.text, n.type === 'math-display');
|
|
2229
|
+
if (out == null)
|
|
2230
|
+
return null;
|
|
2231
|
+
// Trust KaTeX output directly: with KaTeX's default `trust:false` it emits
|
|
2232
|
+
// only safe presentational markup (no scripts/event handlers/links), and
|
|
2233
|
+
// Angular's HTML sanitizer would strip the inline styles KaTeX layout
|
|
2234
|
+
// depends on.
|
|
2235
|
+
return this.sanitizer.bypassSecurityTrustHtml(out);
|
|
2236
|
+
}, ...(ngDevMode ? [{ debugName: "html" }] : []));
|
|
2237
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: MarkdownMathComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2238
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: MarkdownMathComponent, isStandalone: true, selector: "chat-md-math", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
2239
|
+
@if (html(); as h) {
|
|
2240
|
+
<span
|
|
2241
|
+
class="chat-md-math"
|
|
2242
|
+
[class.chat-md-math--display]="display()"
|
|
2243
|
+
[innerHTML]="h"
|
|
2244
|
+
></span>
|
|
2245
|
+
} @else {
|
|
2246
|
+
<span class="chat-md-math chat-md-math--raw">{{ raw() }}</span>
|
|
2247
|
+
}
|
|
2248
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2249
|
+
}
|
|
2250
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: MarkdownMathComponent, decorators: [{
|
|
2251
|
+
type: Component,
|
|
2252
|
+
args: [{
|
|
2253
|
+
selector: 'chat-md-math',
|
|
2254
|
+
standalone: true,
|
|
2255
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2256
|
+
encapsulation: ViewEncapsulation.None,
|
|
2257
|
+
template: `
|
|
2258
|
+
@if (html(); as h) {
|
|
2259
|
+
<span
|
|
2260
|
+
class="chat-md-math"
|
|
2261
|
+
[class.chat-md-math--display]="display()"
|
|
2262
|
+
[innerHTML]="h"
|
|
2263
|
+
></span>
|
|
2264
|
+
} @else {
|
|
2265
|
+
<span class="chat-md-math chat-md-math--raw">{{ raw() }}</span>
|
|
2266
|
+
}
|
|
2267
|
+
`,
|
|
2268
|
+
}]
|
|
2269
|
+
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }] } });
|
|
2270
|
+
|
|
2146
2271
|
// libs/chat/src/lib/markdown/views/markdown-link.component.ts
|
|
2147
2272
|
// SPDX-License-Identifier: MIT
|
|
2148
2273
|
class MarkdownLinkComponent {
|
|
@@ -2484,6 +2609,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
2484
2609
|
}]
|
|
2485
2610
|
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }] } });
|
|
2486
2611
|
|
|
2612
|
+
// libs/chat/src/lib/markdown/views/markdown-html.component.ts
|
|
2613
|
+
// SPDX-License-Identifier: MIT
|
|
2614
|
+
/**
|
|
2615
|
+
* Renders a `html-block` / `html-inline` markdown node as **escaped text** —
|
|
2616
|
+
* the raw HTML is shown literally (Angular interpolation auto-escapes it),
|
|
2617
|
+
* never injected as live markup. This preserves the pre-0.4 behavior where
|
|
2618
|
+
* raw HTML was plain text, and keeps the chat XSS-safe: model-emitted
|
|
2619
|
+
* `<script>`, `<iframe>`, etc. are displayed as text and never executed.
|
|
2620
|
+
*/
|
|
2621
|
+
class MarkdownHtmlComponent {
|
|
2622
|
+
node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
|
|
2623
|
+
raw = computed(() => this.node().raw, ...(ngDevMode ? [{ debugName: "raw" }] : []));
|
|
2624
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: MarkdownHtmlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2625
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: MarkdownHtmlComponent, isStandalone: true, selector: "chat-md-html", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `{{ raw() }}`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2626
|
+
}
|
|
2627
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: MarkdownHtmlComponent, decorators: [{
|
|
2628
|
+
type: Component,
|
|
2629
|
+
args: [{
|
|
2630
|
+
selector: 'chat-md-html',
|
|
2631
|
+
standalone: true,
|
|
2632
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2633
|
+
template: `{{ raw() }}`,
|
|
2634
|
+
}]
|
|
2635
|
+
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }] } });
|
|
2636
|
+
|
|
2487
2637
|
// libs/chat/src/lib/markdown/cacheplane-markdown-views.ts
|
|
2488
2638
|
// SPDX-License-Identifier: MIT
|
|
2489
2639
|
/**
|
|
@@ -2507,6 +2657,8 @@ const cacheplaneMarkdownViews = views({
|
|
|
2507
2657
|
'strong': MarkdownStrongComponent,
|
|
2508
2658
|
'strikethrough': MarkdownStrikethroughComponent,
|
|
2509
2659
|
'inline-code': MarkdownInlineCodeComponent,
|
|
2660
|
+
'math-inline': MarkdownMathComponent,
|
|
2661
|
+
'math-display': MarkdownMathComponent,
|
|
2510
2662
|
'link': MarkdownLinkComponent,
|
|
2511
2663
|
'autolink': MarkdownAutolinkComponent,
|
|
2512
2664
|
'image': MarkdownImageComponent,
|
|
@@ -2516,6 +2668,10 @@ const cacheplaneMarkdownViews = views({
|
|
|
2516
2668
|
'table': MarkdownTableComponent,
|
|
2517
2669
|
'table-row': MarkdownTableRowComponent,
|
|
2518
2670
|
'table-cell': MarkdownTableCellComponent,
|
|
2671
|
+
// Raw HTML (added by partial-markdown 0.4.x) renders as escaped literal text
|
|
2672
|
+
// — XSS-safe, and matches the pre-0.4 behavior where HTML was plain text.
|
|
2673
|
+
'html-block': MarkdownHtmlComponent,
|
|
2674
|
+
'html-inline': MarkdownHtmlComponent,
|
|
2519
2675
|
});
|
|
2520
2676
|
|
|
2521
2677
|
// libs/chat/src/lib/streaming/streaming-markdown.component.ts
|
|
@@ -2609,7 +2765,7 @@ class ChatStreamingMdComponent {
|
|
|
2609
2765
|
@if (root(); as r) {
|
|
2610
2766
|
<chat-md-children [parent]="r" />
|
|
2611
2767
|
}
|
|
2612
|
-
`, isInline: true, styles: ["chat-streaming-md{display:block;color:var(--ngaf-chat-text);line-height:var(--ngaf-chat-line-height)}chat-streaming-md h1,chat-streaming-md h2,chat-streaming-md h3,chat-streaming-md h4,chat-streaming-md h5,chat-streaming-md h6{font-weight:600;line-height:1.25;margin:1.25rem 0 .75rem}chat-streaming-md h1:first-child,chat-streaming-md h2:first-child,chat-streaming-md h3:first-child,chat-streaming-md h4:first-child,chat-streaming-md h5:first-child,chat-streaming-md h6:first-child{margin-top:0}chat-streaming-md h1{font-size:1.5em;font-weight:700}chat-streaming-md h2{font-size:1.25em}chat-streaming-md h3{font-size:1.1em}chat-streaming-md h4{font-size:1em}chat-streaming-md h5,chat-streaming-md h6{font-size:.95em;color:var(--ngaf-chat-text-muted)}chat-streaming-md p{margin:0 0 .75rem;line-height:1.6;font-size:var(--ngaf-chat-font-size)}chat-streaming-md p:last-child{margin-bottom:0}chat-streaming-md strong,chat-streaming-md b{font-weight:700}chat-streaming-md em,chat-streaming-md i{font-style:italic}chat-streaming-md del,chat-streaming-md s{text-decoration:line-through;color:var(--ngaf-chat-text-muted)}chat-streaming-md mark{background:var(--ngaf-chat-surface-alt);padding:0 2px;border-radius:2px}chat-streaming-md sub{font-size:.75em;vertical-align:sub}chat-streaming-md sup{font-size:.75em;vertical-align:super}chat-streaming-md a{color:var(--ngaf-chat-primary);text-decoration:underline;text-underline-offset:2px}chat-streaming-md a:hover{text-decoration-thickness:2px}chat-streaming-md ul,chat-streaming-md ol{margin:0 0 .75rem;padding-left:1.5rem}chat-streaming-md ul{list-style:disc outside}chat-streaming-md ol{list-style:decimal outside}chat-streaming-md ul ul{list-style:circle outside}chat-streaming-md ul ul ul{list-style:square outside}chat-streaming-md li{margin:.2rem 0}chat-streaming-md li::marker{color:var(--ngaf-chat-text-muted)}chat-streaming-md li>p{margin:0 0 .25rem}chat-streaming-md li>ul,chat-streaming-md li>ol{margin:.25rem 0 0}chat-streaming-md li:has(>input[type=checkbox]){list-style:none;margin-left:-1.25rem}chat-streaming-md li>input[type=checkbox]{margin-right:.5rem;vertical-align:middle}chat-streaming-md code{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:1px 5px;border-radius:4px;font-family:var(--ngaf-chat-font-mono);font-size:.9em}chat-streaming-md pre{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:12px 14px;border-radius:var(--ngaf-chat-radius-card);overflow-x:auto;font-family:var(--ngaf-chat-font-mono);font-size:var(--ngaf-chat-font-size-sm);line-height:1.5;margin:0 0 .75rem}chat-streaming-md pre code{background:transparent;padding:0;border-radius:0;font-size:inherit}chat-streaming-md blockquote{border-left:3px solid var(--ngaf-chat-separator);padding:.25rem 0 .25rem 12px;margin:0 0 .75rem;color:var(--ngaf-chat-text-muted)}chat-streaming-md blockquote>:last-child{margin-bottom:0}chat-streaming-md hr{border:none;border-top:1px solid var(--ngaf-chat-separator);margin:1rem 0}chat-streaming-md table{border-collapse:collapse;margin:0 0 .75rem;width:100%;font-size:.95em}chat-streaming-md thead{background:var(--ngaf-chat-surface-alt)}chat-streaming-md th,chat-streaming-md td{border:1px solid var(--ngaf-chat-separator);padding:6px 10px;text-align:left;vertical-align:top}chat-streaming-md th{font-weight:600}chat-streaming-md chat-md-table{display:block;overflow-x:auto;max-width:100%;margin:0 0 .75rem}chat-streaming-md chat-md-table-row{display:contents}chat-streaming-md chat-md-table-cell{display:contents}chat-streaming-md chat-md-table>table{margin:0}chat-streaming-md li.chat-md-list-item--task{list-style:none;margin-left:-1.25rem;display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem}chat-streaming-md li.chat-md-list-item--task>input[type=checkbox]{margin:0;flex:0 0 auto;transform:translateY(2px)}chat-streaming-md li.chat-md-list-item--task>chat-md-children{flex:1 1 auto;min-width:0}chat-streaming-md li.chat-md-list-item--task>chat-md-children>chat-md-paragraph:first-child>p{margin:0}chat-streaming-md img{max-width:100%;height:auto;border-radius:6px}chat-streaming-md .chat-md-image--broken{display:inline-flex;align-items:center;gap:.4rem;padding:.25rem .5rem;background:var(--ngaf-chat-surface-alt);border:1px dashed var(--ngaf-chat-separator);border-radius:6px;font-size:.9em;color:var(--ngaf-chat-text-muted, currentColor);opacity:.85}chat-streaming-md .chat-md-image__icon{font-size:1em;line-height:1}chat-streaming-md .chat-md-image__alt{font-style:italic}\n"], dependencies: [{ kind: "component", type: MarkdownChildrenComponent, selector: "chat-md-children", inputs: ["parent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2768
|
+
`, isInline: true, styles: ["chat-streaming-md{display:block;color:var(--ngaf-chat-text);line-height:var(--ngaf-chat-line-height)}chat-streaming-md h1,chat-streaming-md h2,chat-streaming-md h3,chat-streaming-md h4,chat-streaming-md h5,chat-streaming-md h6{font-weight:600;line-height:1.25;margin:1.25rem 0 .75rem}chat-streaming-md h1:first-child,chat-streaming-md h2:first-child,chat-streaming-md h3:first-child,chat-streaming-md h4:first-child,chat-streaming-md h5:first-child,chat-streaming-md h6:first-child{margin-top:0}chat-streaming-md h1{font-size:1.5em;font-weight:700}chat-streaming-md h2{font-size:1.25em}chat-streaming-md h3{font-size:1.1em}chat-streaming-md h4{font-size:1em}chat-streaming-md h5,chat-streaming-md h6{font-size:.95em;color:var(--ngaf-chat-text-muted)}chat-streaming-md p{margin:0 0 .75rem;line-height:1.6;font-size:var(--ngaf-chat-font-size)}chat-streaming-md p:last-child{margin-bottom:0}chat-streaming-md strong,chat-streaming-md b{font-weight:700}chat-streaming-md em,chat-streaming-md i{font-style:italic}chat-streaming-md del,chat-streaming-md s{text-decoration:line-through;color:var(--ngaf-chat-text-muted)}chat-streaming-md mark{background:var(--ngaf-chat-surface-alt);padding:0 2px;border-radius:2px}chat-streaming-md sub{font-size:.75em;vertical-align:sub}chat-streaming-md sup{font-size:.75em;vertical-align:super}chat-streaming-md a{color:var(--ngaf-chat-primary);text-decoration:underline;text-underline-offset:2px}chat-streaming-md a:hover{text-decoration-thickness:2px}chat-streaming-md ul,chat-streaming-md ol{margin:0 0 .75rem;padding-left:1.5rem}chat-streaming-md ul{list-style:disc outside}chat-streaming-md ol{list-style:decimal outside}chat-streaming-md ul ul{list-style:circle outside}chat-streaming-md ul ul ul{list-style:square outside}chat-streaming-md li{margin:.2rem 0}chat-streaming-md li::marker{color:var(--ngaf-chat-text-muted)}chat-streaming-md li>p{margin:0 0 .25rem}chat-streaming-md li>ul,chat-streaming-md li>ol{margin:.25rem 0 0}chat-streaming-md li:has(>input[type=checkbox]){list-style:none;margin-left:-1.25rem}chat-streaming-md li>input[type=checkbox]{margin-right:.5rem;vertical-align:middle}chat-streaming-md code{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:1px 5px;border-radius:4px;font-family:var(--ngaf-chat-font-mono);font-size:.9em}chat-streaming-md pre{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:12px 14px;border-radius:var(--ngaf-chat-radius-card);overflow-x:auto;font-family:var(--ngaf-chat-font-mono);font-size:var(--ngaf-chat-font-size-sm);line-height:1.5;margin:0 0 .75rem}chat-streaming-md pre code{background:transparent;padding:0;border-radius:0;font-size:inherit}chat-streaming-md blockquote{border-left:3px solid var(--ngaf-chat-separator);padding:.25rem 0 .25rem 12px;margin:0 0 .75rem;color:var(--ngaf-chat-text-muted)}chat-streaming-md blockquote>:last-child{margin-bottom:0}chat-streaming-md hr{border:none;border-top:1px solid var(--ngaf-chat-separator);margin:1rem 0}chat-streaming-md table{border-collapse:collapse;margin:0 0 .75rem;width:100%;font-size:.95em}chat-streaming-md thead{background:var(--ngaf-chat-surface-alt)}chat-streaming-md th,chat-streaming-md td{border:1px solid var(--ngaf-chat-separator);padding:6px 10px;text-align:left;vertical-align:top}chat-streaming-md th{font-weight:600}chat-streaming-md chat-md-table{display:block;overflow-x:auto;max-width:100%;margin:0 0 .75rem}chat-streaming-md chat-md-table-row{display:contents}chat-streaming-md chat-md-table-cell{display:contents}chat-streaming-md chat-md-table>table{margin:0}chat-streaming-md li.chat-md-list-item--task{list-style:none;margin-left:-1.25rem;display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem}chat-streaming-md li.chat-md-list-item--task>input[type=checkbox]{margin:0;flex:0 0 auto;transform:translateY(2px)}chat-streaming-md li.chat-md-list-item--task>chat-md-children{flex:1 1 auto;min-width:0}chat-streaming-md li.chat-md-list-item--task>chat-md-children>chat-md-paragraph:first-child>p{margin:0}chat-streaming-md img{max-width:100%;height:auto;border-radius:6px}chat-streaming-md .chat-md-image--broken{display:inline-flex;align-items:center;gap:.4rem;padding:.25rem .5rem;background:var(--ngaf-chat-surface-alt);border:1px dashed var(--ngaf-chat-separator);border-radius:6px;font-size:.9em;color:var(--ngaf-chat-text-muted, currentColor);opacity:.85}chat-streaming-md .chat-md-image__icon{font-size:1em;line-height:1}chat-streaming-md .chat-md-image__alt{font-style:italic}chat-streaming-md .chat-md-math--display{display:block;margin:.5em 0;overflow-x:auto}chat-streaming-md .chat-md-math--raw{font-family:var(--ngaf-chat-font-mono, ui-monospace, monospace)}\n"], dependencies: [{ kind: "component", type: MarkdownChildrenComponent, selector: "chat-md-children", inputs: ["parent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2613
2769
|
}
|
|
2614
2770
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatStreamingMdComponent, decorators: [{
|
|
2615
2771
|
type: Component,
|
|
@@ -2623,7 +2779,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
2623
2779
|
useFactory: (host) => host.resolvedRegistry(),
|
|
2624
2780
|
deps: [ChatStreamingMdComponent],
|
|
2625
2781
|
},
|
|
2626
|
-
], styles: ["chat-streaming-md{display:block;color:var(--ngaf-chat-text);line-height:var(--ngaf-chat-line-height)}chat-streaming-md h1,chat-streaming-md h2,chat-streaming-md h3,chat-streaming-md h4,chat-streaming-md h5,chat-streaming-md h6{font-weight:600;line-height:1.25;margin:1.25rem 0 .75rem}chat-streaming-md h1:first-child,chat-streaming-md h2:first-child,chat-streaming-md h3:first-child,chat-streaming-md h4:first-child,chat-streaming-md h5:first-child,chat-streaming-md h6:first-child{margin-top:0}chat-streaming-md h1{font-size:1.5em;font-weight:700}chat-streaming-md h2{font-size:1.25em}chat-streaming-md h3{font-size:1.1em}chat-streaming-md h4{font-size:1em}chat-streaming-md h5,chat-streaming-md h6{font-size:.95em;color:var(--ngaf-chat-text-muted)}chat-streaming-md p{margin:0 0 .75rem;line-height:1.6;font-size:var(--ngaf-chat-font-size)}chat-streaming-md p:last-child{margin-bottom:0}chat-streaming-md strong,chat-streaming-md b{font-weight:700}chat-streaming-md em,chat-streaming-md i{font-style:italic}chat-streaming-md del,chat-streaming-md s{text-decoration:line-through;color:var(--ngaf-chat-text-muted)}chat-streaming-md mark{background:var(--ngaf-chat-surface-alt);padding:0 2px;border-radius:2px}chat-streaming-md sub{font-size:.75em;vertical-align:sub}chat-streaming-md sup{font-size:.75em;vertical-align:super}chat-streaming-md a{color:var(--ngaf-chat-primary);text-decoration:underline;text-underline-offset:2px}chat-streaming-md a:hover{text-decoration-thickness:2px}chat-streaming-md ul,chat-streaming-md ol{margin:0 0 .75rem;padding-left:1.5rem}chat-streaming-md ul{list-style:disc outside}chat-streaming-md ol{list-style:decimal outside}chat-streaming-md ul ul{list-style:circle outside}chat-streaming-md ul ul ul{list-style:square outside}chat-streaming-md li{margin:.2rem 0}chat-streaming-md li::marker{color:var(--ngaf-chat-text-muted)}chat-streaming-md li>p{margin:0 0 .25rem}chat-streaming-md li>ul,chat-streaming-md li>ol{margin:.25rem 0 0}chat-streaming-md li:has(>input[type=checkbox]){list-style:none;margin-left:-1.25rem}chat-streaming-md li>input[type=checkbox]{margin-right:.5rem;vertical-align:middle}chat-streaming-md code{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:1px 5px;border-radius:4px;font-family:var(--ngaf-chat-font-mono);font-size:.9em}chat-streaming-md pre{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:12px 14px;border-radius:var(--ngaf-chat-radius-card);overflow-x:auto;font-family:var(--ngaf-chat-font-mono);font-size:var(--ngaf-chat-font-size-sm);line-height:1.5;margin:0 0 .75rem}chat-streaming-md pre code{background:transparent;padding:0;border-radius:0;font-size:inherit}chat-streaming-md blockquote{border-left:3px solid var(--ngaf-chat-separator);padding:.25rem 0 .25rem 12px;margin:0 0 .75rem;color:var(--ngaf-chat-text-muted)}chat-streaming-md blockquote>:last-child{margin-bottom:0}chat-streaming-md hr{border:none;border-top:1px solid var(--ngaf-chat-separator);margin:1rem 0}chat-streaming-md table{border-collapse:collapse;margin:0 0 .75rem;width:100%;font-size:.95em}chat-streaming-md thead{background:var(--ngaf-chat-surface-alt)}chat-streaming-md th,chat-streaming-md td{border:1px solid var(--ngaf-chat-separator);padding:6px 10px;text-align:left;vertical-align:top}chat-streaming-md th{font-weight:600}chat-streaming-md chat-md-table{display:block;overflow-x:auto;max-width:100%;margin:0 0 .75rem}chat-streaming-md chat-md-table-row{display:contents}chat-streaming-md chat-md-table-cell{display:contents}chat-streaming-md chat-md-table>table{margin:0}chat-streaming-md li.chat-md-list-item--task{list-style:none;margin-left:-1.25rem;display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem}chat-streaming-md li.chat-md-list-item--task>input[type=checkbox]{margin:0;flex:0 0 auto;transform:translateY(2px)}chat-streaming-md li.chat-md-list-item--task>chat-md-children{flex:1 1 auto;min-width:0}chat-streaming-md li.chat-md-list-item--task>chat-md-children>chat-md-paragraph:first-child>p{margin:0}chat-streaming-md img{max-width:100%;height:auto;border-radius:6px}chat-streaming-md .chat-md-image--broken{display:inline-flex;align-items:center;gap:.4rem;padding:.25rem .5rem;background:var(--ngaf-chat-surface-alt);border:1px dashed var(--ngaf-chat-separator);border-radius:6px;font-size:.9em;color:var(--ngaf-chat-text-muted, currentColor);opacity:.85}chat-streaming-md .chat-md-image__icon{font-size:1em;line-height:1}chat-streaming-md .chat-md-image__alt{font-style:italic}\n"] }]
|
|
2782
|
+
], styles: ["chat-streaming-md{display:block;color:var(--ngaf-chat-text);line-height:var(--ngaf-chat-line-height)}chat-streaming-md h1,chat-streaming-md h2,chat-streaming-md h3,chat-streaming-md h4,chat-streaming-md h5,chat-streaming-md h6{font-weight:600;line-height:1.25;margin:1.25rem 0 .75rem}chat-streaming-md h1:first-child,chat-streaming-md h2:first-child,chat-streaming-md h3:first-child,chat-streaming-md h4:first-child,chat-streaming-md h5:first-child,chat-streaming-md h6:first-child{margin-top:0}chat-streaming-md h1{font-size:1.5em;font-weight:700}chat-streaming-md h2{font-size:1.25em}chat-streaming-md h3{font-size:1.1em}chat-streaming-md h4{font-size:1em}chat-streaming-md h5,chat-streaming-md h6{font-size:.95em;color:var(--ngaf-chat-text-muted)}chat-streaming-md p{margin:0 0 .75rem;line-height:1.6;font-size:var(--ngaf-chat-font-size)}chat-streaming-md p:last-child{margin-bottom:0}chat-streaming-md strong,chat-streaming-md b{font-weight:700}chat-streaming-md em,chat-streaming-md i{font-style:italic}chat-streaming-md del,chat-streaming-md s{text-decoration:line-through;color:var(--ngaf-chat-text-muted)}chat-streaming-md mark{background:var(--ngaf-chat-surface-alt);padding:0 2px;border-radius:2px}chat-streaming-md sub{font-size:.75em;vertical-align:sub}chat-streaming-md sup{font-size:.75em;vertical-align:super}chat-streaming-md a{color:var(--ngaf-chat-primary);text-decoration:underline;text-underline-offset:2px}chat-streaming-md a:hover{text-decoration-thickness:2px}chat-streaming-md ul,chat-streaming-md ol{margin:0 0 .75rem;padding-left:1.5rem}chat-streaming-md ul{list-style:disc outside}chat-streaming-md ol{list-style:decimal outside}chat-streaming-md ul ul{list-style:circle outside}chat-streaming-md ul ul ul{list-style:square outside}chat-streaming-md li{margin:.2rem 0}chat-streaming-md li::marker{color:var(--ngaf-chat-text-muted)}chat-streaming-md li>p{margin:0 0 .25rem}chat-streaming-md li>ul,chat-streaming-md li>ol{margin:.25rem 0 0}chat-streaming-md li:has(>input[type=checkbox]){list-style:none;margin-left:-1.25rem}chat-streaming-md li>input[type=checkbox]{margin-right:.5rem;vertical-align:middle}chat-streaming-md code{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:1px 5px;border-radius:4px;font-family:var(--ngaf-chat-font-mono);font-size:.9em}chat-streaming-md pre{background:var(--ngaf-chat-surface-alt);color:var(--ngaf-chat-text);padding:12px 14px;border-radius:var(--ngaf-chat-radius-card);overflow-x:auto;font-family:var(--ngaf-chat-font-mono);font-size:var(--ngaf-chat-font-size-sm);line-height:1.5;margin:0 0 .75rem}chat-streaming-md pre code{background:transparent;padding:0;border-radius:0;font-size:inherit}chat-streaming-md blockquote{border-left:3px solid var(--ngaf-chat-separator);padding:.25rem 0 .25rem 12px;margin:0 0 .75rem;color:var(--ngaf-chat-text-muted)}chat-streaming-md blockquote>:last-child{margin-bottom:0}chat-streaming-md hr{border:none;border-top:1px solid var(--ngaf-chat-separator);margin:1rem 0}chat-streaming-md table{border-collapse:collapse;margin:0 0 .75rem;width:100%;font-size:.95em}chat-streaming-md thead{background:var(--ngaf-chat-surface-alt)}chat-streaming-md th,chat-streaming-md td{border:1px solid var(--ngaf-chat-separator);padding:6px 10px;text-align:left;vertical-align:top}chat-streaming-md th{font-weight:600}chat-streaming-md chat-md-table{display:block;overflow-x:auto;max-width:100%;margin:0 0 .75rem}chat-streaming-md chat-md-table-row{display:contents}chat-streaming-md chat-md-table-cell{display:contents}chat-streaming-md chat-md-table>table{margin:0}chat-streaming-md li.chat-md-list-item--task{list-style:none;margin-left:-1.25rem;display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem}chat-streaming-md li.chat-md-list-item--task>input[type=checkbox]{margin:0;flex:0 0 auto;transform:translateY(2px)}chat-streaming-md li.chat-md-list-item--task>chat-md-children{flex:1 1 auto;min-width:0}chat-streaming-md li.chat-md-list-item--task>chat-md-children>chat-md-paragraph:first-child>p{margin:0}chat-streaming-md img{max-width:100%;height:auto;border-radius:6px}chat-streaming-md .chat-md-image--broken{display:inline-flex;align-items:center;gap:.4rem;padding:.25rem .5rem;background:var(--ngaf-chat-surface-alt);border:1px dashed var(--ngaf-chat-separator);border-radius:6px;font-size:.9em;color:var(--ngaf-chat-text-muted, currentColor);opacity:.85}chat-streaming-md .chat-md-image__icon{font-size:1em;line-height:1}chat-streaming-md .chat-md-image__alt{font-style:italic}chat-streaming-md .chat-md-math--display{display:block;margin:.5em 0;overflow-x:auto}chat-streaming-md .chat-md-math--raw{font-family:var(--ngaf-chat-font-mono, ui-monospace, monospace)}\n"] }]
|
|
2627
2783
|
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], viewRegistry: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewRegistry", required: false }] }] } });
|
|
2628
2784
|
|
|
2629
2785
|
// SPDX-License-Identifier: MIT
|
|
@@ -4108,6 +4264,19 @@ const CHAT_ERROR_STYLES = `
|
|
|
4108
4264
|
|
|
4109
4265
|
// libs/chat/src/lib/primitives/chat-error/chat-error.component.ts
|
|
4110
4266
|
// SPDX-License-Identifier: MIT
|
|
4267
|
+
/**
|
|
4268
|
+
* Coerce an unknown error value into a human-readable message string — reads
|
|
4269
|
+
* `.message` from `Error`s, returns strings as-is, and `String()`-casts the
|
|
4270
|
+
* rest. Useful when rendering an agent's `error` outside the built-in
|
|
4271
|
+
* `chat-error` component.
|
|
4272
|
+
*
|
|
4273
|
+
* @param error Any caught/agent error value.
|
|
4274
|
+
* @returns The message text, or `null` when `error` is nullish.
|
|
4275
|
+
* @example
|
|
4276
|
+
* ```ts
|
|
4277
|
+
* const msg = extractErrorMessage(agent.error());
|
|
4278
|
+
* ```
|
|
4279
|
+
*/
|
|
4111
4280
|
function extractErrorMessage(error) {
|
|
4112
4281
|
if (!error)
|
|
4113
4282
|
return null;
|
|
@@ -8791,6 +8960,62 @@ class ChatComponent {
|
|
|
8791
8960
|
const text = typeof message.content === 'string' ? message.content : '';
|
|
8792
8961
|
return text.length === 0;
|
|
8793
8962
|
}
|
|
8963
|
+
/** The nearest preceding assistant message (skipping hidden tool messages), or undefined. */
|
|
8964
|
+
prevAssistant(msgs, index) {
|
|
8965
|
+
for (let j = index - 1; j >= 0; j--) {
|
|
8966
|
+
if (msgs[j].role === 'tool')
|
|
8967
|
+
continue;
|
|
8968
|
+
return msgs[j].role === 'assistant' ? msgs[j] : undefined;
|
|
8969
|
+
}
|
|
8970
|
+
return undefined;
|
|
8971
|
+
}
|
|
8972
|
+
/**
|
|
8973
|
+
* True when message[index] starts a reasoning RUN — a maximal sequence of
|
|
8974
|
+
* consecutive assistant reasoning steps separated only by (hidden) tool
|
|
8975
|
+
* messages. The merged reasoning pill renders once, here.
|
|
8976
|
+
*/
|
|
8977
|
+
reasoningRunStart(index) {
|
|
8978
|
+
const msgs = this.agent().messages();
|
|
8979
|
+
if (!msgs[index]?.reasoning)
|
|
8980
|
+
return false;
|
|
8981
|
+
return !this.prevAssistant(msgs, index)?.reasoning;
|
|
8982
|
+
}
|
|
8983
|
+
/**
|
|
8984
|
+
* Aggregate the reasoning RUN starting at `index`: joins each step's
|
|
8985
|
+
* reasoning, sums durations, counts steps, and computes the streaming flag
|
|
8986
|
+
* and the merged label when N > 1 ("Thought for {total} · {N} steps", or
|
|
8987
|
+
* just "{N} steps" when no step reported timing).
|
|
8988
|
+
*/
|
|
8989
|
+
reasoningRun(index) {
|
|
8990
|
+
const msgs = this.agent().messages();
|
|
8991
|
+
const steps = [];
|
|
8992
|
+
for (let j = index; j < msgs.length; j++) {
|
|
8993
|
+
const m = msgs[j];
|
|
8994
|
+
if (m.role === 'tool')
|
|
8995
|
+
continue; // skip hidden tool messages
|
|
8996
|
+
if (m.role === 'assistant' && m.reasoning) {
|
|
8997
|
+
steps.push({ msg: m, idx: j });
|
|
8998
|
+
continue;
|
|
8999
|
+
}
|
|
9000
|
+
break; // any other message ends the run
|
|
9001
|
+
}
|
|
9002
|
+
const content = steps.map((s) => s.msg.reasoning ?? '').filter(Boolean).join('\n\n');
|
|
9003
|
+
const durations = steps
|
|
9004
|
+
.map((s) => s.msg.reasoningDurationMs)
|
|
9005
|
+
.filter((d) => typeof d === 'number');
|
|
9006
|
+
const durationMs = durations.length ? durations.reduce((a, b) => a + b, 0) : undefined;
|
|
9007
|
+
const last = steps[steps.length - 1];
|
|
9008
|
+
const streaming = last ? this.isReasoningStreaming(last.msg, last.idx) : false;
|
|
9009
|
+
// Only claim a duration when at least one step reported timing. Otherwise
|
|
9010
|
+
// "Thought for <1s" would read as "fast" when it really means "unknown", so
|
|
9011
|
+
// drop the duration phrase and label by step count alone.
|
|
9012
|
+
const label = steps.length > 1
|
|
9013
|
+
? durationMs !== undefined
|
|
9014
|
+
? `Thought for ${formatDuration(durationMs)} · ${steps.length} steps`
|
|
9015
|
+
: `${steps.length} steps`
|
|
9016
|
+
: undefined;
|
|
9017
|
+
return { content, durationMs, streaming, label };
|
|
9018
|
+
}
|
|
8794
9019
|
classifiers = new Map();
|
|
8795
9020
|
destroyRef = inject(DestroyRef);
|
|
8796
9021
|
injector = inject(Injector);
|
|
@@ -9350,11 +9575,19 @@ class ChatComponent {
|
|
|
9350
9575
|
[streaming]="agent().isLoading() && i === agent().messages().length - 1"
|
|
9351
9576
|
[current]="i === agent().messages().length - 1"
|
|
9352
9577
|
>
|
|
9353
|
-
|
|
9578
|
+
<!-- Reasoning is merged across a run of consecutive (tool-
|
|
9579
|
+
separated) reasoning steps and rendered ONCE at the run's
|
|
9580
|
+
first step as "Thought for {total} · {N} steps", so a
|
|
9581
|
+
multi-step agent shows one compact pill instead of a
|
|
9582
|
+
stack of "Thought for 1s" chips. Single-step turns render
|
|
9583
|
+
a normal "Thought for {duration}" pill. -->
|
|
9584
|
+
@if (message.reasoning && reasoningRunStart(i)) {
|
|
9585
|
+
@let run = reasoningRun(i);
|
|
9354
9586
|
<chat-reasoning
|
|
9355
|
-
[content]="
|
|
9356
|
-
[isStreaming]="
|
|
9357
|
-
[durationMs]="
|
|
9587
|
+
[content]="run.content"
|
|
9588
|
+
[isStreaming]="run.streaming"
|
|
9589
|
+
[durationMs]="run.durationMs"
|
|
9590
|
+
[label]="run.label"
|
|
9358
9591
|
/>
|
|
9359
9592
|
}
|
|
9360
9593
|
<chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="excludedToolNames()">
|
|
@@ -9401,14 +9634,21 @@ class ChatComponent {
|
|
|
9401
9634
|
/>
|
|
9402
9635
|
}
|
|
9403
9636
|
}
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9637
|
+
<!-- Only show message actions when there is copyable assistant
|
|
9638
|
+
text. Content-less messages (a bare tool call or a subagent
|
|
9639
|
+
delegation card) have nothing to copy/regenerate/rate, so the
|
|
9640
|
+
actions panel is pure whitespace there — suppress it to keep
|
|
9641
|
+
the stream compact. -->
|
|
9642
|
+
@if (content.trim()) {
|
|
9643
|
+
<chat-message-actions
|
|
9644
|
+
chatMessageControls
|
|
9645
|
+
[content]="content"
|
|
9646
|
+
[disabled]="agent().isLoading()"
|
|
9647
|
+
(regenerate)="onRegenerate(i)"
|
|
9648
|
+
(rate)="onRate(message, $event)"
|
|
9649
|
+
(contentCopied)="onCopy(message, $event)"
|
|
9650
|
+
/>
|
|
9651
|
+
}
|
|
9412
9652
|
</chat-message>
|
|
9413
9653
|
</ng-template>
|
|
9414
9654
|
|
|
@@ -9519,11 +9759,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
9519
9759
|
[streaming]="agent().isLoading() && i === agent().messages().length - 1"
|
|
9520
9760
|
[current]="i === agent().messages().length - 1"
|
|
9521
9761
|
>
|
|
9522
|
-
|
|
9762
|
+
<!-- Reasoning is merged across a run of consecutive (tool-
|
|
9763
|
+
separated) reasoning steps and rendered ONCE at the run's
|
|
9764
|
+
first step as "Thought for {total} · {N} steps", so a
|
|
9765
|
+
multi-step agent shows one compact pill instead of a
|
|
9766
|
+
stack of "Thought for 1s" chips. Single-step turns render
|
|
9767
|
+
a normal "Thought for {duration}" pill. -->
|
|
9768
|
+
@if (message.reasoning && reasoningRunStart(i)) {
|
|
9769
|
+
@let run = reasoningRun(i);
|
|
9523
9770
|
<chat-reasoning
|
|
9524
|
-
[content]="
|
|
9525
|
-
[isStreaming]="
|
|
9526
|
-
[durationMs]="
|
|
9771
|
+
[content]="run.content"
|
|
9772
|
+
[isStreaming]="run.streaming"
|
|
9773
|
+
[durationMs]="run.durationMs"
|
|
9774
|
+
[label]="run.label"
|
|
9527
9775
|
/>
|
|
9528
9776
|
}
|
|
9529
9777
|
<chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="excludedToolNames()">
|
|
@@ -9570,14 +9818,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
9570
9818
|
/>
|
|
9571
9819
|
}
|
|
9572
9820
|
}
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9821
|
+
<!-- Only show message actions when there is copyable assistant
|
|
9822
|
+
text. Content-less messages (a bare tool call or a subagent
|
|
9823
|
+
delegation card) have nothing to copy/regenerate/rate, so the
|
|
9824
|
+
actions panel is pure whitespace there — suppress it to keep
|
|
9825
|
+
the stream compact. -->
|
|
9826
|
+
@if (content.trim()) {
|
|
9827
|
+
<chat-message-actions
|
|
9828
|
+
chatMessageControls
|
|
9829
|
+
[content]="content"
|
|
9830
|
+
[disabled]="agent().isLoading()"
|
|
9831
|
+
(regenerate)="onRegenerate(i)"
|
|
9832
|
+
(rate)="onRate(message, $event)"
|
|
9833
|
+
(contentCopied)="onCopy(message, $event)"
|
|
9834
|
+
/>
|
|
9835
|
+
}
|
|
9581
9836
|
</chat-message>
|
|
9582
9837
|
</ng-template>
|
|
9583
9838
|
|
|
@@ -11612,6 +11867,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
11612
11867
|
}], propDecorators: { axis: [{ type: i0.Input, args: [{ isSignal: true, alias: "axis", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
|
|
11613
11868
|
|
|
11614
11869
|
// SPDX-License-Identifier: MIT
|
|
11870
|
+
/**
|
|
11871
|
+
* Convert an icon identifier to its Material Symbols ligature form.
|
|
11872
|
+
*
|
|
11873
|
+
* Material Symbols ligatures are snake_case (`account_circle`, `trending_up`),
|
|
11874
|
+
* but A2UI catalogs commonly emit camelCase identifiers (`accountCircle`,
|
|
11875
|
+
* `shoppingCart`). Splitting on lower→upper boundaries and lowercasing maps
|
|
11876
|
+
* camelCase → the matching ligature. Already-snake_case names, single words,
|
|
11877
|
+
* and non-identifier glyphs (emoji) have no boundaries to split and pass
|
|
11878
|
+
* through unchanged. Unknown names still fall back to the browser default.
|
|
11879
|
+
*/
|
|
11880
|
+
function toMaterialSymbolName(name) {
|
|
11881
|
+
return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
|
|
11882
|
+
}
|
|
11615
11883
|
class A2uiIconComponent {
|
|
11616
11884
|
/** v1 canonical prop. */
|
|
11617
11885
|
name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
@@ -11625,24 +11893,32 @@ class A2uiIconComponent {
|
|
|
11625
11893
|
childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
|
|
11626
11894
|
spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
|
|
11627
11895
|
effectiveName = computed(() => this.name() ?? this.icon(), ...(ngDevMode ? [{ debugName: "effectiveName" }] : []));
|
|
11896
|
+
/** The effective name as a Material Symbols ligature (camelCase → snake_case). */
|
|
11897
|
+
glyphName = computed(() => toMaterialSymbolName(this.effectiveName()), ...(ngDevMode ? [{ debugName: "glyphName" }] : []));
|
|
11628
11898
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11629
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11899
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiIconComponent, isStandalone: true, selector: "a2ui-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
11900
|
+
@if (effectiveName(); as name) {
|
|
11901
|
+
<span
|
|
11902
|
+
class="a2ui-icon material-symbols-outlined"
|
|
11903
|
+
[style.font-size]="size() ? size() + 'px' : '1.125rem'"
|
|
11904
|
+
[attr.aria-label]="name"
|
|
11905
|
+
role="img"
|
|
11906
|
+
>{{ glyphName() }}</span>
|
|
11907
|
+
}
|
|
11908
|
+
`, isInline: true, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}\n"] });
|
|
11636
11909
|
}
|
|
11637
11910
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiIconComponent, decorators: [{
|
|
11638
11911
|
type: Component,
|
|
11639
11912
|
args: [{ selector: 'a2ui-icon', standalone: true, template: `
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11913
|
+
@if (effectiveName(); as name) {
|
|
11914
|
+
<span
|
|
11915
|
+
class="a2ui-icon material-symbols-outlined"
|
|
11916
|
+
[style.font-size]="size() ? size() + 'px' : '1.125rem'"
|
|
11917
|
+
[attr.aria-label]="name"
|
|
11918
|
+
role="img"
|
|
11919
|
+
>{{ glyphName() }}</span>
|
|
11920
|
+
}
|
|
11921
|
+
`, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}\n"] }]
|
|
11646
11922
|
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
|
|
11647
11923
|
|
|
11648
11924
|
// SPDX-License-Identifier: MIT
|
|
@@ -12537,5 +12813,5 @@ function mockAgent(opts = {}) {
|
|
|
12537
12813
|
* Generated bundle index. Do not edit.
|
|
12538
12814
|
*/
|
|
12539
12815
|
|
|
12540
|
-
export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiMultipleChoiceComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, AGENT_ERROR_MESSAGES, AgentError, CHAT_CONFIG, CHAT_LIFECYCLE, ChatApprovalCardComponent, ChatCitationCardTemplateDirective, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatToolViewsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, a2uiBasicCatalog, action, ask, buildA2uiActionMessage, cacheplaneMarkdownViews, createA2uiSurfaceStore, createAgentRef, createContentClassifier, createParseTreeStore, createPartialArgsBridge, deriveJsonSchema, emitBinding, executeFunctionTool, extractErrorMessage, formatDuration, getInterrupt, getMessageType, injectThreadRouting, isAbortError, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, messageContent, mockAgent, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, startClientToolExecutor, statusColor, submitMessage, toAgentError, toClientToolSpecs, tools, validateArgs, view };
|
|
12816
|
+
export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiMultipleChoiceComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, AGENT_ERROR_MESSAGES, AgentError, CHAT_CONFIG, CHAT_LIFECYCLE, ChatApprovalCardComponent, ChatCitationCardTemplateDirective, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatToolViewsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownHtmlComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownMathComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, a2uiBasicCatalog, action, ask, buildA2uiActionMessage, cacheplaneMarkdownViews, createA2uiSurfaceStore, createAgentRef, createContentClassifier, createParseTreeStore, createPartialArgsBridge, deriveJsonSchema, emitBinding, executeFunctionTool, extractErrorMessage, formatDuration, getInterrupt, getMessageType, injectThreadRouting, isAbortError, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, messageContent, mockAgent, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, startClientToolExecutor, statusColor, submitMessage, toAgentError, toClientToolSpecs, tools, validateArgs, view };
|
|
12541
12817
|
//# sourceMappingURL=threadplane-chat.mjs.map
|