@meshmakers/shared-ui 3.4.1480 → 3.5.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.
Files changed (56) hide show
  1. package/README.md +48 -0
  2. package/assets/octobot/lg/octo_blink.webp +0 -0
  3. package/assets/octobot/lg/octo_blink_still.png +0 -0
  4. package/assets/octobot/lg/octo_idle.webp +0 -0
  5. package/assets/octobot/lg/octo_idle_still.png +0 -0
  6. package/assets/octobot/lg/octo_look.webp +0 -0
  7. package/assets/octobot/lg/octo_look_still.png +0 -0
  8. package/assets/octobot/lg/octo_swim.webp +0 -0
  9. package/assets/octobot/lg/octo_swim_still.png +0 -0
  10. package/assets/octobot/lg/octo_thinking.webp +0 -0
  11. package/assets/octobot/lg/octo_thinking_still.png +0 -0
  12. package/assets/octobot/lg/octo_wave.webp +0 -0
  13. package/assets/octobot/lg/octo_wave_still.png +0 -0
  14. package/assets/octobot/md/octo_blink.webp +0 -0
  15. package/assets/octobot/md/octo_blink_still.png +0 -0
  16. package/assets/octobot/md/octo_idle.webp +0 -0
  17. package/assets/octobot/md/octo_idle_still.png +0 -0
  18. package/assets/octobot/md/octo_look.webp +0 -0
  19. package/assets/octobot/md/octo_look_still.png +0 -0
  20. package/assets/octobot/md/octo_swim.webp +0 -0
  21. package/assets/octobot/md/octo_swim_still.png +0 -0
  22. package/assets/octobot/md/octo_thinking.webp +0 -0
  23. package/assets/octobot/md/octo_thinking_still.png +0 -0
  24. package/assets/octobot/md/octo_wave.webp +0 -0
  25. package/assets/octobot/md/octo_wave_still.png +0 -0
  26. package/assets/octobot/sm/octo_blink.webp +0 -0
  27. package/assets/octobot/sm/octo_blink_still.png +0 -0
  28. package/assets/octobot/sm/octo_idle.webp +0 -0
  29. package/assets/octobot/sm/octo_idle_still.png +0 -0
  30. package/assets/octobot/sm/octo_look.webp +0 -0
  31. package/assets/octobot/sm/octo_look_still.png +0 -0
  32. package/assets/octobot/sm/octo_swim.webp +0 -0
  33. package/assets/octobot/sm/octo_swim_still.png +0 -0
  34. package/assets/octobot/sm/octo_thinking.webp +0 -0
  35. package/assets/octobot/sm/octo_thinking_still.png +0 -0
  36. package/assets/octobot/sm/octo_wave.webp +0 -0
  37. package/assets/octobot/sm/octo_wave_still.png +0 -0
  38. package/fesm2022/meshmakers-shared-ui-assistant-core.mjs +940 -0
  39. package/fesm2022/meshmakers-shared-ui-assistant-core.mjs.map +1 -0
  40. package/fesm2022/meshmakers-shared-ui-assistant.mjs +819 -0
  41. package/fesm2022/meshmakers-shared-ui-assistant.mjs.map +1 -0
  42. package/fesm2022/meshmakers-shared-ui-command-palette.mjs +817 -0
  43. package/fesm2022/meshmakers-shared-ui-command-palette.mjs.map +1 -0
  44. package/fesm2022/meshmakers-shared-ui-shell.mjs +1738 -0
  45. package/fesm2022/meshmakers-shared-ui-shell.mjs.map +1 -0
  46. package/fesm2022/meshmakers-shared-ui-testing.mjs +92 -0
  47. package/fesm2022/meshmakers-shared-ui-testing.mjs.map +1 -0
  48. package/fesm2022/meshmakers-shared-ui.mjs +1550 -203
  49. package/fesm2022/meshmakers-shared-ui.mjs.map +1 -1
  50. package/package.json +28 -2
  51. package/types/meshmakers-shared-ui-assistant-core.d.ts +732 -0
  52. package/types/meshmakers-shared-ui-assistant.d.ts +258 -0
  53. package/types/meshmakers-shared-ui-command-palette.d.ts +398 -0
  54. package/types/meshmakers-shared-ui-shell.d.ts +939 -0
  55. package/types/meshmakers-shared-ui-testing.d.ts +41 -0
  56. package/types/meshmakers-shared-ui.d.ts +1004 -154
