@codady/coax 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/coax.cjs.js +829 -140
  2. package/dist/coax.cjs.min.js +3 -3
  3. package/dist/coax.esm.js +823 -140
  4. package/dist/coax.esm.min.js +3 -3
  5. package/dist/coax.umd.js +849 -138
  6. package/dist/coax.umd.min.js +3 -3
  7. package/examples/.htaccess +0 -0
  8. package/examples/append-highlight.html +82 -0
  9. package/examples/color-selector.html +412 -0
  10. package/examples/css-highlight.html +2 -18
  11. package/examples/deepseek-highlight.html +91 -0
  12. package/examples/js-highlight.html +2 -17
  13. package/examples/md-highlight.html +60 -0
  14. package/examples/nginx.htaccess +0 -0
  15. package/examples/plain-highlight.html +47 -0
  16. package/examples/replace-highlight.html +69 -0
  17. package/examples/stream-highlight.html +64 -0
  18. package/examples/theme-highlight.html +69 -0
  19. package/package.json +19 -3
  20. package/rollup.config.js +3 -3
  21. package/src/Coax.js +50 -184
  22. package/src/Coax.ts +56 -207
  23. package/src/components/CoaxElem.js +457 -0
  24. package/src/components/CoaxElem.ts +488 -0
  25. package/src/modules.js +12 -0
  26. package/src/modules.ts +23 -0
  27. package/src/rules/css.js +11 -0
  28. package/src/rules/css.ts +11 -0
  29. package/src/rules/html.js +13 -0
  30. package/src/rules/html.ts +13 -0
  31. package/src/rules/javascript.js +10 -0
  32. package/src/rules/javascript.ts +10 -0
  33. package/src/rules/markdown.js +29 -0
  34. package/src/rules/markdown.ts +41 -0
  35. package/src/rules/ruleCss - /345/211/257/346/234/254.js" +10 -0
  36. package/src/rules/ruleHTML - /345/211/257/346/234/254.js" +12 -0
  37. package/src/rules/ruleJs - /345/211/257/346/234/254.js" +10 -0
  38. package/src/rules/ruleTs - /345/211/257/346/234/254.js" +12 -0
  39. package/src/rules/typescript.js +12 -0
  40. package/src/rules/typescript.ts +12 -0
  41. package/src/tools/copy.js +26 -0
  42. package/src/tools/copy.ts +29 -0
  43. package/tsconfig.json +2 -2
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2026-1-4 16:0:48
2
+ * @since Last modified: 2026-1-12 9:47:5
3
3
  * @name Coax event management system.
4
- * @version 0.0.1
4
+ * @version 0.0.3
5
5
  * @author AXUI development team <3217728223@qq.com>
