@codady/coax 0.0.5 → 0.0.7
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 +62 -23
- package/dist/coax.cjs.min.js +3 -3
- package/dist/coax.esm.js +62 -23
- package/dist/coax.esm.min.js +3 -3
- package/dist/coax.umd.js +62 -23
- package/dist/coax.umd.min.js +3 -3
- package/examples/{append-highlight.html → append.html} +2 -5
- package/examples/{css-highlight.html → css.html} +2 -5
- package/examples/{deepseek-highlight.html → deepseek.html} +3 -2
- package/examples/{html-highlight.html → html.html} +3 -13
- package/examples/index.html +214 -0
- package/examples/{js-highlight.html → js.html} +3 -4
- package/examples/{md-highlight.html → md.html} +2 -12
- package/examples/{theme-highlight.html → module.html} +4 -7
- package/examples/{plain-highlight.html → plain.html} +2 -5
- package/examples/{replace-highlight.html → replace.html} +2 -6
- package/examples/{stream-highlight.html → steam.html} +11 -16
- package/examples/{color-selector.html → theme.html} +1 -3
- package/examples/tools.html +42 -0
- package/examples/{ts-highlight.html → ts.html} +2 -20
- package/examples/typewriter.html +42 -0
- package/package.json +2 -2
- package/src/Coax.js +1 -1
- package/src/Coax.ts +1 -1
- package/src/components/Coax.js +44 -22
- package/src/components/Coax.ts +52 -23
- package/src/modules.js +1 -1
- package/src/modules.ts +1 -1
package/src/components/Coax.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2026/01/
|
|
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();
|
|
@@ -36,7 +38,7 @@ class Coax extends HTMLElement {
|
|
|
36
38
|
// Attach a Shadow DOM to the custom element
|
|
37
39
|
this.attachShadow({ mode: 'open' });
|
|
38
40
|
// Remove leading/trailing whitespace from the raw code content
|
|
39
|
-
this.source = this.textContent
|
|
41
|
+
this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
|
|
40
42
|
// Initialize the basic structure of the component
|
|
41
43
|
this.shadowRoot.innerHTML = `
|
|
42
44
|
<style id="base-styles">
|
|
@@ -317,7 +319,7 @@ class Coax extends HTMLElement {
|
|
|
317
319
|
* @param line - The line of code to highlight.
|
|
318
320
|
* @param config - The language configuration object.
|
|
319
321
|
*/
|
|
320
|
-
|
|
322
|
+
getLineToFillHighLight(codeWrap, line, config) {
|
|
321
323
|
config = config || Coax.languages.get(this.lang);
|
|
322
324
|
let highlightedLine = this.getHighLightString(line, config);
|
|
323
325
|
// 将高亮后的内容填充到 div 中
|
|
@@ -328,10 +330,10 @@ class Coax extends HTMLElement {
|
|
|
328
330
|
* Highlights new source code and appends it to the code body.
|
|
329
331
|
* @param newCode - The new source code to highlight and append.
|
|
330
332
|
*/
|
|
331
|
-
async highlight(newCode) {
|
|
333
|
+
async highlight(newCode = this.source) {
|
|
332
334
|
const config = Coax.languages.get(this.lang), startIndex = this.highlightEl.children.length,
|
|
333
335
|
// 将新源码按行分割
|
|
334
|
-
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
336
|
+
newCodeLines = newCode ? newCode.split('\n') : [], hasSpeedAttr = this.hasAttribute('speed'), hasSanitizedAttr = this.hasAttribute('sanitized');
|
|
335
337
|
//更新别名
|
|
336
338
|
this.updateName(config);
|
|
337
339
|
if (!newCodeLines.length)
|
|
@@ -339,13 +341,13 @@ class Coax extends HTMLElement {
|
|
|
339
341
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
340
342
|
for (let [index, lineString] of newCodeLines.entries()) {
|
|
341
343
|
//如果是空行则跳过
|
|
342
|
-
if (!lineString.trim() &&
|
|
344
|
+
if (!lineString.trim() && hasSanitizedAttr)
|
|
343
345
|
continue;
|
|
344
346
|
// 创建一个 div 包裹每一行
|
|
345
347
|
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
346
348
|
//标记完成
|
|
347
349
|
lineWrap.completed = true;
|
|
348
|
-
if (
|
|
350
|
+
if (hasSpeedAttr) {
|
|
349
351
|
this.highlightEl.appendChild(lineWrap);
|
|
350
352
|
//流式打字
|
|
351
353
|
await typeWriter(lineString, {
|
|
@@ -354,11 +356,11 @@ class Coax extends HTMLElement {
|
|
|
354
356
|
codeWrap.innerHTML = fullText;
|
|
355
357
|
}
|
|
356
358
|
});
|
|
357
|
-
this.
|
|
359
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
358
360
|
}
|
|
359
361
|
else {
|
|
360
362
|
//直接打出
|
|
361
|
-
this.
|
|
363
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
362
364
|
//
|
|
363
365
|
this.highlightEl.appendChild(lineWrap);
|
|
364
366
|
}
|
|
@@ -423,19 +425,32 @@ class Coax extends HTMLElement {
|
|
|
423
425
|
this.codeNameEl.innerHTML = this.alias;
|
|
424
426
|
}
|
|
425
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
|
+
}
|
|
426
439
|
/**
|
|
427
440
|
* Renders the highlighted code within the shadow DOM.
|
|
428
441
|
*/
|
|
429
|
-
render(
|
|
442
|
+
render() {
|
|
430
443
|
//同时多次改变属性,只执行一次
|
|
431
444
|
if (this._renderQueued)
|
|
432
445
|
return;
|
|
433
446
|
this._renderQueued = true;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
447
|
+
requestAnimationFrame(async () => {
|
|
448
|
+
this.highlightEl.innerHTML = '';
|
|
449
|
+
this.injectThemeStyles();
|
|
450
|
+
//一次性渲染
|
|
451
|
+
await this.highlight();
|
|
452
|
+
this._renderQueued = false;
|
|
453
|
+
});
|
|
439
454
|
}
|
|
440
455
|
/**
|
|
441
456
|
* Clears the current content and resets the state.
|
|
@@ -448,14 +463,20 @@ class Coax extends HTMLElement {
|
|
|
448
463
|
* @param newCode - The new source code to replace the existing code.
|
|
449
464
|
*/
|
|
450
465
|
async replace(newCode) {
|
|
451
|
-
|
|
452
|
-
|
|
466
|
+
newCode = escapeHtmlChars(newCode);
|
|
467
|
+
this.source = trimEmptyLines(newCode);
|
|
468
|
+
if (this._renderQueued)
|
|
469
|
+
return;
|
|
470
|
+
this.highlightEl.innerHTML = '';
|
|
471
|
+
//一次性渲染
|
|
472
|
+
await this.highlight();
|
|
453
473
|
}
|
|
454
474
|
/**
|
|
455
475
|
* Appends new source code to the current content and highlights only the new portion.
|
|
456
476
|
* @param newCode - The new source code to append and highlight.
|
|
457
477
|
*/
|
|
458
478
|
async append(newCode) {
|
|
479
|
+
newCode = escapeHtmlChars(newCode);
|
|
459
480
|
// 将新的代码追加到现有代码末尾
|
|
460
481
|
this.source += `\n${newCode}`;
|
|
461
482
|
// 高亮新的部分
|
|
@@ -483,7 +504,7 @@ class Coax extends HTMLElement {
|
|
|
483
504
|
lineWrap.completed = true;
|
|
484
505
|
//行结束前保存
|
|
485
506
|
this.lastLineString = this.lineString;
|
|
486
|
-
this.
|
|
507
|
+
this.getLineToFillHighLight(lineWrap?.lastElementChild, this.lineString);
|
|
487
508
|
//一行结束,清空临时行文本
|
|
488
509
|
this.lineString = '';
|
|
489
510
|
}
|
|
@@ -504,20 +525,21 @@ class Coax extends HTMLElement {
|
|
|
504
525
|
* @param forceClose - Forcefully close the line if set to `true`.
|
|
505
526
|
*/
|
|
506
527
|
stream(str, forceClose = false) {
|
|
507
|
-
|
|
528
|
+
str = escapeHtmlChars(str);
|
|
529
|
+
const { lineWrap, codeWrap } = this.getLastLine(), isLine = str.startsWith('\n') || str.endsWith('\n');
|
|
508
530
|
this.highlightEl.appendChild(lineWrap);
|
|
509
531
|
//汇集
|
|
510
532
|
this.source += str;
|
|
533
|
+
//临时保存行文本
|
|
534
|
+
this.lineString += isLine ? this.trimLineString(str) : str;
|
|
511
535
|
//如果没有遇到换行符号,也可以强制结束
|
|
512
|
-
if (forceClose ||
|
|
536
|
+
if (forceClose || isLine) {
|
|
513
537
|
//标记完成
|
|
514
538
|
this.close();
|
|
515
539
|
}
|
|
516
540
|
else {
|
|
517
541
|
//插入文本
|
|
518
542
|
codeWrap.innerHTML += str;
|
|
519
|
-
//临时保存行文本
|
|
520
|
-
this.lineString += str;
|
|
521
543
|
}
|
|
522
544
|
//滚动到最底部
|
|
523
545
|
this.autoScrollCode();
|
package/src/components/Coax.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2026/01/
|
|
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
|
|
@@ -58,7 +60,7 @@ class Coax extends HTMLElement {
|
|
|
58
60
|
// Attach a Shadow DOM to the custom element
|
|
59
61
|
this.attachShadow({ mode: 'open' });
|
|
60
62
|
// Remove leading/trailing whitespace from the raw code content
|
|
61
|
-
this.source = this.textContent
|
|
63
|
+
this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
|
|
62
64
|
// Initialize the basic structure of the component
|
|
63
65
|
(this.shadowRoot as any).innerHTML = `
|
|
64
66
|
<style id="base-styles">
|
|
@@ -342,7 +344,7 @@ class Coax extends HTMLElement {
|
|
|
342
344
|
* @param line - The line of code to highlight.
|
|
343
345
|
* @param config - The language configuration object.
|
|
344
346
|
*/
|
|
345
|
-
|
|
347
|
+
getLineToFillHighLight(codeWrap: Element, line: string, config?: LanguageConfig) {
|
|
346
348
|
config = config || Coax.languages.get(this.lang);
|
|
347
349
|
let highlightedLine = this.getHighLightString(line, config);
|
|
348
350
|
// 将高亮后的内容填充到 div 中
|
|
@@ -352,25 +354,27 @@ class Coax extends HTMLElement {
|
|
|
352
354
|
* Highlights new source code and appends it to the code body.
|
|
353
355
|
* @param newCode - The new source code to highlight and append.
|
|
354
356
|
*/
|
|
355
|
-
async highlight(newCode: string) {
|
|
357
|
+
async highlight(newCode: string = this.source) {
|
|
356
358
|
const config = Coax.languages.get(this.lang),
|
|
357
359
|
startIndex = this.highlightEl.children.length,
|
|
358
360
|
// 将新源码按行分割
|
|
359
|
-
newCodeLines = newCode ? newCode.split('\n') : []
|
|
361
|
+
newCodeLines = newCode ? newCode.split('\n') : [],
|
|
362
|
+
hasSpeedAttr = this.hasAttribute('speed'),
|
|
363
|
+
hasSanitizedAttr = this.hasAttribute('sanitized');
|
|
360
364
|
//更新别名
|
|
361
|
-
this.updateName(config)
|
|
365
|
+
this.updateName(config);
|
|
362
366
|
if (!newCodeLines.length) return true;
|
|
363
367
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
364
368
|
for (let [index, lineString] of newCodeLines.entries()) {
|
|
365
369
|
//如果是空行则跳过
|
|
366
|
-
if (!lineString.trim() &&
|
|
370
|
+
if (!lineString.trim() && hasSanitizedAttr) continue;
|
|
367
371
|
// 创建一个 div 包裹每一行
|
|
368
372
|
const lineWrap = this.createLineWrap(index, startIndex),
|
|
369
373
|
codeWrap = lineWrap.lastElementChild as Element;
|
|
370
374
|
//标记完成
|
|
371
375
|
(lineWrap as any).completed = true;
|
|
372
376
|
|
|
373
|
-
if (
|
|
377
|
+
if (hasSpeedAttr) {
|
|
374
378
|
this.highlightEl.appendChild(lineWrap);
|
|
375
379
|
//流式打字
|
|
376
380
|
await typeWriter(lineString, {
|
|
@@ -379,10 +383,10 @@ class Coax extends HTMLElement {
|
|
|
379
383
|
codeWrap.innerHTML = fullText;
|
|
380
384
|
}
|
|
381
385
|
});
|
|
382
|
-
this.
|
|
386
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
383
387
|
} else {
|
|
384
388
|
//直接打出
|
|
385
|
-
this.
|
|
389
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
386
390
|
//
|
|
387
391
|
this.highlightEl.appendChild(lineWrap);
|
|
388
392
|
}
|
|
@@ -452,18 +456,35 @@ class Coax extends HTMLElement {
|
|
|
452
456
|
this.codeNameEl.innerHTML = this.alias;
|
|
453
457
|
}
|
|
454
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
|
+
}
|
|
455
473
|
/**
|
|
456
474
|
* Renders the highlighted code within the shadow DOM.
|
|
457
475
|
*/
|
|
458
|
-
render(
|
|
476
|
+
render() {
|
|
459
477
|
//同时多次改变属性,只执行一次
|
|
460
478
|
if (this._renderQueued) return;
|
|
461
479
|
this._renderQueued = true;
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
480
|
+
requestAnimationFrame(async () => {
|
|
481
|
+
this.highlightEl.innerHTML = '';
|
|
482
|
+
this.injectThemeStyles();
|
|
483
|
+
//一次性渲染
|
|
484
|
+
await this.highlight();
|
|
485
|
+
this._renderQueued = false;
|
|
486
|
+
});
|
|
487
|
+
|
|
467
488
|
}
|
|
468
489
|
/**
|
|
469
490
|
* Clears the current content and resets the state.
|
|
@@ -476,14 +497,19 @@ class Coax extends HTMLElement {
|
|
|
476
497
|
* @param newCode - The new source code to replace the existing code.
|
|
477
498
|
*/
|
|
478
499
|
async replace(newCode: string) {
|
|
479
|
-
|
|
480
|
-
|
|
500
|
+
newCode = escapeHtmlChars(newCode);
|
|
501
|
+
this.source = trimEmptyLines(newCode);
|
|
502
|
+
if (this._renderQueued) return;
|
|
503
|
+
this.highlightEl.innerHTML = '';
|
|
504
|
+
//一次性渲染
|
|
505
|
+
await this.highlight();
|
|
481
506
|
}
|
|
482
507
|
/**
|
|
483
508
|
* Appends new source code to the current content and highlights only the new portion.
|
|
484
509
|
* @param newCode - The new source code to append and highlight.
|
|
485
510
|
*/
|
|
486
511
|
async append(newCode: string) {
|
|
512
|
+
newCode = escapeHtmlChars(newCode);
|
|
487
513
|
// 将新的代码追加到现有代码末尾
|
|
488
514
|
this.source += `\n${newCode}`;
|
|
489
515
|
// 高亮新的部分
|
|
@@ -511,7 +537,7 @@ class Coax extends HTMLElement {
|
|
|
511
537
|
(lineWrap as any).completed = true;
|
|
512
538
|
//行结束前保存
|
|
513
539
|
this.lastLineString = this.lineString;
|
|
514
|
-
this.
|
|
540
|
+
this.getLineToFillHighLight(lineWrap?.lastElementChild as Element, this.lineString);
|
|
515
541
|
//一行结束,清空临时行文本
|
|
516
542
|
this.lineString = '';
|
|
517
543
|
}
|
|
@@ -531,20 +557,23 @@ class Coax extends HTMLElement {
|
|
|
531
557
|
* @param forceClose - Forcefully close the line if set to `true`.
|
|
532
558
|
*/
|
|
533
559
|
stream(str: string, forceClose: boolean = false) {
|
|
534
|
-
|
|
560
|
+
str = escapeHtmlChars(str);
|
|
561
|
+
const { lineWrap, codeWrap } = this.getLastLine(),
|
|
562
|
+
isLine = str.startsWith('\n') || str.endsWith('\n');
|
|
535
563
|
this.highlightEl.appendChild(lineWrap);
|
|
536
564
|
//汇集
|
|
537
565
|
this.source += str;
|
|
538
566
|
|
|
567
|
+
//临时保存行文本
|
|
568
|
+
this.lineString += isLine ? this.trimLineString(str) : str;
|
|
569
|
+
|
|
539
570
|
//如果没有遇到换行符号,也可以强制结束
|
|
540
|
-
if (forceClose ||
|
|
571
|
+
if (forceClose || isLine) {
|
|
541
572
|
//标记完成
|
|
542
573
|
this.close();
|
|
543
574
|
} else {
|
|
544
575
|
//插入文本
|
|
545
576
|
codeWrap.innerHTML += str;
|
|
546
|
-
//临时保存行文本
|
|
547
|
-
this.lineString += str;
|
|
548
577
|
}
|
|
549
578
|
//滚动到最底部
|
|
550
579
|
this.autoScrollCode();
|
package/src/modules.js
CHANGED
package/src/modules.ts
CHANGED