@codady/coax 0.0.4 → 0.0.6

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Last modified: 2026/01/12 14:09:42
2
+ * Last modified: 2026/01/15 19:21:29
3
3
  * Coax - A custom web component for syntax highlighting, code display, and interactive features
4
4
  *
5
5
  */
@@ -9,6 +9,8 @@ import NAMESPACE from "@codady/utils/namespace";
9
9
  import getEl from "@codady/utils/getEl";
10
10
  import createTools from "@codady/utils/createTools";
11
11
  import createEl from "@codady/utils/createEl";
12
+ import trimEmptyLines from "@codady/utils/trimEmptyLines";
13
+ import escapeHtmlChars from "@codady/utils/escapeHtmlChars";
12
14
  class Coax extends HTMLElement {
13
15
  // A static Map to hold the configuration for different languages
14
16
  static languages = new Map();
@@ -19,7 +21,7 @@ class Coax extends HTMLElement {
19
21
  baseStylesEl; // Element for base styles
20
22
  themeStylesEl; // Element for theme styles
21
23
  dynamicStylesEl; // Element for dynamic styles
22
- highlightedCodeEl; // Element that holds the highlighted code
24
+ highlightEl; // Element that holds the highlighted code
23
25
  headerEl; // Header element (for code name, tools, etc.)
24
26
  codeNameEl; // Code name element (shows language or alias)
25
27
  codeToolsEl; // Code tools element (for interactive tools like copy)
@@ -30,12 +32,13 @@ class Coax extends HTMLElement {
30
32
  lastLineString = ''; // The last line's string
31
33
  speed = 5; // Speed of the typing effect (higher is slower)
32
34
  autoScroll = true; // Flag to enable/disable auto-scrolling
35
+ canListen = true; // Flag to enable/disable auto-scrolling
33
36
  constructor() {
34
37
  super();
35
38
  // Attach a Shadow DOM to the custom element
36
39
  this.attachShadow({ mode: 'open' });
37
40
  // Remove leading/trailing whitespace from the raw code content
38
- this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
41
+ this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
39
42
  // Initialize the basic structure of the component
40
43
  this.shadowRoot.innerHTML = `
41
44
  <style id="base-styles">
@@ -166,7 +169,7 @@ class Coax extends HTMLElement {
166
169
  <style id="theme-styles"></style>
167
170
  <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
168
171
  <div id="code-body">
169
- <pre><code id="highlight-code"></code></pre>
172
+ <pre><code id="highlight"></code></pre>
170
173
  </div>
171
174
  `;
172
175
  // Cache references to various elements
@@ -177,7 +180,7 @@ class Coax extends HTMLElement {
177
180
  this.codeNameEl = getEl('#code-name', this.shadowRoot);
178
181
  this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
179
182
  this.codeBodyEl = getEl('#code-body', this.shadowRoot);
180
- this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
183
+ this.highlightEl = getEl('#highlight', this.shadowRoot);
181
184
  this.codeBodyEl.addEventListener('scroll', () => {
182
185
  let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
183
186
  // Check if the user manually scrolled
@@ -231,7 +234,7 @@ class Coax extends HTMLElement {
231
234
  * @param newVal - The new value of the attribute.
232
235
  */
233
236
  attributeChangedCallback(name, oldVal, newVal) {
234
- if (oldVal === newVal)
237
+ if (oldVal === newVal || !this.canListen)
235
238
  return;
236
239
  if (name === 'height' || name === 'max-height') {
237
240
  this.updateStyleByRegExp(name, newVal);
@@ -302,10 +305,10 @@ class Coax extends HTMLElement {
302
305
  createLineWrap(index, startIndex) {
303
306
  let dataIndex = 0;
304
307
  if (index == null && startIndex == null) {
305
- dataIndex = this.highlightedCodeEl.children.length;
308
+ dataIndex = this.highlightEl.children.length;
306
309
  }
307
310
  else {
308
- const start = startIndex || this.highlightedCodeEl.children.length;
311
+ const start = startIndex || this.highlightEl.children.length;
309
312
  dataIndex = start + index;
310
313
  }
311
314
  return createEl('div', { 'data-index': dataIndex }, '<div></div>');
@@ -316,7 +319,7 @@ class Coax extends HTMLElement {
316
319
  * @param line - The line of code to highlight.
317
320
  * @param config - The language configuration object.
318
321
  */
319
- getLineToFill(codeWrap, line, config) {
322
+ getLineToFillHighLight(codeWrap, line, config) {
320
323
  config = config || Coax.languages.get(this.lang);
321
324
  let highlightedLine = this.getHighLightString(line, config);
322
325
  // 将高亮后的内容填充到 div 中
@@ -327,10 +330,10 @@ class Coax extends HTMLElement {
327
330
  * Highlights new source code and appends it to the code body.
328
331
  * @param newCode - The new source code to highlight and append.
329
332
  */
330
- async highlight(newCode) {
331
- const config = Coax.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
333
+ async highlight(newCode = this.source) {
334
+ const config = Coax.languages.get(this.lang), startIndex = this.highlightEl.children.length,
332
335
  // 将新源码按行分割
333
- newCodeLines = newCode ? newCode.split('\n') : [];
336
+ newCodeLines = newCode ? newCode.split('\n') : [], hasSpeedAttr = this.hasAttribute('speed'), hasSanitizedAttr = this.hasAttribute('sanitized');
334
337
  //更新别名
335
338
  this.updateName(config);
336
339
  if (!newCodeLines.length)
@@ -338,14 +341,14 @@ class Coax extends HTMLElement {
338
341
  // 如果没有找到配置,则输出原始代码,并不进行高亮处理
339
342
  for (let [index, lineString] of newCodeLines.entries()) {
340
343
  //如果是空行则跳过
341
- if (!lineString.trim() && this.hasAttribute('sanitized'))
344
+ if (!lineString.trim() && hasSanitizedAttr)
342
345
  continue;
343
346
  // 创建一个 div 包裹每一行
344
347
  const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
345
348
  //标记完成
346
349
  lineWrap.completed = true;
347
- if (this.hasAttribute('speed')) {
348
- this.highlightedCodeEl.appendChild(lineWrap);
350
+ if (hasSpeedAttr) {
351
+ this.highlightEl.appendChild(lineWrap);
349
352
  //流式打字
350
353
  await typeWriter(lineString, {
351
354
  speed: this.speed,
@@ -353,13 +356,13 @@ class Coax extends HTMLElement {
353
356
  codeWrap.innerHTML = fullText;
354
357
  }
355
358
  });
356
- this.getLineToFill(codeWrap, lineString, config);
359
+ this.getLineToFillHighLight(codeWrap, lineString, config);
357
360
  }
358
361
  else {
359
362
  //直接打出
360
- this.getLineToFill(codeWrap, lineString, config);
363
+ this.getLineToFillHighLight(codeWrap, lineString, config);
361
364
  //
362
- this.highlightedCodeEl.appendChild(lineWrap);
365
+ this.highlightEl.appendChild(lineWrap);
363
366
  }
364
367
  }
365
368
  //滚动到最底部
@@ -422,20 +425,30 @@ class Coax extends HTMLElement {
422
425
  this.codeNameEl.innerHTML = this.alias;
423
426
  }
424
427
  }
428
+ trimLineString(str) {
429
+ // 删除开头的第一个换行符
430
+ if (str.startsWith('\n')) {
431
+ str = str.substring(1);
432
+ }
433
+ // 删除结尾的第一个换行符
434
+ if (str.endsWith('\n')) {
435
+ str = str.slice(0, -1);
436
+ }
437
+ return str;
438
+ }
425
439
  /**
426
440
  * Renders the highlighted code within the shadow DOM.
427
441
  */
428
- render(code = this.source) {
442
+ render() {
429
443
  //同时多次改变属性,只执行一次
430
444
  if (this._renderQueued)
431
445
  return;
432
446
  this._renderQueued = true;
433
- // 使用 requestAnimationFrame 将渲染推迟到下一帧
434
447
  requestAnimationFrame(async () => {
435
- this.clear();
448
+ this.highlightEl.innerHTML = '';
436
449
  this.injectThemeStyles();
437
450
  //一次性渲染
438
- await this.highlight(code);
451
+ await this.highlight();
439
452
  this._renderQueued = false;
440
453
  });
441
454
  }
@@ -443,21 +456,27 @@ class Coax extends HTMLElement {
443
456
  * Clears the current content and resets the state.
444
457
  */
445
458
  clear() {
446
- this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
459
+ this.highlightEl.innerHTML = this.source = this.lineString = '';
447
460
  }
448
461
  /**
449
462
  * Replaces the existing code with new source code and re-renders.
450
463
  * @param newCode - The new source code to replace the existing code.
451
464
  */
452
465
  async replace(newCode) {
453
- this.clear();
454
- await this.highlight(newCode);
466
+ newCode = escapeHtmlChars(newCode);
467
+ this.source = trimEmptyLines(newCode);
468
+ if (this._renderQueued)
469
+ return;
470
+ this.highlightEl.innerHTML = '';
471
+ //一次性渲染
472
+ await this.highlight();
455
473
  }
456
474
  /**
457
475
  * Appends new source code to the current content and highlights only the new portion.
458
476
  * @param newCode - The new source code to append and highlight.
459
477
  */
460
478
  async append(newCode) {
479
+ newCode = escapeHtmlChars(newCode);
461
480
  // 将新的代码追加到现有代码末尾
462
481
  this.source += `\n${newCode}`;
463
482
  // 高亮新的部分
@@ -468,7 +487,7 @@ class Coax extends HTMLElement {
468
487
  * @returns An object containing the line wrapper and code wrapper for the last line.
469
488
  */
470
489
  getLastLine() {
471
- const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
490
+ const lastChild = this.highlightEl.lastElementChild, lastLine = !lastChild || this.highlightEl.lastElementChild?.completed ?
472
491
  this.createLineWrap() : lastChild;
473
492
  return {
474
493
  lineWrap: lastLine,
@@ -479,13 +498,13 @@ class Coax extends HTMLElement {
479
498
  * Marks the current line as completed and updates the displayed code.
480
499
  */
481
500
  close() {
482
- const lineWrap = this.highlightedCodeEl.lastElementChild;
501
+ const lineWrap = this.highlightEl.lastElementChild;
483
502
  if (!lineWrap)
484
503
  return;
485
504
  lineWrap.completed = true;
486
505
  //行结束前保存
487
506
  this.lastLineString = this.lineString;
488
- this.getLineToFill(lineWrap?.lastElementChild, this.lineString);
507
+ this.getLineToFillHighLight(lineWrap?.lastElementChild, this.lineString);
489
508
  //一行结束,清空临时行文本
490
509
  this.lineString = '';
491
510
  }
@@ -493,7 +512,7 @@ class Coax extends HTMLElement {
493
512
  * Reopens the last closed line and restores its original content.
494
513
  */
495
514
  open() {
496
- const lineWrap = this.highlightedCodeEl.lastElementChild;
515
+ const lineWrap = this.highlightEl.lastElementChild;
497
516
  if (!lineWrap)
498
517
  return;
499
518
  lineWrap.completed = false;
@@ -506,20 +525,21 @@ class Coax extends HTMLElement {
506
525
  * @param forceClose - Forcefully close the line if set to `true`.
507
526
  */
508
527
  stream(str, forceClose = false) {
509
- const { lineWrap, codeWrap } = this.getLastLine();
510
- this.highlightedCodeEl.appendChild(lineWrap);
528
+ str = escapeHtmlChars(str);
529
+ const { lineWrap, codeWrap } = this.getLastLine(), isLine = str.startsWith('\n') || str.endsWith('\n');
530
+ this.highlightEl.appendChild(lineWrap);
511
531
  //汇集
512
532
  this.source += str;
533
+ //临时保存行文本
534
+ this.lineString += isLine ? this.trimLineString(str) : str;
513
535
  //如果没有遇到换行符号,也可以强制结束
514
- if (forceClose || (str.startsWith('\n') || str.endsWith('\n'))) {
536
+ if (forceClose || isLine) {
515
537
  //标记完成
516
538
  this.close();
517
539
  }
518
540
  else {
519
541
  //插入文本
520
542
  codeWrap.innerHTML += str;
521
- //临时保存行文本
522
- this.lineString += str;
523
543
  }
524
544
  //滚动到最底部
525
545
  this.autoScrollCode();
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Last modified: 2026/01/12 14:09:42
2
+ * Last modified: 2026/01/15 19:21:29
3
3
  * Coax - A custom web component for syntax highlighting, code display, and interactive features
4
4
  *
5
5
  */
@@ -10,6 +10,8 @@ import NAMESPACE from "@codady/utils/namespace";
10
10
  import getEl from "@codady/utils/getEl";
11
11
  import createTools, { toolsItem } from "@codady/utils/createTools";
12
12
  import createEl from "@codady/utils/createEl";
13
+ import trimEmptyLines from "@codady/utils/trimEmptyLines";
14
+ import escapeHtmlChars from "@codady/utils/escapeHtmlChars";
13
15
 
14
16
 
15
17
  // Define the structure for the language configuration
@@ -39,7 +41,7 @@ class Coax extends HTMLElement {
39
41
  private baseStylesEl!: HTMLStyleElement; // Element for base styles
40
42
  private themeStylesEl!: HTMLStyleElement; // Element for theme styles
41
43
  private dynamicStylesEl!: HTMLStyleElement; // Element for dynamic styles
42
- private highlightedCodeEl!: HTMLElement; // Element that holds the highlighted code
44
+ private highlightEl!: HTMLElement; // Element that holds the highlighted code
43
45
  private headerEl!: HTMLElement; // Header element (for code name, tools, etc.)
44
46
  private codeNameEl!: HTMLElement; // Code name element (shows language or alias)
45
47
  private codeToolsEl!: HTMLElement; // Code tools element (for interactive tools like copy)
@@ -51,13 +53,14 @@ class Coax extends HTMLElement {
51
53
  public lastLineString: string = ''; // The last line's string
52
54
  public speed: number = 5; // Speed of the typing effect (higher is slower)
53
55
  public autoScroll: boolean = true; // Flag to enable/disable auto-scrolling
56
+ public canListen: boolean = true; // Flag to enable/disable auto-scrolling
54
57
 
55
58
  constructor() {
56
59
  super();
57
60
  // Attach a Shadow DOM to the custom element
58
61
  this.attachShadow({ mode: 'open' });
59
62
  // Remove leading/trailing whitespace from the raw code content
60
- this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
63
+ this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
61
64
  // Initialize the basic structure of the component
62
65
  (this.shadowRoot as any).innerHTML = `
63
66
  <style id="base-styles">
@@ -188,7 +191,7 @@ class Coax extends HTMLElement {
188
191
  <style id="theme-styles"></style>
189
192
  <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
190
193
  <div id="code-body">
191
- <pre><code id="highlight-code"></code></pre>
194
+ <pre><code id="highlight"></code></pre>
192
195
  </div>
193
196
  `;
194
197
 
@@ -200,7 +203,7 @@ class Coax extends HTMLElement {
200
203
  this.codeNameEl = getEl('#code-name', this.shadowRoot) as HTMLElement;
201
204
  this.codeToolsEl = getEl('#code-tools', this.shadowRoot) as HTMLElement;
202
205
  this.codeBodyEl = getEl('#code-body', this.shadowRoot) as HTMLElement;
203
- this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot) as HTMLElement;
206
+ this.highlightEl = getEl('#highlight', this.shadowRoot) as HTMLElement;
204
207
 
205
208
  this.codeBodyEl.addEventListener('scroll', () => {
206
209
  let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
@@ -257,7 +260,7 @@ class Coax extends HTMLElement {
257
260
  * @param newVal - The new value of the attribute.
258
261
  */
259
262
  attributeChangedCallback(name: string, oldVal: string, newVal: string) {
260
- if (oldVal === newVal) return;
263
+ if (oldVal === newVal || !this.canListen) return;
261
264
  if (name === 'height' || name === 'max-height') {
262
265
  this.updateStyleByRegExp(name, newVal);
263
266
  }
@@ -328,9 +331,9 @@ class Coax extends HTMLElement {
328
331
  createLineWrap(index?: number, startIndex?: number) {
329
332
  let dataIndex = 0;
330
333
  if (index == null && startIndex == null) {
331
- dataIndex = this.highlightedCodeEl.children.length;
334
+ dataIndex = this.highlightEl.children.length;
332
335
  } else {
333
- const start = startIndex || this.highlightedCodeEl.children.length;
336
+ const start = startIndex || this.highlightEl.children.length;
334
337
  dataIndex = start + (index as number);
335
338
  }
336
339
  return createEl('div', { 'data-index': dataIndex }, '<div></div>');
@@ -341,7 +344,7 @@ class Coax extends HTMLElement {
341
344
  * @param line - The line of code to highlight.
342
345
  * @param config - The language configuration object.
343
346
  */
344
- getLineToFill(codeWrap: Element, line: string, config?: LanguageConfig) {
347
+ getLineToFillHighLight(codeWrap: Element, line: string, config?: LanguageConfig) {
345
348
  config = config || Coax.languages.get(this.lang);
346
349
  let highlightedLine = this.getHighLightString(line, config);
347
350
  // 将高亮后的内容填充到 div 中
@@ -351,26 +354,28 @@ class Coax extends HTMLElement {
351
354
  * Highlights new source code and appends it to the code body.
352
355
  * @param newCode - The new source code to highlight and append.
353
356
  */
354
- async highlight(newCode: string) {
357
+ async highlight(newCode: string = this.source) {
355
358
  const config = Coax.languages.get(this.lang),
356
- startIndex = this.highlightedCodeEl.children.length,
359
+ startIndex = this.highlightEl.children.length,
357
360
  // 将新源码按行分割
358
- newCodeLines = newCode ? newCode.split('\n') : [];
361
+ newCodeLines = newCode ? newCode.split('\n') : [],
362
+ hasSpeedAttr = this.hasAttribute('speed'),
363
+ hasSanitizedAttr = this.hasAttribute('sanitized');
359
364
  //更新别名
360
- this.updateName(config)
365
+ this.updateName(config);
361
366
  if (!newCodeLines.length) return true;
362
367
  // 如果没有找到配置,则输出原始代码,并不进行高亮处理
363
368
  for (let [index, lineString] of newCodeLines.entries()) {
364
369
  //如果是空行则跳过
365
- if (!lineString.trim() && this.hasAttribute('sanitized')) continue;
370
+ if (!lineString.trim() && hasSanitizedAttr) continue;
366
371
  // 创建一个 div 包裹每一行
367
372
  const lineWrap = this.createLineWrap(index, startIndex),
368
373
  codeWrap = lineWrap.lastElementChild as Element;
369
374
  //标记完成
370
375
  (lineWrap as any).completed = true;
371
376
 
372
- if (this.hasAttribute('speed')) {
373
- this.highlightedCodeEl.appendChild(lineWrap);
377
+ if (hasSpeedAttr) {
378
+ this.highlightEl.appendChild(lineWrap);
374
379
  //流式打字
375
380
  await typeWriter(lineString, {
376
381
  speed: this.speed,
@@ -378,12 +383,12 @@ class Coax extends HTMLElement {
378
383
  codeWrap.innerHTML = fullText;
379
384
  }
380
385
  });
381
- this.getLineToFill(codeWrap, lineString, config);
386
+ this.getLineToFillHighLight(codeWrap, lineString, config);
382
387
  } else {
383
388
  //直接打出
384
- this.getLineToFill(codeWrap, lineString, config);
389
+ this.getLineToFillHighLight(codeWrap, lineString, config);
385
390
  //
386
- this.highlightedCodeEl.appendChild(lineWrap);
391
+ this.highlightEl.appendChild(lineWrap);
387
392
  }
388
393
  }
389
394
  //滚动到最底部
@@ -451,41 +456,60 @@ class Coax extends HTMLElement {
451
456
  this.codeNameEl.innerHTML = this.alias;
452
457
  }
453
458
  }
459
+
460
+ trimLineString(str: string) {
461
+ // 删除开头的第一个换行符
462
+ if (str.startsWith('\n')) {
463
+ str = str.substring(1);
464
+ }
465
+
466
+ // 删除结尾的第一个换行符
467
+ if (str.endsWith('\n')) {
468
+ str = str.slice(0, -1);
469
+ }
470
+
471
+ return str;
472
+ }
454
473
  /**
455
474
  * Renders the highlighted code within the shadow DOM.
456
475
  */
457
- render(code = this.source) {
476
+ render() {
458
477
  //同时多次改变属性,只执行一次
459
478
  if (this._renderQueued) return;
460
479
  this._renderQueued = true;
461
- // 使用 requestAnimationFrame 将渲染推迟到下一帧
462
480
  requestAnimationFrame(async () => {
463
- this.clear();
481
+ this.highlightEl.innerHTML = '';
464
482
  this.injectThemeStyles();
465
483
  //一次性渲染
466
- await this.highlight(code);
484
+ await this.highlight();
467
485
  this._renderQueued = false;
468
486
  });
487
+
469
488
  }
470
489
  /**
471
490
  * Clears the current content and resets the state.
472
491
  */
473
492
  clear() {
474
- this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
493
+ this.highlightEl.innerHTML = this.source = this.lineString = '';
475
494
  }
476
495
  /**
477
496
  * Replaces the existing code with new source code and re-renders.
478
497
  * @param newCode - The new source code to replace the existing code.
479
498
  */
480
499
  async replace(newCode: string) {
481
- this.clear();
482
- await this.highlight(newCode);
500
+ newCode = escapeHtmlChars(newCode);
501
+ this.source = trimEmptyLines(newCode);
502
+ if (this._renderQueued) return;
503
+ this.highlightEl.innerHTML = '';
504
+ //一次性渲染
505
+ await this.highlight();
483
506
  }
484
507
  /**
485
508
  * Appends new source code to the current content and highlights only the new portion.
486
509
  * @param newCode - The new source code to append and highlight.
487
510
  */
488
511
  async append(newCode: string) {
512
+ newCode = escapeHtmlChars(newCode);
489
513
  // 将新的代码追加到现有代码末尾
490
514
  this.source += `\n${newCode}`;
491
515
  // 高亮新的部分
@@ -496,8 +520,8 @@ class Coax extends HTMLElement {
496
520
  * @returns An object containing the line wrapper and code wrapper for the last line.
497
521
  */
498
522
  getLastLine() {
499
- const lastChild = this.highlightedCodeEl.lastElementChild,
500
- lastLine = !lastChild || (this.highlightedCodeEl.lastElementChild as any)?.completed ?
523
+ const lastChild = this.highlightEl.lastElementChild,
524
+ lastLine = !lastChild || (this.highlightEl.lastElementChild as any)?.completed ?
501
525
  this.createLineWrap() : lastChild;
502
526
  return {
503
527
  lineWrap: lastLine,
@@ -508,12 +532,12 @@ class Coax extends HTMLElement {
508
532
  * Marks the current line as completed and updates the displayed code.
509
533
  */
510
534
  close() {
511
- const lineWrap = this.highlightedCodeEl.lastElementChild;
535
+ const lineWrap = this.highlightEl.lastElementChild;
512
536
  if (!lineWrap) return;
513
537
  (lineWrap as any).completed = true;
514
538
  //行结束前保存
515
539
  this.lastLineString = this.lineString;
516
- this.getLineToFill(lineWrap?.lastElementChild as Element, this.lineString);
540
+ this.getLineToFillHighLight(lineWrap?.lastElementChild as Element, this.lineString);
517
541
  //一行结束,清空临时行文本
518
542
  this.lineString = '';
519
543
  }
@@ -521,7 +545,7 @@ class Coax extends HTMLElement {
521
545
  * Reopens the last closed line and restores its original content.
522
546
  */
523
547
  open() {
524
- const lineWrap = this.highlightedCodeEl.lastElementChild;
548
+ const lineWrap = this.highlightEl.lastElementChild;
525
549
  if (!lineWrap) return;
526
550
  (lineWrap as any).completed = false;
527
551
  //恢复最后一行字符串
@@ -533,20 +557,23 @@ class Coax extends HTMLElement {
533
557
  * @param forceClose - Forcefully close the line if set to `true`.
534
558
  */
535
559
  stream(str: string, forceClose: boolean = false) {
536
- const { lineWrap, codeWrap } = this.getLastLine();
537
- this.highlightedCodeEl.appendChild(lineWrap);
560
+ str = escapeHtmlChars(str);
561
+ const { lineWrap, codeWrap } = this.getLastLine(),
562
+ isLine = str.startsWith('\n') || str.endsWith('\n');
563
+ this.highlightEl.appendChild(lineWrap);
538
564
  //汇集
539
565
  this.source += str;
540
566
 
567
+ //临时保存行文本
568
+ this.lineString += isLine ? this.trimLineString(str) : str;
569
+
541
570
  //如果没有遇到换行符号,也可以强制结束
542
- if (forceClose || (str.startsWith('\n') || str.endsWith('\n'))) {
571
+ if (forceClose || isLine) {
543
572
  //标记完成
544
573
  this.close();
545
574
  } else {
546
575
  //插入文本
547
576
  codeWrap.innerHTML += str;
548
- //临时保存行文本
549
- this.lineString += str;
550
577
  }
551
578
  //滚动到最底部
552
579
  this.autoScrollCode();
package/src/modules.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @since Last modified: 2026/01/12 14:10:29
2
+ * @since Last modified: 2026/01/15 19:21:31
3
3
  */
4
4
  'use strict';
5
5
  import Coax from './components/Coax.js';
package/src/modules.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @since Last modified: 2026/01/12 14:10:29
2
+ * @since Last modified: 2026/01/15 19:21:31
3
3
  */
4
4
  'use strict'
5
5
 
File without changes
File without changes