6
6
  * @description Coax is a custom web component that enables syntax highlighting for various programming languages inside your HTML documents.
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
- !function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";class Coax extends HTMLElement{static languages=new Map;rawCode;constructor(){super(),this.attachShadow({mode:"open"}),this.rawCode=this.textContent?.replace(/^\s*\n|\n\s*$/g,"")||""}static register(e,{rules:n,theme:t={},internalCss:a="",cssPrefix:r=""}){this.languages.set(e,{rules:n,theme:t,internalCss:a,cssPrefix:r}),document.querySelectorAll("ax-code").forEach(n=>{n.getAttribute("lang")===e&&n.render()})}connectedCallback(){this.render()}static get observedAttributes(){return["lang","height","max-height"]}attributeChangedCallback(){this.render()}render(){const e=this.getAttribute("lang")||"js",n=Coax.languages.get(e),t=n?.cssPrefix||e,a=this.getAttribute("height"),r=this.getAttribute("max-height");let s="",o="";if(n){const e=new RegExp(n.rules.map(e=>`(${e.reg.source})`).join("|"),"g");s=this.rawCode.replace(/[&<>]/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;"}[e])).replace(e,(e,...a)=>{const r=a.findIndex(e=>void 0!==e);return-1!==r&&n.rules[r]?`<span class="ax-${t}-${n.rules[r].name}">${e}</span>`:e}),o=n.rules.map(e=>`\n .ax-${t}-${e.name} { color: var(--ax-${t}-${e.name}); }\n `).join("\n")}else s=this.rawCode;const i=n?.theme?Object.entries(n.theme).map(([e,n])=>`--ax-${t}-${e}: ${n};`).join("\n"):"";this.shadowRoot.innerHTML=`\n <style>\n :host { \n ${i} \n\n font-size: var(--ax-code-fs,14px);\n display: block; \n padding: var(--ax-code-p,1em);\n box-sizing:border-box;\n line-height: var(--ax-code-lh,1.5);\n background-color:var(--ax-code-bg,rgba(0,0,0,0.02));\n color:var(--ax-code-c,#333);\n border:var(--ax-code-bw,1px) solid var(--ax-code-bc,rgba(0,0,0,0.08));\n height:${a||"var(--ax-code-h,auto)"};\n max-height:${r||"var(--ax-code-mh,500px)"};\n overflow:auto;\n transition: border-color 0.3s ease,color 0.3s ease; \n border-radius: var(--ax-code-r,9px);\n }\n pre,code{\n font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace", "microsoft yahei", "Microsoft JhengHei", "Yu Mincho", "simsun";\n margin:0;\n padding:0;\n }\n \n \n \n ${o}\n \n \n ${n?.internalCss||""}\n </style>\n <pre><code>${s}</code></pre>`}}customElements.define("ax-code",Coax),Coax.register("html",{rules:[{name:"comment",reg:/&lt;!--[\s\S]*?--&gt;/},{name:"doctype",reg:/&lt;!DOCTYPE[\s\S]*?&gt;/i},{name:"tag",reg:/&lt;\/?[a-zA-Z0-9]+/},{name:"attr",reg:/[a-zA-Z-]+(?=\s*=\s*)/},{name:"string",reg:/(['"])(?:\\.|[^\\])*?\1/},{name:"bracket",reg:/\/?&gt;/}]}),Coax.register("css",{rules:[{name:"comment",reg:/\/\*[\s\S]*?\*\//},{name:"value",reg:/(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/},{name:"func",reg:/[a-z-]+\(?=/},{name:"property",reg:/[a-z-]+(?=\s*:)/},{name:"selector",reg:/[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i},{name:"unit",reg:/(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/},{name:"number",reg:/\b\d+(\.\d+)?\b/},{name:"punct",reg:/[{}();:]/}],theme:{type:"#61afef",keyword:"#d19a66",string:"#98c379",op:"#abb2bf"},internalCss:"\n .ax-css-string { color: var(--ax-code-string); }\n .ax-css-string::first-letter { color: var(--ax-code-c); }\n "}),Coax.register("js",{rules:[{name:"comment",reg:/\/\/[^\n]*|\/\*[\s\S]*?\*\//},{name:"string",reg:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/},{name:"keyword",reg:/\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/},{name:"builtin",reg:/\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise)\b/},{name:"number",reg:/\b\d+\b/},{name:"func",reg:/\b[a-zA-Z_]\w*(?=\s*\()/},{name:"op",reg:/[+\-*/%=<>!&|^~]+/}]}),Coax.register("ts",{rules:[{name:"comment",reg:/\/\/[^\n]*|\/\*[\s\S]*?\*\//},{name:"string",reg:/(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/},{name:"decorator",reg:/@[a-zA-Z_]\w*/},{name:"keyword",reg:/\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/},{name:"builtin",reg:/\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|boolean|true|false|console|window|document)\b/},{name:"type",reg:/\b[A-Z]\w*\b/},{name:"number",reg:/\b\d+\b/},{name:"func",reg:/\b[a-zA-Z_]\w*(?=\s*\()/},{name:"op",reg:/(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/}]})});
15
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).coax=t()}(this,function(){"use strict";const e="ax",typeWriter=(e,t)=>{const n=t.speed||100;return new Promise(i=>{t?.onBeforeType?.(e);let r=0;const o=setInterval(()=>{if(r<e.length){const n=e.charAt(r),i=e.substring(0,r+1);t?.onDuringType?.(n,i),r++}else clearInterval(o),i(e),t?.onAfterType?.(e)},n)})},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)},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),i=getDataType(t),r=i.includes("HTML")||"ShadowRoot"===i?t:document.querySelector(t),o=r&&r instanceof HTMLTemplateElement?r.content:r,a=null;if(e)if(n.includes("HTML"))a=e;else if("String"===n)try{a=(o||document).querySelector(e.trim())}catch{a=null}return a},createEl=(e,t,n)=>{let i=(e=e||"div").toUpperCase().trim(),r=document.createElement(i),o=getDataType(t);if(t&&"Object"===o)for(let e in t)t.hasOwnProperty(e)&&r.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"===i)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)})(r,n),r},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},t="rep",createTools=n=>{const i=createEl("span",{class:`${e}-box-tools`}),renderFn=e=>{const n={},i=e.extendable?`<i ${t}="arrow"></i>`:"",r=(e.icon?`<i ${t}="icon">${e.icon}</i>`:"")+(e.disk?`<i ${t}="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i ${t}="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i ${t}="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i ${t}="label">${e.label}</i>`:"")+i;e.title&&(n.title=e.title),e.focusable&&(n.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(n,e.attrs),r),e.iconEl=e.wrapEl.querySelector(`[${t}="icon"]`),e.cubeEl=e.wrapEl.querySelector(`[${t}="cube"]`),e.diskEl=e.wrapEl.querySelector(`[${t}="disk"]`),e.imageEl=e.wrapEl.querySelector(`[${t}="image"]`),e.labelEl=e.wrapEl.querySelector(`[${t}="label"]`),!isEmpty(e.classes)&&((e,t)=>{const n=getEl(e),i=parseClasses$1(t);n&&0!==i.length&&i.forEach(e=>{n.classList.add(e)})})(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let e of n)renderFn(e),i.appendChild(e.wrapEl),e?.action?.(e);return i};class CoaxElem extends HTMLElement{static languages=new Map;static tools=[];source;_renderQueued=!1;baseStylesEl;themeStylesEl;dynamicStylesEl;highlightedCodeEl;headerEl;codeNameEl;codeToolsEl;codeBodyEl;alias;lineString;speed;autoScroll;constructor(){super(),this.attachShadow({mode:"open"}),this.source=this.textContent?.replace(/^\s*\n|\n\s*$/g,"")||"",this.lang="plain",this.alias="Plain Text",this.lineString="",this.speed=5,this.autoScroll=!0,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(--${e}-code-font-size,var(--font-size));\n display: block; \n box-sizing:border-box;\n background:var(--${e}-code-background-color,var(--background));\n color:var(--${e}-code-color,var(--color-code));\n border:var(--${e}-code-border-width,var(--border-width)) var(--${e}-code-border-style,var(--border-style)) var(--${e}-code-border-color,var(--border-color));\n transition: border-color 0.3s ease,color 0.3s ease; \n border-radius: var(--${e}-code-radius,var(--radius));\n }\n #code-header{\n line-height:calc(var(--${e}-code-line-height,var(--line-height))*1.5);\n padding-inline-start:var(--${e}-code-padding,var(--padding));\n display:flex;\n \n >:first-child{\n flex:auto;\n }\n }\n #code-body{\n padding: var(--${e}-code-padding,var(--padding)) 0;\n height:var(--${e}-code-height,var(--height));\n max-height:var(--${e}-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(--${e}-code-padding,var(--padding));\n line-height: var(--${e}-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(--${e}-code-index-width,2em);\n margin-inline-end:var(--${e}-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 .${e}-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"></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.highlightedCodeEl=getEl("#highlight-code",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){CoaxElem.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&&("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=CoaxElem.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(t,n){if(!(n=n||CoaxElem.languages.get(this.lang)))return t;const i=this.getCssPrefix(n),r=new RegExp(n.rules.map(e=>`(${e.pattern.source})`).join("|"),"g");return t.replace(r,(t,...r)=>{const o=r.findIndex(e=>void 0!==e);return-1!==o&&n.rules[o]?`<span class="${e}-${i}-${n.rules[o].token}">${t}</span>`:t})}createLineWrap(e,t){let n=0;if(null==e&&null==t)n=this.highlightedCodeEl.children.length;else{n=(t||this.highlightedCodeEl.children.length)+e}return createEl("div",{"data-index":n},"<div></div>")}getLineToFill(e,t,n){n=n||CoaxElem.languages.get(this.lang);let i=this.getHighLightString(t,n);e.innerHTML=i}async highlight(e){const t=CoaxElem.languages.get(this.lang),n=this.highlightedCodeEl.children.length,i=e?e.split("\n"):[];if(this.updateName(t),!i.length)return!0;for(let[e,r]of i.entries()){if(!r.trim()&&this.hasAttribute("sanitized"))continue;const i=this.createLineWrap(e,n),o=i.lastElementChild;this.closeLine(i),this.hasAttribute("speed")?(this.highlightedCodeEl.appendChild(i),await typeWriter(r,{speed:this.speed,onDuringType:(e,t)=>{o.innerHTML=t}}),this.getLineToFill(o,r,t)):(this.getLineToFill(o,r,t),this.highlightedCodeEl.appendChild(i))}return this.autoScrollCode(),!0}autoScrollCode(){this.autoScroll&&(this.codeBodyEl.scrollTop=this.codeBodyEl.scrollHeight)}injectThemeStyles(){const t=CoaxElem.languages.get(this.lang);if(!t)return;let n=this.getCssPrefix(t),i=t.rules.map(t=>`\n .${e}-${n}-${t.token} { color: var(--${e}-${n}-${t.token}${t.light?","+t.light:""});}`).join("\n"),r="",o="";r+=':host([scheme="dark"]){',r+=t.rules.map(t=>"\n "+(t.light?`\n .${e}-${n}-${t.token} {color: var(--${e}-${n}-${t.token},${t.dark});}`:"")).join("\n"),r+="}",o="@media (prefers-color-scheme: dark){\n :host{\n ",o+=t.rules.map(t=>`\n ${t.light?`\n .${e}-${n}-${t.token} { color: var(--${e}-${n}-${t.token},${t.dark}); }`:""} `).join("\n"),o+="}",this.dynamicStylesEl.textContent=i+r+o,t?.themeStyles&&(this.themeStylesEl.textContent=t.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,requestAnimationFrame(async()=>{this.clear(),this.injectThemeStyles(),await this.highlight(e),this._renderQueued=!1}))}clear(){this.highlightedCodeEl.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)}getActiveCodeWrap(){const e=this.highlightedCodeEl.lastElementChild,t=!e||this.highlightedCodeEl.lastElementChild?.completed?this.createLineWrap():e;return{lineWrap:t,codeWrap:t.lastElementChild}}closeLine(e){(e=e||this.highlightedCodeEl.lastElementChild)&&(e.completed=!0)}openLine(e){(e=e||this.highlightedCodeEl.lastElementChild)&&(e.completed=!1)}stream(e,t=!1){const{lineWrap:n,codeWrap:i}=this.getActiveCodeWrap();this.highlightedCodeEl.appendChild(n),this.source+=e,t&&this.closeLine(n),e.startsWith("\n")||e.startsWith("\r")?(this.closeLine(n),this.getLineToFill(i,this.lineString),this.lineString=""):(i.innerHTML+=e,this.lineString+=e),this.autoScrollCode()}}const 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," ")}},wrap=(e,t,n=!1,i)=>{const r=i?.size||"1em",o=i?.color||"currentColor",a=i?.thickness||2,l=i?.classes?(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)})(i.classes).join(" "):"",s=t.name.replace(/([A-Z])/g,"-$1").toLowerCase();return`<svg xmlns="http://www.w3.org/2000/svg" width="${r}" height="${r}" viewBox="0 0 24 24" fill="none" stroke="${o}"\n stroke-width="${a}" stroke-linecap="round" stroke-linejoin="round" class="${s} ${l}">\n ${n?((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`)(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),n={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=>{})}}},i=CoaxElem;return i.register("css",{alias:"CSS",rules:[{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"}]}),i.register("html",{alias:"HTML",rules:[{token:"comment",pattern:/&lt;!--[\s\S]*?--&gt;/,light:"#999999",dark:"#6e7681"},{token:"doctype",pattern:/&lt;!DOCTYPE[\s\S]*?&gt;/i,light:"#6a737d",dark:"#8b949e"},{token:"tag",pattern:/&lt;\/?[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:/\/?&gt;/,light:"#24292e",dark:"#c9d1d9"}]}),i.register("js",{alias:"Javascript",rules:[{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"}]}),i.register("ts",{alias:"Typescript",rules:[{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"}]}),i.register("md",{alias:"Markdown",rules:[{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"}]}),i.addTools([n]),customElements.define(`${e}-code`,i),i});
File without changes
@@ -0,0 +1,82 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Coax 代码高亮插件</title>
8
+ <style>
9
+ /* 白天模式变量 */
10
+ :root {}
11
+ </style>
12
+
13
+ </head>
14
+
15
+ <body>
16
+ <ax-code streaming tools="copy" indexed lang="js">
17
+ <code>
18
+ /**
19
+ * Simple animation function
20
+ */
21
+ async function animate(element, duration) {
22
+ const start = Date.now();
23
+
24
+ if (!element) return false;
25
+
26
+ console.log("Animation started...");
27
+
28
+ while (Date.now() - start < duration) {
29
+ let progress = (Date.now() - start) / duration;
30
+ element.style.opacity = Math.min(progress, 1);
31
+ await Promise.resolve();
32
+ }
33
+
34
+ return true;
35
+ }
36
+ </code>
37
+ </ax-code>
38
+
39
+ <button id="append01">尾部增加片段1</button>
40
+ <button id="append02">尾部增加片段2</button>
41
+ <button id="append03">尾部增加片段3</button>
42
+
43
+
44
+
45
+ <script src="../dist/coax.umd.js" type='text/javascript'></script>
46
+ <script type='text/javascript'>
47
+
48
+ let append01 = document.querySelector('#append01');
49
+ let append02 = document.querySelector('#append02');
50
+ let append03 = document.querySelector('#append03');
51
+ let code = document.querySelector('ax-code');
52
+ append01.onclick = () => {
53
+ code.append(`
54
+ function calculateCircleArea(radius) {
55
+ if (typeof radius !== 'number' || radius <= 0) {
56
+ throw new Error('Radius must be a positive number');
57
+ }
58
+ return Math.PI * radius * radius;
59
+ }`);
60
+ }
61
+ append02.onclick = () => {
62
+ code.append(`
63
+ function calculateCircleCircumference(radius) {
64
+ if (typeof radius !== 'number' || radius <= 0) {
65
+ throw new Error('Radius must be a positive number');
66
+ }
67
+ return 2 * Math.PI * radius;
68
+ }`);
69
+ }
70
+
71
+ append03.onclick = () => {
72
+ code.append(`
73
+ while (Date.now() - start < duration) {
74
+ let progress = (Date.now() - start) / duration;
75
+ element.style.opacity = Math.min(progress, 1);
76
+ await Promise.resolve();
77
+ }`);
78
+ }
79
+ </script>
80
+ </body>
81
+
82
+ </html>
@@ -0,0 +1,412 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Coax 语法高亮配色增强实验室</title>
7
+ <style>
8
+ :root {
9
+ --sidebar-width: 380px;
10
+ --primary-color: #007aff;
11
+ }
12
+
13
+ body {
14
+ display: flex;
15
+ height: 100vh;
16
+ margin: 0;
17
+ background: #f5f5f7;
18
+ color: #1d1d1f;
19
+ }
20
+
21
+ /* 布局 */
22
+ #sidebar {
23
+ width: var(--sidebar-width);
24
+ background: white;
25
+ border-right: 1px solid #d2d2d7;
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ #preview-area {
31
+ flex: 1;
32
+ padding: 40px;
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: 20px;
36
+ overflow-y: auto;
37
+ transition: background 0.3s;
38
+ }
39
+
40
+ #preview-area.dark {
41
+ background: #121212;
42
+ }
43
+
44
+ /* 侧边栏控件 */
45
+ .sidebar-header {
46
+ padding: 20px;
47
+ border-bottom: 1px solid #eee;
48
+ }
49
+
50
+ .lang-tabs {
51
+ display: flex;
52
+ background: #eee;
53
+ padding: 4px;
54
+ border-radius: 8px;
55
+ margin: 10px 20px;
56
+ }
57
+
58
+ .tab-btn {
59
+ flex: 1;
60
+ border: none;
61
+ padding: 8px;
62
+ border-radius: 6px;
63
+ cursor: pointer;
64
+ background: transparent;
65
+ font-size: 13px;
66
+ }
67
+
68
+ .tab-btn.active {
69
+ background: white;
70
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
71
+ font-weight: bold;
72
+ }
73
+
74
+ #controls-list {
75
+ flex: 1;
76
+ overflow-y: auto;
77
+ padding: 0 20px 20px;
78
+ }
79
+
80
+ .control-item {
81
+ margin-top: 15px;
82
+ padding-bottom: 15px;
83
+ border-bottom: 1px solid #f5f5f5;
84
+ }
85
+
86
+ .control-item label {
87
+ display: block;
88
+ font-size: 12px;
89
+ font-weight: 600;
90
+ color: #86868b;
91
+ margin-bottom: 8px;
92
+ text-transform: uppercase;
93
+ }
94
+
95
+ .color-row {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 12px;
99
+ margin-bottom: 8px;
100
+ }
101
+
102
+ .color-row span {
103
+ font-size: 11px;
104
+ width: 40px;
105
+ color: #86868b;
106
+ }
107
+
108
+ .picker-wrapper {
109
+ display: flex;
110
+ align-items: center;
111
+ background: #f5f5f7;
112
+ padding: 4px 8px;
113
+ border-radius: 6px;
114
+ flex: 1;
115
+ cursor: pointer;
116
+ border: 1px solid transparent;
117
+ }
118
+
119
+ .picker-wrapper:hover {
120
+ border-color: #d2d2d7;
121
+ }
122
+
123
+ input[type="color"] {
124
+ border: none;
125
+ width: 24px;
126
+ height: 24px;
127
+ background: none;
128
+ cursor: pointer;
129
+ padding: 0;
130
+ }
131
+
132
+ .hex-code {
133
+ font-family: monospace;
134
+ font-size: 13px;
135
+ margin-left: 8px;
136
+ flex: 1;
137
+ color: #1d1d1f;
138
+ }
139
+
140
+ .copy-btn {
141
+ border: none;
142
+ background: #eee;
143
+ font-size: 10px;
144
+ padding: 4px 8px;
145
+ border-radius: 4px;
146
+ cursor: pointer;
147
+ }
148
+
149
+ .copy-btn:hover {
150
+ background: #e2e2e7;
151
+ }
152
+
153
+ /* 预览代码盒 */
154
+ .code-container {
155
+ background: white;
156
+ border-radius: 12px;
157
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
158
+ padding: 24px;
159
+ position: relative;
160
+ transition: 0.3s;
161
+ border: 1px solid #d2d2d7;
162
+ }
163
+
164
+ .dark .code-container {
165
+ background: #1e1e1e;
166
+ border-color: #333;
167
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
168
+ }
169
+
170
+ .mode-toggle {
171
+ position: fixed;
172
+ bottom: 20px;
173
+ right: 20px;
174
+ padding: 12px 24px;
175
+ border-radius: 30px;
176
+ border: none;
177
+ background: var(--primary-color);
178
+ color: white;
179
+ cursor: pointer;
180
+ font-weight: 600;
181
+ box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
182
+ }
183
+
184
+ /* 高亮 Token 占位符(通过动态 CSS 变量渲染) */
185
+ </style>
186
+ </head>
187
+
188
+ <body>
189
+
190
+ <div id="sidebar">
191
+ <div class="sidebar-header">
192
+ <h2 style="margin:0; font-size: 18px;">Coax 配色实验室</h2>
193
+ <p style="font-size: 12px; color: #86868b;">配置各语言 Token,点击 HEX 即可复制</p>
194
+ </div>
195
+
196
+ <div class="lang-tabs" id="lang-tabs">
197
+ <button class="tab-btn active" onclick="switchLang('html')">HTML</button>
198
+ <button class="tab-btn" onclick="switchLang('js')">JS</button>
199
+ <button class="tab-btn" onclick="switchLang('ts')">TS</button>
200
+ <button class="tab-btn" onclick="switchLang('css')">CSS</button>
201
+ </div>
202
+
203
+ <div id="controls-list"></div>
204
+ </div>
205
+
206
+ <div id="preview-area" id="preview-area">
207
+ <div class="code-container">
208
+ <ax-code></ax-code>
209
+ </div>
210
+ <button id="copy">复制主题样式</button>
211
+ <button id="reset">恢复原始样式</button>
212
+ </div>
213
+
214
+ <button class="mode-toggle" onclick="toggleDarkMode()">切换深色模式 (Current: Light)</button>
215
+
216
+ <script src="../dist/coax.umd.js" type='text/javascript'></script>
217
+ <script>
218
+ // 1. 核心数据结构:各语言的配置
219
+ const config = {
220
+ html: {
221
+ tokens: ['doctype', 'tag', 'attr', 'string', 'comment', 'bracket'],
222
+ // 包含 DOCTYPE, 注释, 标签, 属性, 字符串, 闭合括号
223
+ code: `<!--详情页面-->\n<!DOCTYPE html>\n<div class="main-wrapper" data-active="true">\n <h1 id="title">Coax Editor</h1>\n <img src="./assets/logo.svg" alt="Coax Logo" />\n <br />\n</div>`,
224
+ colors: {
225
+ doctype: { light: '#6a737d', dark: '#8b949e' }, // 灰度处理,弱化非结构信息
226
+ tag: { light: '#22863a', dark: '#7ee787' }, // 绿色(标签)
227
+ attr: { light: '#6f42c1', dark: '#d2a8ff' }, // 紫色(属性)
228
+ string: { light: '#032f62', dark: '#a5d6ff' }, // 蓝色(值)
229
+ comment: { light: '#999999', dark: '#6e7681' }, // 弱灰(注释)
230
+ bracket: { light: '#24292e', dark: '#c9d1d9' } // 默认色(括号)
231
+ }
232
+ },
233
+ js: {
234
+ tokens: ['keyword', 'func', 'string', 'builtin', 'number', 'comment', 'op'],
235
+ // 包含 关键字, 函数调用, 字符串, 内置对象, 数字, 注释, 运算符
236
+ code: `// 这是一个 JS 逻辑演示\nasync function fetchData(id) {\n const API = "https://api.coax.com/v1";\n let count = 2026 + id;\n if (count > 0) {\n console.log(\`Fetching \${id}...\`);\n return Math.random() * count;\n }\n return null;\n}`,
237
+ colors: {
238
+ keyword: { light: '#d73a49', dark: '#ff7b72' }, // 鲜红/浅粉(逻辑控制)
239
+ func: { light: '#6f42c1', dark: '#d2a8ff' }, // 紫色(定义与调用)
240
+ string: { light: '#032f62', dark: '#98c379' }, // 绿色(暗色下绿色字符串更护眼)
241
+ builtin: { light: '#e36209', dark: '#ffa657' }, // 橙色(内置变量)
242
+ number: { light: '#005cc5', dark: '#79c0ff' }, // 蓝色(数值)
243
+ comment: { light: '#6a737d', dark: '#8b949e' }, // 灰色(注释)
244
+ op: { light: '#069598', dark: '#56b6c2'} // 青色(将运算符与关键字区分开)
245
+ }
246
+ },
247
+ ts: {
248
+ tokens: ['type', 'decorator', 'keyword', 'func', 'string', 'builtin', 'number', 'comment', 'op'],
249
+ // 包含 类型声明, 装饰器, 关键字, 函数, 字符串, 内置, 数字, 注释, 运算符
250
+ code: `/** TypeScript 装饰器与类型演示 **/\n@Module({ providers: [] })\nexport class User<T> extends Base {\n readonly version: number = 1.0;\n private name: string = "Gemini";\n\n public async save(data: T): Promise<boolean> {\n const status = await this.exec();\n return !!status;\n }\n}`,
251
+ colors: {
252
+ keyword: { light: '#d73a49', dark: '#ff7b72' },
253
+ func: { light: '#6f42c1', dark: '#d2a8ff' },
254
+ type: { light: '#005cc5', dark: '#79c0ff' }, // 蓝色(TS 核心:类型)
255
+ builtin: { light: '#e36209', dark: '#ffa657' },
256
+ decorator: { light: '#953800', dark: '#ffa657' },// 棕/橙(装饰器)
257
+ number: { light: '#005cc5', dark: '#79c0ff' },
258
+ string: { light: '#032f62', dark: '#98c379' },
259
+ comment: { light: '#6a737d', dark: '#8b949e' },
260
+ op: { light: '#089ba6', dark: '#79c0ff'}
261
+ }
262
+ },
263
+ css: {
264
+ tokens: ['selector', 'property', 'value', 'unit', 'comment', 'number', 'func', 'punct'],
265
+ // 包含 选择器, 属性, 值, 单位, 注释, 数字, 函数, 标点
266
+ code: `/* 响应式样式 */\n@media (max-width: 768px) {\n .container > #main:hover {\n background: rgba(0, 0, 0, 0.05);\n margin: 10px 2rem;\n width: calc(100% - 20px);\n font-family: "Consolas", sans-serif;\n }\n}`,
267
+ colors: {
268
+ selector: { light: '#22863a', dark: '#7ee787' }, // 绿色(CSS 选择器)
269
+ property: { light: '#005cc5', dark: '#79c0ff' }, // 蓝色(属性名)
270
+ value: { light: '#032f62', dark: '#a5d6ff' }, // 浅蓝/深蓝(属性值)
271
+ unit: { light: '#d73a49', dark: '#ff7b72' }, // 红色(单位 px, rem)
272
+ number: { light: '#d73a49', dark: '#ff7b72' }, // 红色(纯数字)
273
+ func: { light: '#6f42c1', dark: '#d2a8ff' }, // 紫色(calc, rgba)
274
+ comment: { light: '#6a737d', dark: '#8b949e' },
275
+ punct: { light: '#24292e', dark: '#c9d1d9' } // 默认/白(分号、括号)
276
+ }
277
+ }
278
+ };
279
+
280
+ let currentLang = 'html';
281
+ let isDark = false;
282
+ let codeEl = document.querySelector('ax-code');
283
+ let copyEl = document.querySelector('#copy');
284
+ let resetEl = document.querySelector('#reset');
285
+ let currentStyle = '';
286
+
287
+ // 2. 初始化渲染
288
+ function init() {
289
+ renderControls();
290
+ renderCode();
291
+ updateDynamicStyles();
292
+ }
293
+
294
+ // 切换语言标签
295
+ function switchLang(lang) {
296
+ currentLang = lang;
297
+ document.querySelectorAll('.tab-btn').forEach(btn => {
298
+ btn.classList.toggle('active', btn.innerText.toLowerCase() === lang);
299
+ });
300
+ renderControls();
301
+ renderCode();
302
+ updateDynamicStyles();
303
+ }
304
+
305
+ // 渲染左侧色值控件
306
+ function renderControls() {
307
+ const list = document.getElementById('controls-list');
308
+ list.innerHTML = '';
309
+ const langConfig = config[currentLang];
310
+
311
+ langConfig.tokens.forEach(token => {
312
+ const item = document.createElement('div');
313
+ item.className = 'control-item';
314
+ const colors = langConfig.colors[token];
315
+
316
+ item.innerHTML = `
317
+ <label>${token}</label>
318
+ ${renderColorRow(token, 'light', colors.light)}
319
+ ${renderColorRow(token, 'dark', colors.dark)}
320
+ `;
321
+ list.appendChild(item);
322
+ });
323
+ }
324
+
325
+ function renderColorRow(token, mode, value) {
326
+ return `
327
+ <div class="color-row">
328
+ <span>${mode.toLowerCase()}</span>
329
+ <div class="picker-wrapper">
330
+ <input type="color" value="${value}" oninput="updateTokenColor('${token}', '${mode}', this.value)">
331
+ <div class="hex-code" onclick="copyHex(this.previousElementSibling.value)">${value.toLowerCase()}</div>
332
+ <button class="copy-btn" onclick="copyHex(this.previousElementSibling.previousElementSibling.value)">COPY</button>
333
+ </div>
334
+ </div>
335
+ `;
336
+ }
337
+
338
+ // 3. 颜色更新逻辑
339
+ function updateTokenColor(token, mode, value) {
340
+ config[currentLang].colors[token][mode] = value;
341
+ // 实时更新文字显示,不重新渲染整个列表以免失去焦点
342
+ event.target.nextElementSibling.innerText = value.toLowerCase();
343
+ updateDynamicStyles();
344
+ }
345
+
346
+ // 4. 复制功能
347
+ async function copyHex(hex) {
348
+ try {
349
+ await navigator.clipboard.writeText(hex.toLowerCase());
350
+ alert('色值已复制: ' + hex.toLowerCase());
351
+ } catch (err) {
352
+ console.error('无法复制', err);
353
+ }
354
+ }
355
+
356
+ // 5. 动态生成 CSS 变量
357
+ function updateDynamicStyles() {
358
+ currentStyle = ':root{\n';
359
+ const langConfig = config[currentLang];
360
+
361
+ langConfig.tokens.forEach(token => {
362
+ const color = isDark ? langConfig.colors[token].dark : langConfig.colors[token].light;
363
+
364
+ currentStyle += `--ax-${currentLang}-${token}: ${color};\n`;
365
+
366
+ });
367
+ currentStyle += '}';
368
+
369
+ let styleTag = document.getElementById('dynamic-colors');
370
+ if (!styleTag) {
371
+ styleTag = document.createElement('style');
372
+ styleTag.id = 'dynamic-colors';
373
+ document.head.appendChild(styleTag);
374
+ }
375
+ styleTag.textContent = currentStyle;
376
+ }
377
+
378
+ // 6. 简单的代码预览生成(模拟高亮逻辑)
379
+ function renderCode() {
380
+ const langConfig = config[currentLang];
381
+ let html = langConfig.code
382
+ .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
383
+
384
+ codeEl.replaceCode(html);
385
+ codeEl.setAttribute('lang', currentLang);
386
+ }
387
+
388
+ function toggleDarkMode() {
389
+ isDark = !isDark;
390
+ document.getElementById('preview-area').classList.toggle('dark', isDark);
391
+ document.querySelector('.mode-toggle').innerText = `切换深色模式 (Current: ${isDark ? 'Dark' : 'Light'})`;
392
+ updateDynamicStyles();
393
+ renderControls(); // 重新渲染列表以更新文字状态(可选)
394
+ isDark ? codeEl.setAttribute('scheme', 'dark') : codeEl.removeAttribute('scheme');
395
+ }
396
+
397
+ copyEl.onclick = () => {
398
+ navigator.clipboard.writeText(currentStyle)
399
+ .then(() => {
400
+ alert('已经复制到剪切板,可粘贴到文本文件当中');
401
+ })
402
+ }
403
+ resetEl.onclick = () => {
404
+ document.querySelector('#dynamic-colors')?.remove();
405
+ }
406
+
407
+ init();
408
+ </script>
409
+
410
+ </body>
411
+
412
+ </html>
@@ -7,24 +7,7 @@
7
7
  <title>Coax 代码高亮插件</title>
8
8
  <style>
9
9
  /* 白天模式变量 */
10
- :root {
11
- --ax-css-selector: #6f42c1;
12
- /* 紫色:选择器最显眼 */
13
- --ax-css-property: #005cc5;
14
- /* 蓝色:属性名 */
15
- --ax-css-string: #22863a;
16
- /* 绿色:内容字符串 */
17
- --ax-css-unit: #d73a49;
18
- /* 红色:单位(如 px, rem) */
19
- --ax-css-number: #005cc5;
20
- /* 蓝色:数值 */
21
- --ax-css-func: #e36209;
22
- /* 橙色:函数如 var(), rgba() */
23
- --ax-css-comment: #6a737d;
24
- /* 灰色:注释 */
25
- --ax-css-punct: #24292e;
26
- /* 深灰:符号 */
27
- }
10
+ :root {}
28
11
  </style>
29
12
 
30
13
  </head>
@@ -56,6 +39,7 @@
56
39
  <script src="../dist/coax.umd.js" type='text/javascript'></script>
57
40
  <script type='text/javascript'>
58
41
 
42
+
59
43
  </script>
60
44
  </body>
61
45