@@ -0,0 +1,732 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Type, Signal } from '@angular/core';
3
+ import { Observable } from 'rxjs';
4
+ import * as _meshmakers_shared_ui_assistant_core from '@meshmakers/shared-ui/assistant-core';
5
+ import { ActivatedRouteSnapshot } from '@angular/router';
6
+
7
+ /**
8
+ * The single feature flag of the assistant (ui-concept §5.3, AB#5549).
9
+ * **Off by default.** While it is off nothing assistant-related renders or binds:
10
+ * no top-bar ✦ button, no Cmd/Ctrl+J, no "✦ Explain" affordances, and the command
11
+ * palette's `?` scope only shows "Assistant not available".
12
+ *
13
+ * Enable it for a host with `{ provide: ASSISTANT_ENABLED, useValue: true }`. The host gates its
14
+ * own affordances (top-bar toggle, Cmd/Ctrl+J, palette row) on `AssistantService.enabled`.
15
+ * Turning it on without a real {@link ASSISTANT_TRANSPORT} shows the panel with an
16
+ * honest "Assistant backend not available yet" state — never canned answers.
17
+ */
18
+ declare const ASSISTANT_ENABLED: InjectionToken<boolean>;
19
+ /** Connection state of the transport; only `ready` lets the composer send. */
20
+ type AssistantTransportStatus = 'unavailable' | 'ready' | 'busy' | 'error';
21
+ /** Kinds of context the panel sends as structured data (ids, not DOM). */
22
+ type AssistantContextKind = 'tenant' | 'page' | 'entity';
23
+ /** One removable chip of the context row. */
24
+ interface AssistantContextChip {
25
+ /** Stable per value (`<kind>:<value>`) so a removed chip stays removed until the value changes. */
26
+ id: string;
27
+ kind: AssistantContextKind;
28
+ /** Visible text, e.g. "Integration › Adapters". */
29
+ label: string;
30
+ /** Structured value sent to the backend: tenant id, router URL, or entity id. */
31
+ value: string;
32
+ /** Optional CK type of an entity chip. */
33
+ ckTypeId?: string;
34
+ }
35
+ /** What an "✦ Explain" entry point asks about. */
36
+ interface AssistantExplainTarget {
37
+ /** Human readable name of the object, e.g. "edge-plc-07" or "System.Communication/Adapter". */
38
+ label: string;
39
+ /** Runtime id of the object, when it is an entity. */
40
+ rtId?: string;
41
+ /** CK type id of the object (or the type itself for "explain this type"). */
42
+ ckTypeId?: string;
43
+ /** Overrides the composer prefill; defaults to `/explain <label>`. */
44
+ prompt?: string;
45
+ }
46
+ /** A request to open the panel. */
47
+ interface AssistantOpenRequest {
48
+ /** Prefills the composer (never sent automatically). */
49
+ prompt?: string;
50
+ /** Adds (or replaces) the entity chip, e.g. from an Explain entry point. */
51
+ entity?: AssistantExplainTarget;
52
+ }
53
+ /** A write the assistant wants to perform; rendered as a proposal card. */
54
+ interface AssistantProposal {
55
+ id: string;
56
+ title: string;
57
+ /** Markdown-free one-liner explaining the effect. */
58
+ description?: string;
59
+ /** MCP tool that would run on confirmation, e.g. `deploy_adapter`. */
60
+ toolName: string;
61
+ /** Parameter table shown on the card. */
62
+ parameters: {
63
+ name: string;
64
+ value: string;
65
+ }[];
66
+ /** True when the proposal writes to the tenant (the card says so). */
67
+ writes: boolean;
68
+ }
69
+ /**
70
+ * A tool call the assistant made, shown as a row of the thread. Structurally compatible with
71
+ * `AiToolCallDto` of `@meshmakers/octo-ai-console`, so its events can be passed on unchanged.
72
+ */
73
+ interface AssistantToolCall {
74
+ readonly callId: string;
75
+ readonly toolName: string;
76
+ /** E.g. 'Pending' | 'Approved' | 'Rejected' | 'Succeeded' | 'Failed'. */
77
+ readonly status: string;
78
+ readonly sessionId?: string;
79
+ readonly arguments?: unknown;
80
+ readonly result?: unknown;
81
+ readonly startedAt?: string;
82
+ readonly completedAt?: string | null;
83
+ readonly durationMs?: number | null;
84
+ }
85
+ /**
86
+ * The component that renders one tool call in the thread. It must have a `call` input taking an
87
+ * {@link AssistantToolCall}. Default: a compact name + status row. An OctoMesh host passes
88
+ * `AiToolCallComponent` of `@meshmakers/octo-ai-console` (collapsible details):
89
+ * `{ provide: ASSISTANT_TOOL_CALL_COMPONENT, useValue: AiToolCallComponent }`.
90
+ */
91
+ declare const ASSISTANT_TOOL_CALL_COMPONENT: InjectionToken<Type<unknown>>;
92
+ /**
93
+ * Labels of the active area and tab of the shell, used for the "Page: Area › Tab" context chip.
94
+ * Optional; without it the page chip falls back to the first breadcrumb. With
95
+ * `@meshmakers/shared-ui/shell`:
96
+ *
97
+ * ```ts
98
+ * { provide: ASSISTANT_PAGE_CONTEXT, useFactory: () => {
99
+ * const nav = inject(ShellNavigationService);
100
+ * return { areaText: () => nav.activeArea()?.text ?? null, tabText: () => nav.activeTab()?.text ?? null };
101
+ * } }
102
+ * ```
103
+ *
104
+ * Read inside a computed, so signal-backed functions keep the chips up to date.
105
+ */
106
+ interface AssistantPageContext {
107
+ areaText(): string | null;
108
+ tabText(): string | null;
109
+ }
110
+ declare const ASSISTANT_PAGE_CONTEXT: InjectionToken<AssistantPageContext>;
111
+ /** One rendered item of the thread. */
112
+ type AssistantThreadItem = {
113
+ kind: 'user';
114
+ id: string;
115
+ /** What was sent — for a slash command with `expand`, the expanded prompt. */
116
+ text: string;
117
+ attachments?: readonly AssistantAttachmentRef[];
118
+ /**
119
+ * The slash command as typed (e.g. `/open March`) when {@link text} is its expansion (AB#5621).
120
+ * The thread shows it as a subtle label above the prompt. A transport may store it and return it
121
+ * from `loadSession` so reopened chats show the label too; without it they show the prompt only.
122
+ */
123
+ slashCommand?: string;
124
+ } | {
125
+ kind: 'assistant';
126
+ id: string;
127
+ markdown: string;
128
+ streaming: boolean;
129
+ } | {
130
+ kind: 'tool-call';
131
+ id: string;
132
+ call: AssistantToolCall;
133
+ } | {
134
+ kind: 'proposal';
135
+ id: string;
136
+ proposal: AssistantProposal;
137
+ decision: AssistantProposalDecisionKind | null;
138
+ } | {
139
+ kind: 'error';
140
+ id: string;
141
+ text: string;
142
+ };
143
+ type AssistantProposalDecisionKind = 'run' | 'edit' | 'discard';
144
+ /**
145
+ * A decision on a proposal card. The card only emits it; whatever executes the
146
+ * write (the backend's confirm round-trip, AB#5550) subscribes to
147
+ * `AssistantService.proposalDecisions$`. Nothing in this library executes a proposal.
148
+ */
149
+ interface AssistantProposalDecision {
150
+ kind: AssistantProposalDecisionKind;
151
+ proposal: AssistantProposal;
152
+ }
153
+ /** What the composer hands to the transport. */
154
+ interface AssistantSendRequest {
155
+ /** The typed text; may be empty when the turn only carries {@link files}. */
156
+ text: string;
157
+ /** The chips that remained after the user removed what they did not want to share. */
158
+ context: AssistantContextChip[];
159
+ /**
160
+ * Files attached in the composer. Only set when the transport declares
161
+ * {@link AssistantTransport.attachments} and the person attached at least one file
162
+ * (already checked against `accept`, `maxFiles` and `maxFileSizeBytes`).
163
+ */
164
+ files?: File[];
165
+ /**
166
+ * The slash command as typed (e.g. `/open March`) when {@link text} is the prompt its
167
+ * {@link AssistantSlashCommand.expand} produced (AB#5621). `text` is always what the model should
168
+ * read; store `text` as the turn so a reopened chat matches the live thread, and optionally keep
169
+ * this alongside it (returned as `slashCommand` on the restored user item) for the label.
170
+ */
171
+ slashCommand?: string;
172
+ }
173
+ /** Name (and size) of a file sent with a user turn, shown under the turn in the thread. */
174
+ interface AssistantAttachmentRef {
175
+ name: string;
176
+ /** Bytes, when known. */
177
+ size?: number;
178
+ }
179
+ /**
180
+ * File attachments a transport accepts (AB#5621). Declaring {@link AssistantTransport.attachments}
181
+ * shows an "Attach file" button in the composer; without it there is no attachment UI.
182
+ */
183
+ interface AssistantAttachmentOptions {
184
+ /**
185
+ * Accepted types, in the syntax of `<input type="file" accept>`: comma separated MIME types
186
+ * (`application/pdf`, `image/*`) and/or extensions (`.pdf`). Empty or omitted = any type.
187
+ */
188
+ accept?: string;
189
+ /** How many files one turn may carry (default 1). */
190
+ maxFiles?: number;
191
+ /** Largest accepted file in bytes; omitted = no limit in the UI. */
192
+ maxFileSizeBytes?: number;
193
+ }
194
+ /** A saved conversation of the person, as listed by {@link AssistantTransport.sessions}. */
195
+ interface AssistantSessionSummary {
196
+ id: string;
197
+ title: string;
198
+ /** ISO 8601 timestamp; shown as a secondary line when present. */
199
+ createdAt?: string;
200
+ }
201
+ /** Starter questions as a transport can provide them: synchronously, as a promise or as an observable (first value counts). */
202
+ type AssistantStarterQuestions = readonly string[] | Promise<readonly string[]> | Observable<readonly string[]>;
203
+ /** A result a session method may return: a promise or an observable (its last value counts). */
204
+ type AssistantAsyncResult<T> = Promise<T> | Observable<T>;
205
+ /** Events a transport streams back for one send. */
206
+ type AssistantStreamEvent = {
207
+ type: 'message-delta';
208
+ messageId: string;
209
+ text: string;
210
+ } | {
211
+ type: 'message-end';
212
+ messageId: string;
213
+ } | {
214
+ type: 'tool-call';
215
+ call: AssistantToolCall;
216
+ } | {
217
+ type: 'proposal';
218
+ proposal: AssistantProposal;
219
+ } | {
220
+ type: 'error';
221
+ message: string;
222
+ };
223
+ /**
224
+ * Where assistant turns come from. The library ships **no** implementation that
225
+ * answers: the default is {@link NotConnectedAssistantTransport}. A real one
226
+ * (e.g. the OctoMesh AI services, AB#5550 delegated-identity chat sessions) provides
227
+ * itself under {@link ASSISTANT_TRANSPORT}.
228
+ */
229
+ interface AssistantTransport {
230
+ readonly status: Signal<AssistantTransportStatus>;
231
+ /** Shown in the panel when the status is not `ready`. */
232
+ readonly statusMessage: Signal<string | null>;
233
+ /** One user turn; completes when the assistant turn is complete. */
234
+ send(request: AssistantSendRequest): Observable<AssistantStreamEvent>;
235
+ /**
236
+ * Accepted file attachments. When set, the composer gets an "Attach file" button and
237
+ * {@link AssistantSendRequest.files} carries the files; a turn may then have empty text.
238
+ */
239
+ readonly attachments?: AssistantAttachmentOptions | null;
240
+ /**
241
+ * Example questions for an empty thread, in the UI language. Read each time the panel opens;
242
+ * clicking one sends it as if it had been typed. An observable need not complete: its first
243
+ * emitted value is taken (later emissions are ignored until the next opening).
244
+ */
245
+ starterQuestions?(): AssistantStarterQuestions;
246
+ /**
247
+ * Saved sessions of the signed-in person, newest first. Together with {@link loadSession} it
248
+ * enables the panel's chat list ("Chats" and "New chat" in the header).
249
+ */
250
+ readonly sessions?: Signal<readonly AssistantSessionSummary[]>;
251
+ /** The session the next send writes to; `null` = a fresh chat. Marks the current row in the list. */
252
+ readonly activeSessionId?: Signal<string | null>;
253
+ /** Reloads {@link sessions}; called when the chat list is opened. */
254
+ refreshSessions?(): AssistantAsyncResult<void>;
255
+ /** Makes a saved session active and returns its thread. */
256
+ loadSession?(id: string): AssistantAsyncResult<AssistantThreadItem[]>;
257
+ /** Deletes a saved session. Without it the chat list has no delete buttons. */
258
+ deleteSession?(id: string): AssistantAsyncResult<void>;
259
+ /** Starts a fresh chat: the next send creates a new session. */
260
+ newSession?(): void;
261
+ }
262
+ /** The assistant's transport; defaults to the honest "not connected" one. */
263
+ declare const ASSISTANT_TRANSPORT: InjectionToken<AssistantTransport>;
264
+ /** A composer slash command (ui-concept §5.3). */
265
+ interface AssistantSlashCommand {
266
+ /** The command word with its slash, lower-case, e.g. `/explain`. */
267
+ command: string;
268
+ description: string;
269
+ /**
270
+ * Turns the typed command into the prompt that is sent (AB#5621). Called by
271
+ * `AssistantService.send` when the text is `<command>` or `<command> <args>`: `command` is this
272
+ * command's word, `args` the trimmed rest (`''` when none). The returned text — synchronously or
273
+ * as a promise — is what goes to the transport **and** what the live thread shows (with the typed
274
+ * command as a small label), so a chat reopened from the transport reads the same.
275
+ *
276
+ * If it throws, rejects or returns an empty text, nothing is sent: an error row is added and the
277
+ * draft is restored. Without `expand` the typed text is sent unchanged (default behaviour).
278
+ *
279
+ * ```ts
280
+ * { command: '/open', description: 'Open items',
281
+ * expand: (_command, args) => args ? `Which items are open for ${args}?` : 'Which items are open?' }
282
+ * ```
283
+ */
284
+ expand?: (command: string, args: string) => string | Promise<string>;
285
+ }
286
+ /** The default slash commands (English descriptions). */
287
+ declare const DEFAULT_ASSISTANT_SLASH_COMMANDS: readonly AssistantSlashCommand[];
288
+ /** The composer's slash commands; provide your own list (or translated descriptions) to change them. */
289
+ declare const ASSISTANT_SLASH_COMMANDS: InjectionToken<readonly AssistantSlashCommand[]>;
290
+ /**
291
+ * Layout of `mm-assistant-panel` (AB#5621); every member is optional and the panel's inputs of the
292
+ * same name win. Defaults keep the original look: a docked panel 400 px wide (340 px ≤ 1180 px)
293
+ * that pushes the content, without a resize handle (opt in with `resizable: true`).
294
+ */
295
+ interface AssistantPanelOptions {
296
+ /**
297
+ * `docked` (default): the panel takes its own column and pushes the content.
298
+ * `overlay`: it floats over the content at the right edge (`position: fixed`), from
299
+ * `--mm-assistant-overlay-top` (default 48 px, the shell's top bar) to the bottom.
300
+ */
301
+ mode?: AssistantPanelMode;
302
+ /**
303
+ * Shows the drag handle on the left edge (default `false`: no handle, no extra tab stop; never on
304
+ * phones). Only with it on does a persisted width apply.
305
+ */
306
+ resizable?: boolean;
307
+ /** Smallest width in px the handle allows (default {@link DEFAULT_ASSISTANT_PANEL_MIN_WIDTH}). */
308
+ minWidth?: number;
309
+ /**
310
+ * Largest width in px the handle allows (default {@link DEFAULT_ASSISTANT_PANEL_MAX_WIDTH});
311
+ * further capped so at least 240 px of the window stay free.
312
+ */
313
+ maxWidth?: number;
314
+ /**
315
+ * `localStorage` key of the width the person chose (default
316
+ * {@link DEFAULT_ASSISTANT_PANEL_STORAGE_KEY}); `null` = do not persist. Storage that is missing or
317
+ * throws (private mode, blocked site data) is ignored — the width then lasts as long as the panel.
318
+ */
319
+ storageKey?: string | null;
320
+ }
321
+ type AssistantPanelMode = 'docked' | 'overlay';
322
+ declare const DEFAULT_ASSISTANT_PANEL_MIN_WIDTH = 320;
323
+ declare const DEFAULT_ASSISTANT_PANEL_MAX_WIDTH = 720;
324
+ declare const DEFAULT_ASSISTANT_PANEL_STORAGE_KEY = "mm-assistant-panel-width";
325
+ /** App-wide {@link AssistantPanelOptions}; optional. */
326
+ declare const ASSISTANT_PANEL_OPTIONS: InjectionToken<AssistantPanelOptions>;
327
+
328
+ /**
329
+ * UI strings of the assistant (`mm-assistant-panel`, composer, thread, proposal card), the
330
+ * context chips and the default "not connected" transport.
331
+ *
332
+ * Provide translations app wide with {@link ASSISTANT_MESSAGES} or per component with the
333
+ * `messages` input (wins over the token). Missing, `undefined` or `null` members keep the English
334
+ * default of {@link DEFAULT_ASSISTANT_MESSAGES}; `{name}` placeholders are filled with
335
+ * {@link formatAssistantMessage}. Slash command descriptions come from `ASSISTANT_SLASH_COMMANDS`.
336
+ */
337
+ interface AssistantMessages {
338
+ title: string;
339
+ /** Chip in the header while a backend is connected. */
340
+ readsWithYourPermissions: string;
341
+ closeAssistant: string;
342
+ /** `aria-label` of the context chip list. */
343
+ contextLabel: string;
344
+ /** `aria-label` of a chip's remove button; `{label}` = chip label. */
345
+ removeContext: string;
346
+ /** Fallback when the transport gives no status message. */
347
+ notAvailable: string;
348
+ /** Status message of the default transport (no backend). */
349
+ notConnected: string;
350
+ commandsLabel: string;
351
+ composerLabel: string;
352
+ composerPlaceholder: string;
353
+ send: string;
354
+ stop: string;
355
+ /** Greeting of an empty chat, next to the waving OctoBot (AB#3444). */
356
+ greeting: string;
357
+ /** Shown next to the thinking OctoBot while a reply streams (AB#3444). */
358
+ thinking: string;
359
+ conversationLabel: string;
360
+ /** Screen-reader prefix of user turns. */
361
+ youPrefix: string;
362
+ /** Screen-reader prefix of assistant turns. */
363
+ assistantPrefix: string;
364
+ /** Screen-reader prefix of error rows. */
365
+ errorPrefix: string;
366
+ /** Screen-reader prefix of the typed slash command shown above its expanded prompt. */
367
+ slashCommandPrefix: string;
368
+ /** Error row when a slash command's `expand` fails; `{command}` = command word. */
369
+ slashCommandFailed: string;
370
+ /** `aria-label` of a proposal card; `{title}` = proposal title. */
371
+ proposalLabel: string;
372
+ proposal: string;
373
+ /** Card header addition for writing proposals (after "Proposal ·"). */
374
+ writesToTenant: string;
375
+ tool: string;
376
+ runsAfterConfirm: string;
377
+ run: string;
378
+ edit: string;
379
+ discard: string;
380
+ runRequested: string;
381
+ editRequested: string;
382
+ discarded: string;
383
+ /** `{tenant}` = tenant id. */
384
+ contextTenant: string;
385
+ /** `{page}` = page label. */
386
+ contextPage: string;
387
+ /** Header icon button that opens the list of saved chats (tooltip + `aria-label`). */
388
+ sessions: string;
389
+ /** Header icon button that starts a fresh chat (tooltip + `aria-label`). */
390
+ newChat: string;
391
+ /** `aria-label` of the saved chats list. */
392
+ sessionsLabel: string;
393
+ /** Shown when there are no saved chats. */
394
+ noSessions: string;
395
+ /** Leaves the chat list without opening a chat. */
396
+ backToChat: string;
397
+ /** `aria-label` of a chat's delete button; `{title}` = chat title. */
398
+ deleteSession: string;
399
+ /** Inline confirmation; `{title}` = chat title. */
400
+ deleteSessionConfirm: string;
401
+ /** Confirms the deletion. */
402
+ delete: string;
403
+ cancel: string;
404
+ /** Error row when a saved chat cannot be opened. */
405
+ sessionLoadFailed: string;
406
+ /** Error row when a saved chat cannot be deleted. */
407
+ sessionDeleteFailed: string;
408
+ /** `aria-label` / tooltip of the attach button. */
409
+ attachFile: string;
410
+ /** `aria-label` of the attached files list. */
411
+ attachmentsLabel: string;
412
+ /** `aria-label` of an attachment's remove button; `{name}` = file name. */
413
+ removeAttachment: string;
414
+ /** `{name}` = file name. */
415
+ attachmentWrongType: string;
416
+ /** `{name}` = file name, `{max}` = size limit (e.g. "10 MB"). */
417
+ attachmentTooLarge: string;
418
+ /** `{max}` = most files per message. */
419
+ attachmentTooMany: string;
420
+ /** Heading above the starter questions of an empty chat. */
421
+ startersTitle: string;
422
+ /** `aria-label` / tooltip of the panel's resize handle. */
423
+ resizePanel: string;
424
+ }
425
+ declare const DEFAULT_ASSISTANT_MESSAGES: AssistantMessages;
426
+ /** App-wide translations of the assistant (partial). */
427
+ declare const ASSISTANT_MESSAGES: InjectionToken<Partial<AssistantMessages>>;
428
+ /** Merges overrides over {@link DEFAULT_ASSISTANT_MESSAGES}, later sources winning; null/undefined members are ignored. */
429
+ declare function resolveAssistantMessages(...overrides: (Partial<AssistantMessages> | null | undefined)[]): AssistantMessages;
430
+ /** Replaces `{name}` placeholders; unknown placeholders stay as they are. */
431
+ declare function formatAssistantMessage(message: string, values: Record<string, string | number>): string;
432
+ /** The settled label of a proposal decision. */
433
+ declare function proposalDecisionLabel(kind: AssistantProposalDecisionKind, messages: AssistantMessages): string;
434
+ /**
435
+ * {@link ASSISTANT_MESSAGES} merged with a component input (the input wins).
436
+ * Call it in an injection context.
437
+ * @internal
438
+ */
439
+ declare function assistantMessages(input: Signal<Partial<AssistantMessages> | null | undefined>): Signal<AssistantMessages>;
440
+
441
+ /**
442
+ * State and entry points of the assistant panel (ui-concept §5.3, AB#5549).
443
+ *
444
+ * - **Flag.** Everything is gated by {@link ASSISTANT_ENABLED} (off by default):
445
+ * while it is off every entry point is a no-op that returns `false`, and the
446
+ * service does not even listen to the router.
447
+ * - **Entry points.** {@link open} (top-bar ✦, Cmd/Ctrl+J, palette `?` scope),
448
+ * {@link requestExplain} ("✦ Explain <object>", AB#5545/5547), {@link toggle}.
449
+ * They open the panel with a prefilled composer and context; they never send.
450
+ * - **Thread.** Survives closing the panel and navigating. Items only come from the
451
+ * {@link ASSISTANT_TRANSPORT}; the default transport is not connected, so the
452
+ * library itself never adds an assistant turn.
453
+ * - **Context.** Tenant (route param `tenantId`), page (`ASSISTANT_PAGE_CONTEXT`, else the first
454
+ * breadcrumb of shared-services' `BreadCrumbService`) and entity chips; see {@link deriveAssistantContext}.
455
+ * - **Proposals.** Run / Edit / Discard are published on {@link proposalDecisions$}
456
+ * and nothing else — executing a write is the backend's confirm round-trip (AB#5550).
457
+ * - **Optional transport capabilities (AB#5621).** Sessions ({@link sessionsSupported},
458
+ * {@link openSession}, {@link deleteSession}, {@link newThread}), file attachments
459
+ * ({@link attachments}, {@link draftFiles}) and starter questions ({@link starterQuestions}) are
460
+ * only offered when the transport implements them; otherwise they are inert.
461
+ * - **Slash commands (AB#5621).** A command of `ASSISTANT_SLASH_COMMANDS` with `expand` is sent as
462
+ * the prompt it returns; see {@link send}.
463
+ */
464
+ declare class AssistantService {
465
+ /** The feature flag. Read it to gate any assistant affordance. */
466
+ readonly enabled: boolean;
467
+ private readonly transport;
468
+ private readonly router;
469
+ private readonly destroyRef;
470
+ /** Only resolved while enabled: a disabled assistant binds nothing. */
471
+ private readonly pageContext;
472
+ private readonly messages;
473
+ private readonly slashCommands;
474
+ private readonly _isOpen;
475
+ private readonly _draft;
476
+ private readonly _focusRequest;
477
+ private readonly _explicitEntity;
478
+ private readonly _removedChips;
479
+ private readonly _thread;
480
+ private readonly _sending;
481
+ private readonly _draftFiles;
482
+ private readonly _starterQuestions;
483
+ private starterRequest;
484
+ /** Bumped by every thread switch (open session, new chat, loadThread); stale session loads are dropped. */
485
+ private threadGeneration;
486
+ private readonly _routeTick;
487
+ private readonly _breadcrumbs;
488
+ private readonly decisions;
489
+ private returnFocusTo;
490
+ private pending;
491
+ private itemCounter;
492
+ /** Bumped by {@link stop}: an async slash-command expansion that resolves later is dropped. */
493
+ private expansionGeneration;
494
+ /** Whether the panel is shown. */
495
+ readonly isOpen: Signal<boolean>;
496
+ /** Composer text; prefilled by entry points. */
497
+ readonly draft: Signal<string>;
498
+ /** Bumped whenever an entry point wants the composer focused. */
499
+ readonly focusRequest: Signal<number>;
500
+ /** The rendered thread. */
501
+ readonly thread: Signal<AssistantThreadItem[]>;
502
+ /** A turn is streaming. */
503
+ readonly sending: Signal<boolean>;
504
+ readonly transportStatus: Signal<_meshmakers_shared_ui_assistant_core.AssistantTransportStatus>;
505
+ readonly transportMessage: Signal<string | null>;
506
+ /** The composer may send: the transport is ready and no turn is running. */
507
+ readonly canSend: Signal<boolean>;
508
+ /** The transport has saved sessions (`sessions` + `loadSession`): the panel shows its chat list. */
509
+ readonly sessionsSupported: boolean;
510
+ /** Saved sessions can be deleted (`deleteSession`). */
511
+ readonly sessionDeleteSupported: boolean;
512
+ /** Saved sessions of the transport (empty without session support). */
513
+ readonly sessions: Signal<readonly AssistantSessionSummary[]>;
514
+ /** The session the next send writes to (`null` = fresh chat or no session support). */
515
+ readonly activeSessionId: Signal<string | null>;
516
+ /** Accepted attachments, or `null` when the transport takes no files (no attach UI). */
517
+ readonly attachments: AssistantAttachmentOptions | null;
518
+ /** Files attached in the composer; sent and cleared with the next turn. */
519
+ readonly draftFiles: Signal<readonly File[]>;
520
+ /** Starter questions for an empty thread (see {@link loadStarterQuestions}). */
521
+ readonly starterQuestions: Signal<readonly string[]>;
522
+ /** Context of the current page minus the chips the person removed. */
523
+ readonly contextChips: Signal<AssistantContextChip[]>;
524
+ /** Run / Edit / Discard on proposal cards. Only emitted, never executed here. */
525
+ readonly proposalDecisions$: Observable<AssistantProposalDecision>;
526
+ constructor();
527
+ /** Opens the panel (or re-focuses it) with an optional prefill. Returns false while disabled. */
528
+ open(request?: AssistantOpenRequest): boolean;
529
+ /** "✦ Explain <object>": opens the panel with `/explain <label>` and the object as context chip. */
530
+ requestExplain(target: AssistantExplainTarget): boolean;
531
+ /** Hides the panel and gives focus back to where it was (the ✦ button if that is gone). */
532
+ close(): void;
533
+ /**
534
+ * Forgets an open panel without moving focus — the host calls it when the
535
+ * panel can no longer be shown (tenant left or denied, connection lost), so it
536
+ * does not pop up again later with a stale return-focus target.
537
+ */
538
+ dismiss(): void;
539
+ /** Opens when closed, closes when open. Returns false while disabled. */
540
+ toggle(): boolean;
541
+ setDraft(text: string): void;
542
+ /** Replaces the attached files (ignored while the transport takes no files). */
543
+ setDraftFiles(files: readonly File[]): void;
544
+ /**
545
+ * Asks the transport for its starter questions (the panel calls it when it opens).
546
+ * No-op without `starterQuestions`; a failing source leaves the list empty. Of an observable
547
+ * only the first emitted value counts (it need not complete); one that completes empty yields none.
548
+ */
549
+ loadStarterQuestions(): void;
550
+ /** Reloads the transport's session list. Resolves false without session support or on failure. */
551
+ refreshSessions(): Promise<boolean>;
552
+ /**
553
+ * Opens a saved session: stops a running turn, asks the transport for the session's thread and
554
+ * shows it. On failure the current thread stays and an error row is added. Resolves whether the
555
+ * session was opened.
556
+ */
557
+ openSession(id: string): Promise<boolean>;
558
+ /**
559
+ * Deletes a saved session; deleting the active one leaves an empty thread. On failure an error
560
+ * row is added. Resolves whether it was deleted.
561
+ */
562
+ deleteSession(id: string): Promise<boolean>;
563
+ /**
564
+ * Starts a fresh chat: stops a running turn, empties the thread and the attached files and tells
565
+ * the transport (`newSession`, when it has one). Works without session support too.
566
+ */
567
+ newThread(): void;
568
+ /** Replaces the thread, e.g. with a conversation the host restored itself. Stops a running turn. */
569
+ loadThread(items: readonly AssistantThreadItem[]): void;
570
+ /** Drops a context chip; it stays dropped until its value changes. */
571
+ removeChip(id: string): void;
572
+ /**
573
+ * Sends one user turn through the transport. Does nothing (returns false) unless
574
+ * the transport is `ready` — the default transport never is.
575
+ *
576
+ * With a transport that takes attachments, `files` (default: {@link draftFiles}) go along and
577
+ * the text may be empty; the attached files are cleared with the draft. Explicitly passed `files`
578
+ * are not checked again against {@link attachments} (`accept`, `maxFiles`, `maxFileSizeBytes`) —
579
+ * the composer validates on pick; a host passing files itself is responsible for them.
580
+ *
581
+ * **Slash commands (AB#5621).** When the text is `<command> [args]` and that command of
582
+ * `ASSISTANT_SLASH_COMMANDS` has `expand`, its result is sent instead and shown as the user turn,
583
+ * with the typed text as `slashCommand` (label in the thread, also on the send request). A
584
+ * promise-returning `expand` makes the turn start once it resolves (the composer is busy and Stop
585
+ * cancels meanwhile); returns true when the expansion started. If `expand` throws, rejects or yields
586
+ * an empty prompt, nothing is sent: an error row is added and the draft (text and files) comes back.
587
+ */
588
+ send(text: string, files?: readonly File[]): boolean;
589
+ /** Stops the running turn (the composer's stop button), including a pending slash-command expansion. */
590
+ stop(): void;
591
+ /**
592
+ * Records a decision on a proposal card and publishes it on
593
+ * {@link proposalDecisions$}. Run and Discard settle the card; Edit leaves it open.
594
+ * Nothing is executed here.
595
+ */
596
+ decideProposal(kind: AssistantProposalDecisionKind, proposal: AssistantProposal): void;
597
+ /** Appends the user turn and streams the transport's answer. */
598
+ private startTurn;
599
+ private apply;
600
+ private finishTurn;
601
+ private endMessage;
602
+ private append;
603
+ private nextId;
604
+ static ɵfac: i0.ɵɵFactoryDeclaration<AssistantService, never>;
605
+ static ɵprov: i0.ɵɵInjectableDeclaration<AssistantService>;
606
+ }
607
+ /**
608
+ * Cmd+J (macOS) / Ctrl+J toggles the assistant panel (ui-concept §4.3), also
609
+ * from text fields — it types nothing. Events another handler consumed and
610
+ * IME composition are ignored.
611
+ */
612
+ declare function assistantHotkey(event: KeyboardEvent): boolean;
613
+
614
+ /** The (English) text the panel shows while no backend exists. */
615
+ declare const ASSISTANT_NOT_CONNECTED_MESSAGE: string;
616
+ /**
617
+ * The default {@link AssistantTransport}: it is never `ready`, so the composer
618
+ * cannot send, and it never produces an answer. It exists so the panel can say
619
+ * honestly that there is no assistant backend (AB#5550) instead of faking one.
620
+ * The message follows `ASSISTANT_MESSAGES.notConnected`.
621
+ */
622
+ declare class NotConnectedAssistantTransport implements AssistantTransport {
623
+ private readonly message;
624
+ readonly status: i0.Signal<"unavailable">;
625
+ readonly statusMessage: i0.Signal<string | null>;
626
+ send(_request: AssistantSendRequest): Observable<AssistantStreamEvent>;
627
+ static ɵfac: i0.ɵɵFactoryDeclaration<NotConnectedAssistantTransport, never>;
628
+ static ɵprov: i0.ɵɵInjectableDeclaration<NotConnectedAssistantTransport>;
629
+ }
630
+
631
+ /**
632
+ * Test double for {@link AssistantTransport} (specs only — never provided by the app).
633
+ * Each `send` returns a Subject the spec drives with `emit` / `complete`.
634
+ */
635
+ declare class FakeAssistantTransport implements AssistantTransport {
636
+ readonly status: i0.WritableSignal<AssistantTransportStatus>;
637
+ readonly statusMessage: i0.WritableSignal<string | null>;
638
+ readonly requests: AssistantSendRequest[];
639
+ private turn;
640
+ send(request: AssistantSendRequest): Observable<AssistantStreamEvent>;
641
+ emit(event: AssistantStreamEvent): void;
642
+ fail(error: Error): void;
643
+ complete(): void;
644
+ }
645
+
646
+ /** Inputs of {@link deriveAssistantContext}, all read from the shell. */
647
+ interface AssistantContextSource {
648
+ /** Root of the router state snapshot. */
649
+ root: ActivatedRouteSnapshot | null | undefined;
650
+ /** Router URL of the page (query and fragment are dropped). */
651
+ url: string;
652
+ /** Breadcrumb labels of the page, root first. */
653
+ breadcrumbs: string[];
654
+ /** Active rail area and tab (see `ASSISTANT_PAGE_CONTEXT`). */
655
+ areaText?: string | null;
656
+ tabText?: string | null;
657
+ /** Entity set explicitly by an Explain entry point; wins over the route. */
658
+ explicitEntity?: AssistantExplainTarget | null;
659
+ }
660
+ /**
661
+ * Turns the current route into the panel's context chips (ui-concept §5.3):
662
+ * tenant, page ("Area › Tab", else the breadcrumb) and the selected entity — the
663
+ * one an Explain entry point named, otherwise the deepest id-like route param
664
+ * (labelled with the last breadcrumb). Pure, so it is tested without a router.
665
+ */
666
+ declare function deriveAssistantContext(source: AssistantContextSource, messages?: Partial<AssistantMessages> | null): AssistantContextChip[];
667
+
668
+ /** Result of {@link selectAssistantAttachments}. */
669
+ interface AssistantAttachmentSelection {
670
+ /** The files that stay attached (previous ones first). */
671
+ files: File[];
672
+ /** One translated message per rejected file (or one for "too many"). */
673
+ errors: string[];
674
+ }
675
+ /** "512 B", "1.5 KB", "10 MB" — for the size-limit message. */
676
+ declare function formatAssistantFileSize(bytes: number): string;
677
+ /**
678
+ * Whether a file matches an `accept` list (`<input accept>` syntax: MIME types, `type/*`
679
+ * wildcards and `.ext` extensions, comma separated). An empty list accepts everything.
680
+ */
681
+ declare function assistantFileAccepted(file: Pick<File, 'name' | 'type'>, accept: string | undefined): boolean;
682
+ /**
683
+ * Adds newly picked files to the already attached ones, enforcing the transport's
684
+ * {@link AssistantAttachmentOptions}: wrong type and too large files are rejected individually,
685
+ * files beyond `maxFiles` (default 1) are dropped with one "too many" message. With
686
+ * `maxFiles: 1` a newly picked valid file replaces the attached one. Pure.
687
+ */
688
+ declare function selectAssistantAttachments(current: readonly File[], added: readonly File[], options: AssistantAttachmentOptions, messages: AssistantMessages): AssistantAttachmentSelection;
689
+
690
+ /**
691
+ * Default renderer of a tool call in the thread: tool name in mono plus its status.
692
+ * Replace it through `ASSISTANT_TOOL_CALL_COMPONENT`.
693
+ */
694
+ declare class AssistantToolCallRowComponent {
695
+ readonly call: i0.InputSignal<AssistantToolCall>;
696
+ static ɵfac: i0.ɵɵFactoryDeclaration<AssistantToolCallRowComponent, never>;
697
+ static ɵcmp: i0.ɵɵComponentDeclaration<AssistantToolCallRowComponent, "mm-assistant-tool-call-row", never, { "call": { "alias": "call"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
698
+ }
699
+
700
+ /**
701
+ * Renders its template only while the assistant is enabled — with the flag off
702
+ * the element is not in the DOM at all. Use it on every AI affordance:
703
+ *
704
+ * ```html
705
+ * <button *mmIfAssistant type="button" class="ai-action"
706
+ * [mmAssistantExplain]="{ label: adapter.name, rtId: adapter.rtId, ckTypeId: 'System.Communication/Adapter' }">
707
+ * ✦ Explain
708
+ * </button>
709
+ * ```
710
+ */
711
+ declare class IfAssistantDirective {
712
+ constructor();
713
+ static ɵfac: i0.ɵɵFactoryDeclaration<IfAssistantDirective, never>;
714
+ static ɵdir: i0.ɵɵDirectiveDeclaration<IfAssistantDirective, "[mmIfAssistant]", never, {}, {}, never, never, true, never>;
715
+ }
716
+ /**
717
+ * "✦ Explain <object>" entry point (ui-concept §5.4): a click opens the assistant
718
+ * panel with `/explain <label>` in the composer and the object as context chip.
719
+ * Nothing is sent. Pair it with {@link IfAssistantDirective} so the trigger does
720
+ * not exist while the assistant is disabled; code without a template (a
721
+ * `CommandItem.onClick`) calls `AssistantService.requestExplain()` instead.
722
+ */
723
+ declare class AssistantExplainDirective {
724
+ protected readonly assistant: AssistantService;
725
+ readonly target: i0.InputSignal<AssistantExplainTarget>;
726
+ protected explain(): void;
727
+ static ɵfac: i0.ɵɵFactoryDeclaration<AssistantExplainDirective, never>;
728
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AssistantExplainDirective, "[mmAssistantExplain]", never, { "target": { "alias": "mmAssistantExplain"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
729
+ }
730
+
731
+ export { ASSISTANT_ENABLED, ASSISTANT_MESSAGES, ASSISTANT_NOT_CONNECTED_MESSAGE, ASSISTANT_PAGE_CONTEXT, ASSISTANT_PANEL_OPTIONS, ASSISTANT_SLASH_COMMANDS, ASSISTANT_TOOL_CALL_COMPONENT, ASSISTANT_TRANSPORT, AssistantExplainDirective, AssistantService, AssistantToolCallRowComponent, DEFAULT_ASSISTANT_MESSAGES, DEFAULT_ASSISTANT_PANEL_MAX_WIDTH, DEFAULT_ASSISTANT_PANEL_MIN_WIDTH, DEFAULT_ASSISTANT_PANEL_STORAGE_KEY, DEFAULT_ASSISTANT_SLASH_COMMANDS, FakeAssistantTransport, IfAssistantDirective, NotConnectedAssistantTransport, assistantFileAccepted, assistantHotkey, assistantMessages, deriveAssistantContext, formatAssistantFileSize, formatAssistantMessage, proposalDecisionLabel, resolveAssistantMessages, selectAssistantAttachments };
732
+ export type { AssistantAsyncResult, AssistantAttachmentOptions, AssistantAttachmentRef, AssistantAttachmentSelection, AssistantContextChip, AssistantContextKind, AssistantContextSource, AssistantExplainTarget, AssistantMessages, AssistantOpenRequest, AssistantPageContext, AssistantPanelMode, AssistantPanelOptions, AssistantProposal, AssistantProposalDecision, AssistantProposalDecisionKind, AssistantSendRequest, AssistantSessionSummary, AssistantSlashCommand, AssistantStarterQuestions, AssistantStreamEvent, AssistantThreadItem, AssistantToolCall, AssistantTransport, AssistantTransportStatus };