@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.
- package/CHANGELOG.md +78 -144
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.128.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.128.0}/helpers/decorateMetadata.js +1 -1
- package/dist/components/blocks/UCodeBlock.js +4 -3
- package/dist/components/blocks/UFileBlock.js +2 -2
- package/dist/components/blocks/UJsonBlock.js +2 -2
- package/dist/components/blocks/UMarkedBlock.js +3 -27
- package/dist/components/blocks/URefBlock.js +2 -2
- package/dist/components/blocks/UTableBlock.js +4 -3
- package/dist/components/blocks/UTextBlock.js +2 -2
- package/dist/components/blocks/UThinkBlock.js +2 -2
- package/dist/components/blocks/UToolBlock.js +2 -2
- package/dist/components/buttons/UAttachButton.js +2 -2
- package/dist/components/buttons/UCopyButton.js +2 -2
- package/dist/components/buttons/UVoteButton.js +2 -2
- package/dist/components/intents/UQuestionIntent.js +2 -2
- package/dist/components/message/UMessage.js +2 -2
- package/dist/components/prompt/UPrompt.d.ts +5 -0
- package/dist/components/prompt/UPrompt.js +27 -5
- package/dist/components/references/URefCard.js +2 -2
- package/dist/components/references/URefCardGroup.js +4 -3
- package/dist/components/references/URefTag.js +2 -2
- package/dist/components/views/UChartView.js +2 -2
- package/dist/components/views/UImagesView.js +2 -2
- package/dist/components/views/UMapView.js +2 -2
- package/dist/components/views/UVideoView.js +2 -2
- package/dist/components/views/UView.js +2 -2
- package/dist/events/SendEvent.d.ts +15 -1
- package/dist/events/StopEvent.d.ts +11 -1
- package/dist/types/Intents.js +1 -1
- package/dist/types/Views.js +1 -1
- package/dist/utilities/HtmlPlaceholder.d.ts +11 -0
- package/dist/utilities/HtmlPlaceholder.js +26 -0
- package/package.json +11 -10
- package/skills/iyulab-chat-components/SKILL.md +107 -0
- package/skills/iyulab-chat-components/references/components/attach-button.md +69 -0
- package/skills/iyulab-chat-components/references/components/chart-view.md +65 -0
- package/skills/iyulab-chat-components/references/components/code-block.md +42 -0
- package/skills/iyulab-chat-components/references/components/copy-button.md +46 -0
- package/skills/iyulab-chat-components/references/components/file-block.md +64 -0
- package/skills/iyulab-chat-components/references/components/images-view.md +63 -0
- package/skills/iyulab-chat-components/references/components/json-block.md +41 -0
- package/skills/iyulab-chat-components/references/components/map-view.md +54 -0
- package/skills/iyulab-chat-components/references/components/marked-block.md +53 -0
- package/skills/iyulab-chat-components/references/components/message.md +60 -0
- package/skills/iyulab-chat-components/references/components/prompt.md +102 -0
- package/skills/iyulab-chat-components/references/components/question-intent.md +75 -0
- package/skills/iyulab-chat-components/references/components/ref-block.md +45 -0
- package/skills/iyulab-chat-components/references/components/ref-card-group.md +41 -0
- package/skills/iyulab-chat-components/references/components/ref-card.md +48 -0
- package/skills/iyulab-chat-components/references/components/ref-tag.md +40 -0
- package/skills/iyulab-chat-components/references/components/table-block.md +63 -0
- package/skills/iyulab-chat-components/references/components/text-block.md +59 -0
- package/skills/iyulab-chat-components/references/components/think-block.md +48 -0
- package/skills/iyulab-chat-components/references/components/tool-block.md +57 -0
- package/skills/iyulab-chat-components/references/components/video-view.md +55 -0
- package/skills/iyulab-chat-components/references/components/view.md +51 -0
- package/skills/iyulab-chat-components/references/components/vote-button.md +60 -0
- package/skills/iyulab-chat-components/references/types.md +115 -0
- package/skills/iyulab-chat-components/references/usage.md +254 -0
- package/skills/iyulab-chat-components/references/utilities/intent-prompt-builder.md +123 -0
- 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
|
+
```
|