@codady/coax 0.0.1 → 0.0.3
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/coax.cjs.js +829 -140
- package/dist/coax.cjs.min.js +3 -3
- package/dist/coax.esm.js +823 -140
- package/dist/coax.esm.min.js +3 -3
- package/dist/coax.umd.js +849 -138
- package/dist/coax.umd.min.js +3 -3
- package/examples/.htaccess +0 -0
- package/examples/append-highlight.html +82 -0
- package/examples/color-selector.html +412 -0
- package/examples/css-highlight.html +2 -18
- package/examples/deepseek-highlight.html +91 -0
- package/examples/js-highlight.html +2 -17
- package/examples/md-highlight.html +60 -0
- package/examples/nginx.htaccess +0 -0
- package/examples/plain-highlight.html +47 -0
- package/examples/replace-highlight.html +69 -0
- package/examples/stream-highlight.html +64 -0
- package/examples/theme-highlight.html +69 -0
- package/package.json +19 -3
- package/rollup.config.js +3 -3
- package/src/Coax.js +50 -184
- package/src/Coax.ts +56 -207
- package/src/components/CoaxElem.js +457 -0
- package/src/components/CoaxElem.ts +488 -0
- package/src/modules.js +12 -0
- package/src/modules.ts +23 -0
- package/src/rules/css.js +11 -0
- package/src/rules/css.ts +11 -0
- package/src/rules/html.js +13 -0
- package/src/rules/html.ts +13 -0
- package/src/rules/javascript.js +10 -0
- package/src/rules/javascript.ts +10 -0
- package/src/rules/markdown.js +29 -0
- package/src/rules/markdown.ts +41 -0
- package/src/rules/ruleCss - /345/211/257/346/234/254.js" +10 -0
- package/src/rules/ruleHTML - /345/211/257/346/234/254.js" +12 -0
- package/src/rules/ruleJs - /345/211/257/346/234/254.js" +10 -0
- package/src/rules/ruleTs - /345/211/257/346/234/254.js" +12 -0
- package/src/rules/typescript.js +12 -0
- package/src/rules/typescript.ts +12 -0
- package/src/tools/copy.js +26 -0
- package/src/tools/copy.ts +29 -0
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Last modified: 2026/01/12 09:40:20
|
|
3
|
+
*/
|
|
4
|
+
import typeWriter from "@codady/utils/typeWriter";
|
|
5
|
+
import parseClasses from "@codady/utils/parseClasses";
|
|
6
|
+
import NAMESPACE from "@codady/utils/namespace";
|
|
7
|
+
import getEl from "@codady/utils/getEl";
|
|
8
|
+
import createTools from "@codady/utils/createTools";
|
|
9
|
+
import createEl from "@codady/utils/createEl";
|
|
10
|
+
class CoaxElem extends HTMLElement {
|
|
11
|
+
// A static Map to hold the configuration for different languages
|
|
12
|
+
static languages = new Map();
|
|
13
|
+
static tools = [];
|
|
14
|
+
source;
|
|
15
|
+
_renderQueued = false;
|
|
16
|
+
baseStylesEl;
|
|
17
|
+
themeStylesEl;
|
|
18
|
+
dynamicStylesEl;
|
|
19
|
+
highlightedCodeEl;
|
|
20
|
+
headerEl;
|
|
21
|
+
codeNameEl;
|
|
22
|
+
codeToolsEl;
|
|
23
|
+
codeBodyEl;
|
|
24
|
+
alias;
|
|
25
|
+
lineString;
|
|
26
|
+
speed;
|
|
27
|
+
autoScroll;
|
|
28
|
+
constructor() {
|
|
29
|
+
super();
|
|
30
|
+
// Attach a Shadow DOM to the custom element
|
|
31
|
+
this.attachShadow({ mode: 'open' });
|
|
32
|
+
// Remove leading/trailing whitespace from the raw code content
|
|
33
|
+
this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
34
|
+
this.lang = 'plain';
|
|
35
|
+
this.alias = 'Plain Text';
|
|
36
|
+
this.lineString = '';
|
|
37
|
+
this.speed = 5;
|
|
38
|
+
this.autoScroll = true;
|
|
39
|
+
// 1. 初始化基础骨架
|
|
40
|
+
this.shadowRoot.innerHTML = `
|
|
41
|
+
<style id="base-styles">
|
|
42
|
+
:host {
|
|
43
|
+
--border-width:1px;
|
|
44
|
+
--border-style:solid;
|
|
45
|
+
--radius:9px;
|
|
46
|
+
--height:auto;
|
|
47
|
+
--max-height:500px;
|
|
48
|
+
--radius:9px;
|
|
49
|
+
--padding:1em;
|
|
50
|
+
--font-size:16px;
|
|
51
|
+
--line-height:1.8;
|
|
52
|
+
--background:rgb(247, 247, 247);
|
|
53
|
+
--border-color:rgb(224, 224, 224);
|
|
54
|
+
--color-code:rgb(51, 51, 51);
|
|
55
|
+
--color-index:rgb(153, 153, 153);
|
|
56
|
+
--color-stripe:rgba(0,0,0,0.04);
|
|
57
|
+
--color-hover:rgba(0,0,0,0.06);
|
|
58
|
+
}
|
|
59
|
+
:host([scheme="dark"]){
|
|
60
|
+
--background: #282c34;
|
|
61
|
+
--border-color: transparent;
|
|
62
|
+
--color-code: #abb2bf;
|
|
63
|
+
--color-index:rgb(153, 153, 153);
|
|
64
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
65
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
66
|
+
}
|
|
67
|
+
@media (prefers-color-scheme: dark) {
|
|
68
|
+
:host{
|
|
69
|
+
--background: #282c34;
|
|
70
|
+
--border-color: transparent;
|
|
71
|
+
--color-code: #abb2bf;
|
|
72
|
+
--color-index:rgb(153, 153, 153);
|
|
73
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
74
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
:host {
|
|
78
|
+
font-size: var(--${NAMESPACE}-code-font-size,var(--font-size));
|
|
79
|
+
display: block;
|
|
80
|
+
box-sizing:border-box;
|
|
81
|
+
background:var(--${NAMESPACE}-code-background-color,var(--background));
|
|
82
|
+
color:var(--${NAMESPACE}-code-color,var(--color-code));
|
|
83
|
+
border:var(--${NAMESPACE}-code-border-width,var(--border-width)) var(--${NAMESPACE}-code-border-style,var(--border-style)) var(--${NAMESPACE}-code-border-color,var(--border-color));
|
|
84
|
+
transition: border-color 0.3s ease,color 0.3s ease;
|
|
85
|
+
border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
|
|
86
|
+
}
|
|
87
|
+
#code-header{
|
|
88
|
+
line-height:calc(var(--${NAMESPACE}-code-line-height,var(--line-height))*1.5);
|
|
89
|
+
padding-inline-start:var(--${NAMESPACE}-code-padding,var(--padding));
|
|
90
|
+
display:flex;
|
|
91
|
+
|
|
92
|
+
>:first-child{
|
|
93
|
+
flex:auto;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
#code-body{
|
|
97
|
+
padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
|
|
98
|
+
height:var(--${NAMESPACE}-code-height,var(--height));
|
|
99
|
+
max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
|
|
100
|
+
overflow:auto;
|
|
101
|
+
}
|
|
102
|
+
pre,code{
|
|
103
|
+
font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
|
|
104
|
+
margin:0; padding:0;
|
|
105
|
+
}
|
|
106
|
+
code>div{
|
|
107
|
+
display:flex;
|
|
108
|
+
padding:0 var(--${NAMESPACE}-code-padding,var(--padding));
|
|
109
|
+
line-height: var(--${NAMESPACE}-code-line-height,var(--line-height));
|
|
110
|
+
box-sizing:border-box;
|
|
111
|
+
|
|
112
|
+
>div{
|
|
113
|
+
flex:auto;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
code>div>div:empty:before{
|
|
117
|
+
content:' ';
|
|
118
|
+
}
|
|
119
|
+
:host([indexed]) code>div:before{
|
|
120
|
+
display:inline-flex;
|
|
121
|
+
color:var(--color-index);
|
|
122
|
+
content: attr(data-index);
|
|
123
|
+
min-width:var(--${NAMESPACE}-code-index-width,2em);
|
|
124
|
+
margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
|
|
125
|
+
}
|
|
126
|
+
:host([striped]) code>div:nth-child(odd){
|
|
127
|
+
background-color:var(--color-stripe);
|
|
128
|
+
}
|
|
129
|
+
:host([hoverable]) code>div:hover{
|
|
130
|
+
background-color:var(--color-hover);
|
|
131
|
+
}
|
|
132
|
+
:host([wrapped]) code>div>div{
|
|
133
|
+
white-space: pre-wrap;
|
|
134
|
+
}
|
|
135
|
+
:host([unnamed]) #code-name{
|
|
136
|
+
display:none;
|
|
137
|
+
}
|
|
138
|
+
.${NAMESPACE}-box-tools{
|
|
139
|
+
>*{
|
|
140
|
+
font-size:14px;
|
|
141
|
+
display:inline-flex;
|
|
142
|
+
align-items:center;
|
|
143
|
+
justify-content:center;
|
|
144
|
+
height:2em;
|
|
145
|
+
line-height:2em;
|
|
146
|
+
aspect-ratio:1/1;
|
|
147
|
+
margin-inline-end:8px;
|
|
148
|
+
transition:all 200ms ease;
|
|
149
|
+
border-radius:6px;
|
|
150
|
+
&:hover{
|
|
151
|
+
cursor:pointer;
|
|
152
|
+
background-color:rgba(0,0,0,.04);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
[rep=icon]{
|
|
156
|
+
display:inline-flex;
|
|
157
|
+
align-items:center;
|
|
158
|
+
justify-content:center;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
[disabled]{
|
|
162
|
+
pointer-event:none;
|
|
163
|
+
}
|
|
164
|
+
</style>
|
|
165
|
+
<style id="dynamic-styles"></style>
|
|
166
|
+
<style id="theme-styles"></style>
|
|
167
|
+
<div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
|
|
168
|
+
<div id="code-body">
|
|
169
|
+
<pre><code id="highlight-code"></code></pre>
|
|
170
|
+
</div>
|
|
171
|
+
`;
|
|
172
|
+
// 缓存引用
|
|
173
|
+
this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
|
|
174
|
+
this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
|
|
175
|
+
this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
|
|
176
|
+
this.headerEl = getEl('#code-header', this.shadowRoot);
|
|
177
|
+
this.codeNameEl = getEl('#code-name', this.shadowRoot);
|
|
178
|
+
this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
|
|
179
|
+
this.codeBodyEl = getEl('#code-body', this.shadowRoot);
|
|
180
|
+
this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
|
|
181
|
+
this.codeBodyEl.addEventListener('scroll', () => {
|
|
182
|
+
let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
|
|
183
|
+
// 检测是否是用户手动滚动
|
|
184
|
+
this.autoScroll = !flag;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Registers a new language with a set of syntax highlighting rules.
|
|
189
|
+
* @param name - The name of the programming language.
|
|
190
|
+
* @param config - Configuration for the language, including rules, theme, and optional CSS.
|
|
191
|
+
*/
|
|
192
|
+
static register(name, config) {
|
|
193
|
+
// Store the language configuration in the static map
|
|
194
|
+
this.languages.set(name, { ...config });
|
|
195
|
+
}
|
|
196
|
+
//注册工具箱
|
|
197
|
+
static addTools(items) {
|
|
198
|
+
CoaxElem.tools = items;
|
|
199
|
+
}
|
|
200
|
+
//加入工具箱
|
|
201
|
+
mountTools(toolItems) {
|
|
202
|
+
requestAnimationFrame(() => {
|
|
203
|
+
this.codeToolsEl.innerHTML = '';
|
|
204
|
+
let items = toolItems.map(k => {
|
|
205
|
+
k.action = k.action.bind(this);
|
|
206
|
+
return k;
|
|
207
|
+
});
|
|
208
|
+
this.codeToolsEl.appendChild(createTools(items));
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// Called when the element is connected to the DOM
|
|
212
|
+
connectedCallback() {
|
|
213
|
+
this.render();
|
|
214
|
+
}
|
|
215
|
+
// Observed attributes for changes
|
|
216
|
+
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
|
|
217
|
+
/**
|
|
218
|
+
* Called when any of the observed attributes change.
|
|
219
|
+
*/
|
|
220
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
221
|
+
if (oldVal === newVal)
|
|
222
|
+
return;
|
|
223
|
+
if (name === 'height' || name === 'max-height') {
|
|
224
|
+
this.updateStyleByRegExp(name, newVal);
|
|
225
|
+
}
|
|
226
|
+
if (name === 'speed') {
|
|
227
|
+
this.speed = ~~(!!newVal);
|
|
228
|
+
}
|
|
229
|
+
if (name === 'lang') {
|
|
230
|
+
//更新当前语言
|
|
231
|
+
this.lang = newVal;
|
|
232
|
+
this.render();
|
|
233
|
+
}
|
|
234
|
+
if (name === 'tools') {
|
|
235
|
+
if (!newVal)
|
|
236
|
+
this.codeToolsEl.innerHTML = '';
|
|
237
|
+
const tools = parseClasses(newVal), toolItems = CoaxElem.tools.filter(k => tools.includes(k.name));
|
|
238
|
+
if (!toolItems.length)
|
|
239
|
+
return;
|
|
240
|
+
this.mountTools(toolItems);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 使用正则替换基础样式表中的特定属性,避免操作行内样式
|
|
245
|
+
*/
|
|
246
|
+
updateStyleByRegExp(prop, value) {
|
|
247
|
+
// 构建正则:匹配属性名后面跟着冒号,直到分号或换行
|
|
248
|
+
// 例如:height:\s*[^;]+;
|
|
249
|
+
const regex = new RegExp(`;\\n\\s*${prop}:\\s*[^;]+;`, 'g');
|
|
250
|
+
// 替换为新的属性值
|
|
251
|
+
this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
|
|
252
|
+
}
|
|
253
|
+
getCssPrefix(config) {
|
|
254
|
+
return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
255
|
+
}
|
|
256
|
+
getHighLightString(string, config) {
|
|
257
|
+
config = config || CoaxElem.languages.get(this.lang);
|
|
258
|
+
if (!config)
|
|
259
|
+
return string;
|
|
260
|
+
// 如果找到了配置,则进行高亮处理
|
|
261
|
+
const cssPrefix = this.getCssPrefix(config),
|
|
262
|
+
// 获取用于语法高亮的正则表达式
|
|
263
|
+
combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
|
|
264
|
+
return string.replace(combinedRegex, (match, ...args) => {
|
|
265
|
+
const i = args.findIndex(val => val !== undefined);
|
|
266
|
+
return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
createLineWrap(index, startIndex) {
|
|
270
|
+
let dataIndex = 0;
|
|
271
|
+
if (index == null && startIndex == null) {
|
|
272
|
+
dataIndex = this.highlightedCodeEl.children.length;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
const start = startIndex || this.highlightedCodeEl.children.length;
|
|
276
|
+
dataIndex = start + index;
|
|
277
|
+
}
|
|
278
|
+
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
279
|
+
}
|
|
280
|
+
getLineToFill(codeWrap, line, config) {
|
|
281
|
+
config = config || CoaxElem.languages.get(this.lang);
|
|
282
|
+
let highlightedLine = this.getHighLightString(line, config);
|
|
283
|
+
// 将高亮后的内容填充到 div 中
|
|
284
|
+
codeWrap.innerHTML = highlightedLine;
|
|
285
|
+
}
|
|
286
|
+
;
|
|
287
|
+
/**
|
|
288
|
+
* 处理新源码,按行高亮并追加到高亮区域
|
|
289
|
+
* @param newCode - 新的源码片段
|
|
290
|
+
*/
|
|
291
|
+
async highlight(newCode) {
|
|
292
|
+
const config = CoaxElem.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
|
|
293
|
+
// 将新源码按行分割
|
|
294
|
+
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
295
|
+
//更新别名
|
|
296
|
+
this.updateName(config);
|
|
297
|
+
if (!newCodeLines.length)
|
|
298
|
+
return true;
|
|
299
|
+
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
300
|
+
for (let [index, lineString] of newCodeLines.entries()) {
|
|
301
|
+
//如果是空行则跳过
|
|
302
|
+
if (!lineString.trim() && this.hasAttribute('sanitized'))
|
|
303
|
+
continue;
|
|
304
|
+
// 创建一个 div 包裹每一行
|
|
305
|
+
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
306
|
+
//标记完成
|
|
307
|
+
this.closeLine(lineWrap);
|
|
308
|
+
if (this.hasAttribute('speed')) {
|
|
309
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
310
|
+
//流式打字
|
|
311
|
+
await typeWriter(lineString, {
|
|
312
|
+
speed: this.speed,
|
|
313
|
+
onDuringType: (char, fullText) => {
|
|
314
|
+
codeWrap.innerHTML = fullText;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
//直接打出
|
|
321
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
322
|
+
//
|
|
323
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
//滚动到最底部
|
|
327
|
+
this.autoScrollCode();
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
autoScrollCode() {
|
|
331
|
+
if (this.autoScroll) {
|
|
332
|
+
this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
injectThemeStyles() {
|
|
336
|
+
const config = CoaxElem.languages.get(this.lang);
|
|
337
|
+
if (!config)
|
|
338
|
+
return;
|
|
339
|
+
// Get language name, fallback to 'js' if not provided
|
|
340
|
+
let cssPrefix = this.getCssPrefix(config),
|
|
341
|
+
//Generate dynamic CSS classes for each syntax rule
|
|
342
|
+
// 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
|
|
343
|
+
lightStyles = config.rules.map((rule) => `
|
|
344
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.light ? ',' + rule.light : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
|
|
345
|
+
darkStyles += `:host([scheme="dark"]){`;
|
|
346
|
+
darkStyles += config.rules.map((rule) => `
|
|
347
|
+
${rule.light ? `
|
|
348
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
|
|
349
|
+
darkStyles += `}`;
|
|
350
|
+
schemeStyles = `@media (prefers-color-scheme: dark){
|
|
351
|
+
:host{
|
|
352
|
+
`;
|
|
353
|
+
schemeStyles += config.rules.map((rule) => `
|
|
354
|
+
${rule.light ? `
|
|
355
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
|
|
356
|
+
schemeStyles += `}`;
|
|
357
|
+
// Set the inner HTML of the shadow root, including styles and highlighted code
|
|
358
|
+
// 2. 精确更新 DOM 节点而非重写 ShadowRoot
|
|
359
|
+
this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
|
|
360
|
+
//附加主题样式
|
|
361
|
+
if (config?.themeStyles) {
|
|
362
|
+
this.themeStylesEl.textContent = config.themeStyles;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
updateName(config) {
|
|
366
|
+
if (this.hasAttribute('unnamed'))
|
|
367
|
+
return;
|
|
368
|
+
if (!config) {
|
|
369
|
+
this.codeNameEl.innerHTML = 'Plain Text';
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
//更新别名
|
|
373
|
+
this.alias = config.alias || this.lang;
|
|
374
|
+
this.codeNameEl.innerHTML = this.alias;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Renders the highlighted code within the shadow DOM.
|
|
379
|
+
*/
|
|
380
|
+
render(code = this.source) {
|
|
381
|
+
//同时多次改变属性,只执行一次
|
|
382
|
+
// 如果已经在队列中,则直接返回
|
|
383
|
+
if (this._renderQueued)
|
|
384
|
+
return;
|
|
385
|
+
this._renderQueued = true;
|
|
386
|
+
// 使用 requestAnimationFrame 将渲染推迟到下一帧
|
|
387
|
+
requestAnimationFrame(async () => {
|
|
388
|
+
this.clear();
|
|
389
|
+
this.injectThemeStyles();
|
|
390
|
+
//一次性渲染
|
|
391
|
+
await this.highlight(code);
|
|
392
|
+
this._renderQueued = false;
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
clear() {
|
|
396
|
+
this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* 替换现有的源码并重新渲染
|
|
400
|
+
* @param newCode - 新的源码
|
|
401
|
+
*/
|
|
402
|
+
async replace(newCode) {
|
|
403
|
+
this.clear();
|
|
404
|
+
await this.highlight(newCode);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* 末尾新增源码,仅高亮新增的部分
|
|
408
|
+
* @param newCode - 新的源码片段
|
|
409
|
+
*/
|
|
410
|
+
async append(newCode) {
|
|
411
|
+
// 将新的代码追加到现有代码末尾
|
|
412
|
+
this.source += `\n${newCode}`;
|
|
413
|
+
// 高亮新的部分
|
|
414
|
+
await this.highlight(newCode);
|
|
415
|
+
}
|
|
416
|
+
getActiveCodeWrap() {
|
|
417
|
+
const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
|
|
418
|
+
this.createLineWrap() : lastChild;
|
|
419
|
+
return {
|
|
420
|
+
lineWrap: lastLine,
|
|
421
|
+
codeWrap: lastLine.lastElementChild
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
closeLine(lineWrap) {
|
|
425
|
+
lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
|
|
426
|
+
lineWrap && (lineWrap.completed = true);
|
|
427
|
+
}
|
|
428
|
+
openLine(lineWrap) {
|
|
429
|
+
lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
|
|
430
|
+
lineWrap && (lineWrap.completed = false);
|
|
431
|
+
}
|
|
432
|
+
stream(str, forceEnd = false) {
|
|
433
|
+
const { lineWrap, codeWrap } = this.getActiveCodeWrap();
|
|
434
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
435
|
+
//汇集
|
|
436
|
+
this.source += str;
|
|
437
|
+
//如果没有遇到换行符号,也可以强制结束
|
|
438
|
+
forceEnd && this.closeLine(lineWrap);
|
|
439
|
+
if (str.startsWith('\n') || str.startsWith('\r')) {
|
|
440
|
+
//标记完成
|
|
441
|
+
this.closeLine(lineWrap);
|
|
442
|
+
//渲染
|
|
443
|
+
this.getLineToFill(codeWrap, this.lineString);
|
|
444
|
+
//一行结束,清空临时行文本
|
|
445
|
+
this.lineString = '';
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
//插入文本
|
|
449
|
+
codeWrap.innerHTML += str;
|
|
450
|
+
//临时保存行文本
|
|
451
|
+
this.lineString += str;
|
|
452
|
+
}
|
|
453
|
+
//滚动到最底部
|
|
454
|
+
this.autoScrollCode();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
export default CoaxElem;
|