@m6d/cortex-angular 0.11.1 → 0.12.0
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 +85 -79
- package/fesm2022/m6d-cortex-angular.mjs +886 -367
- package/package.json +6 -3
- package/theme.css +1 -1
- package/types/m6d-cortex-angular.d.ts +84 -33
package/README.md
CHANGED
|
@@ -1,63 +1,64 @@
|
|
|
1
1
|
# @m6d/cortex-angular
|
|
2
2
|
|
|
3
|
-
Angular
|
|
3
|
+
Angular chat widget for Cortex agents: streaming messages, tool call visualization, composer attachments, syntax highlighting, and i18n. `@m6d/cortex-react` is the same widget for React.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import { CortexChatWidgetComponent, CortexAngularConfig } from '@m6d/cortex-angular';
|
|
8
|
+
import { CortexChatWidgetComponent, type CortexAngularConfig } from '@m6d/cortex-angular';
|
|
9
9
|
|
|
10
10
|
@Component({
|
|
11
11
|
imports: [CortexChatWidgetComponent],
|
|
12
12
|
template: `<cortex-chat-widget [config]="config" />`,
|
|
13
13
|
})
|
|
14
14
|
export class MyComponent {
|
|
15
|
-
protected readonly config
|
|
15
|
+
protected readonly config = {
|
|
16
16
|
locale: 'en',
|
|
17
17
|
transport: {
|
|
18
|
-
baseUrl: '/
|
|
18
|
+
baseUrl: 'https://engine.cortex.example.com/agents/support',
|
|
19
19
|
getHeaders: () => ({ Authorization: `Bearer ${token}` }),
|
|
20
20
|
},
|
|
21
|
-
|
|
22
|
-
// `wsUrl` only to override it, and point it at that same agent-scoped path.
|
|
23
|
-
};
|
|
21
|
+
} satisfies CortexAngularConfig;
|
|
24
22
|
}
|
|
25
23
|
```
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
|
|
52
|
-
`
|
|
25
|
+
The widget attaches its own stylesheet; add `@m6d/cortex-angular/theme.css` to
|
|
26
|
+
your styles to override its tokens in one place (see Theming).
|
|
27
|
+
|
|
28
|
+
`baseUrl` is the agent's URL: the engine's public origin (`ENGINE_PUBLIC_URL`) plus `/agents/<agent slug>`. Add the page's origin to `ENGINE_CORS_ORIGINS`, or proxy the engine under your own origin as the samples do. The WebSocket URL is `baseUrl` plus `/ws`; set `wsUrl` only to override it, and point it at that same agent-scoped path. The bearer is the end user's token, which Cortex verifies the way the agent's auth says: a JWKS, a shared secret, the agent's server, or nobody for an anonymous agent.
|
|
29
|
+
|
|
30
|
+
No root-level providers are needed. Each widget is self-contained, so one page can render several widgets with different configs.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
`CortexAngularConfig` is the shared `CortexConfig` plus two Angular-only fields, `toolComponents` and `injector`.
|
|
35
|
+
|
|
36
|
+
| Property | Type | Description |
|
|
37
|
+
| -------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
38
|
+
| `locale` | `'en' \| 'ar'` | Active locale (default: `'en'`) |
|
|
39
|
+
| `transport.baseUrl` | `string \| () => string` | The agent's URL, `<engine origin>/agents/<slug>` |
|
|
40
|
+
| `transport.getHeaders` | `() => Headers \| Promise<Headers>` | Request headers provider; `Headers` is `Record<string, string>` |
|
|
41
|
+
| `wsUrl` | `string \| false` | WebSocket URL override; `false` disables the socket (optional) |
|
|
42
|
+
| `viewMode` | `'helper' \| 'full'` | Compact panel or full layout with a sidebar (default: `'helper'`) |
|
|
43
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | Color scheme (default: `'light'`) |
|
|
44
|
+
| `agentName` | `string \| { en, ar }` | Header title while no thread is selected (optional) |
|
|
45
|
+
| `showDebugButton` | `boolean` | Show/hide the debug toggle button (default: `false`) |
|
|
46
|
+
| `mediaRenderers` | `Record<string, MediaRenderer>` | Replace or add the media fences the assistant can draw (optional) |
|
|
47
|
+
| `trustSameOriginEmbeds` | `boolean` | Allow embedded tool pages served from this page's own origin (default: `false`) |
|
|
48
|
+
| `hooks.onToolCall` | `(toolCall, context) => unknown` | Answers a client tool; return `undefined` for tools you do not own (optional) |
|
|
49
|
+
| `hooks.onThreadSelected` | `(thread) => void \| Promise<void>` | Runs when a thread opens (optional) |
|
|
50
|
+
| `hooks.onThreadDeselected` | `(thread) => void \| Promise<void>` | Runs when a thread closes (optional) |
|
|
51
|
+
| `toolComponents` | `Record<string, Type<CortexToolComponentInputs>>` | Custom Angular components for tool rendering (optional) |
|
|
52
|
+
| `injector` | `Injector` | Injector for `toolComponents` (optional) |
|
|
53
|
+
|
|
54
|
+
`transport.getHeaders` returns the headers every call carries, usually a bearer `Authorization`. Called straight from another origin, the engine accepts only `Authorization` and the headers the widget sets itself; route anything else through a same-origin proxy. The WebSocket takes its `token` query param from a bearer `Authorization` header when there is one.
|
|
53
55
|
|
|
54
56
|
## Custom parts
|
|
55
57
|
|
|
56
58
|
Put an `ng-template` marked `cortexSlot` inside the widget to draw one of its
|
|
57
59
|
parts yourself: `header`, `landing` (the blank chat) or `threadList` (the
|
|
58
|
-
history's list). Each
|
|
59
|
-
|
|
60
|
-
you need:
|
|
60
|
+
history's list). Each receives the context the default is drawn from, typed by
|
|
61
|
+
the slot name. Use only the slots you need:
|
|
61
62
|
|
|
62
63
|
```ts
|
|
63
64
|
import { NgTemplateOutlet } from '@angular/common';
|
|
@@ -83,7 +84,9 @@ import { CortexChatWidgetComponent, CortexSlotDirective } from '@m6d/cortex-angu
|
|
|
83
84
|
|
|
84
85
|
<ng-template cortexSlot="landing" let-landing>
|
|
85
86
|
<div class="cortex-widget__landing">
|
|
86
|
-
|
|
87
|
+
@if (landing.greeting) {
|
|
88
|
+
<h1>{{ landing.greeting }}</h1>
|
|
89
|
+
}
|
|
87
90
|
@for (suggestion of landing.suggestions; track $index) {
|
|
88
91
|
<button (click)="landing.send(suggestion.prompt)">{{ suggestion.title }}</button>
|
|
89
92
|
}
|
|
@@ -116,19 +119,19 @@ for typing your own rows and chips.
|
|
|
116
119
|
|
|
117
120
|
One header serves both screens; `screen` says which is showing.
|
|
118
121
|
|
|
119
|
-
| Field | Type | Description
|
|
120
|
-
| -------------- | --------------------- |
|
|
121
|
-
| `screen` | `'chat' \| 'threads'` | Which screen the header is on
|
|
122
|
-
| `viewMode` | `'helper' \| 'full'` | The configured view mode
|
|
123
|
-
| `title` | `string \| null` | The open thread's title; `null` on a new chat
|
|
124
|
-
| `agentName` | `string \| undefined` | `agentName` in the widget's locale
|
|
125
|
-
| `threadCount` | `number`
|
|
126
|
-
| `isWorking` | `boolean` | The agent is answering
|
|
127
|
-
| `debug` | `{ shown, on }` | Whether `showDebugButton` is set, and whether debug mode is on
|
|
128
|
-
| `newChat` | `() => void` | Starts a new chat
|
|
129
|
-
| `openHistory` | `() => void` | Shows the history: its screen, or the sidebar in full mode
|
|
130
|
-
| `closeHistory` | `() => void` | Returns to the open chat
|
|
131
|
-
| `toggleDebug` | `() => void` | Switches debug mode
|
|
122
|
+
| Field | Type | Description |
|
|
123
|
+
| -------------- | --------------------- | ------------------------------------------------------------------ |
|
|
124
|
+
| `screen` | `'chat' \| 'threads'` | Which screen the header is on |
|
|
125
|
+
| `viewMode` | `'helper' \| 'full'` | The configured view mode |
|
|
126
|
+
| `title` | `string \| null` | The open thread's title; `null` on a new chat |
|
|
127
|
+
| `agentName` | `string \| undefined` | `agentName` in the widget's locale |
|
|
128
|
+
| `threadCount` | `number \| undefined` | How many conversations the history lists; undefined while it loads |
|
|
129
|
+
| `isWorking` | `boolean` | The agent is answering |
|
|
130
|
+
| `debug` | `{ shown, on }` | Whether `showDebugButton` is set, and whether debug mode is on |
|
|
131
|
+
| `newChat` | `() => void` | Starts a new chat |
|
|
132
|
+
| `openHistory` | `() => void` | Shows the history: its screen, or the sidebar in full mode |
|
|
133
|
+
| `closeHistory` | `() => void` | Returns to the open chat |
|
|
134
|
+
| `toggleDebug` | `() => void` | Switches debug mode |
|
|
132
135
|
|
|
133
136
|
### `landing`
|
|
134
137
|
|
|
@@ -137,7 +140,7 @@ landing for it.
|
|
|
137
140
|
|
|
138
141
|
| Field | Type | Description |
|
|
139
142
|
| ------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
140
|
-
| `greeting` | `string` | The agent's greeting in the widget's locale, or the built-in one
|
|
143
|
+
| `greeting` | `string` | The agent's greeting in the widget's locale, or the built-in one; empty until the handshake answers |
|
|
141
144
|
| `suggestions` | `{ title, prompt }[]` | Empty until the handshake answers, and for an agent that takes no messages |
|
|
142
145
|
| `threads` | `ThreadSummary[] \| undefined` | Newest first; `undefined` while the first load is out |
|
|
143
146
|
| `composer` | `TemplateRef` | The widget's composer, or its "messages disabled" notice; place it with `ngTemplateOutlet` |
|
|
@@ -159,9 +162,11 @@ The history's list. Its header is the `header` slot.
|
|
|
159
162
|
|
|
160
163
|
## Theming
|
|
161
164
|
|
|
162
|
-
Every
|
|
163
|
-
the widget
|
|
164
|
-
|
|
165
|
+
Every color is a `--cortex-color-*` token whose light and dark defaults ship
|
|
166
|
+
with the widget; `theme` picks the side. `@m6d/cortex-angular/theme.css` lists every
|
|
167
|
+
token with its default, and any of them can be overridden on `:root` or on any
|
|
168
|
+
ancestor of the widget. Beside the colors, radius and `--cortex-font-size`,
|
|
169
|
+
four tokens ship unset and fall back when you leave them out:
|
|
165
170
|
|
|
166
171
|
| Token | Falls back to | Styles |
|
|
167
172
|
| -------------------------------- | --------------------------- | ------------------------------------ |
|
|
@@ -170,12 +175,12 @@ ship unset and fall back when you leave them out:
|
|
|
170
175
|
| `--cortex-color-send-empty` | `transparent` | The send button with nothing to send |
|
|
171
176
|
| `--cortex-color-send-empty-text` | `--cortex-color-text-muted` | Its icon |
|
|
172
177
|
|
|
173
|
-
The composer resets its own focus and disabled styles, so a
|
|
178
|
+
The composer resets its own focus and disabled styles, so a page's global
|
|
174
179
|
`textarea:focus` ring or disabled fill does not reach it.
|
|
175
180
|
|
|
176
181
|
## Tables
|
|
177
182
|
|
|
178
|
-
A markdown table in an answer scrolls sideways
|
|
183
|
+
A markdown table in an answer scrolls sideways rather than squeezing its
|
|
179
184
|
columns. Once it has finished arriving, hovering it (or focusing into it)
|
|
180
185
|
shows two actions at its top end: expand, which opens it in a dialog with room
|
|
181
186
|
for every column, and export, which downloads it as an `.xlsx` file. Numbers
|
|
@@ -183,45 +188,46 @@ export as numbers; the header row is bold.
|
|
|
183
188
|
|
|
184
189
|
## Client tools
|
|
185
190
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
the same party and the embeds are the site's own pages, set
|
|
191
|
+
Tools of type `embedded` render inside the chat as an embedded flow (payment,
|
|
192
|
+
signature, ...): the widget asks Cortex to initiate, shows the returned page in
|
|
193
|
+
a sandboxed iframe (inline card or modal, per tool config), and submits the
|
|
194
|
+
page's `cortex:client-tool` postMessage outcome back to the run as-is.
|
|
195
|
+
Anything that must actually be true lives in your backend's own records. No
|
|
196
|
+
widget configuration is needed. One caveat: an embed served from the page's
|
|
197
|
+
**own origin** is refused by default, because a same-origin iframe cannot be
|
|
198
|
+
sandboxed: it could reach into the page, and embed URLs come from the tool's
|
|
199
|
+
definition in Cortex, whose administrator is not necessarily the site owner.
|
|
200
|
+
When both are the same party and the embeds are the site's own pages, set
|
|
197
201
|
`trustSameOriginEmbeds: true` to render them anyway (with full access, as any
|
|
198
|
-
of the site's own pages has).
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return `
|
|
203
|
-
|
|
202
|
+
of the site's own pages has).
|
|
203
|
+
|
|
204
|
+
Other pending tool calls go to `toolComponents` or `hooks.onToolCall`.
|
|
205
|
+
That is how a tool of type `client` ("Answered by your app" in the console)
|
|
206
|
+
works: return its output from `hooks.onToolCall` for that tool name, or draw it
|
|
207
|
+
with a tool component. A `hooks.onToolCall` must return `undefined` for tool
|
|
208
|
+
names it does not own, since answering an unknown pending call would settle an
|
|
209
|
+
embedded flow before the user completes it.
|
|
204
210
|
|
|
205
211
|
## Media blocks
|
|
206
212
|
|
|
207
213
|
The assistant can answer with more than text. A fenced block tagged `mermaid`,
|
|
208
|
-
`vega-lite`, `map` or `report` renders as a diagram, chart, map or report
|
|
209
|
-
with a placeholder while the block is
|
|
210
|
-
the source) when it cannot be drawn. An image attachment the
|
|
211
|
-
as `` on its own line renders inline.
|
|
214
|
+
`vega-lite`, `map` or `report` renders as a diagram, chart, map or report
|
|
215
|
+
inside the message, with a placeholder while the block is streaming and an
|
|
216
|
+
error card (with the source) when it cannot be drawn. An image attachment the
|
|
217
|
+
model references as `` on its own line renders inline.
|
|
212
218
|
|
|
213
219
|
The renderers are peer dependencies your bundler must be able to resolve, but
|
|
214
220
|
they load only when a block of their kind first appears: `mermaid` for
|
|
215
221
|
diagrams, `vega`, `vega-lite` and `vega-embed` for charts, and `leaflet` for
|
|
216
222
|
maps (plus its stylesheet, `leaflet/dist/leaflet.css`, in your page). Maps
|
|
217
223
|
draw OpenStreetMap tiles and need no key. A block whose library fails to load
|
|
218
|
-
|
|
224
|
+
shows the error card.
|
|
219
225
|
|
|
220
226
|
A report is an HTML fragment the agent writes. The widget removes scripts,
|
|
221
227
|
images, links and anything that would load a URL, draws the rest in a shadow
|
|
222
228
|
root that takes its colors from your `--cortex-color-*` theme, and draws each
|
|
223
229
|
`<figure data-chart>` inside it with the chart renderer. Which forms an agent
|
|
224
|
-
writes is its `media` setting
|
|
230
|
+
writes is its `media` setting in Cortex; reports are off by default.
|
|
225
231
|
|
|
226
232
|
Leaflet finds its marker images next to wherever the build put the one the
|
|
227
233
|
stylesheet references, so an Angular build needs the other two copied there as
|