@iyulab/chat-components 0.3.0 → 0.4.0
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 +43 -0
- package/dist/assets/action-prompt.md.js +3 -0
- package/dist/assets/widget-prompt.md.js +3 -0
- package/dist/components/actions/UQuestionAction.component.d.ts +18 -0
- package/dist/components/actions/UQuestionAction.component.js +58 -0
- package/dist/components/actions/UQuestionAction.d.ts +7 -0
- package/dist/components/actions/UQuestionAction.js +5 -0
- package/dist/components/actions/UQuestionAction.styles.js +45 -0
- package/dist/components/blocks/UCodeBlock.component.d.ts +5 -3
- package/dist/components/blocks/UCodeBlock.component.js +14 -2
- package/dist/components/blocks/UCodeBlock.styles.js +6 -0
- package/dist/components/blocks/UFilesBlock.component.d.ts +22 -0
- package/dist/components/blocks/UFilesBlock.component.js +192 -0
- package/dist/components/blocks/UFilesBlock.d.ts +7 -0
- package/dist/components/blocks/UFilesBlock.js +5 -0
- package/dist/components/blocks/UFilesBlock.styles.d.ts +1 -0
- package/dist/components/blocks/UFilesBlock.styles.js +206 -0
- package/dist/components/blocks/UJsonBlock.component.d.ts +3 -3
- package/dist/components/blocks/UJsonBlock.component.js +2 -2
- package/dist/components/blocks/UMarkedBlock.component.d.ts +17 -22
- package/dist/components/blocks/UMarkedBlock.component.js +92 -62
- package/dist/components/blocks/URefBlock.component.d.ts +3 -3
- package/dist/components/blocks/URefBlock.component.js +6 -7
- package/dist/components/blocks/UTableBlock.component.d.ts +49 -0
- package/dist/components/blocks/UTableBlock.component.js +228 -0
- package/dist/components/blocks/UTableBlock.d.ts +7 -0
- package/dist/components/blocks/UTableBlock.js +5 -0
- package/dist/components/blocks/UTableBlock.styles.d.ts +1 -0
- package/dist/components/blocks/UTableBlock.styles.js +134 -0
- package/dist/components/blocks/UTextBlock.component.d.ts +3 -3
- package/dist/components/blocks/UTextBlock.component.js +2 -2
- package/dist/components/blocks/UThinkBlock.component.d.ts +3 -3
- package/dist/components/blocks/UThinkBlock.component.js +2 -2
- package/dist/components/blocks/UToolBlock.component.d.ts +3 -3
- package/dist/components/blocks/UToolBlock.component.js +2 -2
- package/dist/components/buttons/UAttachButton.component.d.ts +3 -3
- package/dist/components/buttons/UAttachButton.component.js +2 -2
- package/dist/components/buttons/UCopyButton.component.d.ts +3 -3
- package/dist/components/buttons/UCopyButton.component.js +2 -2
- package/dist/components/buttons/UReportButton.component.d.ts +3 -3
- package/dist/components/buttons/UReportButton.component.js +2 -2
- package/dist/components/buttons/URetryButton.component.d.ts +3 -3
- package/dist/components/buttons/URetryButton.component.js +2 -2
- package/dist/components/buttons/UShareButton.component.d.ts +3 -3
- package/dist/components/buttons/UShareButton.component.js +2 -2
- package/dist/components/buttons/UVoteButton.component.d.ts +3 -3
- package/dist/components/buttons/UVoteButton.component.js +7 -4
- package/dist/components/message/UMessage.component.d.ts +7 -7
- package/dist/components/message/UMessage.component.js +16 -50
- package/dist/components/message/UMessage.styles.js +38 -11
- package/dist/components/prompt/UPrompt.component.d.ts +3 -3
- package/dist/components/prompt/UPrompt.component.js +2 -2
- package/dist/components/prompt/UPrompt.styles.js +28 -0
- package/dist/components/references/URefCard.component.d.ts +9 -6
- package/dist/components/references/URefCard.component.js +14 -10
- package/dist/components/references/URefCardGroup.component.d.ts +4 -3
- package/dist/components/references/URefCardGroup.component.js +3 -3
- package/dist/components/references/URefTag.component.d.ts +3 -3
- package/dist/components/references/URefTag.component.js +2 -2
- package/dist/components/widgets/UChartWidget.component.d.ts +36 -0
- package/dist/components/widgets/UChartWidget.component.js +180 -0
- package/dist/components/widgets/UChartWidget.d.ts +7 -0
- package/dist/components/widgets/UChartWidget.js +5 -0
- package/dist/components/widgets/UChartWidget.styles.d.ts +1 -0
- package/dist/components/widgets/UChartWidget.styles.js +86 -0
- package/dist/components/widgets/UImagesWidget.component.d.ts +30 -0
- package/dist/components/widgets/UImagesWidget.component.js +164 -0
- package/dist/components/widgets/UImagesWidget.d.ts +7 -0
- package/dist/components/widgets/UImagesWidget.js +5 -0
- package/dist/components/widgets/UImagesWidget.styles.d.ts +1 -0
- package/dist/components/widgets/UImagesWidget.styles.js +218 -0
- package/dist/components/widgets/UMapWidget.component.d.ts +20 -0
- package/dist/components/widgets/UMapWidget.component.js +65 -0
- package/dist/components/widgets/UMapWidget.d.ts +7 -0
- package/dist/components/widgets/UMapWidget.js +5 -0
- package/dist/components/widgets/UMapWidget.styles.d.ts +1 -0
- package/dist/components/widgets/UMapWidget.styles.js +47 -0
- package/dist/components/widgets/UVideoWidget.component.d.ts +21 -0
- package/dist/components/widgets/UVideoWidget.component.js +106 -0
- package/dist/components/widgets/UVideoWidget.d.ts +7 -0
- package/dist/components/widgets/UVideoWidget.js +5 -0
- package/dist/components/widgets/UVideoWidget.styles.d.ts +1 -0
- package/dist/components/widgets/UVideoWidget.styles.js +36 -0
- package/dist/components/widgets/UWidget.component.d.ts +43 -0
- package/dist/components/widgets/UWidget.component.js +140 -0
- package/dist/components/widgets/UWidget.d.ts +7 -0
- package/dist/components/widgets/UWidget.js +5 -0
- package/dist/components/widgets/UWidget.styles.d.ts +1 -0
- package/dist/components/widgets/UWidget.styles.js +33 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +20 -2
- package/dist/types/Actions.d.ts +24 -0
- package/dist/types/Actions.js +34 -0
- package/dist/types/BlockItem.d.ts +32 -1
- package/dist/types/JsonSchema.d.ts +59 -0
- package/dist/types/Widgets.d.ts +34 -0
- package/dist/types/Widgets.js +115 -0
- package/dist/utilities/ActionPromptBuilder.d.ts +40 -0
- package/dist/utilities/ActionPromptBuilder.js +93 -0
- package/dist/utilities/WidgetPromptBuilder.d.ts +28 -0
- package/dist/utilities/WidgetPromptBuilder.js +87 -0
- package/package.json +12 -12
- package/dist/components/loaders/UDotLoader.component.d.ts +0 -9
- package/dist/components/loaders/UDotLoader.component.js +0 -23
- package/dist/components/loaders/UDotLoader.d.ts +0 -7
- package/dist/components/loaders/UDotLoader.js +0 -5
- package/dist/components/loaders/UDotLoader.styles.js +0 -50
- /package/dist/components/{loaders/UDotLoader.styles.d.ts → actions/UQuestionAction.styles.d.ts} +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
|
|
3
|
+
const styles = css`
|
|
4
|
+
:host {
|
|
5
|
+
display: grid;
|
|
6
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
7
|
+
gap: 6px;
|
|
8
|
+
font-size: 14px;
|
|
9
|
+
width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.item {
|
|
13
|
+
position: relative;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: row;
|
|
16
|
+
align-items: center;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
gap: 10px;
|
|
19
|
+
padding: 8px 10px;
|
|
20
|
+
border: 1px solid var(--u-border-color);
|
|
21
|
+
border-radius: 8px;
|
|
22
|
+
background-color: var(--u-neutral-50);
|
|
23
|
+
overflow: visible;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
transition: background-color 0.12s ease, border-color 0.12s ease;
|
|
26
|
+
}
|
|
27
|
+
.item:hover {
|
|
28
|
+
background-color: var(--u-neutral-100);
|
|
29
|
+
border-color: var(--u-border-color-strong);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* uploading */
|
|
33
|
+
.item[phase="uploading"] {
|
|
34
|
+
border-color: var(--u-blue-400);
|
|
35
|
+
background-color: var(--u-blue-0);
|
|
36
|
+
cursor: default;
|
|
37
|
+
}
|
|
38
|
+
.item[phase="uploading"] .thumbnail {
|
|
39
|
+
background-color: var(--u-blue-100);
|
|
40
|
+
color: var(--u-blue-600);
|
|
41
|
+
}
|
|
42
|
+
.item[phase="uploading"]:hover {
|
|
43
|
+
background-color: var(--u-blue-0);
|
|
44
|
+
border-color: var(--u-blue-400);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* error */
|
|
48
|
+
.item[phase="error"] {
|
|
49
|
+
border-color: var(--u-red-400);
|
|
50
|
+
background-color: var(--u-red-0);
|
|
51
|
+
}
|
|
52
|
+
.item[phase="error"] .thumbnail {
|
|
53
|
+
background-color: var(--u-red-100);
|
|
54
|
+
color: var(--u-red-600);
|
|
55
|
+
}
|
|
56
|
+
.item[phase="error"]:hover {
|
|
57
|
+
background-color: var(--u-red-0);
|
|
58
|
+
border-color: var(--u-red-500);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.thumbnail {
|
|
62
|
+
position: relative;
|
|
63
|
+
flex-shrink: 0;
|
|
64
|
+
width: 36px;
|
|
65
|
+
height: 36px;
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
color: var(--u-icon-color);
|
|
70
|
+
font-size: 16px;
|
|
71
|
+
border-radius: 6px;
|
|
72
|
+
background-color: var(--u-neutral-200);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.thumbnail-overlay {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
border-radius: 6px;
|
|
82
|
+
font-size: 14px;
|
|
83
|
+
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
|
84
|
+
}
|
|
85
|
+
.item[phase="uploading"] .thumbnail-overlay {
|
|
86
|
+
animation: pulse 1.4s ease-in-out infinite;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.info {
|
|
90
|
+
flex: 1;
|
|
91
|
+
min-width: 0;
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: 2px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.name {
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
font-weight: 500;
|
|
100
|
+
color: var(--u-txt-color-strong);
|
|
101
|
+
white-space: nowrap;
|
|
102
|
+
overflow: hidden;
|
|
103
|
+
text-overflow: ellipsis;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.meta {
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: row;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 6px;
|
|
111
|
+
color: var(--u-txt-color-weak);
|
|
112
|
+
font-size: 10px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.type {
|
|
116
|
+
padding: 1px 5px;
|
|
117
|
+
border-radius: 4px;
|
|
118
|
+
background-color: var(--u-neutral-200);
|
|
119
|
+
font-weight: 700;
|
|
120
|
+
text-transform: uppercase;
|
|
121
|
+
letter-spacing: 0.04em;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.size {
|
|
125
|
+
font-variant-numeric: tabular-nums;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.error-msg {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
gap: 3px;
|
|
132
|
+
color: var(--u-red-600);
|
|
133
|
+
font-size: 10px;
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
white-space: nowrap;
|
|
136
|
+
overflow: hidden;
|
|
137
|
+
text-overflow: ellipsis;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.upload-progress {
|
|
141
|
+
position: absolute;
|
|
142
|
+
bottom: 0;
|
|
143
|
+
left: 0;
|
|
144
|
+
right: 0;
|
|
145
|
+
height: 3px;
|
|
146
|
+
border-radius: 0 0 8px 8px;
|
|
147
|
+
border: none;
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
}
|
|
150
|
+
.upload-progress::part(indicator) {
|
|
151
|
+
background-color: var(--u-blue-500);
|
|
152
|
+
border-radius: 0 0 8px 8px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.download-btn {
|
|
156
|
+
position: absolute;
|
|
157
|
+
inset: 0;
|
|
158
|
+
width: 100%;
|
|
159
|
+
height: 100%;
|
|
160
|
+
color: var(--u-neutral-0);
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
border-radius: 6px;
|
|
163
|
+
background-color: color-mix(in srgb, var(--u-neutral-900) 70%, transparent);
|
|
164
|
+
opacity: 0;
|
|
165
|
+
pointer-events: none;
|
|
166
|
+
transition: opacity 0.15s ease;
|
|
167
|
+
}
|
|
168
|
+
.item:hover .download-btn {
|
|
169
|
+
opacity: 1;
|
|
170
|
+
pointer-events: auto;
|
|
171
|
+
}
|
|
172
|
+
.download-btn:hover {
|
|
173
|
+
background-color: color-mix(in srgb, var(--u-neutral-900) 85%, transparent);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.remove-btn {
|
|
177
|
+
position: absolute;
|
|
178
|
+
z-index: 10;
|
|
179
|
+
top: -8px;
|
|
180
|
+
right: -8px;
|
|
181
|
+
border-radius: 50%;
|
|
182
|
+
font-size: 10px;
|
|
183
|
+
background-color: var(--u-neutral-600);
|
|
184
|
+
color: var(--u-neutral-0);
|
|
185
|
+
opacity: 0;
|
|
186
|
+
pointer-events: none;
|
|
187
|
+
transition: opacity 0.12s ease, background-color 0.12s ease;
|
|
188
|
+
}
|
|
189
|
+
.item:hover .remove-btn {
|
|
190
|
+
opacity: 1;
|
|
191
|
+
pointer-events: auto;
|
|
192
|
+
}
|
|
193
|
+
.remove-btn:hover {
|
|
194
|
+
background-color: var(--u-red-600);
|
|
195
|
+
}
|
|
196
|
+
.remove-btn:active {
|
|
197
|
+
background-color: var(--u-red-700);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@keyframes pulse {
|
|
201
|
+
0%, 100% { opacity: 1; }
|
|
202
|
+
50% { opacity: 0.4; }
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
export { styles };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { PropertyValues, TemplateResult } from 'lit';
|
|
2
|
-
import {
|
|
2
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
3
3
|
import { JsonNode } from '../../types/JsonNode.js';
|
|
4
4
|
/**
|
|
5
5
|
* json 데이터를 트리 형태로 시각화하는 컴포넌트입니다.
|
|
6
6
|
*/
|
|
7
|
-
export declare class UJsonBlock extends
|
|
7
|
+
export declare class UJsonBlock extends UElement {
|
|
8
8
|
static styles: import('lit').CSSResultGroup[];
|
|
9
|
-
static dependencies: Record<string, typeof
|
|
9
|
+
static dependencies: Record<string, typeof UElement>;
|
|
10
10
|
state: Record<string, boolean>;
|
|
11
11
|
/** 초기 확장 상태를 관리하는 속성입니다. */
|
|
12
12
|
expanded: boolean;
|
|
@@ -2,7 +2,7 @@ import { html } from 'lit';
|
|
|
2
2
|
import { state, property } from 'lit/decorators.js';
|
|
3
3
|
import { map } from 'lit/directives/map.js';
|
|
4
4
|
import { when } from 'lit/directives/when.js';
|
|
5
|
-
import {
|
|
5
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
6
6
|
import { jsonAttrConverter } from '../../utilities/converters.js';
|
|
7
7
|
import { styles } from './UJsonBlock.styles.js';
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
15
15
|
if (result) __defProp(target, key, result);
|
|
16
16
|
return result;
|
|
17
17
|
};
|
|
18
|
-
class UJsonBlock extends
|
|
18
|
+
class UJsonBlock extends UElement {
|
|
19
19
|
constructor() {
|
|
20
20
|
super(...arguments);
|
|
21
21
|
this.state = {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
2
|
import { ReferenceCitation } from '../../types/References.js';
|
|
3
3
|
/**
|
|
4
4
|
* 마크다운 컨텐츠를 렌더링하는 컴포넌트입니다.
|
|
@@ -6,40 +6,35 @@ import { ReferenceCitation } from '../../types/References.js';
|
|
|
6
6
|
* 주의:
|
|
7
7
|
* - refs로 삽입되는 커스텀 태그(u-ref-*)는 속성/내용을 모두 escape하여 XSS를 방지합니다.
|
|
8
8
|
* - 코드블록 내부는 HTML을 전부 텍스트로 취급(escape)하며, refs 태그는 통째 제거합니다.
|
|
9
|
-
* - 인덱스 기반 삽입(ref.endIndex)이 깨지지 않도록 "normalize(제로폭 제거)"를
|
|
9
|
+
* - 인덱스 기반 삽입(ref.endIndex)이 깨지지 않도록 "normalize(제로폭 제거)"를 insertRefs 전에 1회 수행합니다.
|
|
10
10
|
*/
|
|
11
|
-
export declare class UMarkedBlock extends
|
|
11
|
+
export declare class UMarkedBlock extends UElement {
|
|
12
12
|
static styles: import('lit').CSSResultGroup[];
|
|
13
|
-
static dependencies: Record<string, typeof
|
|
14
|
-
private parser;
|
|
13
|
+
static dependencies: Record<string, typeof UElement>;
|
|
15
14
|
/** 마크다운 컨텐츠를 렌더링할 때 사용할 값입니다. */
|
|
16
15
|
value?: string;
|
|
17
16
|
/** 컨텐츠의 인용 출처들입니다. */
|
|
18
17
|
refs?: ReferenceCitation[];
|
|
18
|
+
/** Marked 인스턴스: 커스텀 렌더러와 KaTeX 확장 포함 */
|
|
19
|
+
private parser;
|
|
20
|
+
/** Marked 파싱 중 ref HTML을 임시 보관하는 플레이스홀더 */
|
|
21
|
+
private placeholder;
|
|
19
22
|
render(): import('lit-html/directive.js').DirectiveResult<typeof import('lit-html/directives/unsafe-html.js').UnsafeHTMLDirective>;
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param value 삽입할 마크다운 문자열
|
|
23
|
-
* @return 삽입된 마크다운 문자열
|
|
24
|
-
*/
|
|
25
|
-
private insert;
|
|
26
|
-
/**
|
|
27
|
-
* 참조 카드 엘리먼트를 안전하게 빌드합니다.
|
|
24
|
+
* 코드블록은 별도의 컴포넌트로 렌더링 합니다.
|
|
28
25
|
*/
|
|
29
|
-
private
|
|
26
|
+
private renderCode;
|
|
30
27
|
/**
|
|
31
|
-
*
|
|
32
|
-
* (ref.endIndex 기준을 흔들지 않게 insert 전에 1회 수행)
|
|
28
|
+
* 테이블은 JSON 데이터로 변환하여 별도의 컴포넌트로 렌더링합니다.
|
|
33
29
|
*/
|
|
34
|
-
private
|
|
30
|
+
private renderTable;
|
|
35
31
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* - 나머지는 전부 HTML escape
|
|
32
|
+
* 인용/출처 참조객체가 있을때 ref 태그 HTML을 this.placeholder에 보관하고
|
|
33
|
+
* 마크다운 문자열의 해당 위치에 주석 플레이스홀더를 삽입합니다.
|
|
39
34
|
*/
|
|
40
|
-
private
|
|
35
|
+
private insertRefs;
|
|
41
36
|
/**
|
|
42
|
-
*
|
|
37
|
+
* 코드블록 내부의 <!--ref:idx--> 플레이스홀더와 그 외 refs 태그를 모두 제거하여 HTML이 코드로 렌더링되도록 합니다.
|
|
43
38
|
*/
|
|
44
|
-
private
|
|
39
|
+
private removeRefs;
|
|
45
40
|
}
|
|
@@ -3,12 +3,17 @@ import { property } from 'lit/decorators.js';
|
|
|
3
3
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
4
4
|
import { Marked } from 'marked';
|
|
5
5
|
import markedKatex from 'marked-katex-extension';
|
|
6
|
-
import {
|
|
6
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
7
|
+
import { UJsonElement } from '@iyulab/components/dist/components/UJsonElement.js';
|
|
8
|
+
import { stripZeroWidth, escapeHtmlText } from '@iyulab/components/dist/utilities/sanitizers.js';
|
|
9
|
+
import { buildElementHTML } from '@iyulab/components/dist/utilities/elements.js';
|
|
7
10
|
import { UTooltip } from '@iyulab/components/dist/components/tooltip/UTooltip.component.js';
|
|
8
11
|
import { UCodeBlock } from './UCodeBlock.component.js';
|
|
12
|
+
import { UTableBlock } from './UTableBlock.component.js';
|
|
9
13
|
import { URefTag } from '../references/URefTag.component.js';
|
|
10
14
|
import { URefCard } from '../references/URefCard.component.js';
|
|
11
15
|
import { URefCardGroup } from '../references/URefCardGroup.component.js';
|
|
16
|
+
import { UWidget } from '../widgets/UWidget.component.js';
|
|
12
17
|
import { styles } from './UMarkedBlock.styles.js';
|
|
13
18
|
|
|
14
19
|
var __defProp = Object.defineProperty;
|
|
@@ -20,23 +25,22 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
20
25
|
if (result) __defProp(target, key, result);
|
|
21
26
|
return result;
|
|
22
27
|
};
|
|
23
|
-
class UMarkedBlock extends
|
|
28
|
+
class UMarkedBlock extends UElement {
|
|
24
29
|
constructor() {
|
|
25
30
|
super(...arguments);
|
|
31
|
+
/** Marked 인스턴스: 커스텀 렌더러와 KaTeX 확장 포함 */
|
|
26
32
|
this.parser = new Marked({
|
|
27
33
|
pedantic: false,
|
|
28
34
|
gfm: true,
|
|
29
35
|
breaks: true,
|
|
30
36
|
silent: true,
|
|
31
37
|
renderer: {
|
|
32
|
-
code: (
|
|
33
|
-
|
|
34
|
-
const safeLang = this.escapeHTML(lang);
|
|
35
|
-
const safeText = this.sanitizeText(text);
|
|
36
|
-
return `<u-code-block lang="${safeLang}">${safeText}</u-code-block>`;
|
|
37
|
-
}
|
|
38
|
+
code: (token) => this.renderCode(token),
|
|
39
|
+
table: (token) => this.renderTable(token)
|
|
38
40
|
}
|
|
39
41
|
}).use(markedKatex({ output: "mathml" }));
|
|
42
|
+
/** Marked 파싱 중 ref HTML을 임시 보관하는 플레이스홀더 */
|
|
43
|
+
this.placeholder = new HtmlPlaceholder();
|
|
40
44
|
}
|
|
41
45
|
static {
|
|
42
46
|
this.styles = [super.styles, styles];
|
|
@@ -45,78 +49,83 @@ class UMarkedBlock extends BaseElement {
|
|
|
45
49
|
this.dependencies = {
|
|
46
50
|
"u-tooltip": UTooltip,
|
|
47
51
|
"u-code-block": UCodeBlock,
|
|
52
|
+
"u-table-block": UTableBlock,
|
|
48
53
|
"u-ref-tag": URefTag,
|
|
49
54
|
"u-ref-card": URefCard,
|
|
50
|
-
"u-ref-card-group": URefCardGroup
|
|
55
|
+
"u-ref-card-group": URefCardGroup,
|
|
56
|
+
"u-widget": UWidget
|
|
51
57
|
};
|
|
52
58
|
}
|
|
53
59
|
render() {
|
|
54
60
|
if (!this.value) return nothing;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
this.placeholder.reset();
|
|
62
|
+
let value = stripZeroWidth(this.value);
|
|
63
|
+
if (this.refs?.length) {
|
|
64
|
+
value = this.insertRefs(value, this.refs);
|
|
58
65
|
}
|
|
59
|
-
|
|
60
|
-
return unsafeHTML(html);
|
|
66
|
+
let html = this.parser.parse(value, { async: false });
|
|
67
|
+
return unsafeHTML(this.placeholder.restore(html));
|
|
61
68
|
}
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
* @param value 삽입할 마크다운 문자열
|
|
65
|
-
* @return 삽입된 마크다운 문자열
|
|
69
|
+
/**
|
|
70
|
+
* 코드블록은 별도의 컴포넌트로 렌더링 합니다.
|
|
66
71
|
*/
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const cards = sources.map((s) => this.buildCard(s, false)).join("");
|
|
80
|
-
tooltip = `<u-ref-card-group slot="tooltip">${cards}</u-ref-card-group>`;
|
|
81
|
-
}
|
|
82
|
-
const tag = `<u-ref-tag href="${tagLink}" title="${tagName}">${tagName}${tooltip}</u-ref-tag>`;
|
|
83
|
-
value = value.slice(0, ref.endIndex) + tag + value.slice(ref.endIndex);
|
|
84
|
-
});
|
|
85
|
-
return value;
|
|
72
|
+
renderCode(token) {
|
|
73
|
+
const lang = token.lang ?? "plaintext";
|
|
74
|
+
const trimmed = token.raw.trimEnd();
|
|
75
|
+
const isClosed = trimmed.endsWith("```") || trimmed.endsWith("~~~");
|
|
76
|
+
if (lang === "widget-json") {
|
|
77
|
+
return UJsonElement.buildHTML("u-widget", token.text, { loading: !isClosed });
|
|
78
|
+
}
|
|
79
|
+
if (lang === "action-json") {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
const safeText = this.removeRefs(token.text);
|
|
83
|
+
return buildElementHTML("u-code-block", { lang, loading: !isClosed }, safeText);
|
|
86
84
|
}
|
|
87
|
-
/**
|
|
88
|
-
*
|
|
85
|
+
/**
|
|
86
|
+
* 테이블은 JSON 데이터로 변환하여 별도의 컴포넌트로 렌더링합니다.
|
|
89
87
|
*/
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
renderTable(token) {
|
|
89
|
+
const headers = token.header.map((h) => ({
|
|
90
|
+
text: h.text,
|
|
91
|
+
align: h.align
|
|
92
|
+
}));
|
|
93
|
+
const rows = token.rows.map(
|
|
94
|
+
(row) => row.map((cell) => ({
|
|
95
|
+
text: cell.text,
|
|
96
|
+
align: cell.align
|
|
97
|
+
}))
|
|
98
|
+
);
|
|
99
|
+
return UJsonElement.buildHTML("u-table-block", { headers, rows });
|
|
98
100
|
}
|
|
99
101
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
+
* 인용/출처 참조객체가 있을때 ref 태그 HTML을 this.placeholder에 보관하고
|
|
103
|
+
* 마크다운 문자열의 해당 위치에 주석 플레이스홀더를 삽입합니다.
|
|
102
104
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
insertRefs(value, refs) {
|
|
106
|
+
const sorted = [...refs].sort((a, b) => b.endIndex - a.endIndex);
|
|
107
|
+
for (const ref of sorted) {
|
|
108
|
+
const sources = ref.sources ?? [];
|
|
109
|
+
const label = escapeHtmlText(ref.label ?? `[${refs.indexOf(ref) + 1}]`);
|
|
110
|
+
const link = sources.at(0)?.url;
|
|
111
|
+
let tooltip = "";
|
|
112
|
+
if (sources.length > 0) {
|
|
113
|
+
const cards = sources.map((s) => UJsonElement.buildHTML("u-ref-card", s)).join("");
|
|
114
|
+
tooltip = buildElementHTML("u-ref-card-group", { slot: "tooltip" }, cards);
|
|
115
|
+
}
|
|
116
|
+
const html = buildElementHTML("u-ref-tag", { href: link }, label + tooltip);
|
|
117
|
+
const key = this.placeholder.store(html);
|
|
118
|
+
value = value.slice(0, ref.endIndex) + key + value.slice(ref.endIndex);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
105
121
|
}
|
|
106
122
|
/**
|
|
107
|
-
* 코드블록
|
|
108
|
-
* - u-ref-tag는 통째로 제거(코드블록 안에서 UI 태그가 살아남는 것 방지)
|
|
109
|
-
* - 나머지는 전부 HTML escape
|
|
123
|
+
* 코드블록 내부의 <!--ref:idx--> 플레이스홀더와 그 외 refs 태그를 모두 제거하여 HTML이 코드로 렌더링되도록 합니다.
|
|
110
124
|
*/
|
|
111
|
-
|
|
125
|
+
removeRefs(value) {
|
|
112
126
|
value = value.replace(/<u-ref-tag\b[^>]*>[\s\S]*?<\/u-ref-tag>/gi, "");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* HTML 텍스트 노드로 안전하게 삽입하기 위해 escape 합니다.
|
|
117
|
-
*/
|
|
118
|
-
escapeHTML(value) {
|
|
119
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
127
|
+
value = value.replace(/<!--ref:\d+-->/g, "");
|
|
128
|
+
return value;
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
__decorateClass([
|
|
@@ -125,5 +134,26 @@ __decorateClass([
|
|
|
125
134
|
__decorateClass([
|
|
126
135
|
property({ type: Array })
|
|
127
136
|
], UMarkedBlock.prototype, "refs");
|
|
137
|
+
class HtmlPlaceholder {
|
|
138
|
+
constructor() {
|
|
139
|
+
this.map = {};
|
|
140
|
+
this.idx = 0;
|
|
141
|
+
}
|
|
142
|
+
reset() {
|
|
143
|
+
this.map = {};
|
|
144
|
+
this.idx = 0;
|
|
145
|
+
}
|
|
146
|
+
/** html을 저장하고 대응하는 플레이스홀더 키를 반환합니다. */
|
|
147
|
+
store(html) {
|
|
148
|
+
const key = `<!--ref:${this.idx++}-->`;
|
|
149
|
+
this.map[key] = html;
|
|
150
|
+
return key;
|
|
151
|
+
}
|
|
152
|
+
/** html 안의 등록된 모든 플레이스홀더를 원래 HTML로 복원합니다. */
|
|
153
|
+
restore(html) {
|
|
154
|
+
if (this.idx === 0) return html;
|
|
155
|
+
return html.replace(/<!--ref:\d+-->/g, (key) => this.map[key] ?? "");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
128
158
|
|
|
129
159
|
export { UMarkedBlock };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
2
|
import { ReferenceSource } from '../../types/References.js';
|
|
3
3
|
/**
|
|
4
4
|
* 레퍼런스 정보를 블록 형태로 표시하는 컴포넌트입니다.
|
|
5
5
|
* 단일 또는 다중 레퍼런스 카드를 그리드 레이아웃으로 표시합니다.
|
|
6
6
|
*/
|
|
7
|
-
export declare class URefBlock extends
|
|
7
|
+
export declare class URefBlock extends UElement {
|
|
8
8
|
static styles: import('lit').CSSResultGroup[];
|
|
9
|
-
static dependencies: Record<string, typeof
|
|
9
|
+
static dependencies: Record<string, typeof UElement>;
|
|
10
10
|
/** 접힘 상태 @default: true */
|
|
11
11
|
collapsed: boolean;
|
|
12
12
|
/** 블록 제목 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
2
|
import { property } from 'lit/decorators.js';
|
|
3
|
-
import {
|
|
3
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
4
4
|
import { URefCard } from '../references/URefCard.component.js';
|
|
5
5
|
import { styles } from './URefBlock.styles.js';
|
|
6
6
|
import { repeat } from 'lit/directives/repeat.js';
|
|
@@ -14,7 +14,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
14
14
|
if (result) __defProp(target, key, result);
|
|
15
15
|
return result;
|
|
16
16
|
};
|
|
17
|
-
class URefBlock extends
|
|
17
|
+
class URefBlock extends UElement {
|
|
18
18
|
constructor() {
|
|
19
19
|
super(...arguments);
|
|
20
20
|
this.collapsed = true;
|
|
@@ -49,12 +49,11 @@ class URefBlock extends BaseElement {
|
|
|
49
49
|
return html`
|
|
50
50
|
<u-ref-card
|
|
51
51
|
.type=${source.type}
|
|
52
|
-
.
|
|
53
|
-
.
|
|
52
|
+
.url=${source.url}
|
|
53
|
+
.title=${source.title || ""}
|
|
54
|
+
.snippet=${source.snippet || ""}
|
|
54
55
|
.tags=${source.tags}
|
|
55
|
-
>
|
|
56
|
-
${source.snippet || ""}
|
|
57
|
-
</u-ref-card>
|
|
56
|
+
></u-ref-card>
|
|
58
57
|
`;
|
|
59
58
|
})}
|
|
60
59
|
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
|
+
import { UJsonElement } from '@iyulab/components/dist/components/UJsonElement.js';
|
|
3
|
+
/** 테이블 셀 데이터 타입 */
|
|
4
|
+
export interface TableCell {
|
|
5
|
+
/** 셀에 표시할 텍스트 내용 */
|
|
6
|
+
text: string;
|
|
7
|
+
/** 셀 내용의 정렬 방식. 기본값은 "left" */
|
|
8
|
+
align: "left" | "center" | "right" | null;
|
|
9
|
+
}
|
|
10
|
+
/** 테이블 정렬 상태 */
|
|
11
|
+
export interface SortState {
|
|
12
|
+
/** 현재 정렬 기준이 되는 컬럼 인덱스. -1이면 정렬 안 된 상태 */
|
|
13
|
+
index: number;
|
|
14
|
+
/** 정렬 방향. "asc"는 오름차순, "desc"는 내림차순 */
|
|
15
|
+
dir: "asc" | "desc";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 마크다운 테이블 데이터를 렌더링하는 컴포넌트입니다.
|
|
19
|
+
* 컬럼 정렬, CSV 다운로드 기능을 지원합니다.
|
|
20
|
+
* light DOM 내 `<script type="application/json">` 에서 데이터를 자동으로 읽어냅니다.
|
|
21
|
+
*/
|
|
22
|
+
export declare class UTableBlock extends UJsonElement {
|
|
23
|
+
static styles: (import('lit').CSSResult | import('lit').CSSResultGroup[])[];
|
|
24
|
+
static dependencies: Record<string, typeof UElement>;
|
|
25
|
+
/** 테이블 헤더 목록. 각 헤더는 { text, align } 형식입니다. */
|
|
26
|
+
headers: TableCell[];
|
|
27
|
+
/** 테이블 행 목록. 각 행은 셀 배열입니다. */
|
|
28
|
+
rows: TableCell[][];
|
|
29
|
+
private loading;
|
|
30
|
+
private sort;
|
|
31
|
+
private search;
|
|
32
|
+
private searchCache;
|
|
33
|
+
private searchTimer;
|
|
34
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
35
|
+
/**
|
|
36
|
+
* 하이라이트 렌더링: search 기준으로 매칭되는 부분을 <mark>로 감쌉니다.
|
|
37
|
+
* 내부적으로 안전하게 escape한 뒤 매칭을 위해 RegExp를 사용합니다.
|
|
38
|
+
*/
|
|
39
|
+
private renderHighlightedText;
|
|
40
|
+
/**
|
|
41
|
+
* 현재 검색어와 정렬 상태에 따라 필터링 및 정렬된 행 목록을 반환합니다.
|
|
42
|
+
*/
|
|
43
|
+
private getFilteredSortedRows;
|
|
44
|
+
private handleSearchInput;
|
|
45
|
+
private handleSortColumn;
|
|
46
|
+
private handleDownloadCSV;
|
|
47
|
+
private handleDownloadXLS;
|
|
48
|
+
private triggerDownload;
|
|
49
|
+
}
|