@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/dist/coax.cjs.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2026-1-
|
|
3
|
+
* @since Last modified: 2026-1-15 19:21:59
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.6
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description Coax is a lightweight web component for elegant code display with syntax highlighting, typewriter effects, and theme switching. Supports JavaScript, HTML, CSS, TypeScript, and Markdown with copy tools and customization.
|
|
8
8
|
* @see {@link https://coax.axui.cn|Official website}
|
|
@@ -268,6 +268,25 @@ const createTools = (data) => {
|
|
|
268
268
|
return toolsEl;
|
|
269
269
|
};
|
|
270
270
|
|
|
271
|
+
const trimEmptyLines = (str) => {
|
|
272
|
+
if (str == null)
|
|
273
|
+
return '';
|
|
274
|
+
return str.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const escapeHtmlChars = (text) => {
|
|
278
|
+
// Check if the input text is empty or undefined
|
|
279
|
+
if (!text)
|
|
280
|
+
return '';
|
|
281
|
+
// Replace the special characters with their corresponding HTML entities
|
|
282
|
+
return text
|
|
283
|
+
.replace(/&/g, '&') // Replace '&' with '&'
|
|
284
|
+
.replace(/</g, '<') // Replace '<' with '<'
|
|
285
|
+
.replace(/>/g, '>') // Replace '>' with '>'
|
|
286
|
+
.replace(/"/g, '"') // Replace '"' with '"'
|
|
287
|
+
.replace(/'/g, '''); // Replace "'" with '''
|
|
288
|
+
};
|
|
289
|
+
|
|
271
290
|
class Coax extends HTMLElement {
|
|
272
291
|
// A static Map to hold the configuration for different languages
|
|
273
292
|
static languages = new Map();
|
|
@@ -295,7 +314,7 @@ class Coax extends HTMLElement {
|
|
|
295
314
|
// Attach a Shadow DOM to the custom element
|
|
296
315
|
this.attachShadow({ mode: 'open' });
|
|
297
316
|
// Remove leading/trailing whitespace from the raw code content
|
|
298
|
-
this.source = this.textContent
|
|
317
|
+
this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
|
|
299
318
|
// Initialize the basic structure of the component
|
|
300
319
|
this.shadowRoot.innerHTML = `
|
|
301
320
|
<style id="base-styles">
|
|
@@ -534,7 +553,7 @@ class Coax extends HTMLElement {
|
|
|
534
553
|
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
535
554
|
}
|
|
536
555
|
|
|
537
|
-
|
|
556
|
+
getLineToFillHighLight(codeWrap, line, config) {
|
|
538
557
|
config = config || Coax.languages.get(this.lang);
|
|
539
558
|
let highlightedLine = this.getHighLightString(line, config);
|
|
540
559
|
// 将高亮后的内容填充到 div 中
|
|
@@ -542,10 +561,10 @@ class Coax extends HTMLElement {
|
|
|
542
561
|
}
|
|
543
562
|
;
|
|
544
563
|
|
|
545
|
-
async highlight(newCode) {
|
|
564
|
+
async highlight(newCode = this.source) {
|
|
546
565
|
const config = Coax.languages.get(this.lang), startIndex = this.highlightEl.children.length,
|
|
547
566
|
// 将新源码按行分割
|
|
548
|
-
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
567
|
+
newCodeLines = newCode ? newCode.split('\n') : [], hasSpeedAttr = this.hasAttribute('speed'), hasSanitizedAttr = this.hasAttribute('sanitized');
|
|
549
568
|
//更新别名
|
|
550
569
|
this.updateName(config);
|
|
551
570
|
if (!newCodeLines.length)
|
|
@@ -553,13 +572,13 @@ class Coax extends HTMLElement {
|
|
|
553
572
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
554
573
|
for (let [index, lineString] of newCodeLines.entries()) {
|
|
555
574
|
//如果是空行则跳过
|
|
556
|
-
if (!lineString.trim() &&
|
|
575
|
+
if (!lineString.trim() && hasSanitizedAttr)
|
|
557
576
|
continue;
|
|
558
577
|
// 创建一个 div 包裹每一行
|
|
559
578
|
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
560
579
|
//标记完成
|
|
561
580
|
lineWrap.completed = true;
|
|
562
|
-
if (
|
|
581
|
+
if (hasSpeedAttr) {
|
|
563
582
|
this.highlightEl.appendChild(lineWrap);
|
|
564
583
|
//流式打字
|
|
565
584
|
await typeWriter(lineString, {
|
|
@@ -568,11 +587,11 @@ class Coax extends HTMLElement {
|
|
|
568
587
|
codeWrap.innerHTML = fullText;
|
|
569
588
|
}
|
|
570
589
|
});
|
|
571
|
-
this.
|
|
590
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
572
591
|
}
|
|
573
592
|
else {
|
|
574
593
|
//直接打出
|
|
575
|
-
this.
|
|
594
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
576
595
|
//
|
|
577
596
|
this.highlightEl.appendChild(lineWrap);
|
|
578
597
|
}
|
|
@@ -630,17 +649,30 @@ class Coax extends HTMLElement {
|
|
|
630
649
|
this.codeNameEl.innerHTML = this.alias;
|
|
631
650
|
}
|
|
632
651
|
}
|
|
652
|
+
trimLineString(str) {
|
|
653
|
+
// 删除开头的第一个换行符
|
|
654
|
+
if (str.startsWith('\n')) {
|
|
655
|
+
str = str.substring(1);
|
|
656
|
+
}
|
|
657
|
+
// 删除结尾的第一个换行符
|
|
658
|
+
if (str.endsWith('\n')) {
|
|
659
|
+
str = str.slice(0, -1);
|
|
660
|
+
}
|
|
661
|
+
return str;
|
|
662
|
+
}
|
|
633
663
|
|
|
634
|
-
render(
|
|
664
|
+
render() {
|
|
635
665
|
//同时多次改变属性,只执行一次
|
|
636
666
|
if (this._renderQueued)
|
|
637
667
|
return;
|
|
638
668
|
this._renderQueued = true;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
669
|
+
requestAnimationFrame(async () => {
|
|
670
|
+
this.highlightEl.innerHTML = '';
|
|
671
|
+
this.injectThemeStyles();
|
|
672
|
+
//一次性渲染
|
|
673
|
+
await this.highlight();
|
|
674
|
+
this._renderQueued = false;
|
|
675
|
+
});
|
|
644
676
|
}
|
|
645
677
|
|
|
646
678
|
clear() {
|
|
@@ -648,11 +680,17 @@ class Coax extends HTMLElement {
|
|
|
648
680
|
}
|
|
649
681
|
|
|
650
682
|
async replace(newCode) {
|
|
651
|
-
|
|
652
|
-
|
|
683
|
+
newCode = escapeHtmlChars(newCode);
|
|
684
|
+
this.source = trimEmptyLines(newCode);
|
|
685
|
+
if (this._renderQueued)
|
|
686
|
+
return;
|
|
687
|
+
this.highlightEl.innerHTML = '';
|
|
688
|
+
//一次性渲染
|
|
689
|
+
await this.highlight();
|
|
653
690
|
}
|
|
654
691
|
|
|
655
692
|
async append(newCode) {
|
|
693
|
+
newCode = escapeHtmlChars(newCode);
|
|
656
694
|
// 将新的代码追加到现有代码末尾
|
|
657
695
|
this.source += `\n${newCode}`;
|
|
658
696
|
// 高亮新的部分
|
|
@@ -675,7 +713,7 @@ class Coax extends HTMLElement {
|
|
|
675
713
|
lineWrap.completed = true;
|
|
676
714
|
//行结束前保存
|
|
677
715
|
this.lastLineString = this.lineString;
|
|
678
|
-
this.
|
|
716
|
+
this.getLineToFillHighLight(lineWrap?.lastElementChild, this.lineString);
|
|
679
717
|
//一行结束,清空临时行文本
|
|
680
718
|
this.lineString = '';
|
|
681
719
|
}
|
|
@@ -690,20 +728,21 @@ class Coax extends HTMLElement {
|
|
|
690
728
|
}
|
|
691
729
|
|
|
692
730
|
stream(str, forceClose = false) {
|
|
693
|
-
|
|
731
|
+
str = escapeHtmlChars(str);
|
|
732
|
+
const { lineWrap, codeWrap } = this.getLastLine(), isLine = str.startsWith('\n') || str.endsWith('\n');
|
|
694
733
|
this.highlightEl.appendChild(lineWrap);
|
|
695
734
|
//汇集
|
|
696
735
|
this.source += str;
|
|
736
|
+
//临时保存行文本
|
|
737
|
+
this.lineString += isLine ? this.trimLineString(str) : str;
|
|
697
738
|
//如果没有遇到换行符号,也可以强制结束
|
|
698
|
-
if (forceClose ||
|
|
739
|
+
if (forceClose || isLine) {
|
|
699
740
|
//标记完成
|
|
700
741
|
this.close();
|
|
701
742
|
}
|
|
702
743
|
else {
|
|
703
744
|
//插入文本
|
|
704
745
|
codeWrap.innerHTML += str;
|
|
705
|
-
//临时保存行文本
|
|
706
|
-
this.lineString += str;
|
|
707
746
|
}
|
|
708
747
|
//滚动到最底部
|
|
709
748
|
this.autoScrollCode();
|
package/dist/coax.cjs.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @since Last modified: 2026-1-
|
|
2
|
+
* @since Last modified: 2026-1-15 19:21:59
|
|
3
3
|
* @name Coax event management system.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.6
|
|
5
5
|
* @author AXUI development team <3217728223@qq.com>
|
|
6
6
|
* @description Coax is a lightweight web component for elegant code display with syntax highlighting, typewriter effects, and theme switching. Supports JavaScript, HTML, CSS, TypeScript, and Markdown with copy tools and customization.
|
|
7
7
|
* @see {@link https://coax.axui.cn|Official website}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'coax', 'Coax' and 'COAX' within the software.
|
|
13
13
|
* @license MIT license
|
|
14
14
|
*/
|
|
15
|
-
"use strict";const typeWriter=(e,t)=>{const n=t.speed||100;return new Promise(r=>{t?.onBeforeType?.(e);let i=0;const a=setInterval(()=>{if(i<e.length){const n=e.charAt(i),r=e.substring(0,i+1);t?.onDuringType?.(n,r),i++}else clearInterval(a),r(e),t?.onAfterType?.(e)},n)})},COMMA$1=",",SPACE$1=" ",trim$1=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses$1=e=>{let t,n=[];return Array.isArray(e)?n=e.filter(e=>e&&"string"==typeof e):(t=(e=trim$1(e)).includes(",")?",":" ",n=e.split(t)),n.map(e=>trim$1(e,"global")).filter(Boolean)},NAMESPACE="ax",getDataType=e=>{let t,n=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===n&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===n&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":n,t},getEl=(e,t=document.body)=>{let n=getDataType(e),r=getDataType(t),i=r.includes("HTML")||"ShadowRoot"===r?t:document.querySelector(t),a=i&&i instanceof HTMLTemplateElement?i.content:i,o=null;if(e)if(n.includes("HTML"))o=e;else if("String"===n)try{o=(a||document).querySelector(e.trim())}catch{o=null}return o},createEl=(e,t,n)=>{let r=(e=e||"div").toUpperCase().trim(),i=document.createElement(r),a=getDataType(t);if(t&&"Object"===a)for(let e in t)t.hasOwnProperty(e)&&i.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let n=getDataType(t);if("TEMPLATE"===r)e.innerHTML=t.toString();else if("Array"===n&&t.length>0)for(let n of t){if(getDataType(n).includes("HTML"))e.appendChild(n);else{let t=createEl(n.name,n.attrs,n.content);t&&e.appendChild(t)}}else if(n.includes("HTML"))e.appendChild(t);else if("String"===n&&t.trim().startsWith("#")&&t.trim().length>1){let n=getEl(t);if(!n)return;"TEMPLATE"===n.nodeName?e.appendChild(n.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",n.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(i,n),i},isEmpty=e=>{let t,n=getDataType(e);return t=!e||("Object"===n?0===Object.keys(e).length:"Array"===n?""===e.join(""):"Function"===n?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===n?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===n||"Map"===n?0===e.size:"Date"===n?isNaN(e.getTime()):"RegExp"===n?""===e.source:"ArrayBuffer"===n?0===e.byteLength:"NodeList"===n||"HTMLCollection"===n||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===n||e instanceof Error?""===e.message:!(!n.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(n))&&0===e.length),t},ALIAS="rep",addClasses=(e,t,n)=>{const r=getEl(e),i=parseClasses$1(t);r&&0!==i.length&&i.forEach(e=>{r.classList.add(e)})},createTools=e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},n=e.extendable?'<i rep="arrow"></i>':"",r=(e.icon?`<i rep="icon">${e.icon}</i>`:"")+(e.disk?`<i rep="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i rep="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i rep="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i rep="label">${e.label}</i>`:"")+n;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),r),e.iconEl=e.wrapEl.querySelector('[rep="icon"]'),e.cubeEl=e.wrapEl.querySelector('[rep="cube"]'),e.diskEl=e.wrapEl.querySelector('[rep="disk"]'),e.imageEl=e.wrapEl.querySelector('[rep="image"]'),e.labelEl=e.wrapEl.querySelector('[rep="label"]'),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let n of e)renderFn(n),t.appendChild(n.wrapEl),n?.action?.(n);return t};class Coax extends HTMLElement{static languages=new Map;static tools=[];source;_renderQueued=!1;baseStylesEl;themeStylesEl;dynamicStylesEl;highlightEl;headerEl;codeNameEl;codeToolsEl;codeBodyEl;lang="plain";alias="Plain Text";lineString="";lastLineString="";speed=5;autoScroll=!0;canListen=!0;constructor(){super(),this.attachShadow({mode:"open"}),this.source=this.textContent?.replace(/^\s*\n|\n\s*$/g,"")||"",this.shadowRoot.innerHTML=`\n <style id="base-styles">\n :host { \n --border-width:1px;\n --border-style:solid;\n --radius:9px;\n --height:auto;\n --max-height:500px;\n --radius:9px;\n --padding:1em;\n --font-size:16px;\n --line-height:1.8;\n --background:rgb(247, 247, 247);\n --border-color:rgb(224, 224, 224);\n --color-code:rgb(51, 51, 51);\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(0,0,0,0.04);\n --color-hover:rgba(0,0,0,0.06);\n } \n :host([scheme="dark"]){\n --background: #282c34;\n --border-color: transparent;\n --color-code: #abb2bf;\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(255,255,255,0.04);\n --color-hover:rgba(255,255,255,0.06);\n }\n @media (prefers-color-scheme: dark) {\n :host{\n --background: #282c34;\n --border-color: transparent;\n --color-code: #abb2bf;\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(255,255,255,0.04);\n --color-hover:rgba(255,255,255,0.06);\n }\n }\n :host {\n font-size: var(--ax-code-font-size,var(--font-size));\n display: block; \n box-sizing:border-box;\n background:var(--ax-code-background-color,var(--background));\n color:var(--ax-code-color,var(--color-code));\n border:var(--ax-code-border-width,var(--border-width)) var(--ax-code-border-style,var(--border-style)) var(--ax-code-border-color,var(--border-color));\n transition: border-color 0.3s ease,color 0.3s ease; \n border-radius: var(--ax-code-radius,var(--radius));\n }\n #code-header{\n line-height:calc(var(--ax-code-line-height,var(--line-height))*1.5);\n padding-inline-start:var(--ax-code-padding,var(--padding));\n display:flex;\n \n >:first-child{\n flex:auto;\n }\n }\n #code-body{\n padding: var(--ax-code-padding,var(--padding)) 0;\n height:var(--ax-code-height,var(--height));\n max-height:var(--ax-code-max-height,var(--max-height));\n overflow:auto;\n }\n pre,code{\n font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";\n margin:0; padding:0;\n }\n code>div{\n display:flex;\n padding:0 var(--ax-code-padding,var(--padding));\n line-height: var(--ax-code-line-height,var(--line-height));\n box-sizing:border-box;\n \n >div{\n flex:auto;\n }\n }\n code>div>div:empty:before{\n content:' ';\n }\n :host([indexed]) code>div:before{\n display:inline-flex;\n color:var(--color-index);\n content: attr(data-index);\n min-width:var(--ax-code-index-width,2em);\n margin-inline-end:var(--ax-code-padding,8px);\n }\n :host([striped]) code>div:nth-child(odd){\n background-color:var(--color-stripe);\n }\n :host([hoverable]) code>div:hover{\n background-color:var(--color-hover);\n }\n :host([wrapped]) code>div>div{\n white-space: pre-wrap;\n }\n :host([unnamed]) #code-name{\n display:none;\n }\n .ax-box-tools{\n >*{\n font-size:14px;\n display:inline-flex;\n align-items:center;\n justify-content:center;\n height:2em;\n line-height:2em;\n aspect-ratio:1/1;\n margin-inline-end:8px;\n transition:all 200ms ease;\n border-radius:6px;\n &:hover{\n cursor:pointer;\n background-color:rgba(0,0,0,.04);\n }\n }\n [rep=icon]{\n display:inline-flex;\n align-items:center;\n justify-content:center;\n }\n }\n [disabled]{\n pointer-event:none;\n }\n </style>\n <style id="dynamic-styles"></style>\n <style id="theme-styles"></style>\n <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>\n <div id="code-body">\n <pre><code id="highlight"></code></pre>\n </div>\n `,this.baseStylesEl=getEl("#base-styles",this.shadowRoot),this.themeStylesEl=getEl("#theme-styles",this.shadowRoot),this.dynamicStylesEl=getEl("#dynamic-styles",this.shadowRoot),this.headerEl=getEl("#code-header",this.shadowRoot),this.codeNameEl=getEl("#code-name",this.shadowRoot),this.codeToolsEl=getEl("#code-tools",this.shadowRoot),this.codeBodyEl=getEl("#code-body",this.shadowRoot),this.highlightEl=getEl("#highlight",this.shadowRoot),this.codeBodyEl.addEventListener("scroll",()=>{let e=this.codeBodyEl.scrollTop+this.codeBodyEl.clientHeight<this.codeBodyEl.scrollHeight;this.autoScroll=!e})}static register(e,t){this.languages.set(e,{...t})}static addTools(e){Coax.tools=e}mountTools(e){requestAnimationFrame(()=>{this.codeToolsEl.innerHTML="";let t=e.map(e=>(e.action=e.action.bind(this),e));this.codeToolsEl.appendChild(createTools(t))})}connectedCallback(){this.render()}static get observedAttributes(){return["lang","height","max-height","tools","speed"]}attributeChangedCallback(e,t,n){if(t!==n&&this.canListen&&("height"!==e&&"max-height"!==e||this.updateStyleByRegExp(e,n),"speed"===e&&(this.speed=~~!!n),"lang"===e&&(this.lang=n,this.render()),"tools"===e)){n||(this.codeToolsEl.innerHTML="");const e=parseClasses$1(n),t=Coax.tools.filter(t=>e.includes(t.name));if(!t.length)return;this.mountTools(t)}}updateStyleByRegExp(e,t){const n=new RegExp(`;\\n\\s*${e}:\\s*[^;]+;`,"g");this.baseStylesEl.textContent=this.baseStylesEl.textContent.replace(n,`;\n${e}: ${t};`)}getCssPrefix(e){return(e?.cssPrefix||this.lang).replace(/[^a-zA-Z0-9_-]/g,"\\$&")}getHighLightString(e,t){if(!(t=t||Coax.languages.get(this.lang)))return e;const n=this.getCssPrefix(t),r=new RegExp(t.rules.map(e=>`(${e.pattern.source})`).join("|"),"g");return e.replace(r,(e,...r)=>{const i=r.findIndex(e=>void 0!==e);return-1!==i&&t.rules[i]?`<span class="ax-${n}-${t.rules[i].token}">${e}</span>`:e})}createLineWrap(e,t){let n=0;if(null==e&&null==t)n=this.highlightEl.children.length;else{n=(t||this.highlightEl.children.length)+e}return createEl("div",{"data-index":n},"<div></div>")}getLineToFill(e,t,n){n=n||Coax.languages.get(this.lang);let r=this.getHighLightString(t,n);e.innerHTML=r}async highlight(e){const t=Coax.languages.get(this.lang),n=this.highlightEl.children.length,r=e?e.split("\n"):[];if(this.updateName(t),!r.length)return!0;for(let[e,i]of r.entries()){if(!i.trim()&&this.hasAttribute("sanitized"))continue;const r=this.createLineWrap(e,n),a=r.lastElementChild;r.completed=!0,this.hasAttribute("speed")?(this.highlightEl.appendChild(r),await typeWriter(i,{speed:this.speed,onDuringType:(e,t)=>{a.innerHTML=t}}),this.getLineToFill(a,i,t)):(this.getLineToFill(a,i,t),this.highlightEl.appendChild(r))}return this.autoScrollCode(),!0}autoScrollCode(){this.autoScroll&&(this.codeBodyEl.scrollTop=this.codeBodyEl.scrollHeight)}injectThemeStyles(){const e=Coax.languages.get(this.lang);if(!e)return;let t=this.getCssPrefix(e),n=e.rules.map(e=>`\n .ax-${t}-${e.token} { color: var(--ax-${t}-${e.token}${e.light?","+e.light:""});}`).join("\n"),r="",i="";r+=':host([scheme="dark"]){',r+=e.rules.map(e=>"\n "+(e.light?`\n .ax-${t}-${e.token} {color: var(--ax-${t}-${e.token},${e.dark});}`:"")).join("\n"),r+="}",i="@media (prefers-color-scheme: dark){\n :host{\n ",i+=e.rules.map(e=>`\n ${e.light?`\n .ax-${t}-${e.token} { color: var(--ax-${t}-${e.token},${e.dark}); }`:""} `).join("\n"),i+="}",this.dynamicStylesEl.textContent=n+r+i,e?.themeStyles&&(this.themeStylesEl.textContent=e.themeStyles)}updateName(e){this.hasAttribute("unnamed")||(e?(this.alias=e.alias||this.lang,this.codeNameEl.innerHTML=this.alias):this.codeNameEl.innerHTML="Plain Text")}render(e=this.source){this._renderQueued||(this._renderQueued=!0,this.clear(),this.injectThemeStyles(),this.highlight(e),this._renderQueued=!1)}clear(){this.highlightEl.innerHTML=this.source=this.lineString=""}async replace(e){this.clear(),await this.highlight(e)}async append(e){this.source+=`\n${e}`,await this.highlight(e)}getLastLine(){const e=this.highlightEl.lastElementChild,t=!e||this.highlightEl.lastElementChild?.completed?this.createLineWrap():e;return{lineWrap:t,codeWrap:t.lastElementChild}}close(){const e=this.highlightEl.lastElementChild;e&&(e.completed=!0,this.lastLineString=this.lineString,this.getLineToFill(e?.lastElementChild,this.lineString),this.lineString="")}open(){const e=this.highlightEl.lastElementChild;e&&(e.completed=!1,(e?.lastElementChild).textContent=this.lineString=this.lastLineString)}stream(e,t=!1){const{lineWrap:n,codeWrap:r}=this.getLastLine();this.highlightEl.appendChild(n),this.source+=e,t||e.startsWith("\n")||e.endsWith("\n")?this.close():(r.innerHTML+=e,this.lineString+=e),this.autoScrollCode()}}const css=[{token:"comment",pattern:/\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"value",pattern:/(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/,light:"#032f62",dark:"#a5d6ff"},{token:"func",pattern:/[a-z-]+\(?=/,light:"#e36209",dark:"#ffa657"},{token:"property",pattern:/[a-z-]+(?=\s*:)/,light:"#005cc5",dark:"#79c0ff"},{token:"selector",pattern:/[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i,light:"#22863a",dark:"#7ee787"},{token:"unit",pattern:/(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/,light:"#d73a49",dark:"#ff7b72"},{token:"number",pattern:/\b\d+(\.\d+)?\b/,light:"#005cc5",dark:"#79c0ff"},{token:"punct",pattern:/[{}();:]/,light:"#24292e",dark:"#c9d1d9"}],html=[{token:"comment",pattern:/<!--[\s\S]*?-->/,light:"#999999",dark:"#6e7681"},{token:"doctype",pattern:/<!DOCTYPE[\s\S]*?>/i,light:"#6a737d",dark:"#8b949e"},{token:"tag",pattern:/<\/?[a-zA-Z0-9]+/,light:"#22863a",dark:"#7ee787"},{token:"attr",pattern:/[a-zA-Z-]+(?=\s*=\s*)/,light:"#6f42c1",dark:"#d2a8ff"},{token:"string",pattern:/(['"])(?:\\.|[^\\])*?\1/,light:"#032f62",dark:"#a5d6ff"},{token:"bracket",pattern:/\/?>/,light:"#24292e",dark:"#c9d1d9"}],javascript=[{token:"comment",pattern:/\/\/[^\n]*|\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"string",pattern:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/,light:"#032f62",dark:"#98c379"},{token:"keyword",pattern:/\b(async|await|break|case|catch|class|const|continue|default|delete|do|else|export|extends|finally|for|function|if|import|in|instanceof|new|return|super|switch|this|throw|try|typeof|var|while|with|yield|let|static)\b/,light:"#d73a49",dark:"#ff7b72"},{token:"builtin",pattern:/\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/,light:"#e36209",dark:"#ffa657"},{token:"number",pattern:/\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/,light:"#005cc5",dark:"#79c0ff"},{token:"func",pattern:/\b[a-zA-Z_]\w*(?=\s*\()/,light:"#6f42c1",dark:"#d2a8ff"},{token:"op",pattern:/[+\-*/%=<>!&|^~]+/,light:"#069598",dark:"#56b6c2"}],markdown=[{token:"comment",pattern:/<!--[\s\S]*?-->/,light:"#6a737d",dark:"#8b949e"},{token:"heading",pattern:/(^|\n)(#{1,6})\s*(.+)/,light:"#e36209",dark:"#ffa657"},{token:"bold",pattern:/\*\*([^*]+)\*\*|__([^_]+)__/g,light:"#d73a49",dark:"#ff7b72"},{token:"italic",pattern:/\*([^*]+)\*|_([^_]+)_/g,light:"#032f62",dark:"#a5d6ff"},{token:"link",pattern:/\[([^\]]+)\]\(([^)]+)\)/g,light:"#0288d1",dark:"#80c0ff"},{token:"inline-code",pattern:/`([^`]+)`/g,light:"#032f62",dark:"#98c379"},{token:"code-block",pattern:/```([^\n]+)\n([\s\S]*?)```/g,light:"#24292e",dark:"#c9d1d9"},{token:"list-item",pattern:/(^|\n)([-*])\s+(.+)/g,light:"#5c6e7c",dark:"#8b949e"},{token:"quote",pattern:/(^|\n)>[ \t]*(.+)/g,light:"#6f42c1",dark:"#d2a8ff"},{token:"image",pattern:/!\[([^\]]+)\]\(([^)]+)\)/g,light:"#d73a49",dark:"#ff7b72"},{token:"hr",pattern:/^(---|___|\*\*\*)\s*$/gm,light:"#24292e",dark:"#c9d1d9"},{token:"strikethrough",pattern:/~~([^~]+)~~/g,light:"#e36209",dark:"#ffa657"},{token:"table",pattern:/\|([^\|]+)\|([^\|]+)\|/g,light:"#5c6e7c",dark:"#8b949e"}],typescript=[{token:"comment",pattern:/\/\/[^\n]*|\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"string",pattern:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/,light:"#032f62",dark:"#98c379"},{token:"decorator",pattern:/@[a-zA-Z_]\w*/,light:"#953800",dark:"#ffa657"},{token:"keyword",pattern:/\b(abstract|as|async|await|break|case|catch|class|const|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|package|private|protected|public|readonly|return|set|static|super|switch|this|throw|try|type|typeof|var|while|with|yield)\b/,light:"#d73a49",dark:"#ff7b72"},{token:"builtin",pattern:/\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/,light:"#e36209",dark:"#ffa657"},{token:"type",pattern:/\b[A-Z]\w*\b/,light:"#005cc5",dark:"#79c0ff"},{token:"number",pattern:/\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/,light:"#005cc5",dark:"#79c0ff"},{token:"func",pattern:/\b[a-zA-Z_]\w*(?=\s*\()/,light:"#6f42c1",dark:"#d2a8ff"},{token:"op",pattern:/(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/,light:"#089ba6",dark:"#79c0ff"}],COMMA=",",SPACE=" ",trim=(e,t="")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,n=[];return Array.isArray(e)?n=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",n=e.split(t)),n.map(e=>trim(e,"global")).filter(Boolean)},rtlStyle=(e="")=>`\n <style>\n :where([dir="rtl"]) .icax-${e},\n :where(:dir(rtl)) .icax-${e} {\n transform: scaleX(-1);\n transform-origin: center;\n }\n </style>\n`,wrap=(e,t,n=!1,r)=>{const i=r?.size||"1em",a=r?.color||"currentColor",o=r?.thickness||2,l=r?.classes?parseClasses(r.classes).join(" "):"",s=t.name.replace(/([A-Z])/g,"-$1").toLowerCase();return`<svg xmlns="http://www.w3.org/2000/svg" width="${i}" height="${i}" viewBox="0 0 24 24" fill="none" stroke="${a}"\n stroke-width="${o}" stroke-linecap="round" stroke-linejoin="round" class="${s} ${l}">\n ${n?rtlStyle(s.split("-")[1]):""}\n ${e}\n </svg>`},icaxCheck=e=>wrap('<polyline points="20 6 9 17 4 12"></polyline>',icaxCheck,!1,e),icaxCopy=e=>wrap('<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>',icaxCopy,!1,e),copy={name:"copy",icon:icaxCopy(),action:function(e){e.wrapEl.onclick=()=>{navigator.clipboard.writeText(this.source).then(()=>{e.iconEl.innerHTML=icaxCheck(),e.iconEl.toggleAttribute("disabled",!0),setTimeout(()=>{e.iconEl.removeAttribute("disabled"),e.iconEl.innerHTML=icaxCopy()},2e3)}).catch(e=>{})}}};exports.Coax=Coax,exports.copy=copy,exports.css=css,exports.html=html,exports.javascript=javascript,exports.markdown=markdown,exports.typescript=typescript;
|
|
15
|
+
"use strict";const typeWriter=(e,t)=>{const n=t.speed||100;return new Promise(r=>{t?.onBeforeType?.(e);let i=0;const a=setInterval(()=>{if(i<e.length){const n=e.charAt(i),r=e.substring(0,i+1);t?.onDuringType?.(n,r),i++}else clearInterval(a),r(e),t?.onAfterType?.(e)},n)})},COMMA$1=",",SPACE$1=" ",trim$1=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses$1=e=>{let t,n=[];return Array.isArray(e)?n=e.filter(e=>e&&"string"==typeof e):(t=(e=trim$1(e)).includes(",")?",":" ",n=e.split(t)),n.map(e=>trim$1(e,"global")).filter(Boolean)},NAMESPACE="ax",getDataType=e=>{let t,n=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===n&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===n&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":n,t},getEl=(e,t=document.body)=>{let n=getDataType(e),r=getDataType(t),i=r.includes("HTML")||"ShadowRoot"===r?t:document.querySelector(t),a=i&&i instanceof HTMLTemplateElement?i.content:i,o=null;if(e)if(n.includes("HTML"))o=e;else if("String"===n)try{o=(a||document).querySelector(e.trim())}catch{o=null}return o},createEl=(e,t,n)=>{let r=(e=e||"div").toUpperCase().trim(),i=document.createElement(r),a=getDataType(t);if(t&&"Object"===a)for(let e in t)t.hasOwnProperty(e)&&i.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let n=getDataType(t);if("TEMPLATE"===r)e.innerHTML=t.toString();else if("Array"===n&&t.length>0)for(let n of t){if(getDataType(n).includes("HTML"))e.appendChild(n);else{let t=createEl(n.name,n.attrs,n.content);t&&e.appendChild(t)}}else if(n.includes("HTML"))e.appendChild(t);else if("String"===n&&t.trim().startsWith("#")&&t.trim().length>1){let n=getEl(t);if(!n)return;"TEMPLATE"===n.nodeName?e.appendChild(n.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",n.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(i,n),i},isEmpty=e=>{let t,n=getDataType(e);return t=!e||("Object"===n?0===Object.keys(e).length:"Array"===n?""===e.join(""):"Function"===n?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===n?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===n||"Map"===n?0===e.size:"Date"===n?isNaN(e.getTime()):"RegExp"===n?""===e.source:"ArrayBuffer"===n?0===e.byteLength:"NodeList"===n||"HTMLCollection"===n||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===n||e instanceof Error?""===e.message:!(!n.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(n))&&0===e.length),t},ALIAS="rep",addClasses=(e,t,n)=>{const r=getEl(e),i=parseClasses$1(t);r&&0!==i.length&&i.forEach(e=>{r.classList.add(e)})},createTools=e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},n=e.extendable?'<i rep="arrow"></i>':"",r=(e.icon?`<i rep="icon">${e.icon}</i>`:"")+(e.disk?`<i rep="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i rep="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i rep="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i rep="label">${e.label}</i>`:"")+n;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),r),e.iconEl=e.wrapEl.querySelector('[rep="icon"]'),e.cubeEl=e.wrapEl.querySelector('[rep="cube"]'),e.diskEl=e.wrapEl.querySelector('[rep="disk"]'),e.imageEl=e.wrapEl.querySelector('[rep="image"]'),e.labelEl=e.wrapEl.querySelector('[rep="label"]'),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let n of e)renderFn(n),t.appendChild(n.wrapEl),n?.action?.(n);return t},trimEmptyLines=e=>null==e?"":e.replace(/^\s*\n|\n\s*$/g,"")||"",escapeHtmlChars=e=>e?e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"):"";class Coax extends HTMLElement{static languages=new Map;static tools=[];source;_renderQueued=!1;baseStylesEl;themeStylesEl;dynamicStylesEl;highlightEl;headerEl;codeNameEl;codeToolsEl;codeBodyEl;lang="plain";alias="Plain Text";lineString="";lastLineString="";speed=5;autoScroll=!0;canListen=!0;constructor(){super(),this.attachShadow({mode:"open"}),this.source=escapeHtmlChars(trimEmptyLines(this.textContent)),this.shadowRoot.innerHTML=`\n <style id="base-styles">\n :host { \n --border-width:1px;\n --border-style:solid;\n --radius:9px;\n --height:auto;\n --max-height:500px;\n --radius:9px;\n --padding:1em;\n --font-size:16px;\n --line-height:1.8;\n --background:rgb(247, 247, 247);\n --border-color:rgb(224, 224, 224);\n --color-code:rgb(51, 51, 51);\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(0,0,0,0.04);\n --color-hover:rgba(0,0,0,0.06);\n } \n :host([scheme="dark"]){\n --background: #282c34;\n --border-color: transparent;\n --color-code: #abb2bf;\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(255,255,255,0.04);\n --color-hover:rgba(255,255,255,0.06);\n }\n @media (prefers-color-scheme: dark) {\n :host{\n --background: #282c34;\n --border-color: transparent;\n --color-code: #abb2bf;\n --color-index:rgb(153, 153, 153);\n --color-stripe:rgba(255,255,255,0.04);\n --color-hover:rgba(255,255,255,0.06);\n }\n }\n :host {\n font-size: var(--ax-code-font-size,var(--font-size));\n display: block; \n box-sizing:border-box;\n background:var(--ax-code-background-color,var(--background));\n color:var(--ax-code-color,var(--color-code));\n border:var(--ax-code-border-width,var(--border-width)) var(--ax-code-border-style,var(--border-style)) var(--ax-code-border-color,var(--border-color));\n transition: border-color 0.3s ease,color 0.3s ease; \n border-radius: var(--ax-code-radius,var(--radius));\n }\n #code-header{\n line-height:calc(var(--ax-code-line-height,var(--line-height))*1.5);\n padding-inline-start:var(--ax-code-padding,var(--padding));\n display:flex;\n \n >:first-child{\n flex:auto;\n }\n }\n #code-body{\n padding: var(--ax-code-padding,var(--padding)) 0;\n height:var(--ax-code-height,var(--height));\n max-height:var(--ax-code-max-height,var(--max-height));\n overflow:auto;\n }\n pre,code{\n font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";\n margin:0; padding:0;\n }\n code>div{\n display:flex;\n padding:0 var(--ax-code-padding,var(--padding));\n line-height: var(--ax-code-line-height,var(--line-height));\n box-sizing:border-box;\n \n >div{\n flex:auto;\n }\n }\n code>div>div:empty:before{\n content:' ';\n }\n :host([indexed]) code>div:before{\n display:inline-flex;\n color:var(--color-index);\n content: attr(data-index);\n min-width:var(--ax-code-index-width,2em);\n margin-inline-end:var(--ax-code-padding,8px);\n }\n :host([striped]) code>div:nth-child(odd){\n background-color:var(--color-stripe);\n }\n :host([hoverable]) code>div:hover{\n background-color:var(--color-hover);\n }\n :host([wrapped]) code>div>div{\n white-space: pre-wrap;\n }\n :host([unnamed]) #code-name{\n display:none;\n }\n .ax-box-tools{\n >*{\n font-size:14px;\n display:inline-flex;\n align-items:center;\n justify-content:center;\n height:2em;\n line-height:2em;\n aspect-ratio:1/1;\n margin-inline-end:8px;\n transition:all 200ms ease;\n border-radius:6px;\n &:hover{\n cursor:pointer;\n background-color:rgba(0,0,0,.04);\n }\n }\n [rep=icon]{\n display:inline-flex;\n align-items:center;\n justify-content:center;\n }\n }\n [disabled]{\n pointer-event:none;\n }\n </style>\n <style id="dynamic-styles"></style>\n <style id="theme-styles"></style>\n <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>\n <div id="code-body">\n <pre><code id="highlight"></code></pre>\n </div>\n `,this.baseStylesEl=getEl("#base-styles",this.shadowRoot),this.themeStylesEl=getEl("#theme-styles",this.shadowRoot),this.dynamicStylesEl=getEl("#dynamic-styles",this.shadowRoot),this.headerEl=getEl("#code-header",this.shadowRoot),this.codeNameEl=getEl("#code-name",this.shadowRoot),this.codeToolsEl=getEl("#code-tools",this.shadowRoot),this.codeBodyEl=getEl("#code-body",this.shadowRoot),this.highlightEl=getEl("#highlight",this.shadowRoot),this.codeBodyEl.addEventListener("scroll",()=>{let e=this.codeBodyEl.scrollTop+this.codeBodyEl.clientHeight<this.codeBodyEl.scrollHeight;this.autoScroll=!e})}static register(e,t){this.languages.set(e,{...t})}static addTools(e){Coax.tools=e}mountTools(e){requestAnimationFrame(()=>{this.codeToolsEl.innerHTML="";let t=e.map(e=>(e.action=e.action.bind(this),e));this.codeToolsEl.appendChild(createTools(t))})}connectedCallback(){this.render()}static get observedAttributes(){return["lang","height","max-height","tools","speed"]}attributeChangedCallback(e,t,n){if(t!==n&&this.canListen&&("height"!==e&&"max-height"!==e||this.updateStyleByRegExp(e,n),"speed"===e&&(this.speed=~~!!n),"lang"===e&&(this.lang=n,this.render()),"tools"===e)){n||(this.codeToolsEl.innerHTML="");const e=parseClasses$1(n),t=Coax.tools.filter(t=>e.includes(t.name));if(!t.length)return;this.mountTools(t)}}updateStyleByRegExp(e,t){const n=new RegExp(`;\\n\\s*${e}:\\s*[^;]+;`,"g");this.baseStylesEl.textContent=this.baseStylesEl.textContent.replace(n,`;\n${e}: ${t};`)}getCssPrefix(e){return(e?.cssPrefix||this.lang).replace(/[^a-zA-Z0-9_-]/g,"\\$&")}getHighLightString(e,t){if(!(t=t||Coax.languages.get(this.lang)))return e;const n=this.getCssPrefix(t),r=new RegExp(t.rules.map(e=>`(${e.pattern.source})`).join("|"),"g");return e.replace(r,(e,...r)=>{const i=r.findIndex(e=>void 0!==e);return-1!==i&&t.rules[i]?`<span class="ax-${n}-${t.rules[i].token}">${e}</span>`:e})}createLineWrap(e,t){let n=0;if(null==e&&null==t)n=this.highlightEl.children.length;else{n=(t||this.highlightEl.children.length)+e}return createEl("div",{"data-index":n},"<div></div>")}getLineToFillHighLight(e,t,n){n=n||Coax.languages.get(this.lang);let r=this.getHighLightString(t,n);e.innerHTML=r}async highlight(e=this.source){const t=Coax.languages.get(this.lang),n=this.highlightEl.children.length,r=e?e.split("\n"):[],i=this.hasAttribute("speed"),a=this.hasAttribute("sanitized");if(this.updateName(t),!r.length)return!0;for(let[e,o]of r.entries()){if(!o.trim()&&a)continue;const r=this.createLineWrap(e,n),s=r.lastElementChild;r.completed=!0,i?(this.highlightEl.appendChild(r),await typeWriter(o,{speed:this.speed,onDuringType:(e,t)=>{s.innerHTML=t}}),this.getLineToFillHighLight(s,o,t)):(this.getLineToFillHighLight(s,o,t),this.highlightEl.appendChild(r))}return this.autoScrollCode(),!0}autoScrollCode(){this.autoScroll&&(this.codeBodyEl.scrollTop=this.codeBodyEl.scrollHeight)}injectThemeStyles(){const e=Coax.languages.get(this.lang);if(!e)return;let t=this.getCssPrefix(e),n=e.rules.map(e=>`\n .ax-${t}-${e.token} { color: var(--ax-${t}-${e.token}${e.light?","+e.light:""});}`).join("\n"),r="",i="";r+=':host([scheme="dark"]){',r+=e.rules.map(e=>"\n "+(e.light?`\n .ax-${t}-${e.token} {color: var(--ax-${t}-${e.token},${e.dark});}`:"")).join("\n"),r+="}",i="@media (prefers-color-scheme: dark){\n :host{\n ",i+=e.rules.map(e=>`\n ${e.light?`\n .ax-${t}-${e.token} { color: var(--ax-${t}-${e.token},${e.dark}); }`:""} `).join("\n"),i+="}",this.dynamicStylesEl.textContent=n+r+i,e?.themeStyles&&(this.themeStylesEl.textContent=e.themeStyles)}updateName(e){this.hasAttribute("unnamed")||(e?(this.alias=e.alias||this.lang,this.codeNameEl.innerHTML=this.alias):this.codeNameEl.innerHTML="Plain Text")}trimLineString(e){return e.startsWith("\n")&&(e=e.substring(1)),e.endsWith("\n")&&(e=e.slice(0,-1)),e}render(){this._renderQueued||(this._renderQueued=!0,requestAnimationFrame(async()=>{this.highlightEl.innerHTML="",this.injectThemeStyles(),await this.highlight(),this._renderQueued=!1}))}clear(){this.highlightEl.innerHTML=this.source=this.lineString=""}async replace(e){e=escapeHtmlChars(e),this.source=trimEmptyLines(e),this._renderQueued||(this.highlightEl.innerHTML="",await this.highlight())}async append(e){e=escapeHtmlChars(e),this.source+=`\n${e}`,await this.highlight(e)}getLastLine(){const e=this.highlightEl.lastElementChild,t=!e||this.highlightEl.lastElementChild?.completed?this.createLineWrap():e;return{lineWrap:t,codeWrap:t.lastElementChild}}close(){const e=this.highlightEl.lastElementChild;e&&(e.completed=!0,this.lastLineString=this.lineString,this.getLineToFillHighLight(e?.lastElementChild,this.lineString),this.lineString="")}open(){const e=this.highlightEl.lastElementChild;e&&(e.completed=!1,(e?.lastElementChild).textContent=this.lineString=this.lastLineString)}stream(e,t=!1){e=escapeHtmlChars(e);const{lineWrap:n,codeWrap:r}=this.getLastLine(),i=e.startsWith("\n")||e.endsWith("\n");this.highlightEl.appendChild(n),this.source+=e,this.lineString+=i?this.trimLineString(e):e,t||i?this.close():r.innerHTML+=e,this.autoScrollCode()}}const css=[{token:"comment",pattern:/\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"value",pattern:/(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/,light:"#032f62",dark:"#a5d6ff"},{token:"func",pattern:/[a-z-]+\(?=/,light:"#e36209",dark:"#ffa657"},{token:"property",pattern:/[a-z-]+(?=\s*:)/,light:"#005cc5",dark:"#79c0ff"},{token:"selector",pattern:/[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i,light:"#22863a",dark:"#7ee787"},{token:"unit",pattern:/(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/,light:"#d73a49",dark:"#ff7b72"},{token:"number",pattern:/\b\d+(\.\d+)?\b/,light:"#005cc5",dark:"#79c0ff"},{token:"punct",pattern:/[{}();:]/,light:"#24292e",dark:"#c9d1d9"}],html=[{token:"comment",pattern:/<!--[\s\S]*?-->/,light:"#999999",dark:"#6e7681"},{token:"doctype",pattern:/<!DOCTYPE[\s\S]*?>/i,light:"#6a737d",dark:"#8b949e"},{token:"tag",pattern:/<\/?[a-zA-Z0-9]+/,light:"#22863a",dark:"#7ee787"},{token:"attr",pattern:/[a-zA-Z-]+(?=\s*=\s*)/,light:"#6f42c1",dark:"#d2a8ff"},{token:"string",pattern:/(['"])(?:\\.|[^\\])*?\1/,light:"#032f62",dark:"#a5d6ff"},{token:"bracket",pattern:/\/?>/,light:"#24292e",dark:"#c9d1d9"}],javascript=[{token:"comment",pattern:/\/\/[^\n]*|\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"string",pattern:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/,light:"#032f62",dark:"#98c379"},{token:"keyword",pattern:/\b(async|await|break|case|catch|class|const|continue|default|delete|do|else|export|extends|finally|for|function|if|import|in|instanceof|new|return|super|switch|this|throw|try|typeof|var|while|with|yield|let|static)\b/,light:"#d73a49",dark:"#ff7b72"},{token:"builtin",pattern:/\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/,light:"#e36209",dark:"#ffa657"},{token:"number",pattern:/\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/,light:"#005cc5",dark:"#79c0ff"},{token:"func",pattern:/\b[a-zA-Z_]\w*(?=\s*\()/,light:"#6f42c1",dark:"#d2a8ff"},{token:"op",pattern:/[+\-*/%=<>!&|^~]+/,light:"#069598",dark:"#56b6c2"}],markdown=[{token:"comment",pattern:/<!--[\s\S]*?-->/,light:"#6a737d",dark:"#8b949e"},{token:"heading",pattern:/(^|\n)(#{1,6})\s*(.+)/,light:"#e36209",dark:"#ffa657"},{token:"bold",pattern:/\*\*([^*]+)\*\*|__([^_]+)__/g,light:"#d73a49",dark:"#ff7b72"},{token:"italic",pattern:/\*([^*]+)\*|_([^_]+)_/g,light:"#032f62",dark:"#a5d6ff"},{token:"link",pattern:/\[([^\]]+)\]\(([^)]+)\)/g,light:"#0288d1",dark:"#80c0ff"},{token:"inline-code",pattern:/`([^`]+)`/g,light:"#032f62",dark:"#98c379"},{token:"code-block",pattern:/```([^\n]+)\n([\s\S]*?)```/g,light:"#24292e",dark:"#c9d1d9"},{token:"list-item",pattern:/(^|\n)([-*])\s+(.+)/g,light:"#5c6e7c",dark:"#8b949e"},{token:"quote",pattern:/(^|\n)>[ \t]*(.+)/g,light:"#6f42c1",dark:"#d2a8ff"},{token:"image",pattern:/!\[([^\]]+)\]\(([^)]+)\)/g,light:"#d73a49",dark:"#ff7b72"},{token:"hr",pattern:/^(---|___|\*\*\*)\s*$/gm,light:"#24292e",dark:"#c9d1d9"},{token:"strikethrough",pattern:/~~([^~]+)~~/g,light:"#e36209",dark:"#ffa657"},{token:"table",pattern:/\|([^\|]+)\|([^\|]+)\|/g,light:"#5c6e7c",dark:"#8b949e"}],typescript=[{token:"comment",pattern:/\/\/[^\n]*|\/\*[\s\S]*?\*\//,light:"#6a737d",dark:"#8b949e"},{token:"string",pattern:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/,light:"#032f62",dark:"#98c379"},{token:"decorator",pattern:/@[a-zA-Z_]\w*/,light:"#953800",dark:"#ffa657"},{token:"keyword",pattern:/\b(abstract|as|async|await|break|case|catch|class|const|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|package|private|protected|public|readonly|return|set|static|super|switch|this|throw|try|type|typeof|var|while|with|yield)\b/,light:"#d73a49",dark:"#ff7b72"},{token:"builtin",pattern:/\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/,light:"#e36209",dark:"#ffa657"},{token:"type",pattern:/\b[A-Z]\w*\b/,light:"#005cc5",dark:"#79c0ff"},{token:"number",pattern:/\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/,light:"#005cc5",dark:"#79c0ff"},{token:"func",pattern:/\b[a-zA-Z_]\w*(?=\s*\()/,light:"#6f42c1",dark:"#d2a8ff"},{token:"op",pattern:/(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/,light:"#089ba6",dark:"#79c0ff"}],COMMA=",",SPACE=" ",trim=(e,t="")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,n=[];return Array.isArray(e)?n=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",n=e.split(t)),n.map(e=>trim(e,"global")).filter(Boolean)},rtlStyle=(e="")=>`\n <style>\n :where([dir="rtl"]) .icax-${e},\n :where(:dir(rtl)) .icax-${e} {\n transform: scaleX(-1);\n transform-origin: center;\n }\n </style>\n`,wrap=(e,t,n=!1,r)=>{const i=r?.size||"1em",a=r?.color||"currentColor",o=r?.thickness||2,s=r?.classes?parseClasses(r.classes).join(" "):"",l=t.name.replace(/([A-Z])/g,"-$1").toLowerCase();return`<svg xmlns="http://www.w3.org/2000/svg" width="${i}" height="${i}" viewBox="0 0 24 24" fill="none" stroke="${a}"\n stroke-width="${o}" stroke-linecap="round" stroke-linejoin="round" class="${l} ${s}">\n ${n?rtlStyle(l.split("-")[1]):""}\n ${e}\n </svg>`},icaxCheck=e=>wrap('<polyline points="20 6 9 17 4 12"></polyline>',icaxCheck,!1,e),icaxCopy=e=>wrap('<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>',icaxCopy,!1,e),copy={name:"copy",icon:icaxCopy(),action:function(e){e.wrapEl.onclick=()=>{navigator.clipboard.writeText(this.source).then(()=>{e.iconEl.innerHTML=icaxCheck(),e.iconEl.toggleAttribute("disabled",!0),setTimeout(()=>{e.iconEl.removeAttribute("disabled"),e.iconEl.innerHTML=icaxCopy()},2e3)}).catch(e=>{})}}};exports.Coax=Coax,exports.copy=copy,exports.css=css,exports.html=html,exports.javascript=javascript,exports.markdown=markdown,exports.typescript=typescript;
|
package/dist/coax.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2026-1-
|
|
3
|
+
* @since Last modified: 2026-1-15 19:21:59
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.6
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description Coax is a lightweight web component for elegant code display with syntax highlighting, typewriter effects, and theme switching. Supports JavaScript, HTML, CSS, TypeScript, and Markdown with copy tools and customization.
|
|
8
8
|
* @see {@link https://coax.axui.cn|Official website}
|
|
@@ -266,6 +266,25 @@ const createTools = (data) => {
|
|
|
266
266
|
return toolsEl;
|
|
267
267
|
};
|
|
268
268
|
|
|
269
|
+
const trimEmptyLines = (str) => {
|
|
270
|
+
if (str == null)
|
|
271
|
+
return '';
|
|
272
|
+
return str.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const escapeHtmlChars = (text) => {
|
|
276
|
+
// Check if the input text is empty or undefined
|
|
277
|
+
if (!text)
|
|
278
|
+
return '';
|
|
279
|
+
// Replace the special characters with their corresponding HTML entities
|
|
280
|
+
return text
|
|
281
|
+
.replace(/&/g, '&') // Replace '&' with '&'
|
|
282
|
+
.replace(/</g, '<') // Replace '<' with '<'
|
|
283
|
+
.replace(/>/g, '>') // Replace '>' with '>'
|
|
284
|
+
.replace(/"/g, '"') // Replace '"' with '"'
|
|
285
|
+
.replace(/'/g, '''); // Replace "'" with '''
|
|
286
|
+
};
|
|
287
|
+
|
|
269
288
|
class Coax extends HTMLElement {
|
|
270
289
|
// A static Map to hold the configuration for different languages
|
|
271
290
|
static languages = new Map();
|
|
@@ -293,7 +312,7 @@ class Coax extends HTMLElement {
|
|
|
293
312
|
// Attach a Shadow DOM to the custom element
|
|
294
313
|
this.attachShadow({ mode: 'open' });
|
|
295
314
|
// Remove leading/trailing whitespace from the raw code content
|
|
296
|
-
this.source = this.textContent
|
|
315
|
+
this.source = escapeHtmlChars(trimEmptyLines(this.textContent));
|
|
297
316
|
// Initialize the basic structure of the component
|
|
298
317
|
this.shadowRoot.innerHTML = `
|
|
299
318
|
<style id="base-styles">
|
|
@@ -532,7 +551,7 @@ class Coax extends HTMLElement {
|
|
|
532
551
|
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
533
552
|
}
|
|
534
553
|
|
|
535
|
-
|
|
554
|
+
getLineToFillHighLight(codeWrap, line, config) {
|
|
536
555
|
config = config || Coax.languages.get(this.lang);
|
|
537
556
|
let highlightedLine = this.getHighLightString(line, config);
|
|
538
557
|
// 将高亮后的内容填充到 div 中
|
|
@@ -540,10 +559,10 @@ class Coax extends HTMLElement {
|
|
|
540
559
|
}
|
|
541
560
|
;
|
|
542
561
|
|
|
543
|
-
async highlight(newCode) {
|
|
562
|
+
async highlight(newCode = this.source) {
|
|
544
563
|
const config = Coax.languages.get(this.lang), startIndex = this.highlightEl.children.length,
|
|
545
564
|
// 将新源码按行分割
|
|
546
|
-
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
565
|
+
newCodeLines = newCode ? newCode.split('\n') : [], hasSpeedAttr = this.hasAttribute('speed'), hasSanitizedAttr = this.hasAttribute('sanitized');
|
|
547
566
|
//更新别名
|
|
548
567
|
this.updateName(config);
|
|
549
568
|
if (!newCodeLines.length)
|
|
@@ -551,13 +570,13 @@ class Coax extends HTMLElement {
|
|
|
551
570
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
552
571
|
for (let [index, lineString] of newCodeLines.entries()) {
|
|
553
572
|
//如果是空行则跳过
|
|
554
|
-
if (!lineString.trim() &&
|
|
573
|
+
if (!lineString.trim() && hasSanitizedAttr)
|
|
555
574
|
continue;
|
|
556
575
|
// 创建一个 div 包裹每一行
|
|
557
576
|
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
558
577
|
//标记完成
|
|
559
578
|
lineWrap.completed = true;
|
|
560
|
-
if (
|
|
579
|
+
if (hasSpeedAttr) {
|
|
561
580
|
this.highlightEl.appendChild(lineWrap);
|
|
562
581
|
//流式打字
|
|
563
582
|
await typeWriter(lineString, {
|
|
@@ -566,11 +585,11 @@ class Coax extends HTMLElement {
|
|
|
566
585
|
codeWrap.innerHTML = fullText;
|
|
567
586
|
}
|
|
568
587
|
});
|
|
569
|
-
this.
|
|
588
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
570
589
|
}
|
|
571
590
|
else {
|
|
572
591
|
//直接打出
|
|
573
|
-
this.
|
|
592
|
+
this.getLineToFillHighLight(codeWrap, lineString, config);
|
|
574
593
|
//
|
|
575
594
|
this.highlightEl.appendChild(lineWrap);
|
|
576
595
|
}
|
|
@@ -628,17 +647,30 @@ class Coax extends HTMLElement {
|
|
|
628
647
|
this.codeNameEl.innerHTML = this.alias;
|
|
629
648
|
}
|
|
630
649
|
}
|
|
650
|
+
trimLineString(str) {
|
|
651
|
+
// 删除开头的第一个换行符
|
|
652
|
+
if (str.startsWith('\n')) {
|
|
653
|
+
str = str.substring(1);
|
|
654
|
+
}
|
|
655
|
+
// 删除结尾的第一个换行符
|
|
656
|
+
if (str.endsWith('\n')) {
|
|
657
|
+
str = str.slice(0, -1);
|
|
658
|
+
}
|
|
659
|
+
return str;
|
|
660
|
+
}
|
|
631
661
|
|
|
632
|
-
render(
|
|
662
|
+
render() {
|
|
633
663
|
//同时多次改变属性,只执行一次
|
|
634
664
|
if (this._renderQueued)
|
|
635
665
|
return;
|
|
636
666
|
this._renderQueued = true;
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
667
|
+
requestAnimationFrame(async () => {
|
|
668
|
+
this.highlightEl.innerHTML = '';
|
|
669
|
+
this.injectThemeStyles();
|
|
670
|
+
//一次性渲染
|
|
671
|
+
await this.highlight();
|
|
672
|
+
this._renderQueued = false;
|
|
673
|
+
});
|
|
642
674
|
}
|
|
643
675
|
|
|
644
676
|
clear() {
|
|
@@ -646,11 +678,17 @@ class Coax extends HTMLElement {
|
|
|
646
678
|
}
|
|
647
679
|
|
|
648
680
|
async replace(newCode) {
|
|
649
|
-
|
|
650
|
-
|
|
681
|
+
newCode = escapeHtmlChars(newCode);
|
|
682
|
+
this.source = trimEmptyLines(newCode);
|
|
683
|
+
if (this._renderQueued)
|
|
684
|
+
return;
|
|
685
|
+
this.highlightEl.innerHTML = '';
|
|
686
|
+
//一次性渲染
|
|
687
|
+
await this.highlight();
|
|
651
688
|
}
|
|
652
689
|
|
|
653
690
|
async append(newCode) {
|
|
691
|
+
newCode = escapeHtmlChars(newCode);
|
|
654
692
|
// 将新的代码追加到现有代码末尾
|
|
655
693
|
this.source += `\n${newCode}`;
|
|
656
694
|
// 高亮新的部分
|
|
@@ -673,7 +711,7 @@ class Coax extends HTMLElement {
|
|
|
673
711
|
lineWrap.completed = true;
|
|
674
712
|
//行结束前保存
|
|
675
713
|
this.lastLineString = this.lineString;
|
|
676
|
-
this.
|
|
714
|
+
this.getLineToFillHighLight(lineWrap?.lastElementChild, this.lineString);
|
|
677
715
|
//一行结束,清空临时行文本
|
|
678
716
|
this.lineString = '';
|
|
679
717
|
}
|
|
@@ -688,20 +726,21 @@ class Coax extends HTMLElement {
|
|
|
688
726
|
}
|
|
689
727
|
|
|
690
728
|
stream(str, forceClose = false) {
|
|
691
|
-
|
|
729
|
+
str = escapeHtmlChars(str);
|
|
730
|
+
const { lineWrap, codeWrap } = this.getLastLine(), isLine = str.startsWith('\n') || str.endsWith('\n');
|
|
692
731
|
this.highlightEl.appendChild(lineWrap);
|
|
693
732
|
//汇集
|
|
694
733
|
this.source += str;
|
|
734
|
+
//临时保存行文本
|
|
735
|
+
this.lineString += isLine ? this.trimLineString(str) : str;
|
|
695
736
|
//如果没有遇到换行符号,也可以强制结束
|
|
696
|
-
if (forceClose ||
|
|
737
|
+
if (forceClose || isLine) {
|
|
697
738
|
//标记完成
|
|
698
739
|
this.close();
|
|
699
740
|
}
|
|
700
741
|
else {
|
|
701
742
|
//插入文本
|
|
702
743
|
codeWrap.innerHTML += str;
|
|
703
|
-
//临时保存行文本
|
|
704
|
-
this.lineString += str;
|
|
705
744
|
}
|
|
706
745
|
//滚动到最底部
|
|
707
746
|
this.autoScrollCode();
|