@iyulab/chat-components 0.13.0 → 0.14.1

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 (64) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +1 -0
  3. package/dist/_virtual/_glob-assets_raw/alert-triangle-fill.svg.4cfc4305.js +4 -0
  4. package/dist/_virtual/_glob-assets_raw/arrows-sort.svg.dda43a85.js +4 -0
  5. package/dist/_virtual/_glob-assets_raw/code.svg.c69bc144.js +4 -0
  6. package/dist/_virtual/_glob-assets_raw/download.svg.bce82177.js +4 -0
  7. package/dist/_virtual/_glob-assets_raw/external-link.svg.a9f7f02d.js +4 -0
  8. package/dist/_virtual/_glob-assets_raw/file-code.svg.0e706f93.js +4 -0
  9. package/dist/_virtual/_glob-assets_raw/file-music.svg.b9970203.js +4 -0
  10. package/dist/_virtual/_glob-assets_raw/file-spreadsheet.svg.8c03bdfa.js +4 -0
  11. package/dist/_virtual/_glob-assets_raw/file-text.svg.85bdd923.js +4 -0
  12. package/dist/_virtual/_glob-assets_raw/file-type-pdf.svg.bc12db6d.js +4 -0
  13. package/dist/_virtual/_glob-assets_raw/file-zip.svg.76dde660.js +4 -0
  14. package/dist/_virtual/_glob-assets_raw/file.svg.28f5d6f1.js +4 -0
  15. package/dist/_virtual/_glob-assets_raw/photo.svg.cc273cc1.js +4 -0
  16. package/dist/_virtual/_glob-assets_raw/player-stop-fill.svg.5378b752.js +4 -0
  17. package/dist/_virtual/_glob-assets_raw/send.svg.02326253.js +4 -0
  18. package/dist/_virtual/_glob-assets_raw/sort-ascending-letters.svg.14a3df67.js +4 -0
  19. package/dist/_virtual/_glob-assets_raw/sort-descending-letters.svg.3e03647c.js +4 -0
  20. package/dist/_virtual/_glob-assets_raw/video.svg.c183f03c.js +4 -0
  21. package/dist/_virtual/_glob-assets_raw/world.svg.cd9903c8.js +4 -0
  22. package/dist/components/UDataElement.d.ts +2 -0
  23. package/dist/components/UDataElement.js +9 -1
  24. package/dist/components/blocks/UCodeBlock.js +1 -0
  25. package/dist/components/blocks/UElementBlock.d.ts +12 -16
  26. package/dist/components/blocks/UElementBlock.js +23 -31
  27. package/dist/components/blocks/UFileBlock.js +1 -0
  28. package/dist/components/blocks/UMarkedBlock.d.ts +10 -1
  29. package/dist/components/blocks/UMarkedBlock.js +43 -13
  30. package/dist/components/blocks/UMarkedBlock.styles.js +10 -0
  31. package/dist/components/blocks/UTableBlock.js +1 -0
  32. package/dist/components/prompt/UPrompt.js +3 -1
  33. package/dist/components/references/URefCard.js +3 -1
  34. package/dist/components/references/URefTag.js +3 -1
  35. package/dist/components-extra/UChartBlock.js +4 -0
  36. package/dist/components-extra/UImagesBlock.d.ts +3 -0
  37. package/dist/components-extra/UImagesBlock.js +18 -3
  38. package/dist/components-extra/UImagesBlock.styles.js +19 -0
  39. package/dist/components-extra/UMapBlock.js +3 -0
  40. package/dist/components-extra/UVideoBlock.js +3 -0
  41. package/dist/extra.d.ts +7 -1
  42. package/dist/extra.js +12 -6
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +5 -2
  45. package/dist/utilities/ElementRegistry.d.ts +13 -0
  46. package/dist/utilities/ElementRegistry.js +33 -0
  47. package/dist/utilities/HtmlPlaceholder.d.ts +8 -0
  48. package/dist/utilities/HtmlPlaceholder.js +13 -1
  49. package/dist/utilities/PromptBuilder.d.ts +3 -0
  50. package/dist/utilities/PromptBuilder.js +5 -0
  51. package/dist/utilities/icons.js +52 -0
  52. package/dist/utilities/imageSources.d.ts +25 -0
  53. package/dist/utilities/imageSources.js +61 -0
  54. package/dist/utilities/messages.d.ts +1 -1
  55. package/dist/utilities/messages.js +6 -2
  56. package/dist/utilities/sanitizers.d.ts +10 -1
  57. package/dist/utilities/sanitizers.js +16 -5
  58. package/package.json +4 -2
  59. package/skills/iyulab-chat-components/SKILL.md +1 -0
  60. package/skills/iyulab-chat-components/references/components/element-block.md +5 -6
  61. package/skills/iyulab-chat-components/references/components/images-block.md +1 -0
  62. package/skills/iyulab-chat-components/references/components/marked-block.md +17 -0
  63. package/skills/iyulab-chat-components/references/extra-system.md +4 -4
  64. package/skills/iyulab-chat-components/references/security.md +23 -0
