@iyulab/chat-components 0.5.0 → 0.5.2

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 (62) hide show
  1. package/CHANGELOG.md +78 -144
  2. package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.128.0}/helpers/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.128.0}/helpers/decorateMetadata.js +1 -1
  4. package/dist/components/blocks/UCodeBlock.js +4 -3
  5. package/dist/components/blocks/UFileBlock.js +2 -2
  6. package/dist/components/blocks/UJsonBlock.js +2 -2
  7. package/dist/components/blocks/UMarkedBlock.js +3 -27
  8. package/dist/components/blocks/URefBlock.js +2 -2
  9. package/dist/components/blocks/UTableBlock.js +4 -3
  10. package/dist/components/blocks/UTextBlock.js +2 -2
  11. package/dist/components/blocks/UThinkBlock.js +2 -2
  12. package/dist/components/blocks/UToolBlock.js +2 -2
  13. package/dist/components/buttons/UAttachButton.js +2 -2
  14. package/dist/components/buttons/UCopyButton.js +2 -2
  15. package/dist/components/buttons/UVoteButton.js +2 -2
  16. package/dist/components/intents/UQuestionIntent.js +2 -2
  17. package/dist/components/message/UMessage.js +2 -2
  18. package/dist/components/prompt/UPrompt.d.ts +5 -0
  19. package/dist/components/prompt/UPrompt.js +27 -5
  20. package/dist/components/references/URefCard.js +2 -2
  21. package/dist/components/references/URefCardGroup.js +4 -3
  22. package/dist/components/references/URefTag.js +2 -2
  23. package/dist/components/views/UChartView.js +2 -2
  24. package/dist/components/views/UImagesView.js +2 -2
  25. package/dist/components/views/UMapView.js +2 -2
  26. package/dist/components/views/UVideoView.js +2 -2
  27. package/dist/components/views/UView.js +2 -2
  28. package/dist/events/SendEvent.d.ts +15 -1
  29. package/dist/events/StopEvent.d.ts +11 -1
  30. package/dist/types/Intents.js +1 -1
  31. package/dist/types/Views.js +1 -1
  32. package/dist/utilities/HtmlPlaceholder.d.ts +11 -0
  33. package/dist/utilities/HtmlPlaceholder.js +26 -0
  34. package/package.json +11 -10
  35. package/skills/iyulab-chat-components/SKILL.md +107 -0
  36. package/skills/iyulab-chat-components/references/components/attach-button.md +69 -0
  37. package/skills/iyulab-chat-components/references/components/chart-view.md +65 -0
  38. package/skills/iyulab-chat-components/references/components/code-block.md +42 -0
  39. package/skills/iyulab-chat-components/references/components/copy-button.md +46 -0
  40. package/skills/iyulab-chat-components/references/components/file-block.md +64 -0
  41. package/skills/iyulab-chat-components/references/components/images-view.md +63 -0
  42. package/skills/iyulab-chat-components/references/components/json-block.md +41 -0
  43. package/skills/iyulab-chat-components/references/components/map-view.md +54 -0
  44. package/skills/iyulab-chat-components/references/components/marked-block.md +53 -0
  45. package/skills/iyulab-chat-components/references/components/message.md +60 -0
  46. package/skills/iyulab-chat-components/references/components/prompt.md +102 -0
  47. package/skills/iyulab-chat-components/references/components/question-intent.md +75 -0
  48. package/skills/iyulab-chat-components/references/components/ref-block.md +45 -0
  49. package/skills/iyulab-chat-components/references/components/ref-card-group.md +41 -0
  50. package/skills/iyulab-chat-components/references/components/ref-card.md +48 -0
  51. package/skills/iyulab-chat-components/references/components/ref-tag.md +40 -0
  52. package/skills/iyulab-chat-components/references/components/table-block.md +63 -0
  53. package/skills/iyulab-chat-components/references/components/text-block.md +59 -0
  54. package/skills/iyulab-chat-components/references/components/think-block.md +48 -0
  55. package/skills/iyulab-chat-components/references/components/tool-block.md +57 -0
  56. package/skills/iyulab-chat-components/references/components/video-view.md +55 -0
  57. package/skills/iyulab-chat-components/references/components/view.md +51 -0
  58. package/skills/iyulab-chat-components/references/components/vote-button.md +60 -0
  59. package/skills/iyulab-chat-components/references/types.md +115 -0
  60. package/skills/iyulab-chat-components/references/usage.md +254 -0
  61. package/skills/iyulab-chat-components/references/utilities/intent-prompt-builder.md +123 -0
  62. package/skills/iyulab-chat-components/references/utilities/view-prompt-builder.md +112 -0
