@bitforgehq/ui 0.4.0 → 0.5.0-next.3

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 (80) hide show
  1. package/CHANGELOG.md +303 -0
  2. package/README.md +12 -12
  3. package/ai/MANIFEST.index.json +65 -2
  4. package/ai/MANIFEST.json +2221 -737
  5. package/ai/REFERENCE.index.json +161 -0
  6. package/ai/REFERENCE.json +1336 -27
  7. package/ai/UI_GUIDE.md +61 -20
  8. package/ai/components/app-frame.md +1 -1
  9. package/ai/components/chat-composer.md +357 -0
  10. package/ai/components/chat-message.md +251 -0
  11. package/ai/components/chat-thread.md +521 -0
  12. package/ai/components/checkbox.md +7 -0
  13. package/ai/components/combobox.md +12 -0
  14. package/ai/components/date-input.md +9 -0
  15. package/ai/components/datetime-input.md +2 -0
  16. package/ai/components/field.md +10 -0
  17. package/ai/components/file-input.md +498 -0
  18. package/ai/components/header.md +1 -1
  19. package/ai/components/image-input.md +368 -0
  20. package/ai/components/image-viewer.md +309 -0
  21. package/ai/components/media-card.md +478 -0
  22. package/ai/components/menu-button.md +2 -0
  23. package/ai/components/menu.md +10 -0
  24. package/ai/components/multi-combobox.md +7 -0
  25. package/ai/components/nav-group.md +10 -3
  26. package/ai/components/nav-item.md +11 -2
  27. package/ai/components/number-input.md +2 -7
  28. package/ai/components/popover.md +13 -0
  29. package/ai/components/radio-group.md +6 -0
  30. package/ai/components/segmented.md +14 -2
  31. package/ai/components/select.md +100 -42
  32. package/ai/components/sidebar-toggle.md +16 -9
  33. package/ai/components/sidebar.md +41 -13
  34. package/ai/components/tab-group.md +18 -2
  35. package/ai/components/table.md +17 -2
  36. package/ai/components/theme-menu-button.md +1 -0
  37. package/ai/components/time-input.md +2 -0
  38. package/ai/components/toolbar.md +7 -18
  39. package/ai/reference/bf-chat-composer-harness.md +64 -0
  40. package/ai/reference/bf-chat-message-role.md +27 -0
  41. package/ai/reference/bf-chat-message-status.md +28 -0
  42. package/ai/reference/bf-fade-in-directive.md +18 -0
  43. package/ai/reference/bf-file-input-harness.md +67 -0
  44. package/ai/reference/bf-file-input-variant.md +27 -0
  45. package/ai/reference/bf-image-input-harness.md +67 -0
  46. package/ai/reference/bf-image-input-shape.md +27 -0
  47. package/ai/reference/bf-image-viewer-actions-context.md +29 -0
  48. package/ai/reference/bf-image-viewer-harness.md +87 -0
  49. package/ai/reference/bf-image-viewer-image.md +25 -0
  50. package/ai/reference/bf-image-viewer-options.md +33 -0
  51. package/ai/reference/bf-image-viewer-origin.md +23 -0
  52. package/ai/reference/bf-image-viewer-ref.md +55 -0
  53. package/ai/reference/bf-image-viewer-service.md +32 -0
  54. package/ai/reference/bf-labels.md +105 -75
  55. package/ai/reference/bf-media-card-aspect.md +28 -0
  56. package/ai/reference/bf-media-card-harness.md +66 -0
  57. package/ai/reference/bf-motion-list-directive.md +22 -0
  58. package/ai/reference/bf-prose-directive.md +16 -0
  59. package/ai/reference/bf-rejected-file-reason.md +28 -0
  60. package/ai/reference/bf-rejected-file.md +28 -0
  61. package/ai/reference/bf-select-harness.md +14 -5
  62. package/ai/reference/bf-select-option.md +2 -0
  63. package/ai/reference/bf-size.md +3 -0
  64. package/ai/reference/bf_table_sort_select.md +23 -0
  65. package/ai/reference/provide-bf-labels.md +10 -3
  66. package/ai/reference/provide-bf-select.md +20 -0
  67. package/bin/bf-ui.js +1 -0
  68. package/fesm2022/bitforgehq-ui-overlay.mjs +2649 -606
  69. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  70. package/fesm2022/bitforgehq-ui-testing.mjs +356 -10
  71. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  72. package/fesm2022/bitforgehq-ui.mjs +7531 -3989
  73. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  74. package/llms-full.txt +4151 -130
  75. package/llms.txt +31 -1
  76. package/package.json +1 -1
  77. package/theme.css +60 -0
  78. package/types/bitforgehq-ui-overlay.d.ts +684 -7
  79. package/types/bitforgehq-ui-testing.d.ts +434 -6
  80. package/types/bitforgehq-ui.d.ts +1473 -182