@@ -76,18 +76,29 @@ var HREF_STRIP_CHARS_REGEX = /[\u0000-\u001F\u007F\s]+/g;
76
76
  /** href/src에서 차단해야 하는 위험 protocol */
77
77
  var HREF_UNSAFE_PROTOCOL_REGEX = /^(?:javascript|data|vbscript):/i;
78
78
  /**
79
- * href / src attribute용 문자열 처리
79
+ * URL 의 프로토콜을 검사해 안전한 URL 문자열을 돌려줍니다 — HTML escape 는 하지 않습니다.
80
+ * Lit 의 속성 바인딩처럼 escape 를 스스로 하는 자리에서 씁니다(두 번 escape 하면 `&` 가 `&` 가 된다).
81
+ * 위험한 URL(javascript:/data:/vbscript:, protocol-relative)은 `#` 가 됩니다.
80
82
  *
81
83
  * 예:
82
- * <a href="{value}">
84
+ * html`<a href=${sanitizeHref(url)}>`
83
85
  */
84
- function escapeHtmlHref(value) {
86
+ function sanitizeHref(value) {
85
87
  let normalized = value.replace(ZERO_WIDTH_STRICT_REGEX, "").trim();
86
88
  if (HREF_STRIP_CHARS_REGEX.test(normalized)) normalized = normalized.replace(HREF_STRIP_CHARS_REGEX, "");
87
89
  normalized = normalized.replace(/\\/g, "/");
88
90
  if (normalized.startsWith("//")) return "#";
89
91
  if (HREF_UNSAFE_PROTOCOL_REGEX.test(normalized)) return "#";
90
- return escapeHtmlAttr(normalized);
92
+ return normalized;
93
+ }
94
+ /**
95
+ * href / src attribute용 문자열 처리 — HTML 문자열을 직접 조립하는 자리에서 씁니다.
96
+ *
97
+ * 예:
98
+ * <a href="{value}">
99
+ */
100
+ function escapeHtmlHref(value) {
101
+ return escapeHtmlAttr(sanitizeHref(value));
91
102
  }
92
103
  //#endregion
93
- export { escapeHtmlAttr, escapeHtmlHref, escapeHtmlText, stripZeroWidth };
104
+ export { escapeHtmlAttr, escapeHtmlHref, escapeHtmlText, sanitizeHref, stripZeroWidth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/chat-components",
3
3
  "description": "llm chat components for building chat interfaces",
4
- "version": "0.13.0",
4
+ "version": "0.14.1",
5
5
  "author": "iyulab",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -38,6 +38,8 @@
38
38
  "./src/extra.ts",
39
39
  "./dist/index.js",
40
40
  "./src/index.ts",
41
+ "./dist/utilities/icons.js",
42
+ "./src/utilities/icons.ts",
41
43
  "./dist/react/index.js"
42
44
  ],
43
45
  "exports": {
@@ -67,7 +69,7 @@
67
69
  "marked": "^18.0.6"
68
70
  },
69
71
  "peerDependencies": {
70
- "@iyulab/components": ">=1.49.0",
72
+ "@iyulab/components": ">=1.51.0",
71
73
  "@lit/react": ">=1.0.8",
72
74
  "chart.js": ">=4.5.1",
73
75
  "react": ">=18.0.0"
@@ -31,6 +31,7 @@ import '@iyulab/chat-components/dist/components/prompt/UPrompt.js';
31
31
  ```
32
32
 
33
33
  > For detailed setup and usage patterns, see [./references/usage.md](./references/usage.md).
34
+ > Model output is untrusted — see [./references/security.md](./references/security.md) for what each input may contain.
34
35
 
35
36
  ---
36
37
 
@@ -6,7 +6,7 @@ import '@iyulab/chat-components/dist/components/blocks/UElementBlock.js';
6
6
 
7
7
  **Tag:** `u-element-block`
8
8
 
9
- Dynamic custom element renderer. Creates the element specified by `tag`, binds `properties` to it, and shows a skeleton placeholder while `loading`. Used internally by `u-marked-block` to render `block-json` code fences (see [../extra-system.md](../extra-system.md)) — you generally don't create it directly.
9
+ Dynamic block renderer. Creates the registered block specified by `tag`, binds the schema's `properties` to it, and shows a skeleton placeholder while `loading`. Used internally by `u-marked-block` to render `block-json` code fences (see [../extra-system.md](../extra-system.md)) — you generally don't create it directly.
10
10
 
11
11
  Extends `UDataElement`, so `tag`/`properties` can also be injected via a `<script type="application/json">` slot.
12
12
 
@@ -43,15 +43,14 @@ This is converted to `<u-element-block tag="u-images-block" ...>` via `UElementB
43
43
 
44
44
  | Property | Type | Default | Reflect | Description |
45
45
  |----------|------|---------|---------|-------------|
46
- | `tag` | `string` | `undefined` | — | Custom element tag to render. Must already be registered |
47
- | `properties` | `Record<string, unknown>` | `undefined` | — | Properties to bind to the rendered element |
46
+ | `tag` | `string` | `undefined` | — | Block tag to render. Must be a registered block (`registerElementBlock` or `ElementPromptBuilder.add`) whose element is defined |
47
+ | `properties` | `Record<string, unknown>` | `undefined` | — | Values to bind. Only names listed in the block schema's `properties` are assigned |
48
48
  | `loading` | `boolean` | `false` | ✓ | Shows a skeleton placeholder instead of the element |
49
- | `blacklist` | `string[]` | `['innerHTML', 'outerHTML', 'textContent', 'innerText', 'outerText', 'srcdoc']` | — | Property names blocked from binding for XSS prevention |
50
49
 
51
50
  ## Error Handling
52
51
 
53
- If `tag` is missing, unregistered, or `properties` fails validation/assignment, `u-element-block` does **not** show an error card — it renders nothing and logs to the console (`[u-element-block] ...`). While `loading` is `true`, JSON parse failures from the underlying `UDataElement` are silently ignored too, since incomplete streamed JSON is expected mid-stream.
52
+ If `tag` is missing or not a registered block, or `properties` is not an object or fails to assign, `u-element-block` does **not** show an error card — it renders nothing and logs to the console (`[u-element-block] ...`). While `loading` is `true`, JSON parse failures from the underlying `UDataElement` are silently ignored too, since incomplete streamed JSON is expected mid-stream.
54
53
 
55
54
  ## Security
56
55
 
57
- The following property names are blocked by default to prevent XSS: `innerHTML`, `outerHTML`, `textContent`, `innerText`, `outerText`, `srcdoc`. Extend `blacklist` to add more restrictions.
56
+ `tag` and `properties` usually come from model output, so they are treated as untrusted. Only registered blocks are created, and only the property names in the block's schema are assigned — any other key (for example `style`) is dropped and logged. See [../extra-system.md](../extra-system.md#security).
@@ -71,3 +71,4 @@ interface ImageSlide {
71
71
  | Carousel | Up to 3 slides visible; draggable |
72
72
  | Lightbox | Full-screen modal on image click |
73
73
  | Keyboard | `←` `→` to navigate, `Esc` to close |
74
+ | Image origin policy | `setAllowedImagePrefixes()` applies — a blocked item shows its alt text and is not requested (see [../security.md](../security.md)) |
@@ -65,8 +65,25 @@ Vendored from GitHub's markdown theme — light values shown, dark auto-applies
65
65
  | `--base-size-4` / `-8` / `-16` / `-24` / `-40` | Spacing scale used throughout (margins, padding, gaps) |
66
66
  | `--base-text-weight-normal` / `-medium` / `-semibold` | Font-weight scale (body / table header / heading) |
67
67
 
68
+ ## CSS Parts
69
+
70
+ | Part | Description |
71
+ |------|-------------|
72
+ | `image-blocked` | Alt text shown in place of an image the image origin policy blocked (see Images) |
73
+
74
+ ## Images
75
+
76
+ Markdown images (table cells included) follow the page-wide image origin policy set with
77
+ `setAllowedImagePrefixes()`. A blocked image is not requested; its alt text is shown in a
78
+ `part="image-blocked"` span (`Image blocked: …`, localized). See [../security.md](../security.md).
79
+
68
80
  ## ReferenceCitation Type
69
81
 
82
+ A citation is drawn at `endIndex` in the markdown source. While a response streams, a citation whose
83
+ `endIndex` is past the text received so far is held until that text arrives; a negative or non-integer
84
+ index is ignored. If `endIndex` falls inside a link URL, an image, or inline code, the citation is drawn
85
+ right after that element; inside a fenced code block it is dropped.
86
+
70
87
  ```ts
71
88
  interface ReferenceCitation {
72
89
  startIndex: number; // Start character index in the markdown text
@@ -76,20 +76,20 @@ const def: ElementSchema = {
76
76
  ElementPromptBuilder.instance.add(def);
77
77
  ```
78
78
 
79
- The element just needs to accept standard property assignments — `u-element-block` binds `properties` keys directly to element properties.
79
+ The element just needs to accept standard property assignments. `add()` also registers the schema as a renderable block — `u-element-block` assigns only the names in the schema's `properties`. If you write your own prompt instead, call `registerElementBlock(def)` (exported from `@iyulab/chat-components`).
80
80
 
81
81
  ## Error Handling
82
82
 
83
- If the LLM outputs a `tag` that isn't registered (e.g. missing `@iyulab/chat-components/extra` import, or a custom extra's element isn't defined yet), `u-element-block` does **not** show an error card — it renders nothing and logs to the console instead. Genuine data errors (invalid `properties`, assignment failures) are also console-only.
83
+ If the LLM outputs a `tag` that isn't a registered block (e.g. missing `@iyulab/chat-components/extra` import, or a custom extra's schema was never registered or its element isn't defined yet), `u-element-block` does **not** show an error card — it renders nothing and logs to the console instead. Genuine data errors (invalid `properties`, assignment failures) are also console-only.
84
84
 
85
85
  ## Security
86
86
 
87
- `u-element-block` maintains a `blacklist` of dangerous property names that are never bound: `innerHTML`, `outerHTML`, `textContent`, `innerText`, `outerText`, `srcdoc`. Custom elements registered as extras should only accept well-typed, data-only properties.
87
+ `block-json` is model output and is treated as untrusted. `u-element-block` creates only registered blocks (`ElementPromptBuilder.add()` or `registerElementBlock()`; built-in extras register on import) and assigns only the property names in the block's schema `properties` — other keys, including `style`, are dropped and logged. Custom extras should accept data only; if a property is rendered as HTML, sanitize it inside the element.
88
88
 
89
89
  ## `ElementPromptBuilder` API
90
90
 
91
91
  | Method | Description |
92
92
  |--------|-------------|
93
93
  | `ElementPromptBuilder.instance` | Singleton accessor |
94
- | `.add(definition: ElementSchema)` | Register a custom extra (throws on tag conflict) |
94
+ | `.add(definition: ElementSchema)` | Register a custom extra (throws on tag conflict). Also registers it as a renderable block |
95
95
  | `.build(): string` | Generate system prompt instruction string |
@@ -0,0 +1,23 @@
1
+ # Security model
2
+
3
+ Model output is **untrusted** (prompt injection can put third-party text in any response). What each input
4
+ is trusted to contain:
5
+
6
+ | Input | Treated as | Handling |
7
+ |---|---|---|
8
+ | `u-marked-block` `value` | untrusted | Raw HTML shown as text · link/image URLs protocol-checked (`javascript:`/`data:`/`vbscript:`/`//host` → `#`) · code fences escaped · table cells same rules |
9
+ | `block-json` fences | untrusted | Only registered blocks; only the schema's property names are assigned |
10
+ | `refs` | untrusted | Text rendered as text · URLs protocol-checked in `u-ref-tag` and `u-ref-card` |
11
+ | `u-table-block` cell `text` bound directly | **trusted HTML** | Sanitize before binding. `block-json` cannot reach it |
12
+ | Your custom block | your element's contract | The schema allows names, not content — sanitize HTML-rendered properties inside the element |
13
+
14
+ Requests: markdown images and `u-images-block` load the `http:`/`https:` URL the model writes as soon as they
15
+ render (an image URL can carry data out). When the model reads content you do not control, call
16
+ `setAllowedImagePrefixes([location.origin + '/', 'https://cdn.example.com/images/'])` before rendering —
17
+ images outside the list are not requested and show their alt text (`Image blocked: …`). Prefixes match the
18
+ resolved absolute URL; prefer paths to whole domains (open redirects). `data:image/…` is always shown (no
19
+ request). Default: all allowed. Add CSP `img-src` as a second layer.
20
+ `u-map-block` (OpenStreetMap iframe) and `u-video-block` (YouTube/Vimeo/direct) embed external content.
21
+ `u-ref-card` requests no favicon by default.
22
+
23
+ Do not insert model output with `innerHTML` yourself; route it through `u-marked-block`.