@iyulab/chat-components 0.11.8 → 0.11.9
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 +30 -0
- package/dist/components/blocks/UMarkedBlock.d.ts +7 -2
- package/dist/components/blocks/UMarkedBlock.js +9 -4
- package/dist/react/UCodeBlock.d.ts +1 -1
- package/dist/react/UCodeBlock.js +1 -1
- package/dist/react/UElementBlock.d.ts +1 -1
- package/dist/react/UElementBlock.js +1 -1
- package/dist/react/UFileBlock.d.ts +1 -1
- package/dist/react/UFileBlock.js +1 -1
- package/dist/react/UImagesBlock.d.ts +1 -1
- package/dist/react/UImagesBlock.js +1 -1
- package/dist/react/UMapBlock.d.ts +1 -1
- package/dist/react/UMapBlock.js +1 -1
- package/dist/react/UMarkedBlock.d.ts +1 -1
- package/dist/react/UMarkedBlock.js +1 -1
- package/dist/react/UMessage.d.ts +1 -1
- package/dist/react/UMessage.js +1 -1
- package/dist/react/UPrompt.d.ts +3 -3
- package/dist/react/UPrompt.js +1 -1
- package/dist/react/URefBlock.d.ts +1 -1
- package/dist/react/URefBlock.js +1 -1
- package/dist/react/URefCard.d.ts +1 -1
- package/dist/react/URefCard.js +1 -1
- package/dist/react/URefCardGroup.d.ts +1 -1
- package/dist/react/URefCardGroup.js +1 -1
- package/dist/react/URefTag.d.ts +1 -1
- package/dist/react/URefTag.js +1 -1
- package/dist/react/UTableBlock.d.ts +1 -1
- package/dist/react/UTableBlock.js +1 -1
- package/dist/react/UTextBlock.d.ts +1 -1
- package/dist/react/UTextBlock.js +1 -1
- package/dist/react/UVideoBlock.d.ts +1 -1
- package/dist/react/UVideoBlock.js +1 -1
- package/dist/utilities/HtmlPlaceholder.d.ts +18 -1
- package/dist/utilities/HtmlPlaceholder.js +59 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.9] - 2026-09-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Inline citation badges rendered as the literal placeholder text instead of a
|
|
8
|
+
`u-ref-tag`.** Any `u-marked-block` given a non-empty `refs` array was affected,
|
|
9
|
+
with no special content required, from `0.11.7` onward. The XSS hardening added
|
|
10
|
+
in that release escapes every raw HTML token, and the placeholder the component
|
|
11
|
+
splices into the markdown source was an HTML comment — which the markdown
|
|
12
|
+
parser classifies as exactly that. Escaping turned it into text that the restore
|
|
13
|
+
pass no longer recognised, so the marker survived into the DOM and appeared
|
|
14
|
+
mid-sentence.
|
|
15
|
+
|
|
16
|
+
The placeholder is now a private-use sentinel that markdown gives no meaning to,
|
|
17
|
+
so it tokenises as plain text and escaping is a no-op on it. This removes the
|
|
18
|
+
coupling between the sanitizer and the placeholder format rather than adding an
|
|
19
|
+
exemption to it: a future sanitizing hook cannot break the round trip again.
|
|
20
|
+
Exempting the comment form in the renderer would also have let author text
|
|
21
|
+
matching the internal format through unescaped.
|
|
22
|
+
|
|
23
|
+
**The hardening is unchanged.** Raw HTML written by an author is still escaped,
|
|
24
|
+
and a regression covers that alongside the round trip.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **A `<!--ref:N-->` written by an author inside a code block is no longer
|
|
29
|
+
stripped.** It used to collide with the component's internal placeholder format
|
|
30
|
+
and was removed with it; now it is ordinary content, and a code block shows what
|
|
31
|
+
was written.
|
|
32
|
+
|
|
3
33
|
## [0.11.8] - 2026-09-07
|
|
4
34
|
|
|
5
35
|
### Fixed
|
|
@@ -64,11 +64,16 @@ export declare class UMarkedBlock extends UElement {
|
|
|
64
64
|
private renderImage;
|
|
65
65
|
/**
|
|
66
66
|
* 인용/출처 참조객체가 있을때 ref 태그 HTML을 this.placeholder에 보관하고
|
|
67
|
-
* 마크다운 문자열의 해당 위치에
|
|
67
|
+
* 마크다운 문자열의 해당 위치에 플레이스홀더 센티널을 삽입합니다.
|
|
68
68
|
*/
|
|
69
69
|
private insertRefs;
|
|
70
70
|
/**
|
|
71
|
-
* 코드블록 내부의
|
|
71
|
+
* 코드블록 내부의 플레이스홀더 센티널과 그 외 refs 태그를 모두 제거하여 HTML이 코드로
|
|
72
|
+
* 렌더링되도록 합니다.
|
|
73
|
+
*
|
|
74
|
+
* ⚠**작성자가 쓴 `<!--ref:N-->` 는 더 이상 지우지 않는다.** 종전에는 그것이 우리 내부
|
|
75
|
+
* 플레이스홀더의 형태와 같아 함께 지워졌지만, 이제 그 문자열은 **작성자의 콘텐츠일 뿐**
|
|
76
|
+
* 이므로 코드블록 안에서 적힌 그대로 보이는 것이 옳다.
|
|
72
77
|
*/
|
|
73
78
|
private removeRefs;
|
|
74
79
|
}
|
|
@@ -8,7 +8,7 @@ import "../references/URefTag.js";
|
|
|
8
8
|
import { URefCard } from "../references/URefCard.js";
|
|
9
9
|
import "../references/URefCardGroup.js";
|
|
10
10
|
import { UTableBlock } from "./UTableBlock.js";
|
|
11
|
-
import { HtmlPlaceholder } from "../../utilities/HtmlPlaceholder.js";
|
|
11
|
+
import { HtmlPlaceholder, stripRefPlaceholders } from "../../utilities/HtmlPlaceholder.js";
|
|
12
12
|
import { styles } from "./UMarkedBlock.styles.js";
|
|
13
13
|
import { nothing } from "lit";
|
|
14
14
|
import { customElement, property, state } from "lit/decorators.js";
|
|
@@ -135,7 +135,7 @@ var UMarkedBlock = class UMarkedBlock extends UElement {
|
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* 인용/출처 참조객체가 있을때 ref 태그 HTML을 this.placeholder에 보관하고
|
|
138
|
-
* 마크다운 문자열의 해당 위치에
|
|
138
|
+
* 마크다운 문자열의 해당 위치에 플레이스홀더 센티널을 삽입합니다.
|
|
139
139
|
*/
|
|
140
140
|
insertRefs(value, refs) {
|
|
141
141
|
const sorted = [...refs].sort((a, b) => b.endIndex - a.endIndex);
|
|
@@ -155,11 +155,16 @@ var UMarkedBlock = class UMarkedBlock extends UElement {
|
|
|
155
155
|
return value;
|
|
156
156
|
}
|
|
157
157
|
/**
|
|
158
|
-
* 코드블록 내부의
|
|
158
|
+
* 코드블록 내부의 플레이스홀더 센티널과 그 외 refs 태그를 모두 제거하여 HTML이 코드로
|
|
159
|
+
* 렌더링되도록 합니다.
|
|
160
|
+
*
|
|
161
|
+
* ⚠**작성자가 쓴 `<!--ref:N-->` 는 더 이상 지우지 않는다.** 종전에는 그것이 우리 내부
|
|
162
|
+
* 플레이스홀더의 형태와 같아 함께 지워졌지만, 이제 그 문자열은 **작성자의 콘텐츠일 뿐**
|
|
163
|
+
* 이므로 코드블록 안에서 적힌 그대로 보이는 것이 옳다.
|
|
159
164
|
*/
|
|
160
165
|
removeRefs(value) {
|
|
161
166
|
value = value.replace(/<u-ref-tag\b[^>]*>[\s\S]*?<\/u-ref-tag>/gi, "");
|
|
162
|
-
value = value
|
|
167
|
+
value = stripRefPlaceholders(value);
|
|
163
168
|
return value;
|
|
164
169
|
}
|
|
165
170
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UCodeBlock as UCodeBlockElement } from '
|
|
2
|
+
import { UCodeBlock as UCodeBlockElement } from '@iyulab/chat-components/dist/components/blocks/UCodeBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UCodeBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UCodeBlockElement>, keyof React.HTMLAttributes<UCodeBlockElement>>
|
package/dist/react/UCodeBlock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UCodeBlock as UCodeBlockElement } from '
|
|
3
|
+
import { UCodeBlock as UCodeBlockElement } from '@iyulab/chat-components/dist/components/blocks/UCodeBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UCodeBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UElementBlock as UElementBlockElement } from '
|
|
2
|
+
import { UElementBlock as UElementBlockElement } from '@iyulab/chat-components/dist/components/blocks/UElementBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UElementBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UElementBlockElement>, keyof React.HTMLAttributes<UElementBlockElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UElementBlock as UElementBlockElement } from '
|
|
3
|
+
import { UElementBlock as UElementBlockElement } from '@iyulab/chat-components/dist/components/blocks/UElementBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UElementBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UFileBlock as UFileBlockElement } from '
|
|
2
|
+
import { UFileBlock as UFileBlockElement } from '@iyulab/chat-components/dist/components/blocks/UFileBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UFileBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UFileBlockElement>, keyof React.HTMLAttributes<UFileBlockElement>>
|
package/dist/react/UFileBlock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UFileBlock as UFileBlockElement } from '
|
|
3
|
+
import { UFileBlock as UFileBlockElement } from '@iyulab/chat-components/dist/components/blocks/UFileBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UFileBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UImagesBlock as UImagesBlockElement } from '
|
|
2
|
+
import { UImagesBlock as UImagesBlockElement } from '@iyulab/chat-components/dist/components-extra/UImagesBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UImagesBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UImagesBlockElement>, keyof React.HTMLAttributes<UImagesBlockElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UImagesBlock as UImagesBlockElement } from '
|
|
3
|
+
import { UImagesBlock as UImagesBlockElement } from '@iyulab/chat-components/dist/components-extra/UImagesBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UImagesBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UMapBlock as UMapBlockElement } from '
|
|
2
|
+
import { UMapBlock as UMapBlockElement } from '@iyulab/chat-components/dist/components-extra/UMapBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UMapBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UMapBlockElement>, keyof React.HTMLAttributes<UMapBlockElement>>
|
package/dist/react/UMapBlock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UMapBlock as UMapBlockElement } from '
|
|
3
|
+
import { UMapBlock as UMapBlockElement } from '@iyulab/chat-components/dist/components-extra/UMapBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UMapBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UMarkedBlock as UMarkedBlockElement } from '
|
|
2
|
+
import { UMarkedBlock as UMarkedBlockElement } from '@iyulab/chat-components/dist/components/blocks/UMarkedBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UMarkedBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UMarkedBlockElement>, keyof React.HTMLAttributes<UMarkedBlockElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UMarkedBlock as UMarkedBlockElement } from '
|
|
3
|
+
import { UMarkedBlock as UMarkedBlockElement } from '@iyulab/chat-components/dist/components/blocks/UMarkedBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UMarkedBlock = createComponent({
|
|
6
6
|
react: React,
|
package/dist/react/UMessage.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UMessage as UMessageElement } from '
|
|
2
|
+
import { UMessage as UMessageElement } from '@iyulab/chat-components/dist/components/message/UMessage.js';
|
|
3
3
|
|
|
4
4
|
export declare const UMessage: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UMessageElement>, keyof React.HTMLAttributes<UMessageElement>>
|
package/dist/react/UMessage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UMessage as UMessageElement } from '
|
|
3
|
+
import { UMessage as UMessageElement } from '@iyulab/chat-components/dist/components/message/UMessage.js';
|
|
4
4
|
|
|
5
5
|
export const UMessage = createComponent({
|
|
6
6
|
react: React,
|
package/dist/react/UPrompt.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UPrompt as UPromptElement } from '
|
|
3
|
-
import { type StopEventDetail } from '
|
|
4
|
-
import { type SendEventDetail } from '
|
|
2
|
+
import { UPrompt as UPromptElement } from '@iyulab/chat-components/dist/components/prompt/UPrompt.js';
|
|
3
|
+
import { type StopEventDetail } from '@iyulab/chat-components/dist/events/StopEvent.js';
|
|
4
|
+
import { type SendEventDetail } from '@iyulab/chat-components/dist/events/SendEvent.js';
|
|
5
5
|
|
|
6
6
|
export declare const UPrompt: React.ForwardRefExoticComponent<
|
|
7
7
|
Omit<Partial<UPromptElement>, keyof React.HTMLAttributes<UPromptElement>>
|
package/dist/react/UPrompt.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UPrompt as UPromptElement } from '
|
|
3
|
+
import { UPrompt as UPromptElement } from '@iyulab/chat-components/dist/components/prompt/UPrompt.js';
|
|
4
4
|
|
|
5
5
|
export const UPrompt = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { URefBlock as URefBlockElement } from '
|
|
2
|
+
import { URefBlock as URefBlockElement } from '@iyulab/chat-components/dist/components/blocks/URefBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const URefBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<URefBlockElement>, keyof React.HTMLAttributes<URefBlockElement>>
|
package/dist/react/URefBlock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { URefBlock as URefBlockElement } from '
|
|
3
|
+
import { URefBlock as URefBlockElement } from '@iyulab/chat-components/dist/components/blocks/URefBlock.js';
|
|
4
4
|
|
|
5
5
|
export const URefBlock = createComponent({
|
|
6
6
|
react: React,
|
package/dist/react/URefCard.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { URefCard as URefCardElement } from '
|
|
2
|
+
import { URefCard as URefCardElement } from '@iyulab/chat-components/dist/components/references/URefCard.js';
|
|
3
3
|
|
|
4
4
|
export declare const URefCard: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<URefCardElement>, keyof React.HTMLAttributes<URefCardElement>>
|
package/dist/react/URefCard.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { URefCard as URefCardElement } from '
|
|
3
|
+
import { URefCard as URefCardElement } from '@iyulab/chat-components/dist/components/references/URefCard.js';
|
|
4
4
|
|
|
5
5
|
export const URefCard = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { URefCardGroup as URefCardGroupElement } from '
|
|
2
|
+
import { URefCardGroup as URefCardGroupElement } from '@iyulab/chat-components/dist/components/references/URefCardGroup.js';
|
|
3
3
|
|
|
4
4
|
export declare const URefCardGroup: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<URefCardGroupElement>, keyof React.HTMLAttributes<URefCardGroupElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { URefCardGroup as URefCardGroupElement } from '
|
|
3
|
+
import { URefCardGroup as URefCardGroupElement } from '@iyulab/chat-components/dist/components/references/URefCardGroup.js';
|
|
4
4
|
|
|
5
5
|
export const URefCardGroup = createComponent({
|
|
6
6
|
react: React,
|
package/dist/react/URefTag.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { URefTag as URefTagElement } from '
|
|
2
|
+
import { URefTag as URefTagElement } from '@iyulab/chat-components/dist/components/references/URefTag.js';
|
|
3
3
|
|
|
4
4
|
export declare const URefTag: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<URefTagElement>, keyof React.HTMLAttributes<URefTagElement>>
|
package/dist/react/URefTag.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { URefTag as URefTagElement } from '
|
|
3
|
+
import { URefTag as URefTagElement } from '@iyulab/chat-components/dist/components/references/URefTag.js';
|
|
4
4
|
|
|
5
5
|
export const URefTag = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UTableBlock as UTableBlockElement } from '
|
|
2
|
+
import { UTableBlock as UTableBlockElement } from '@iyulab/chat-components/dist/components/blocks/UTableBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UTableBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UTableBlockElement>, keyof React.HTMLAttributes<UTableBlockElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UTableBlock as UTableBlockElement } from '
|
|
3
|
+
import { UTableBlock as UTableBlockElement } from '@iyulab/chat-components/dist/components/blocks/UTableBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UTableBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UTextBlock as UTextBlockElement } from '
|
|
2
|
+
import { UTextBlock as UTextBlockElement } from '@iyulab/chat-components/dist/components/blocks/UTextBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UTextBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UTextBlockElement>, keyof React.HTMLAttributes<UTextBlockElement>>
|
package/dist/react/UTextBlock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UTextBlock as UTextBlockElement } from '
|
|
3
|
+
import { UTextBlock as UTextBlockElement } from '@iyulab/chat-components/dist/components/blocks/UTextBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UTextBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UVideoBlock as UVideoBlockElement } from '
|
|
2
|
+
import { UVideoBlock as UVideoBlockElement } from '@iyulab/chat-components/dist/components-extra/UVideoBlock.js';
|
|
3
3
|
|
|
4
4
|
export declare const UVideoBlock: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UVideoBlockElement>, keyof React.HTMLAttributes<UVideoBlockElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UVideoBlock as UVideoBlockElement } from '
|
|
3
|
+
import { UVideoBlock as UVideoBlockElement } from '@iyulab/chat-components/dist/components-extra/UVideoBlock.js';
|
|
4
4
|
|
|
5
5
|
export const UVideoBlock = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Marked 파싱 중 HTML이 GFM에 의해 오염되는 것을 방지하기 위해
|
|
3
|
-
* HTML을
|
|
3
|
+
* HTML을 센티널로 치환하고, 나중에 원래 HTML로 복원하는 유틸입니다.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* 문자열에서 ref 플레이스홀더 센티널을 제거합니다.
|
|
7
|
+
*
|
|
8
|
+
* 코드블록처럼 **복원 대상이 아닌** 경로에서 쓴다 — 거기서는 ref 태그를 렌더하지 않고
|
|
9
|
+
* 원문을 그대로 보여야 하므로, 남은 센티널은 지운다(안 지우면 PUA 문자가 두부로 보인다).
|
|
10
|
+
* 형태를 아는 곳이 이 파일 하나뿐이도록 함수로 내보낸다 — 패턴을 호출부에 복제하면
|
|
11
|
+
* 종전처럼 한쪽만 바뀌어 어긋난다.
|
|
12
|
+
*/
|
|
13
|
+
export declare function stripRefPlaceholders(value: string): string;
|
|
5
14
|
export declare class HtmlPlaceholder {
|
|
6
15
|
private map;
|
|
7
16
|
private idx;
|
|
8
17
|
reset(): void;
|
|
9
18
|
store(html: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* 파싱 결과에서 센티널을 원래 HTML로 되돌립니다.
|
|
21
|
+
*
|
|
22
|
+
* ⚠**아무것도 보관하지 않았으면 손대지 않는다**(`idx === 0`). 그 경우 문자열에 센티널이
|
|
23
|
+
* 있다면 그것은 **작성자가 직접 쓴 글자**이지 우리가 넣은 것이 아니므로, 지우는 것이
|
|
24
|
+
* 오히려 콘텐츠 손실이다. 보관분이 있을 때 매칭되지 않는 키는 빈 문자열로 지운다 —
|
|
25
|
+
* 우리 것이 아닌 센티널이 화면에 두부로 남는 것을 막는다.
|
|
26
|
+
*/
|
|
10
27
|
restore(html: string): string;
|
|
11
28
|
}
|
|
@@ -1,8 +1,55 @@
|
|
|
1
1
|
//#region src/utilities/HtmlPlaceholder.ts
|
|
2
2
|
/**
|
|
3
3
|
* Marked 파싱 중 HTML이 GFM에 의해 오염되는 것을 방지하기 위해
|
|
4
|
-
* HTML을
|
|
4
|
+
* HTML을 센티널로 치환하고, 나중에 원래 HTML로 복원하는 유틸입니다.
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* 센티널 경계 문자 — Unicode **Private Use Area**(U+E000..U+F8FF).
|
|
8
|
+
*
|
|
9
|
+
* ⚠**이 선택이 이 유틸의 계약 전부다.** 센티널은 marked 의 토크나이저를 **통과**해야 하고
|
|
10
|
+
* (그래야 `restore()` 가 출력에서 되찾는다), 동시에 marked 가 **HTML로 분류하지 않아야**
|
|
11
|
+
* 한다 — 분류되는 순간 `html` 토큰이 되어 소비자의 sanitizing 렌더러 손에 들어가고,
|
|
12
|
+
* 거기서 형태가 바뀌면 `restore()` 는 자기가 넣은 것을 더 이상 알아보지 못한다.
|
|
13
|
+
*
|
|
14
|
+
* 🔴**종전 판은 HTML 주석(`<!--ref:N-->`)이었고 바로 그 이유로 깨졌다.** marked 는 주석을
|
|
15
|
+
* `html` 토큰으로 잡고(실측: `text, html, text`), `UMarkedBlock` 의 XSS 하드닝 렌더러가
|
|
16
|
+
* 그것을 escape 하면 `<!--ref:N-->` 가 되어 복원 패턴에 걸리지 않는다 — 플레이스홀더가
|
|
17
|
+
* 화면에 **글자 그대로** 남는다. 「주석은 건드려지지 않는다」는 전제가 하드닝과 함께 깨진
|
|
18
|
+
* 것이고, ***전제를 고치는 대신 하드닝에 예외를 두면 다음 하드닝에서 같은 자리가 다시 깨진다.***
|
|
19
|
+
*
|
|
20
|
+
* PUA 문자는 마크다운 문법에서 아무 의미가 없어 **평문 텍스트로만** 토큰화되고(실측),
|
|
21
|
+
* `escapeHtmlText` 의 대상 문자(`& < > " '`)도 아니라 **escape 가 no-op** 이다. 코드블록·
|
|
22
|
+
* 테이블 셀·강조 안에서도 원형이 보존되는 것을 확인했다. ⇒ ***어떤 sanitizing 렌더러가***
|
|
23
|
+
* ***나중에 추가돼도 이 왕복은 깨지지 않는다.***
|
|
24
|
+
*
|
|
25
|
+
* ⚠**소스에는 escape 표기로 적는다** — 원시 PUA 문자를 파일에 두면 에디터·터미널·diff 에서
|
|
26
|
+
* 두부(tofu)나 빈칸으로 보여 「거기 무엇이 있는지」를 읽을 수 없다.
|
|
27
|
+
*/
|
|
28
|
+
var SENTINEL_OPEN = "";
|
|
29
|
+
var SENTINEL_CLOSE = "";
|
|
30
|
+
/**
|
|
31
|
+
* 센티널 매칭 패턴.
|
|
32
|
+
*
|
|
33
|
+
* `lastIndex` 상태를 공유하지 않도록 **매번 새로 만든다** — `g` 플래그가 붙은 정규식을
|
|
34
|
+
* 모듈 상수로 두고 `test()`/`replace()` 에 섞어 쓰면 호출 순서에 따라 조용히 빗나간다.
|
|
35
|
+
*
|
|
36
|
+
* ⚠**정규식 «리터럴»로 적고 문자열로 조립하지 않는다.** 템플릿 리터럴로 `new RegExp(...)`
|
|
37
|
+
* 을 만들면 백슬래시가 리터럴 단계에서 한 번 소비돼 숫자가 아니라 **글자 d 를 찾는** 정규식이
|
|
38
|
+
* 조용히 만들어진다(이 파일을 쓰다 실제로 한 번 만들었다). 경계 문자를 위 상수와
|
|
39
|
+
* 같은 값으로 두 번 적는 대가를 치르되, 두 자리가 **같은 파일 안**에 있고 회귀 테스트가 둘의 일치를 고정한다.
|
|
40
|
+
*/
|
|
41
|
+
var sentinelPattern = () => /\u{E000}\d+\u{E001}/gu;
|
|
42
|
+
/**
|
|
43
|
+
* 문자열에서 ref 플레이스홀더 센티널을 제거합니다.
|
|
44
|
+
*
|
|
45
|
+
* 코드블록처럼 **복원 대상이 아닌** 경로에서 쓴다 — 거기서는 ref 태그를 렌더하지 않고
|
|
46
|
+
* 원문을 그대로 보여야 하므로, 남은 센티널은 지운다(안 지우면 PUA 문자가 두부로 보인다).
|
|
47
|
+
* 형태를 아는 곳이 이 파일 하나뿐이도록 함수로 내보낸다 — 패턴을 호출부에 복제하면
|
|
48
|
+
* 종전처럼 한쪽만 바뀌어 어긋난다.
|
|
49
|
+
*/
|
|
50
|
+
function stripRefPlaceholders(value) {
|
|
51
|
+
return value.replace(sentinelPattern(), "");
|
|
52
|
+
}
|
|
6
53
|
var HtmlPlaceholder = class {
|
|
7
54
|
constructor() {
|
|
8
55
|
this.map = {};
|
|
@@ -13,14 +60,22 @@ var HtmlPlaceholder = class {
|
|
|
13
60
|
this.idx = 0;
|
|
14
61
|
}
|
|
15
62
|
store(html) {
|
|
16
|
-
const key =
|
|
63
|
+
const key = `${SENTINEL_OPEN}${this.idx++}${SENTINEL_CLOSE}`;
|
|
17
64
|
this.map[key] = html;
|
|
18
65
|
return key;
|
|
19
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* 파싱 결과에서 센티널을 원래 HTML로 되돌립니다.
|
|
69
|
+
*
|
|
70
|
+
* ⚠**아무것도 보관하지 않았으면 손대지 않는다**(`idx === 0`). 그 경우 문자열에 센티널이
|
|
71
|
+
* 있다면 그것은 **작성자가 직접 쓴 글자**이지 우리가 넣은 것이 아니므로, 지우는 것이
|
|
72
|
+
* 오히려 콘텐츠 손실이다. 보관분이 있을 때 매칭되지 않는 키는 빈 문자열로 지운다 —
|
|
73
|
+
* 우리 것이 아닌 센티널이 화면에 두부로 남는 것을 막는다.
|
|
74
|
+
*/
|
|
20
75
|
restore(html) {
|
|
21
76
|
if (this.idx === 0) return html;
|
|
22
|
-
return html.replace(
|
|
77
|
+
return html.replace(sentinelPattern(), (key) => this.map[key] ?? "");
|
|
23
78
|
}
|
|
24
79
|
};
|
|
25
80
|
//#endregion
|
|
26
|
-
export { HtmlPlaceholder };
|
|
81
|
+
export { HtmlPlaceholder, stripRefPlaceholders };
|