@@ -0,0 +1,57 @@
1
+ # u-tool-block
2
+
3
+ ```ts
4
+ import '@iyulab/chat-components/dist/components/blocks/UToolBlock.js';
5
+ ```
6
+
7
+ **Tag:** `u-tool-block`
8
+
9
+ Displays a tool call's input and output as collapsible JSON trees. Used to show LLM tool usage inline in a message.
10
+
11
+ ```html
12
+ <!-- Tool call in progress -->
13
+ <u-tool-block loading title="search_web"></u-tool-block>
14
+
15
+ <!-- Completed tool call -->
16
+ <u-tool-block
17
+ title="search_web"
18
+ .input=${{ query: "weather today" }}
19
+ .output=${{ results: ["Sunny, 25°C"] }}
20
+ ></u-tool-block>
21
+
22
+ <!-- Expanded by default -->
23
+ <u-tool-block
24
+ title="code_execution"
25
+ .collapsed=${false}
26
+ .input=${{ code: "print('hello')" }}
27
+ .output=${{ stdout: "hello\n" }}
28
+ ></u-tool-block>
29
+ ```
30
+
31
+ Pass JSON via HTML attribute (uses `jsonAttrConverter`):
32
+
33
+ ```html
34
+ <u-tool-block
35
+ title="calculator"
36
+ input='{"expression": "2 + 2"}'
37
+ output='{"result": 4}'
38
+ ></u-tool-block>
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Properties
44
+
45
+ | Property | Type | Default | Reflect | Description |
46
+ |----------|------|---------|---------|-------------|
47
+ | `title` | `string` | `''` | — | Tool name shown in the header (falls back to 'Tool Usage') |
48
+ | `loading` | `boolean` | `false` | ✓ | Loading state. Shows spinner in header |
49
+ | `collapsed` | `boolean` | `true` | ✓ | Collapsed state. Toggle by clicking the header |
50
+ | `input` | `JsonNode` | `undefined` | — | Tool input JSON |
51
+ | `output` | `JsonNode` | `undefined` | — | Tool output JSON |
52
+
53
+ ## CSS Parts
54
+
55
+ | Part | Description |
56
+ |------|-------------|
57
+ | `body` | Input / output display area |
@@ -0,0 +1,55 @@
1
+ # u-video-view
2
+
3
+ ```ts
4
+ import '@iyulab/chat-components/dist/components/views/UVideoView.js';
5
+ ```
6
+
7
+ **Tag:** `u-video-view`
8
+
9
+ Embeds a video from YouTube, Vimeo, or a direct file URL. Automatically detects the platform from the URL and selects the appropriate player.
10
+
11
+ ```html
12
+ <!-- YouTube -->
13
+ <u-video-view src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"></u-video-view>
14
+
15
+ <!-- Vimeo -->
16
+ <u-video-view src="https://vimeo.com/123456789"></u-video-view>
17
+
18
+ <!-- Direct video file -->
19
+ <u-video-view
20
+ src="https://example.com/video.mp4"
21
+ poster="https://example.com/thumbnail.jpg"
22
+ ratio="4:3"
23
+ ></u-video-view>
24
+ ```
25
+
26
+ LLM output example (`view-json` code block):
27
+
28
+ ````
29
+ ```view-json
30
+ {
31
+ "tag": "u-video-view",
32
+ "properties": {
33
+ "src": "https://www.youtube.com/watch?v=VIDEO_ID"
34
+ }
35
+ }
36
+ ```
37
+ ````
38
+
39
+ ---
40
+
41
+ ## Properties
42
+
43
+ | Property | Type | Default | Description |
44
+ |----------|------|---------|-------------|
45
+ | `src` | `string` | `undefined` | Video URL: YouTube, Vimeo, or direct file |
46
+ | `poster` | `string` | `undefined` | Poster image URL for direct video files |
47
+ | `ratio` | `'16:9'\|'4:3'\|'1:1'` | `'16:9'` | Aspect ratio |
48
+
49
+ ## Supported Platforms
50
+
51
+ | Platform | URL Pattern | Embed Method |
52
+ |----------|-------------|--------------|
53
+ | YouTube | `youtube.com`, `youtu.be` | `youtube.com/embed/{id}` iframe |
54
+ | Vimeo | `vimeo.com` | `player.vimeo.com/video/{id}` iframe |
55
+ | Other | any URL | Native `<video>` element |
@@ -0,0 +1,51 @@
1
+ # u-view
2
+
3
+ ```ts
4
+ import '@iyulab/chat-components/dist/components/views/UView.js';
5
+ ```
6
+
7
+ **Tag:** `u-view`
8
+
9
+ Dynamic custom element renderer. Creates the element specified by `tag`, binds `properties` to it, and handles loading and error states. Used internally by `u-marked-block` to render `view-json` code blocks.
10
+
11
+ ```html
12
+ <!-- Render u-chart-view dynamically -->
13
+ <u-view
14
+ tag="u-chart-view"
15
+ .properties=${{
16
+ type: 'bar',
17
+ data: { labels: ['A', 'B'], datasets: [{ data: [10, 20] }] }
18
+ }}
19
+ ></u-view>
20
+
21
+ <!-- Loading state (skeleton placeholder) -->
22
+ <u-view tag="u-chart-view" loading></u-view>
23
+ ```
24
+
25
+ How `u-marked-block` processes a `view-json` block:
26
+
27
+ ````
28
+ ```view-json
29
+ {
30
+ "tag": "u-images-view",
31
+ "properties": { "items": [{ "src": "https://..." }] }
32
+ }
33
+ ```
34
+ ````
35
+
36
+ This is converted to `<u-view tag="u-images-view" ...>` via `UView.buildHTML()`.
37
+
38
+ ---
39
+
40
+ ## Properties
41
+
42
+ | Property | Type | Default | Reflect | Description |
43
+ |----------|------|---------|---------|-------------|
44
+ | `tag` | `string` | `undefined` | — | Custom element tag to render. Must already be registered |
45
+ | `properties` | `Record<string, unknown>` | `undefined` | — | Properties to bind to the rendered element |
46
+ | `loading` | `boolean` | `false` | ✓ | Shows a skeleton placeholder instead of the element |
47
+ | `blacklist` | `string[]` | `['innerHTML', 'outerHTML', ...]` | — | Property names blocked from binding for XSS prevention |
48
+
49
+ ## Security
50
+
51
+ The following property names are blocked by default to prevent XSS: `innerHTML`, `outerHTML`, `textContent`, `innerText`, `outerText`, `srcdoc`. Extend `blacklist` to add more restrictions.
@@ -0,0 +1,60 @@
1
+ # u-vote-button
2
+
3
+ ```ts
4
+ import '@iyulab/chat-components/dist/components/buttons/UVoteButton.js';
5
+ ```
6
+
7
+ **Tag:** `u-vote-button`
8
+
9
+ Thumbs-up / thumbs-down vote button pair. Clicking a button toggles its active state; clicking the same button again resets back to `none`.
10
+
11
+ ```html
12
+ <!-- Basic usage -->
13
+ <u-vote-button></u-vote-button>
14
+
15
+ <!-- Pre-set state -->
16
+ <u-vote-button value="up"></u-vote-button>
17
+
18
+ <!-- With tooltips -->
19
+ <u-vote-button>
20
+ <span slot="up">Helpful</span>
21
+ <span slot="down">Not helpful</span>
22
+ </u-vote-button>
23
+ ```
24
+
25
+ ```ts
26
+ const voteBtn = document.querySelector('u-vote-button');
27
+
28
+ voteBtn.addEventListener('change', () => {
29
+ console.log('vote:', voteBtn.value); // 'up' | 'down' | 'none'
30
+ });
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Slots
36
+
37
+ | Name | Description |
38
+ |------|-------------|
39
+ | `up` | Tooltip text for the thumbs-up button |
40
+ | `down` | Tooltip text for the thumbs-down button |
41
+
42
+ ## Properties
43
+
44
+ | Property | Type | Default | Description |
45
+ |----------|------|---------|-------------|
46
+ | `value` | `'none'\|'up'\|'down'` | `'none'` | Current vote state |
47
+
48
+ ## Events
49
+
50
+ | Event | Description |
51
+ |-------|-------------|
52
+ | `change` | Fired when vote state changes (`bubbles: true, composed: true`) |
53
+
54
+ ## CSS Parts
55
+
56
+ | Part | Description |
57
+ |------|-------------|
58
+ | `up-btn` | Thumbs-up button |
59
+ | `down-btn` | Thumbs-down button |
60
+ | `icon` | Button icon |
@@ -0,0 +1,115 @@
1
+ # Types
2
+
3
+ ## BlockItem
4
+
5
+ ```ts
6
+ import type { BlockItem } from '@iyulab/chat-components';
7
+ ```
8
+
9
+ Union type for all message content blocks. Used to exchange structured message data with a server API.
10
+
11
+ ```ts
12
+ type BlockItem =
13
+ | ThinkingBlockItem
14
+ | TextBlockItem
15
+ | MarkdownBlockItem
16
+ | FileBlockItem
17
+ | ToolBlockItem
18
+ | ReferenceBlockItem;
19
+ ```
20
+
21
+ ### ThinkingBlockItem → `u-think-block`
22
+
23
+ ```ts
24
+ interface ThinkingBlockItem {
25
+ type: 'thinking';
26
+ loading?: boolean; // Streaming in progress
27
+ value?: string; // Reasoning text content
28
+ }
29
+ ```
30
+
31
+ ### TextBlockItem → `u-text-block`
32
+
33
+ ```ts
34
+ interface TextBlockItem {
35
+ type: 'text';
36
+ value?: string;
37
+ }
38
+ ```
39
+
40
+ ### MarkdownBlockItem → `u-marked-block`
41
+
42
+ ```ts
43
+ interface MarkdownBlockItem {
44
+ type: 'markdown';
45
+ value?: string;
46
+ refs?: ReferenceCitation[];
47
+ }
48
+ ```
49
+
50
+ ### FileBlockItem → `u-file-block` / `u-prompt.files`
51
+
52
+ ```ts
53
+ interface FileBlockItem {
54
+ type: 'file';
55
+ status?: 'idle' | 'uploading' | 'error';
56
+ name?: string; // File name
57
+ size?: number; // File size in bytes
58
+ mimeType?: string; // MIME type, e.g. "image/png"
59
+ url?: string; // Download URL
60
+ data?: any;
61
+ }
62
+ ```
63
+
64
+ ### ToolBlockItem → `u-tool-block`
65
+
66
+ ```ts
67
+ interface ToolBlockItem {
68
+ type: 'tool';
69
+ loading?: boolean;
70
+ title?: string;
71
+ input?: JsonNode;
72
+ output?: JsonNode;
73
+ }
74
+ ```
75
+
76
+ ### ReferenceBlockItem → `u-ref-block`
77
+
78
+ ```ts
79
+ interface ReferenceBlockItem {
80
+ type: 'reference';
81
+ sources: ReferenceSource[];
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## ReferenceSource & ReferenceCitation
88
+
89
+ ```ts
90
+ interface ReferenceSource {
91
+ type: 'web' | 'document';
92
+ url?: string;
93
+ title?: string;
94
+ snippet?: string;
95
+ tags?: string[];
96
+ }
97
+
98
+ interface ReferenceCitation {
99
+ startIndex: number; // Start char index in markdown text
100
+ endIndex: number; // End char index
101
+ label?: string; // Display label, e.g. "[1]"
102
+ sources: ReferenceSource[];
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## JsonNode
109
+
110
+ ```ts
111
+ type JsonValue = string | number | boolean | null;
112
+ type JsonArray = JsonNode[];
113
+ type JsonObject = { [key: string]: JsonNode };
114
+ type JsonNode = JsonValue | JsonArray | JsonObject;
115
+ ```
@@ -0,0 +1,254 @@
1
+ # Usage Guide
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ npm install @iyulab/chat-components
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Importing Components
12
+
13
+ ### All at once
14
+
15
+ ```ts
16
+ import '@iyulab/chat-components';
17
+ ```
18
+
19
+ ### Individual (tree-shakable)
20
+
21
+ ```ts
22
+ import '@iyulab/chat-components/dist/components/message/UMessage.js';
23
+ import '@iyulab/chat-components/dist/components/prompt/UPrompt.js';
24
+ import '@iyulab/chat-components/dist/components/blocks/UMarkedBlock.js';
25
+ ```
26
+
27
+ ### Types only
28
+
29
+ ```ts
30
+ import type { BlockItem, ReferenceSource } from '@iyulab/chat-components';
31
+ import { PresetIntent, PresetView } from '@iyulab/chat-components';
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Basic Chat UI
37
+
38
+ Minimal setup connecting a message list and input prompt:
39
+
40
+ ```html
41
+ <div id="chat-container">
42
+ <div id="messages"></div>
43
+ <u-prompt id="prompt" placeholder="Type a message..."></u-prompt>
44
+ </div>
45
+ ```
46
+
47
+ ```ts
48
+ import '@iyulab/chat-components';
49
+
50
+ const prompt = document.getElementById('prompt');
51
+ const messages = document.getElementById('messages');
52
+
53
+ // Handle send event
54
+ prompt.addEventListener('send', () => {
55
+ const value = prompt.value;
56
+ if (!value) return;
57
+
58
+ // Add user message
59
+ const userMsg = document.createElement('u-message');
60
+ userMsg.position = 'right';
61
+ userMsg.innerHTML = `<u-text-block .value="${value}"></u-text-block>`;
62
+ messages.appendChild(userMsg);
63
+ prompt.value = '';
64
+
65
+ // Add AI response (loading)
66
+ const aiMsg = document.createElement('u-message');
67
+ aiMsg.loading = true;
68
+ messages.appendChild(aiMsg);
69
+
70
+ // Handle LLM streaming...
71
+ });
72
+
73
+ // Handle file attachments
74
+ prompt.addEventListener('attach', (e) => {
75
+ const { files } = e.detail;
76
+ prompt.files = files.map(f => ({
77
+ type: 'file',
78
+ name: f.name,
79
+ size: f.size,
80
+ mimeType: f.type,
81
+ status: 'idle',
82
+ }));
83
+ });
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Rendering Markdown Responses
89
+
90
+ Stream LLM responses as markdown:
91
+
92
+ ```html
93
+ <u-message>
94
+ <u-marked-block
95
+ .value=${"# Hello\n\nThis is **markdown** with `code`."}
96
+ ></u-marked-block>
97
+ </u-message>
98
+ ```
99
+
100
+ With citation references:
101
+
102
+ ```ts
103
+ import type { ReferenceCitation } from '@iyulab/chat-components';
104
+
105
+ const refs: ReferenceCitation[] = [
106
+ {
107
+ startIndex: 10,
108
+ endIndex: 20,
109
+ label: '[1]',
110
+ sources: [{ type: 'web', url: 'https://example.com', title: 'Example' }]
111
+ }
112
+ ];
113
+
114
+ markedBlock.value = '...markdown text...';
115
+ markedBlock.refs = refs;
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Intent System
121
+
122
+ Make the LLM output structured intent-json blocks by adding instructions to the system prompt:
123
+
124
+ ```ts
125
+ import { IntentPromptBuilder, PresetIntent } from '@iyulab/chat-components';
126
+
127
+ // Use preset intents
128
+ IntentPromptBuilder.instance.use(PresetIntent.Questions);
129
+
130
+ // Add custom intent
131
+ IntentPromptBuilder.instance.add({
132
+ type: 'rating',
133
+ description: 'Ask user to rate something on a scale',
134
+ properties: {
135
+ label: { type: 'string', description: 'What to rate' },
136
+ max: { type: 'number', description: 'Max score (default: 5)' }
137
+ },
138
+ required: ['label']
139
+ });
140
+
141
+ // Inject into system prompt
142
+ const systemPrompt = IntentPromptBuilder.instance.build();
143
+ ```
144
+
145
+ Parse intent-json from the LLM response and render components:
146
+
147
+ ```ts
148
+ const [cleanText, intents] = IntentPromptBuilder.instance.parse(llmResponse);
149
+
150
+ for (const intent of intents) {
151
+ if (intent.type === 'question') {
152
+ const el = document.createElement('u-question-intent');
153
+ el.question = intent.properties?.question;
154
+ el.choices = intent.properties?.choices ?? [];
155
+ messageEl.appendChild(el);
156
+ }
157
+ }
158
+
159
+ markedBlock.value = cleanText;
160
+ ```
161
+
162
+ ---
163
+
164
+ ## View System
165
+
166
+ Let the LLM render rich content (charts, maps, videos, etc.) via view-json code blocks:
167
+
168
+ ```ts
169
+ import { ViewPromptBuilder, PresetView } from '@iyulab/chat-components';
170
+
171
+ // Use all preset views
172
+ ViewPromptBuilder.instance.use(PresetView.All);
173
+ // Or selectively
174
+ ViewPromptBuilder.instance.use(PresetView.Images | PresetView.Chart);
175
+
176
+ // Inject into system prompt
177
+ const systemPrompt = ViewPromptBuilder.instance.build();
178
+ ```
179
+
180
+ When `u-marked-block` encounters a view-json code block from the LLM, it automatically renders it via `u-view`:
181
+
182
+ ````
183
+ ```view-json
184
+ {
185
+ "tag": "u-chart-view",
186
+ "properties": {
187
+ "type": "bar",
188
+ "data": { "labels": ["A", "B"], "datasets": [{ "data": [10, 20] }] }
189
+ }
190
+ }
191
+ ```
192
+ ````
193
+
194
+ ---
195
+
196
+ ## Rendering BlockItem Arrays
197
+
198
+ Pattern for rendering structured message data received from a server:
199
+
200
+ ```ts
201
+ import type { BlockItem } from '@iyulab/chat-components';
202
+
203
+ function renderBlocks(blocks: BlockItem[], container: HTMLElement) {
204
+ for (const block of blocks) {
205
+ switch (block.type) {
206
+ case 'text': {
207
+ const el = document.createElement('u-text-block');
208
+ el.value = block.value;
209
+ container.appendChild(el);
210
+ break;
211
+ }
212
+ case 'markdown': {
213
+ const el = document.createElement('u-marked-block');
214
+ el.value = block.value;
215
+ el.refs = block.refs;
216
+ container.appendChild(el);
217
+ break;
218
+ }
219
+ case 'thinking': {
220
+ const el = document.createElement('u-think-block');
221
+ el.loading = block.loading ?? false;
222
+ el.value = block.value;
223
+ container.appendChild(el);
224
+ break;
225
+ }
226
+ case 'tool': {
227
+ const el = document.createElement('u-tool-block');
228
+ el.loading = block.loading ?? false;
229
+ el.title = block.title ?? '';
230
+ el.input = block.input;
231
+ el.output = block.output;
232
+ container.appendChild(el);
233
+ break;
234
+ }
235
+ case 'file': {
236
+ const el = document.createElement('u-file-block');
237
+ el.name = block.name;
238
+ el.size = block.size;
239
+ el.type = block.mimeType;
240
+ el.url = block.url;
241
+ el.status = block.status ?? 'idle';
242
+ container.appendChild(el);
243
+ break;
244
+ }
245
+ case 'reference': {
246
+ const el = document.createElement('u-ref-block');
247
+ el.sources = block.sources;
248
+ container.appendChild(el);
249
+ break;
250
+ }
251
+ }
252
+ }
253
+ }
254
+ ```
@@ -0,0 +1,123 @@
1
+ # IntentPromptBuilder
2
+
3
+ ```ts
4
+ import { IntentPromptBuilder, PresetIntent } from '@iyulab/chat-components';
5
+ ```
6
+
7
+ Singleton utility that registers intents, generates LLM system prompt instructions, and parses `intent-json` blocks from LLM responses.
8
+
9
+ ---
10
+
11
+ ## Overview
12
+
13
+ ```ts
14
+ import { IntentPromptBuilder, PresetIntent } from '@iyulab/chat-components';
15
+
16
+ const builder = IntentPromptBuilder.instance;
17
+
18
+ // 1. Register preset intents
19
+ builder.use(PresetIntent.Questions);
20
+
21
+ // 2. Register custom intents
22
+ builder.add({
23
+ type: 'rating',
24
+ description: 'Prompt the user to rate something on a scale',
25
+ properties: {
26
+ label: { type: 'string', description: 'What to rate' },
27
+ max: { type: 'number', description: 'Maximum score (default: 5)' }
28
+ },
29
+ required: ['label']
30
+ });
31
+
32
+ // 3. Inject into LLM system prompt
33
+ const instruction = builder.build();
34
+
35
+ // 4. Parse LLM response
36
+ const [cleanText, intents] = builder.parse(llmResponse);
37
+ // cleanText → markdown with intent-json blocks removed
38
+ // intents → parsed IntentSchema[]
39
+
40
+ // 5. Render components
41
+ for (const intent of intents) {
42
+ if (intent.type === 'question') {
43
+ const el = document.createElement('u-question-intent');
44
+ el.question = intent.properties?.question as string;
45
+ el.choices = intent.properties?.choices as string[] ?? [];
46
+ container.appendChild(el);
47
+ }
48
+ }
49
+
50
+ markedBlock.value = cleanText;
51
+ ```
52
+
53
+ ---
54
+
55
+ ## API
56
+
57
+ ### `IntentPromptBuilder.instance`
58
+
59
+ Returns the singleton. Created on first access.
60
+
61
+ ### `.use(flags: PresetIntent): this`
62
+
63
+ Registers preset intents by bit flag.
64
+
65
+ ```ts
66
+ builder.use(PresetIntent.Questions);
67
+ builder.use(PresetIntent.All);
68
+ ```
69
+
70
+ ### `.add(definition: IntentDefinition): this`
71
+
72
+ Registers a custom intent. Throws if the same `type` is already registered.
73
+
74
+ ```ts
75
+ interface IntentDefinition {
76
+ type: string;
77
+ description: string;
78
+ properties?: Record<string, JsonSchema>;
79
+ required?: string[];
80
+ }
81
+ ```
82
+
83
+ ### `.build(): string`
84
+
85
+ Returns LLM system prompt instructions for all registered intents. Returns an empty string if no intents are registered.
86
+
87
+ ### `.parse(value: string): [string, IntentSchema[]]`
88
+
89
+ Extracts `intent-json` blocks from an LLM response string.
90
+
91
+ - Index `[0]` — clean markdown text with intent-json blocks removed
92
+ - Index `[1]` — array of parsed `IntentSchema` objects
93
+
94
+ ```ts
95
+ interface IntentSchema {
96
+ type: string;
97
+ properties?: Record<string, unknown>;
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## PresetIntent Flags
104
+
105
+ ```ts
106
+ enum PresetIntent {
107
+ Questions = 1 << 0,
108
+ All = Questions
109
+ }
110
+ ```
111
+
112
+ ### Questions Intent Schema
113
+
114
+ ```json
115
+ {
116
+ "type": "question",
117
+ "properties": {
118
+ "question": { "type": "string" },
119
+ "choices": { "type": "array", "items": { "type": "string" }, "minItems": 1, "maxItems": 5 }
120
+ },
121
+ "required": ["choices"]
122
+ }
123
+ ```