@blueking/ai-ui-sdk 0.2.0-beta.9 → 0.2.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/dist/common/util.d.ts +5 -0
- package/dist/common/util.ts.js +8 -0
- package/dist/components/render-auto-height-textarea/index.vue.css +11 -11
- package/dist/components/render-auto-height-textarea/index.vue.js +1 -1
- package/dist/components/render-auto-refresh/index.vue.css +8 -8
- package/dist/components/render-auto-refresh/index.vue.js +1 -1
- package/dist/components/render-checkbox/index.script.vue.js.js +12 -6
- package/dist/components/render-checkbox/index.vue.css +9 -9
- package/dist/components/render-checkbox/index.vue.d.ts +3 -0
- package/dist/components/render-checkbox/index.vue.js +1 -1
- package/dist/components/render-choose-tag/index.script.vue.js.js +62 -1
- package/dist/components/render-choose-tag/index.vue.css +7 -7
- package/dist/components/render-choose-tag/index.vue.d.ts +1 -0
- package/dist/components/render-choose-tag/index.vue.js +1 -1
- package/dist/components/render-collapse/index.vue.css +2 -2
- package/dist/components/render-collapse/index.vue.js +1 -1
- package/dist/components/render-dataset/components/batch-delete-dataset-data/index.vue.css +3 -3
- package/dist/components/render-dataset/components/batch-delete-dataset-data/index.vue.js +1 -1
- package/dist/components/render-dataset/components/batch-edit-valid-dataset-data/index.vue.css +1 -1
- package/dist/components/render-dataset/components/batch-edit-valid-dataset-data/index.vue.js +1 -1
- package/dist/components/render-dataset/components/dataset-data-tool-icons/index.script.vue.js +1 -1
- package/dist/components/render-dataset/components/dataset-data-valid/index.vue.css +1 -1
- package/dist/components/render-dataset/components/dataset-data-valid/index.vue.js +1 -1
- package/dist/components/render-dataset/components/edit-dataset-data/index.vue.css +3 -3
- package/dist/components/render-dataset/components/edit-dataset-data/index.vue.js +1 -1
- package/dist/components/render-dataset/components/operation-record/index.vue.css +4 -4
- package/dist/components/render-dataset/components/operation-record/index.vue.js +1 -1
- package/dist/components/render-dataset/components/operation-record/table/index.vue.css +1 -1
- package/dist/components/render-dataset/components/operation-record/table/index.vue.js +1 -1
- package/dist/components/render-dataset/components/operation-record/view-op-result/index.vue.css +2 -2
- package/dist/components/render-dataset/components/operation-record/view-op-result/index.vue.js +1 -1
- package/dist/components/render-dataset/components/plus-dataset/index.script.vue.js.js +1 -3
- package/dist/components/render-dataset/components/plus-dataset/index.vue.css +2 -2
- package/dist/components/render-dataset/components/plus-dataset/index.vue.js +1 -1
- package/dist/components/render-dataset/components/plus-dataset-data/form/api.script.vue.js.js +1 -1
- package/dist/components/render-dataset/components/plus-dataset-data/form/api.vue.css +2 -2
- package/dist/components/render-dataset/components/plus-dataset-data/form/api.vue.js +1 -1
- package/dist/components/render-dataset/components/plus-dataset-data/form/footer.vue.css +1 -1
- package/dist/components/render-dataset/components/plus-dataset-data/form/footer.vue.js +1 -1
- package/dist/components/render-dataset/components/plus-dataset-data/form/index.vue.css +2 -2
- package/dist/components/render-dataset/components/plus-dataset-data/form/index.vue.js +1 -1
- package/dist/components/render-dataset/components/review-dataset-data/index.vue.css +10 -10
- package/dist/components/render-dataset/components/review-dataset-data/index.vue.js +1 -1
- package/dist/components/render-dataset-detail-origin-data/index.vue.css +3 -3
- package/dist/components/render-dataset-detail-origin-data/index.vue.js +1 -1
- package/dist/components/render-dataset-list/components/main.vue.css +3 -3
- package/dist/components/render-dataset-list/components/main.vue.js +1 -1
- package/dist/components/render-dataset-list/index.vue.css +2 -2
- package/dist/components/render-dataset-list/index.vue.js +1 -1
- package/dist/components/render-file-card/index.vue.css +11 -11
- package/dist/components/render-file-card/index.vue.js +1 -1
- package/dist/components/render-file-type-icon/index.vue.css +5 -5
- package/dist/components/render-file-type-icon/index.vue.js +1 -1
- package/dist/components/render-header/index.vue.css +3 -3
- package/dist/components/render-header/index.vue.js +1 -1
- package/dist/components/render-like/index.vue.css +5 -5
- package/dist/components/render-like/index.vue.js +1 -1
- package/dist/components/render-markdown/common.d.ts +19 -0
- package/dist/components/render-markdown/common.ts.js +74 -0
- package/dist/components/render-markdown/edit.d.ts +82 -0
- package/dist/components/render-markdown/edit.postcss.css +92 -0
- package/dist/components/render-markdown/edit.ts.js +228 -0
- package/dist/components/render-markdown/filter-css.d.ts +3 -0
- package/dist/components/render-markdown/filter-css.ts.js +119 -0
- package/dist/components/render-markdown/katex.d.ts +83 -0
- package/dist/components/render-markdown/katex.ts.js +309 -0
- package/dist/components/render-markdown/mark-down-code-copy.d.ts +7 -0
- package/dist/components/render-markdown/mark-down-code-copy.ts.js +73 -0
- package/dist/components/render-markdown/prism-atom-one-dark.postcss.css +192 -0
- package/dist/components/render-markdown/{mark-down.d.ts → view.d.ts} +2 -27
- package/dist/components/render-markdown/view.postcss.css +99 -0
- package/dist/components/render-markdown/view.ts.js +167 -0
- package/dist/components/render-router-tab/index.vue.css +3 -3
- package/dist/components/render-router-tab/index.vue.js +1 -1
- package/dist/components/render-t-table/index.vue.css +27 -27
- package/dist/components/render-t-table/index.vue.js +1 -1
- package/dist/components/render-tag-tree/index.vue.css +7 -7
- package/dist/components/render-tag-tree/index.vue.js +1 -1
- package/dist/components/render-tree/index.script.vue.js +2 -1
- package/dist/components.d.ts +3 -2
- package/dist/components.ts.js +3 -2
- package/dist/css/tailwind.css +19 -3
- package/dist/hooks/use-chat.d.ts +2 -0
- package/dist/hooks/use-ellipsis-tooltip.ts.js +6 -2
- package/dist/hooks/use-form-leave-confirm.ts.js +2 -0
- package/dist/http/helper/tool.ts.js +2 -1
- package/dist/types/enum.d.ts +1 -0
- package/dist/types/enum.ts.js +1 -0
- package/dist/types/tool.d.ts +2 -0
- package/package.json +8 -4
- package/dist/components/render-markdown/mark-down-code.d.ts +0 -2
- package/dist/components/render-markdown/mark-down-code.ts.js +0 -171
- package/dist/components/render-markdown/mark-down-emoji.d.ts +0 -2
- package/dist/components/render-markdown/mark-down-emoji.ts.js +0 -4
- package/dist/components/render-markdown/mark-down.postcss.css +0 -154
- package/dist/components/render-markdown/mark-down.ts.js +0 -152
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _object_spread(target) {
|
|
15
|
+
for(var i = 1; i < arguments.length; i++){
|
|
16
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
17
|
+
var ownKeys = Object.keys(source);
|
|
18
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
19
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
20
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
ownKeys.forEach(function(key) {
|
|
24
|
+
_define_property(target, key, source[key]);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return target;
|
|
28
|
+
}
|
|
29
|
+
import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
30
|
+
import Editor from '@toast-ui/editor';
|
|
31
|
+
import { commonConfig } from './common.ts.js';
|
|
32
|
+
import '@toast-ui/editor/dist/toastui-editor.css';
|
|
33
|
+
import './edit.postcss.css';
|
|
34
|
+
var EditorMode;
|
|
35
|
+
(function(EditorMode) {
|
|
36
|
+
EditorMode["Markdown"] = "markdown";
|
|
37
|
+
EditorMode["Wysiwyg"] = "wysiwyg";
|
|
38
|
+
})(EditorMode || (EditorMode = {}));
|
|
39
|
+
export default defineComponent({
|
|
40
|
+
props: {
|
|
41
|
+
modelValue: {
|
|
42
|
+
type: String
|
|
43
|
+
},
|
|
44
|
+
placeholder: {
|
|
45
|
+
type: String
|
|
46
|
+
},
|
|
47
|
+
height: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: '500px'
|
|
50
|
+
},
|
|
51
|
+
disabled: {
|
|
52
|
+
type: Boolean
|
|
53
|
+
},
|
|
54
|
+
fontSize: {
|
|
55
|
+
type: String,
|
|
56
|
+
default: '14px'
|
|
57
|
+
},
|
|
58
|
+
backgroundColor: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: '#fff'
|
|
61
|
+
},
|
|
62
|
+
mode: {
|
|
63
|
+
type: String,
|
|
64
|
+
default: "wysiwyg"
|
|
65
|
+
},
|
|
66
|
+
isSimpleToolbar: {
|
|
67
|
+
type: Boolean,
|
|
68
|
+
default: false
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
emits: {
|
|
72
|
+
'update:modelValue': (value)=>value
|
|
73
|
+
},
|
|
74
|
+
setup (props, { emit }) {
|
|
75
|
+
const editorRef = ref();
|
|
76
|
+
let editor;
|
|
77
|
+
const handleChange = ()=>{
|
|
78
|
+
const value = (editor === null || editor === void 0 ? void 0 : editor.getMarkdown()) || '';
|
|
79
|
+
emit('update:modelValue', value);
|
|
80
|
+
};
|
|
81
|
+
const handleInit = ()=>{
|
|
82
|
+
const defaultMode = props.mode || "wysiwyg";
|
|
83
|
+
// 切换模式按钮
|
|
84
|
+
const modeSwitchEl = document.createElement('section');
|
|
85
|
+
modeSwitchEl.className = 'toastui-editor-toolbar-custom-button g-cursor-pointer g-ml-4';
|
|
86
|
+
modeSwitchEl.innerHTML = '<i class="bkaidev-icon bkaidev-qiehuan g-mr-4 g-text-16"></i>切换代码模式';
|
|
87
|
+
modeSwitchEl.dataset.mode = defaultMode;
|
|
88
|
+
modeSwitchEl.addEventListener('click', ()=>{
|
|
89
|
+
const mode = modeSwitchEl.dataset.mode;
|
|
90
|
+
if (mode === "markdown") {
|
|
91
|
+
editor.changeMode("wysiwyg");
|
|
92
|
+
modeSwitchEl.dataset.mode = "wysiwyg";
|
|
93
|
+
modeSwitchEl.innerHTML = '<i class="bkaidev-icon bkaidev-qiehuan g-mr-4 g-text-16"></i>切换代码模式';
|
|
94
|
+
} else {
|
|
95
|
+
editor.changeMode("markdown");
|
|
96
|
+
modeSwitchEl.dataset.mode = "markdown";
|
|
97
|
+
modeSwitchEl.innerHTML = '<i class="bkaidev-icon bkaidev-qiehuan g-mr-4 g-text-16"></i>切换富文本模式';
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// 全屏按钮复用 use-click-proxy 的全局 full-screen 代理。
|
|
101
|
+
const fullScreenEl = document.createElement('section');
|
|
102
|
+
fullScreenEl.className = 'toastui-editor-toolbar-custom-button g-cursor-pointer g-ml-12';
|
|
103
|
+
fullScreenEl.innerHTML = [
|
|
104
|
+
'<span class="click-full-screen">',
|
|
105
|
+
'<i class="ai-ui-sdk-icon ai-ui-sdk-full-screen g-mr-4 g-text-14"></i>全屏',
|
|
106
|
+
'</span>',
|
|
107
|
+
'<span class="click-un-full-screen">',
|
|
108
|
+
'<i class="ai-ui-sdk-icon ai-ui-sdk-un-full-screen g-mr-4 g-text-14"></i>取消全屏',
|
|
109
|
+
'</span>'
|
|
110
|
+
].join('');
|
|
111
|
+
// 初始化编辑器
|
|
112
|
+
editor = new Editor(_object_spread({
|
|
113
|
+
el: editorRef.value,
|
|
114
|
+
initialEditType: defaultMode,
|
|
115
|
+
previewStyle: 'vertical',
|
|
116
|
+
hideModeSwitch: true,
|
|
117
|
+
autofocus: false,
|
|
118
|
+
toolbarItems: [
|
|
119
|
+
[
|
|
120
|
+
'heading',
|
|
121
|
+
'bold',
|
|
122
|
+
'italic',
|
|
123
|
+
'strike'
|
|
124
|
+
],
|
|
125
|
+
...props.isSimpleToolbar ? [] : [
|
|
126
|
+
[
|
|
127
|
+
'hr',
|
|
128
|
+
'quote'
|
|
129
|
+
],
|
|
130
|
+
[
|
|
131
|
+
'ul',
|
|
132
|
+
'ol',
|
|
133
|
+
'task',
|
|
134
|
+
'indent',
|
|
135
|
+
'outdent'
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
'table',
|
|
139
|
+
'image',
|
|
140
|
+
'link'
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
'code',
|
|
144
|
+
'codeblock'
|
|
145
|
+
]
|
|
146
|
+
],
|
|
147
|
+
[
|
|
148
|
+
...props.isSimpleToolbar ? [] : [
|
|
149
|
+
{
|
|
150
|
+
name: 'modeSwitch',
|
|
151
|
+
el: modeSwitchEl
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
{
|
|
155
|
+
name: 'fullScreen',
|
|
156
|
+
el: fullScreenEl
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
],
|
|
160
|
+
placeholder: props.placeholder,
|
|
161
|
+
initialValue: props.modelValue || '',
|
|
162
|
+
height: props.height,
|
|
163
|
+
events: {
|
|
164
|
+
change: handleChange
|
|
165
|
+
}
|
|
166
|
+
}, commonConfig));
|
|
167
|
+
};
|
|
168
|
+
const handleDestroy = ()=>{
|
|
169
|
+
editor.destroy();
|
|
170
|
+
editor = null;
|
|
171
|
+
};
|
|
172
|
+
const handleDisabled = ()=>{
|
|
173
|
+
const root = editorRef.value;
|
|
174
|
+
const editContent = root.querySelector('.ww-mode [contenteditable]');
|
|
175
|
+
const toolbar = root.querySelector('.toastui-editor-toolbar');
|
|
176
|
+
// 禁用事件
|
|
177
|
+
if (root) {
|
|
178
|
+
const stopEvent = (event)=>{
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
event.stopPropagation();
|
|
181
|
+
event.stopImmediatePropagation();
|
|
182
|
+
};
|
|
183
|
+
if (props.disabled) {
|
|
184
|
+
root.addEventListener('beforeinput', stopEvent, true);
|
|
185
|
+
root.addEventListener('input', stopEvent, true);
|
|
186
|
+
root.addEventListener('paste', stopEvent, true);
|
|
187
|
+
root.addEventListener('cut', stopEvent, true);
|
|
188
|
+
root.addEventListener('drop', stopEvent, true);
|
|
189
|
+
root.addEventListener('compositionstart', stopEvent, true);
|
|
190
|
+
} else {
|
|
191
|
+
root.removeEventListener('beforeinput', stopEvent, true);
|
|
192
|
+
root.removeEventListener('input', stopEvent, true);
|
|
193
|
+
root.removeEventListener('paste', stopEvent, true);
|
|
194
|
+
root.removeEventListener('cut', stopEvent, true);
|
|
195
|
+
root.removeEventListener('drop', stopEvent, true);
|
|
196
|
+
root.removeEventListener('compositionstart', stopEvent, true);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (editContent) {
|
|
200
|
+
// 设置样式
|
|
201
|
+
editContent.setAttribute('contenteditable', String(!props.disabled));
|
|
202
|
+
editContent.style.cursor = props.disabled ? 'not-allowed' : 'auto';
|
|
203
|
+
}
|
|
204
|
+
if (toolbar) {
|
|
205
|
+
toolbar.style.pointerEvents = props.disabled ? 'none' : 'auto';
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
watch(()=>props.modelValue, (value)=>{
|
|
209
|
+
if (!editor) return;
|
|
210
|
+
if (value !== editor.getMarkdown()) {
|
|
211
|
+
editor.setMarkdown(value || '');
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
onMounted(()=>{
|
|
215
|
+
handleInit();
|
|
216
|
+
handleDisabled();
|
|
217
|
+
});
|
|
218
|
+
onBeforeUnmount(handleDestroy);
|
|
219
|
+
watch(()=>props.disabled, handleDisabled);
|
|
220
|
+
return ()=>h('section', {
|
|
221
|
+
ref: editorRef,
|
|
222
|
+
class: `full-screen-wrap ${props.disabled ? 'is-disabled' : ''}`,
|
|
223
|
+
style: {
|
|
224
|
+
backgroundColor: props.backgroundColor
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const NUMERIC_PROPS = new Set([
|
|
2
|
+
'height',
|
|
3
|
+
'top',
|
|
4
|
+
'bottom',
|
|
5
|
+
'margin-right',
|
|
6
|
+
'margin-left',
|
|
7
|
+
'border-bottom-width',
|
|
8
|
+
'width',
|
|
9
|
+
'min-width',
|
|
10
|
+
'font-size'
|
|
11
|
+
]);
|
|
12
|
+
const COLOR_PROPS = new Set([
|
|
13
|
+
'color',
|
|
14
|
+
'background-color'
|
|
15
|
+
]);
|
|
16
|
+
const FONT_WEIGHT_PROPS = new Set([
|
|
17
|
+
'font-weight'
|
|
18
|
+
]);
|
|
19
|
+
const VERTICAL_ALIGN_VALUES = new Set([
|
|
20
|
+
'baseline',
|
|
21
|
+
'middle',
|
|
22
|
+
'sub',
|
|
23
|
+
'super',
|
|
24
|
+
'text-top',
|
|
25
|
+
'text-bottom',
|
|
26
|
+
'top',
|
|
27
|
+
'bottom'
|
|
28
|
+
]);
|
|
29
|
+
const DANGEROUS_CSS_VALUE_RE = /(url\s*\(|expression\s*\(|javascript\s*:|vbscript\s*:|data\s*:|@import|var\s*\(|calc\s*\()/i;
|
|
30
|
+
const NUMERIC_VALUE_RE = /^-?\d+(\.\d+)?(em|px|rem|%)?$/i;
|
|
31
|
+
const FONT_WEIGHT_VALUE_RE = /^(normal|bold|bolder|lighter|[1-9]00)$/i;
|
|
32
|
+
const HEX_COLOR_RE = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
33
|
+
const RGB_COLOR_RE = /^rgba?\(\s*((25[0-5]|2[0-4]\d|1?\d?\d)\s*,\s*){2}(25[0-5]|2[0-4]\d|1?\d?\d)(\s*,\s*(0|1|0?\.\d+))?\s*\)$/i;
|
|
34
|
+
const HSL_COLOR_RE = /^hsla?\(\s*(360|3[0-5]\d|[12]?\d?\d)\s*,\s*(100|[1-9]?\d)%\s*,\s*(100|[1-9]?\d)%(\s*,\s*(0|1|0?\.\d+))?\s*\)$/i;
|
|
35
|
+
const SAFE_COLOR_KEYWORDS = new Set([
|
|
36
|
+
'transparent',
|
|
37
|
+
'currentcolor',
|
|
38
|
+
'black',
|
|
39
|
+
'white',
|
|
40
|
+
'red',
|
|
41
|
+
'green',
|
|
42
|
+
'blue',
|
|
43
|
+
'gray',
|
|
44
|
+
'grey',
|
|
45
|
+
'yellow',
|
|
46
|
+
'orange',
|
|
47
|
+
'purple',
|
|
48
|
+
'pink',
|
|
49
|
+
'brown'
|
|
50
|
+
]);
|
|
51
|
+
const isSafeCssValue = (value)=>{
|
|
52
|
+
return !DANGEROUS_CSS_VALUE_RE.test(value);
|
|
53
|
+
};
|
|
54
|
+
const isSafeNumericValue = (value)=>{
|
|
55
|
+
return NUMERIC_VALUE_RE.test(value);
|
|
56
|
+
};
|
|
57
|
+
const isSafeFontWeightValue = (value)=>{
|
|
58
|
+
if (!FONT_WEIGHT_VALUE_RE.test(value)) return false;
|
|
59
|
+
const num = Number(value);
|
|
60
|
+
if (Number.isNaN(num)) return true;
|
|
61
|
+
return num >= 100 && num <= 900 && num % 100 === 0;
|
|
62
|
+
};
|
|
63
|
+
const isSafeColorValue = (value)=>{
|
|
64
|
+
const normalized = value.trim().toLowerCase();
|
|
65
|
+
if (!isSafeCssValue(normalized)) return false;
|
|
66
|
+
if (SAFE_COLOR_KEYWORDS.has(normalized)) return true;
|
|
67
|
+
if (HEX_COLOR_RE.test(normalized)) return true;
|
|
68
|
+
if (RGB_COLOR_RE.test(normalized)) return true;
|
|
69
|
+
if (HSL_COLOR_RE.test(normalized)) return true;
|
|
70
|
+
return false;
|
|
71
|
+
};
|
|
72
|
+
const isSafeVerticalAlignValue = (value)=>{
|
|
73
|
+
const normalized = value.trim().toLowerCase();
|
|
74
|
+
return VERTICAL_ALIGN_VALUES.has(normalized) || isSafeNumericValue(normalized);
|
|
75
|
+
};
|
|
76
|
+
export const filterStyle = (style)=>{
|
|
77
|
+
return style.split(';').map((item)=>item.trim()).filter(Boolean).map((item)=>{
|
|
78
|
+
const [rawProp, ...rest] = item.split(':');
|
|
79
|
+
const prop = rawProp === null || rawProp === void 0 ? void 0 : rawProp.trim().toLowerCase();
|
|
80
|
+
const value = rest.join(':').trim();
|
|
81
|
+
return {
|
|
82
|
+
prop,
|
|
83
|
+
value,
|
|
84
|
+
normalizedValue: value.toLowerCase()
|
|
85
|
+
};
|
|
86
|
+
}).filter(({ prop, value, normalizedValue })=>{
|
|
87
|
+
if (!prop || !value) return false;
|
|
88
|
+
if (!isSafeCssValue(normalizedValue)) return false;
|
|
89
|
+
if (NUMERIC_PROPS.has(prop)) {
|
|
90
|
+
return isSafeNumericValue(normalizedValue);
|
|
91
|
+
}
|
|
92
|
+
if (COLOR_PROPS.has(prop)) {
|
|
93
|
+
return isSafeColorValue(normalizedValue);
|
|
94
|
+
}
|
|
95
|
+
if (FONT_WEIGHT_PROPS.has(prop)) {
|
|
96
|
+
return isSafeFontWeightValue(normalizedValue);
|
|
97
|
+
}
|
|
98
|
+
if (prop === 'vertical-align') {
|
|
99
|
+
return isSafeVerticalAlignValue(normalizedValue);
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}).map(({ prop, value })=>`${prop}: ${value}`).join('; ');
|
|
103
|
+
};
|
|
104
|
+
export const cleanStyleAfterXss = (root)=>{
|
|
105
|
+
root.querySelectorAll('[style]').forEach((el)=>{
|
|
106
|
+
const nextStyle = filterStyle(el.getAttribute('style') || '');
|
|
107
|
+
if (nextStyle) {
|
|
108
|
+
el.setAttribute('style', nextStyle);
|
|
109
|
+
} else {
|
|
110
|
+
el.removeAttribute('style');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
export const filterCSS = (html)=>{
|
|
115
|
+
const template = document.createElement('template');
|
|
116
|
+
template.innerHTML = html;
|
|
117
|
+
cleanStyleAfterXss(template.content);
|
|
118
|
+
return template.innerHTML;
|
|
119
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type KatexOptions } from 'katex';
|
|
2
|
+
import 'katex/dist/katex.min.css';
|
|
3
|
+
type MdNode = {
|
|
4
|
+
literal?: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
parent?: MdNode | null;
|
|
7
|
+
};
|
|
8
|
+
export interface TuiKatexOptions {
|
|
9
|
+
/**
|
|
10
|
+
* 兼容 100% 写法。
|
|
11
|
+
* 标准 LaTeX 应该写 100\%
|
|
12
|
+
*/
|
|
13
|
+
autoEscapePercent?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* 是否支持 $E=mc^2$。
|
|
16
|
+
* 如果业务里经常出现 $100 这种价格,建议设为 false。
|
|
17
|
+
*/
|
|
18
|
+
enableSingleDollar?: boolean;
|
|
19
|
+
katexOptions?: KatexOptions;
|
|
20
|
+
}
|
|
21
|
+
export declare function normalizeLatex(input: string, options?: TuiKatexOptions): string;
|
|
22
|
+
export declare function renderKatexToHtml(latex: string, displayMode?: boolean, options?: TuiKatexOptions): string;
|
|
23
|
+
export declare function createTuiKatexCustomRenderer(options?: TuiKatexOptions): {
|
|
24
|
+
paragraph(_node: MdNode, context: any): any;
|
|
25
|
+
text(node: MdNode): {
|
|
26
|
+
type: "text";
|
|
27
|
+
content: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "html";
|
|
30
|
+
content: string;
|
|
31
|
+
};
|
|
32
|
+
softbreak(_node: MdNode, context: any): any;
|
|
33
|
+
linebreak(_node: MdNode, context: any): any;
|
|
34
|
+
/**
|
|
35
|
+
* 支持 TUI custom block:
|
|
36
|
+
*
|
|
37
|
+
* $$katex
|
|
38
|
+
* \frac{15}{5000}\times 100\%=0.3\%
|
|
39
|
+
* $$
|
|
40
|
+
*/
|
|
41
|
+
katex: (node: MdNode) => ({
|
|
42
|
+
type: "openTag";
|
|
43
|
+
tagName: string;
|
|
44
|
+
outerNewLine: boolean;
|
|
45
|
+
classNames: string[];
|
|
46
|
+
content?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
type: "html";
|
|
49
|
+
content: string;
|
|
50
|
+
tagName?: undefined;
|
|
51
|
+
outerNewLine?: undefined;
|
|
52
|
+
classNames?: undefined;
|
|
53
|
+
} | {
|
|
54
|
+
type: "closeTag";
|
|
55
|
+
tagName: string;
|
|
56
|
+
outerNewLine: boolean;
|
|
57
|
+
classNames?: undefined;
|
|
58
|
+
content?: undefined;
|
|
59
|
+
})[];
|
|
60
|
+
/**
|
|
61
|
+
* 这里 latex 也按数学公式处理,不走 latex.js。
|
|
62
|
+
*/
|
|
63
|
+
latex: (node: MdNode) => ({
|
|
64
|
+
type: "openTag";
|
|
65
|
+
tagName: string;
|
|
66
|
+
outerNewLine: boolean;
|
|
67
|
+
classNames: string[];
|
|
68
|
+
content?: undefined;
|
|
69
|
+
} | {
|
|
70
|
+
type: "html";
|
|
71
|
+
content: string;
|
|
72
|
+
tagName?: undefined;
|
|
73
|
+
outerNewLine?: undefined;
|
|
74
|
+
classNames?: undefined;
|
|
75
|
+
} | {
|
|
76
|
+
type: "closeTag";
|
|
77
|
+
tagName: string;
|
|
78
|
+
outerNewLine: boolean;
|
|
79
|
+
classNames?: undefined;
|
|
80
|
+
content?: undefined;
|
|
81
|
+
})[];
|
|
82
|
+
};
|
|
83
|
+
export {};
|