@@ -0,0 +1,251 @@
1
+ # bf-chat-message
2
+
3
+ One chat message, a user bubble or a full-width assistant reply that can stream
4
+
5
+ `BfChatMessageComponent` from `@bitforgehq/ui` · Data display · since 0.5.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { BfChatMessageComponent, BfChatThreadComponent } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-chat-message overview.
15
+ *
16
+ * A user message is a tinted bubble at the inline end and an assistant message spans the row
17
+ * with no bubble. Each one names its author and time in a small header; the time is a `time`
18
+ * element with the exact value, shown as hours and minutes or as `timeText`.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-chat-message-overview',
22
+ imports: [BfChatThreadComponent, BfChatMessageComponent],
23
+ template: `
24
+ <bf-chat-thread label="Two messages">
25
+ <bf-chat-message role="user" author="You" time="2026-11-06T09:30:00Z" timeText="9:30">
26
+ What should the cafe's first menu board say?
27
+ </bf-chat-message>
28
+ <bf-chat-message author="Assistant" time="2026-11-06T09:30:00Z" timeText="9:30">
29
+ <p>
30
+ Keep it to five lines: the coffee, the tea, one cake, one sandwich and the price of a
31
+ refill.
32
+ </p>
33
+ <p>Write it by hand. Regulars read a hand-written board as a sign that someone cares.</p>
34
+ </bf-chat-message>
35
+ </bf-chat-thread>
36
+ `,
37
+ })
38
+ export class ChatMessageOverviewExampleComponent {}
39
+ ```
40
+
41
+ ## Overview
42
+
43
+ One message of a `bf-chat-thread`. A `user` message is a tinted bubble at the inline end; an
44
+ `assistant` message is full width with no bubble. The default slot is the message text, styled
45
+ as prose (`bfProse`) for markdown the app renders. An optional header names the `author` and
46
+ the `time`. While `streaming`, a caret pulses at the end of the text and the message is
47
+ `aria-busy`, so the thread announces it once, when it is done. A `stopped` or `failed` message
48
+ shows a short status line, with the `action` slot beside it (a Retry button).
49
+
50
+ One message of a [bf-chat-thread](https://bitforge-ui.web.app/components/chat-thread): a tinted bubble for the user, a full-width reply without a bubble for the assistant, with an optional header, a streaming caret and a status line.
51
+
52
+ ## When to use
53
+
54
+ Use `bf-chat-message` inside a [bf-chat-thread](https://bitforge-ui.web.app/components/chat-thread), one per message. Use [bf-timeline-event](https://bitforge-ui.web.app/components/timeline-event) instead for an event in a history, and [bf-alert](https://bitforge-ui.web.app/components/alert) for a message from the app itself rather than from a conversation.
55
+
56
+ ## Basic usage
57
+
58
+ `role` is `assistant` (the default: full width, no bubble) or `user` (a `primary-soft` bubble at the inline end, up to 85 % of the row). The default slot is the text: plain text, paragraphs, or the app's rendered markdown. `author` and `time` (a Date or an ISO 8601 string, shown as hours and minutes in the app's locale, or as `timeText`) fill a small header above the message; leave both empty for none.
59
+
60
+ ```ts
61
+ import { Component } from '@angular/core';
62
+ import { BfChatMessageComponent, BfChatThreadComponent } from '@bitforgehq/ui';
63
+
64
+ /**
65
+ * The bf-chat-message overview.
66
+ *
67
+ * A user message is a tinted bubble at the inline end and an assistant message spans the row
68
+ * with no bubble. Each one names its author and time in a small header; the time is a `time`
69
+ * element with the exact value, shown as hours and minutes or as `timeText`.
70
+ */
71
+ @Component({
72
+ selector: 'app-example-chat-message-overview',
73
+ imports: [BfChatThreadComponent, BfChatMessageComponent],
74
+ template: `
75
+ <bf-chat-thread label="Two messages">
76
+ <bf-chat-message role="user" author="You" time="2026-11-06T09:30:00Z" timeText="9:30">
77
+ What should the cafe's first menu board say?
78
+ </bf-chat-message>
79
+ <bf-chat-message author="Assistant" time="2026-11-06T09:30:00Z" timeText="9:30">
80
+ <p>
81
+ Keep it to five lines: the coffee, the tea, one cake, one sandwich and the price of a
82
+ refill.
83
+ </p>
84
+ <p>Write it by hand. Regulars read a hand-written board as a sign that someone cares.</p>
85
+ </bf-chat-message>
86
+ </bf-chat-thread>
87
+ `,
88
+ })
89
+ export class ChatMessageOverviewExampleComponent {}
90
+ ```
91
+
92
+ ## Streaming
93
+
94
+ Set `streaming` while the reply's text is still arriving and append the text as it comes. A caret pulses at the end of the text (after bare text, inside the last paragraph or list item, also one wrapper deep), and the message and the thread are `aria-busy`, so a screen reader reads the reply once, when `streaming` goes back to false. An empty streaming message shows the caret alone, as a "thinking" cue.
95
+
96
+ ## Stopped and failed
97
+
98
+ `status` is `done` (the default), `stopped` (a muted "Stopped" line) or `failed` (a "Something went wrong" line in the danger colour). `statusMessage` replaces the built-in line, such as with the error the server returned. The `action` slot sits beside the line, for a Retry button.
99
+
100
+ ```ts
101
+ import { Component, signal } from '@angular/core';
102
+ import {
103
+ BfButtonComponent,
104
+ BfChatMessageComponent,
105
+ BfChatThreadComponent,
106
+ type BfChatMessageStatus,
107
+ } from '@bitforgehq/ui';
108
+
109
+ /**
110
+ * Stopped and failed messages.
111
+ *
112
+ * A reply the user stopped shows "Stopped" under its text; a reply that failed shows "Something
113
+ * went wrong" in the danger colour, with a Retry button in the message's `action` slot. Retry
114
+ * here answers at once; a real app would ask its model again.
115
+ */
116
+ @Component({
117
+ selector: 'app-example-chat-thread-states',
118
+ imports: [BfChatThreadComponent, BfChatMessageComponent, BfButtonComponent],
119
+ template: `
120
+ <bf-chat-thread label="Message states" class="max-h-[24rem]">
121
+ <bf-chat-message role="user">Write a slogan for the cafe.</bf-chat-message>
122
+ <bf-chat-message status="stopped">
123
+ <p>Good coffee, slow mornings and a</p>
124
+ </bf-chat-message>
125
+ <bf-chat-message role="user">Try a shorter one.</bf-chat-message>
126
+ <bf-chat-message [status]="status()">
127
+ @if (status() === 'done') {
128
+ <p>Stay for the second cup.</p>
129
+ }
130
+ @if (status() === 'failed') {
131
+ <bf-button bfSlot="action" variant="text" size="sm" (clicked)="retry()">Retry</bf-button>
132
+ }
133
+ </bf-chat-message>
134
+ </bf-chat-thread>
135
+ `,
136
+ })
137
+ export class ChatThreadStatesExampleComponent {
138
+ readonly status = signal<BfChatMessageStatus>('failed');
139
+
140
+ retry(): void {
141
+ this.status.set('done');
142
+ }
143
+ }
144
+ ```
145
+
146
+ ## Rich content
147
+
148
+ The body is a `bfProse` element: headings, lists, links, inline code, code blocks, quotes and rules in the library tokens. The `content` slot below it holds inline content the prose styles leave alone, such as cards, images or chips.
149
+
150
+ ```ts
151
+ import { Component, signal } from '@angular/core';
152
+ import {
153
+ BfCardComponent,
154
+ BfChatMessageComponent,
155
+ BfChatThreadComponent,
156
+ BfChipComponent,
157
+ } from '@bitforgehq/ui';
158
+
159
+ /* The app renders its markdown to HTML (and sanitises it) itself; the message styles it. */
160
+ const REPLY_HTML = `
161
+ <h3>Three name ideas</h3>
162
+ <ol>
163
+ <li><strong>Ember and Oat</strong>: warm and rustic.</li>
164
+ <li><strong>Second Cup</strong>: playful, for regulars.</li>
165
+ <li><strong>Field Notes</strong>: quiet and bookish.</li>
166
+ </ol>
167
+ <p>Check the domain with <code>whois embersandoat.com</code> before you print the menus.</p>
168
+ <blockquote>A short name is easier to say out loud and to find.</blockquote>
169
+ `;
170
+
171
+ /**
172
+ * Rendered markdown and inline content.
173
+ *
174
+ * The reply's body is the app's rendered markdown, styled as prose: a heading, a numbered list,
175
+ * inline code and a quote. Below it, the `content` slot holds a card and chips the user can pick
176
+ * from, outside the prose styling.
177
+ */
178
+ @Component({
179
+ selector: 'app-example-chat-thread-rich-content',
180
+ imports: [BfChatThreadComponent, BfChatMessageComponent, BfCardComponent, BfChipComponent],
181
+ template: `
182
+ <bf-chat-thread label="Rich reply" class="max-h-[30rem]">
183
+ <bf-chat-message role="user" author="You">Name ideas, with a logo mood?</bf-chat-message>
184
+ <bf-chat-message author="Assistant">
185
+ <div [innerHTML]="replyHtml"></div>
186
+ <bf-card bfSlot="content" variant="tint" class="w-64">
187
+ <p class="text-label text-text font-bold">Logo mood</p>
188
+ <p class="text-caption text-text-muted">Hand-drawn, charcoal on oat paper</p>
189
+ </bf-card>
190
+ <div bfSlot="content" class="flex flex-wrap gap-2">
191
+ @for (name of names; track name) {
192
+ <bf-chip [selected]="picked() === name" (changed)="picked.set($event ? name : '')">{{
193
+ name
194
+ }}</bf-chip>
195
+ }
196
+ </div>
197
+ </bf-chat-message>
198
+ </bf-chat-thread>
199
+ `,
200
+ })
201
+ export class ChatThreadRichContentExampleComponent {
202
+ readonly replyHtml = REPLY_HTML;
203
+ readonly names = ['Ember and Oat', 'Second Cup', 'Field Notes'];
204
+ readonly picked = signal('');
205
+ }
206
+ ```
207
+
208
+ ## Desktop
209
+
210
+ A message has no hover or pointer state of its own; links in the prose underline more thickly on hover and show the focus ring on keyboard focus, and code blocks scroll sideways with the trackpad. The user bubble stops at 42rem on a wide window.
211
+
212
+ ## Theming
213
+
214
+ - User bubble: `primary-soft` background, `text` text, `rounded-field` corners. Assistant reply: no surface.
215
+ - Header: `text-muted` (the time), `text-secondary` (the author), the caption step.
216
+ - Prose: `text` body and headings, `primary-text` links with a `ring` focus ring, `surface-muted` inline code, `surface` code blocks with a `border` edge, `border-strong` quote rule with `text-secondary` text, `text-muted` list markers.
217
+ - Status line: `text-muted` for stopped, `danger` for failed.
218
+ - Caret: the text colour (`CanvasText` under forced colours), pulsing with `--animate-chat-caret`.
219
+
220
+ ## Accessibility
221
+
222
+ The message is plain content in the thread's log: no role of its own (the `role` input sets the look, and the host drops the attribute). The header's time is a `time` element whose `datetime` keeps the exact value. While `streaming`, the message is `aria-busy="true"` and the caret is a pseudo-element, which is never read. The status line is text in the log, so a screen reader announces it when the status changes. A `user` message is told apart by its `author` text, not only by its colour and side.
223
+
224
+ ## Inputs
225
+
226
+ | Name | Type | Default | Description |
227
+ | --------------- | --------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
228
+ | `role` | `BfChatMessageRole` (`'user' \| 'assistant'`) | `'assistant'` | Who wrote it: `user` (a bubble at the inline end) or `assistant` (full width, no bubble). |
229
+ | `author` | `string` | `''` | Whose message it is, shown in the header, such as "You" or the assistant's name; empty shows none. |
230
+ | `time` | `Date \| string \| null` | `null` | When it was written: a Date or an ISO 8601 string, shown as hours and minutes; empty or invalid shows no time. |
231
+ | `timeText` | `string` | `''` | Shown in place of the formatted time, such as "just now"; the `time` element's `datetime` keeps the exact value. |
232
+ | `streaming` | `boolean` | `false` | The text is still arriving: a caret pulses at its end and the message is `aria-busy`. |
233
+ | `status` | `BfChatMessageStatus` (`'done' \| 'stopped' \| 'failed'`) | `'done'` | How it ended: `done` (no status line), `stopped` or `failed`. |
234
+ | `statusMessage` | `string` | `''` | Shown in place of the built-in status line (`chatMessageStopped`, `chatMessageFailed`), such as the error the server returned. |
235
+
236
+ ## Slots
237
+
238
+ - `default`: The message text: plain text or the app's rendered markdown, styled as prose.
239
+ - `content`: Inline content below the text, such as cards, images or chips (not styled as prose).
240
+ - `action`: Beside the status line, such as a Retry button.
241
+
242
+ ## Notes
243
+
244
+ ### Do
245
+
246
+ - Set `author` on every message, so its speaker is never shown by colour and position alone.
247
+ - Turn `streaming` off when the last piece of text has arrived, also when the request fails or stops.
248
+
249
+ ### Don't
250
+
251
+ - Don't put buttons in the text: use the `action` slot for one beside the status line.