@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
package/dist/coax.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2026-1-4 16:0:48
3
+ * @since Last modified: 2026-1-12 9:47:5
4
4
  * @name Coax event management system.
5
- * @version 0.0.1
5
+ * @version 0.0.3
6
6
  * @author AXUI development team <3217728223@qq.com>
7
7
  * @description Coax is a custom web component that enables syntax highlighting for various programming languages inside your HTML documents.
8
8
  * @see {@link https://coax.axui.cn|Official website}
@@ -14,173 +14,884 @@
14
14
  * @license MIT license
15
15
  */
16
16
 
17
- (function (factory) {
17
+ (function (global, factory) {
18
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
18
19
  typeof define === 'function' && define.amd ? define(factory) :
19
- factory();
20
- })((function () { 'use strict';
20
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.coax = factory());
21
+ })(this, (function () { 'use strict';
21
22
 
22
- class Coax extends HTMLElement {
23
+ const NAMESPACE = 'ax';
24
+
25
+ const typeWriter = (text, options) => {
26
+ const speed = options.speed || 100; // Set typing speed (default to 100ms per character)
27
+ return new Promise((resolve) => {
28
+ // Callback before typing starts
29
+ options?.onBeforeType?.(text);
30
+ let index = 0;
31
+ // Timer to type the text character by character at the given speed
32
+ const timer = setInterval(() => {
33
+ if (index < text.length) {
34
+ const char = text.charAt(index); // Get the character at the current index
35
+ const typedText = text.substring(0, index + 1); // The text typed so far
36
+ // Callback during typing each character
37
+ options?.onDuringType?.(char, typedText);
38
+ index++;
39
+ }
40
+ else {
41
+ // Clear the timer once typing is complete
42
+ clearInterval(timer);
43
+ // Resolve the Promise when typing is complete
44
+ resolve(text);
45
+ // Callback after typing is finished
46
+ options?.onAfterType?.(text);
47
+ }
48
+ }, speed);
49
+ });
50
+ };
51
+
52
+ const COMMA$1 = ',';
53
+
54
+ const SPACE$1 = ' ';
55
+
56
+ const trim$1 = (str, placement = 'compress') => {
57
+ if (typeof str !== 'string') {
58
+ console.warn('Expected a string input');
59
+ return '';
60
+ }
61
+ switch (placement) {
62
+ case 'start':
63
+ return str.trimStart();
64
+ case 'end':
65
+ return str.trimEnd();
66
+ case 'both':
67
+ return str.trim();
68
+ case 'global':
69
+ return str.replace(/[\s\r\n]+/g, '');
70
+ default:
71
+ return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
72
+ }
73
+ };
74
+
75
+ const parseClasses$1 = (data) => {
76
+ let separator, result = [];
77
+ if (Array.isArray(data)) {
78
+ // If data is already an array, filter out invalid values
79
+ result = data.filter((k) => k && typeof k === 'string');
80
+ }
81
+ else {
82
+ // Trim the input string and handle multiple spaces
83
+ data = trim$1(data);
84
+ // Use comma as the separator if present, otherwise use space
85
+ separator = data.includes(COMMA$1) ? COMMA$1 : SPACE$1;
86
+ result = data.split(separator);
87
+ }
88
+ // Trim each item globally and filter out any empty strings
89
+ return result.map((k) => trim$1(k, 'global')).filter(Boolean);
90
+ };
91
+
92
+ const getDataType = (obj) => {
93
+ let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
94
+ if (tmp === 'Function' && /^\s*class\s+/.test(obj.toString())) {
95
+ result = 'Class';
96
+ }
97
+ else if (tmp === 'Object' && Object.getPrototypeOf(obj) !== Object.prototype) {
98
+ result = 'Instance';
99
+ }
100
+ else {
101
+ result = tmp;
102
+ }
103
+ return result;
104
+ //document.createElement -> HTMLxxxElement
105
+ //document.createDocumentFragment() -> DocumentFragment
106
+ //document.createComment() -> Comment
107
+ //document.createTextNode -> Text
108
+ //document.createCDATASection() -> XMLDocument
109
+ //document.createProcessingInstruction() -> ProcessingInstruction
110
+ //document.createRange() -> Range
111
+ //document.createTreeWalker() -> TreeWalker
112
+ //document.createNodeIterator() -> NodeIterator
113
+ //document.createElementNS('http://www.w3.org/2000/svg', 'svg'); -> SVGSVGElement
114
+ //document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math'); -> MathMLElement
115
+ };
116
+
117
+ const getEl = (obj, wrap = document.body) => {
118
+ let objType = getDataType(obj), parType = getDataType(wrap), parent = parType.includes('HTML') || parType === 'ShadowRoot' ? wrap : document.querySelector(wrap),
119
+ //如果parent是template节点,需要通过node.content.querySelector取得子节点
120
+ root = parent && parent instanceof HTMLTemplateElement ? parent.content : parent, result = null;
121
+ if (obj) {
122
+ if (objType.includes('HTML')) {
123
+ result = obj;
124
+ }
125
+ else if (objType === 'String') {
126
+ try {
127
+ result = (root || document).querySelector(obj.trim());
128
+ //可能会报错,报错则返回null
129
+ }
130
+ catch {
131
+ result = null;
132
+ }
133
+ }
134
+ }
135
+ return result;
136
+ };
137
+
138
+ const createEl = (name, attrs, content) => {
139
+ //默认为div
140
+ name = name || 'div';
141
+ //统一大小写
142
+ let rootName = name.toUpperCase().trim(), rootEl = document.createElement(rootName), attrsType = getDataType(attrs), loop = (host, data) => {
143
+ if (data === '' || data === null || data === undefined) {
144
+ //为空、未定义则不再执行
145
+ return false;
146
+ }
147
+ let dataType = getDataType(data);
148
+ //template是DocumentFragment类型不能用insertAdjacentHTML
149
+ if (rootName === 'TEMPLATE') {
150
+ host.innerHTML = data.toString();
151
+ }
152
+ else {
153
+ if (dataType === 'Array' && data.length > 0) {
154
+ //节点数组
155
+ //data.forEach((i: T_obj) => loop(el, i));
156
+ for (let k of data) {
157
+ let childType = getDataType(k);
158
+ if (childType.includes('HTML')) {
159
+ //是个节点
160
+ host.appendChild(k);
161
+ }
162
+ else {
163
+ //是个对象{name:'',attrs:{},content:''}
164
+ let child = createEl(k.name, k.attrs, k.content);
165
+ child && host.appendChild(child);
166
+ }
167
+ }
168
+ }
169
+ else if (dataType.includes('HTML')) {
170
+ //HTML节点
171
+ host.appendChild(data);
172
+ }
173
+ else if (dataType === 'String' && data.trim().startsWith('#') && data.trim().length > 1) {
174
+ //是字符串且是#id选择器,则取其文本
175
+ let el = getEl(data);
176
+ if (!el)
177
+ return;
178
+ //如果是template模板节点则需要特殊处理
179
+ el.nodeName === 'TEMPLATE' ? host.appendChild(el.content.cloneNode(true)) : host.insertAdjacentHTML('beforeEnd', el.innerHTML);
180
+ }
181
+ else {
182
+ //字符串数字等
183
+ host.insertAdjacentHTML('beforeEnd', data);
184
+ }
185
+ }
186
+ };
187
+ //添加属性
188
+ if (attrs && attrsType === 'Object') {
189
+ for (let k in attrs) {
190
+ //注意,attrs[k]可能是一个{}或[],不一定是字符串
191
+ // JSON.stringify可以将所有格式的数据都转成文本,会忽略函数和节点数据
192
+ //字符串则不需要stringify,否则会将字符串的引号也一并输出
193
+ attrs.hasOwnProperty(k) && rootEl.setAttribute(k, typeof attrs[k] === 'string' ? attrs[k] : JSON.stringify(attrs[k]));
194
+ }
195
+ }
196
+ //执行循环创建子节点
197
+ loop(rootEl, content);
198
+ return rootEl;
199
+ };
200
+
201
+ const isEmpty = (data) => {
202
+ let type = getDataType(data), flag;
203
+ if (!data) {
204
+ //0,'',false,undefined,null
205
+ flag = true;
206
+ }
207
+ else {
208
+ //function(){}|()=>{}
209
+ //[null]|[undefined]|['']|[""]
210
+ //[]|{}
211
+ //Symbol()|Symbol.for()
212
+ //Set,Map
213
+ //Date/Regex
214
+ flag = (type === 'Object') ? (Object.keys(data).length === 0) :
215
+ (type === 'Array') ? data.join('') === '' :
216
+ (type === 'Function') ? (data.toString().replace(/\s+/g, '').match(/{.*}/g)[0] === '{}') :
217
+ (type === 'Symbol') ? (data.toString().replace(/\s+/g, '').match(/\(.*\)/g)[0] === '()') :
218
+ (type === 'Set' || type === 'Map') ? data.size === 0 :
219
+ type === 'Date' ? isNaN(data.getTime()) :
220
+ type === 'RegExp' ? data.source === '' :
221
+ type === 'ArrayBuffer' ? data.byteLength === 0 :
222
+ (type === 'NodeList' || type === 'HTMLCollection') ? data.length === 0 :
223
+ ('length' in data && typeof data.length === 'number') ? data.length === 0 :
224
+ ('size' in data && typeof data.size === 'number') ? data.size === 0 :
225
+ (type === 'Error' || data instanceof Error) ? data.message === '' :
226
+ (type.includes('Array') && (['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array', 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array'].includes(type))) ? data.length === 0 :
227
+ false;
228
+ }
229
+ return flag;
230
+ };
231
+
232
+ const ALIAS = 'rep';
233
+
234
+ const addClasses = (target, classes, intercept) => {
235
+ const el = getEl(target), arr = parseClasses$1(classes);
236
+ if (!el || arr.length === 0) {
237
+ return;
238
+ }
239
+ arr.forEach((k) => {
240
+ {
241
+ el.classList.add(k);
242
+ }
243
+ });
244
+ };
245
+
246
+ const createTools = (data) => {
247
+
248
+ const toolsEl = createEl('span', { class: `${NAMESPACE}-box-tools` }), renderFn = (props) => {
249
+ const dftAttrs = {}, arrow = props.extendable ? `<i ${ALIAS}="arrow"></i>` : '', iconStr = props.icon ? `<i ${ALIAS}="icon">${props.icon}</i>` : '', diskStr = props.disk ? `<i ${ALIAS}="disk"><img src="${props.disk}"/></i>` : '', cubeStr = props.cube ? `<i ${ALIAS}="cube"><img src="${props.cube}"/></i>` : '', imageStr = props.image ? `<i ${ALIAS}="image"><img src="${props.image}"/></i>` : '', label = props.label ? `<i ${ALIAS}="label">${props.label}</i>` : '', html = iconStr + diskStr + cubeStr + imageStr + label + arrow;
250
+ //使用title提示
251
+ props.title && (dftAttrs.title = props.title);
252
+ //可聚焦,增加tabindex=1
253
+ props.focusable && (dftAttrs.tabindex = 1);
254
+ //attrs是其他属性,可能会覆盖title、tabindex
255
+ props.wrapEl = createEl(props.nodeName || 'span', Object.assign(dftAttrs, props.attrs), html);
256
+ props.iconEl = props.wrapEl.querySelector(`[${ALIAS}="icon"]`);
257
+ props.cubeEl = props.wrapEl.querySelector(`[${ALIAS}="cube"]`);
258
+ props.diskEl = props.wrapEl.querySelector(`[${ALIAS}="disk"]`);
259
+ props.imageEl = props.wrapEl.querySelector(`[${ALIAS}="image"]`);
260
+ props.labelEl = props.wrapEl.querySelector(`[${ALIAS}="label"]`);
261
+ //增加classes和styles
262
+ !isEmpty(props.classes) && addClasses(props.wrapEl, props.classes);
263
+ !isEmpty(props.styles) && (props.wrapEl.style.cssText += props.styles);
264
+ };
265
+ //此处不用map方法,是避免改变原data的内存地址指向
266
+ for (let item of data) {
267
+ //data=[{},{},'toggle','close']
268
+ renderFn(item);
269
+ toolsEl.appendChild(item.wrapEl);
270
+ item?.action?.(item);
271
+ }
272
+ return toolsEl;
273
+ };
274
+
275
+ class CoaxElem extends HTMLElement {
23
276
  // A static Map to hold the configuration for different languages
24
277
  static languages = new Map();
25
- // Raw code as text from the HTML content
26
- rawCode;
278
+ static tools = [];
279
+ source;
280
+ _renderQueued = false;
281
+ baseStylesEl;
282
+ themeStylesEl;
283
+ dynamicStylesEl;
284
+ highlightedCodeEl;
285
+ headerEl;
286
+ codeNameEl;
287
+ codeToolsEl;
288
+ codeBodyEl;
289
+ alias;
290
+ lineString;
291
+ speed;
292
+ autoScroll;
27
293
  constructor() {
28
294
  super();
29
295
  // Attach a Shadow DOM to the custom element
30
296
  this.attachShadow({ mode: 'open' });
31
297
  // Remove leading/trailing whitespace from the raw code content
32
- this.rawCode = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
298
+ this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
299
+ this.lang = 'plain';
300
+ this.alias = 'Plain Text';
301
+ this.lineString = '';
302
+ this.speed = 5;
303
+ this.autoScroll = true;
304
+ // 1. 初始化基础骨架
305
+ this.shadowRoot.innerHTML = `
306
+ <style id="base-styles">
307
+ :host {
308
+ --border-width:1px;
309
+ --border-style:solid;
310
+ --radius:9px;
311
+ --height:auto;
312
+ --max-height:500px;
313
+ --radius:9px;
314
+ --padding:1em;
315
+ --font-size:16px;
316
+ --line-height:1.8;
317
+ --background:rgb(247, 247, 247);
318
+ --border-color:rgb(224, 224, 224);
319
+ --color-code:rgb(51, 51, 51);
320
+ --color-index:rgb(153, 153, 153);
321
+ --color-stripe:rgba(0,0,0,0.04);
322
+ --color-hover:rgba(0,0,0,0.06);
323
+ }
324
+ :host([scheme="dark"]){
325
+ --background: #282c34;
326
+ --border-color: transparent;
327
+ --color-code: #abb2bf;
328
+ --color-index:rgb(153, 153, 153);
329
+ --color-stripe:rgba(255,255,255,0.04);
330
+ --color-hover:rgba(255,255,255,0.06);
331
+ }
332
+ @media (prefers-color-scheme: dark) {
333
+ :host{
334
+ --background: #282c34;
335
+ --border-color: transparent;
336
+ --color-code: #abb2bf;
337
+ --color-index:rgb(153, 153, 153);
338
+ --color-stripe:rgba(255,255,255,0.04);
339
+ --color-hover:rgba(255,255,255,0.06);
340
+ }
341
+ }
342
+ :host {
343
+ font-size: var(--${NAMESPACE}-code-font-size,var(--font-size));
344
+ display: block;
345
+ box-sizing:border-box;
346
+ background:var(--${NAMESPACE}-code-background-color,var(--background));
347
+ color:var(--${NAMESPACE}-code-color,var(--color-code));
348
+ border:var(--${NAMESPACE}-code-border-width,var(--border-width)) var(--${NAMESPACE}-code-border-style,var(--border-style)) var(--${NAMESPACE}-code-border-color,var(--border-color));
349
+ transition: border-color 0.3s ease,color 0.3s ease;
350
+ border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
351
+ }
352
+ #code-header{
353
+ line-height:calc(var(--${NAMESPACE}-code-line-height,var(--line-height))*1.5);
354
+ padding-inline-start:var(--${NAMESPACE}-code-padding,var(--padding));
355
+ display:flex;
356
+
357
+ >:first-child{
358
+ flex:auto;
359
+ }
360
+ }
361
+ #code-body{
362
+ padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
363
+ height:var(--${NAMESPACE}-code-height,var(--height));
364
+ max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
365
+ overflow:auto;
366
+ }
367
+ pre,code{
368
+ font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
369
+ margin:0; padding:0;
370
+ }
371
+ code>div{
372
+ display:flex;
373
+ padding:0 var(--${NAMESPACE}-code-padding,var(--padding));
374
+ line-height: var(--${NAMESPACE}-code-line-height,var(--line-height));
375
+ box-sizing:border-box;
376
+
377
+ >div{
378
+ flex:auto;
379
+ }
380
+ }
381
+ code>div>div:empty:before{
382
+ content:' ';
383
+ }
384
+ :host([indexed]) code>div:before{
385
+ display:inline-flex;
386
+ color:var(--color-index);
387
+ content: attr(data-index);
388
+ min-width:var(--${NAMESPACE}-code-index-width,2em);
389
+ margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
390
+ }
391
+ :host([striped]) code>div:nth-child(odd){
392
+ background-color:var(--color-stripe);
393
+ }
394
+ :host([hoverable]) code>div:hover{
395
+ background-color:var(--color-hover);
396
+ }
397
+ :host([wrapped]) code>div>div{
398
+ white-space: pre-wrap;
399
+ }
400
+ :host([unnamed]) #code-name{
401
+ display:none;
402
+ }
403
+ .${NAMESPACE}-box-tools{
404
+ >*{
405
+ font-size:14px;
406
+ display:inline-flex;
407
+ align-items:center;
408
+ justify-content:center;
409
+ height:2em;
410
+ line-height:2em;
411
+ aspect-ratio:1/1;
412
+ margin-inline-end:8px;
413
+ transition:all 200ms ease;
414
+ border-radius:6px;
415
+ &:hover{
416
+ cursor:pointer;
417
+ background-color:rgba(0,0,0,.04);
418
+ }
419
+ }
420
+ [rep=icon]{
421
+ display:inline-flex;
422
+ align-items:center;
423
+ justify-content:center;
424
+ }
425
+ }
426
+ [disabled]{
427
+ pointer-event:none;
428
+ }
429
+ </style>
430
+ <style id="dynamic-styles"></style>
431
+ <style id="theme-styles"></style>
432
+ <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
433
+ <div id="code-body">
434
+ <pre><code id="highlight-code"></code></pre>
435
+ </div>
436
+ `;
437
+ // 缓存引用
438
+ this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
439
+ this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
440
+ this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
441
+ this.headerEl = getEl('#code-header', this.shadowRoot);
442
+ this.codeNameEl = getEl('#code-name', this.shadowRoot);
443
+ this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
444
+ this.codeBodyEl = getEl('#code-body', this.shadowRoot);
445
+ this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
446
+ this.codeBodyEl.addEventListener('scroll', () => {
447
+ let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
448
+ // 检测是否是用户手动滚动
449
+ this.autoScroll = !flag;
450
+ });
33
451
  }
34
452
 
35
- static register(name, { rules, theme = {}, internalCss = '', cssPrefix = '' }) {
453
+ static register(name, config) {
36
454
  // Store the language configuration in the static map
37
- this.languages.set(name, { rules, theme, internalCss, cssPrefix });
38
- // Render the highlighted code for all elements with the registered language
39
- document.querySelectorAll('ax-code').forEach(el => {
40
- if (el.getAttribute('lang') === name)
41
- el.render();
455
+ this.languages.set(name, { ...config });
456
+ }
457
+ //注册工具箱
458
+ static addTools(items) {
459
+ CoaxElem.tools = items;
460
+ }
461
+ //加入工具箱
462
+ mountTools(toolItems) {
463
+ requestAnimationFrame(() => {
464
+ this.codeToolsEl.innerHTML = '';
465
+ let items = toolItems.map(k => {
466
+ k.action = k.action.bind(this);
467
+ return k;
468
+ });
469
+ this.codeToolsEl.appendChild(createTools(items));
42
470
  });
43
471
  }
44
472
  // Called when the element is connected to the DOM
45
- connectedCallback() { this.render(); }
473
+ connectedCallback() {
474
+ this.render();
475
+ }
46
476
  // Observed attributes for changes
47
- static get observedAttributes() { return ['lang', 'height', 'max-height']; }
477
+ static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
48
478
 
49
- attributeChangedCallback() {
50
- this.render();
479
+ attributeChangedCallback(name, oldVal, newVal) {
480
+ if (oldVal === newVal)
481
+ return;
482
+ if (name === 'height' || name === 'max-height') {
483
+ this.updateStyleByRegExp(name, newVal);
484
+ }
485
+ if (name === 'speed') {
486
+ this.speed = ~~(!!newVal);
487
+ }
488
+ if (name === 'lang') {
489
+ //更新当前语言
490
+ this.lang = newVal;
491
+ this.render();
492
+ }
493
+ if (name === 'tools') {
494
+ if (!newVal)
495
+ this.codeToolsEl.innerHTML = '';
496
+ const tools = parseClasses$1(newVal), toolItems = CoaxElem.tools.filter(k => tools.includes(k.name));
497
+ if (!toolItems.length)
498
+ return;
499
+ this.mountTools(toolItems);
500
+ }
51
501
  }
52
502
 
53
- render() {
54
- // Get language name, fallback to 'js' if not provided
55
- const lang = this.getAttribute('lang') || 'js',
56
- // Get the language configuration
57
- config = Coax.languages.get(lang),
58
- // Default to language name if no prefix is set
59
- cssPrefix = config?.cssPrefix || lang, height = this.getAttribute('height'), maxHeight = this.getAttribute('max-height');
60
- let highlightedCode = '', dynamicStyles = '';
61
- if (config) {
62
- //Generate the highlighted HTML by applying the syntax rules
63
- const combinedRegex = new RegExp(config.rules.map((r) => `(${r.reg.source})`).join('|'), 'g'), escaped = this.rawCode.replace(/[&<>]/g, m => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[m]));
64
- // Replace code with highlighted syntax
65
- highlightedCode = escaped.replace(combinedRegex, (match, ...args) => {
66
- const index = args.findIndex(val => val !== undefined);
67
- return index !== -1 && config.rules[index] ? `<span class="ax-${cssPrefix}-${config.rules[index].name}">${match}</span>` : match;
68
- });
69
- //Generate dynamic CSS classes for each syntax rule
70
- // rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
71
- dynamicStyles = config.rules.map((rule) => `
72
- .ax-${cssPrefix}-${rule.name} { color: var(--ax-${cssPrefix}-${rule.name}); }
73
- `).join('\n');
503
+ updateStyleByRegExp(prop, value) {
504
+ // 构建正则:匹配属性名后面跟着冒号,直到分号或换行
505
+ // 例如:height:\s*[^;]+;
506
+ const regex = new RegExp(`;\\n\\s*${prop}:\\s*[^;]+;`, 'g');
507
+ // 替换为新的属性值
508
+ this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
509
+ }
510
+ getCssPrefix(config) {
511
+ return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
512
+ }
513
+ getHighLightString(string, config) {
514
+ config = config || CoaxElem.languages.get(this.lang);
515
+ if (!config)
516
+ return string;
517
+ // 如果找到了配置,则进行高亮处理
518
+ const cssPrefix = this.getCssPrefix(config),
519
+ // 获取用于语法高亮的正则表达式
520
+ combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
521
+ return string.replace(combinedRegex, (match, ...args) => {
522
+ const i = args.findIndex(val => val !== undefined);
523
+ return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
524
+ });
525
+ }
526
+ createLineWrap(index, startIndex) {
527
+ let dataIndex = 0;
528
+ if (index == null && startIndex == null) {
529
+ dataIndex = this.highlightedCodeEl.children.length;
74
530
  }
75
531
  else {
76
- // If no config, display raw code without highlighting
77
- highlightedCode = this.rawCode;
532
+ const start = startIndex || this.highlightedCodeEl.children.length;
533
+ dataIndex = start + index;
78
534
  }
79
- //Generate CSS theme variables if a theme is provided
80
- const themeVars = config?.theme
81
- ? Object.entries(config.theme).map(([k, v]) => `--ax-${cssPrefix}-${k}: ${v};`).join('\n')
82
- : '';
535
+ return createEl('div', { 'data-index': dataIndex }, '<div></div>');
536
+ }
537
+ getLineToFill(codeWrap, line, config) {
538
+ config = config || CoaxElem.languages.get(this.lang);
539
+ let highlightedLine = this.getHighLightString(line, config);
540
+ // 将高亮后的内容填充到 div 中
541
+ codeWrap.innerHTML = highlightedLine;
542
+ }
543
+ ;
544
+
545
+ async highlight(newCode) {
546
+ const config = CoaxElem.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
547
+ // 将新源码按行分割
548
+ newCodeLines = newCode ? newCode.split('\n') : [];
549
+ //更新别名
550
+ this.updateName(config);
551
+ if (!newCodeLines.length)
552
+ return true;
553
+ // 如果没有找到配置,则输出原始代码,并不进行高亮处理
554
+ for (let [index, lineString] of newCodeLines.entries()) {
555
+ //如果是空行则跳过
556
+ if (!lineString.trim() && this.hasAttribute('sanitized'))
557
+ continue;
558
+ // 创建一个 div 包裹每一行
559
+ const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
560
+ //标记完成
561
+ this.closeLine(lineWrap);
562
+ if (this.hasAttribute('speed')) {
563
+ this.highlightedCodeEl.appendChild(lineWrap);
564
+ //流式打字
565
+ await typeWriter(lineString, {
566
+ speed: this.speed,
567
+ onDuringType: (char, fullText) => {
568
+ codeWrap.innerHTML = fullText;
569
+ }
570
+ });
571
+ this.getLineToFill(codeWrap, lineString, config);
572
+ }
573
+ else {
574
+ //直接打出
575
+ this.getLineToFill(codeWrap, lineString, config);
576
+ //
577
+ this.highlightedCodeEl.appendChild(lineWrap);
578
+ }
579
+ }
580
+ //滚动到最底部
581
+ this.autoScrollCode();
582
+ return true;
583
+ }
584
+ autoScrollCode() {
585
+ if (this.autoScroll) {
586
+ this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
587
+ }
588
+ }
589
+ injectThemeStyles() {
590
+ const config = CoaxElem.languages.get(this.lang);
591
+ if (!config)
592
+ return;
593
+ // Get language name, fallback to 'js' if not provided
594
+ let cssPrefix = this.getCssPrefix(config),
595
+ //Generate dynamic CSS classes for each syntax rule
596
+ // 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
597
+ lightStyles = config.rules.map((rule) => `
598
+ .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.light ? ',' + rule.light : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
599
+ darkStyles += `:host([scheme="dark"]){`;
600
+ darkStyles += config.rules.map((rule) => `
601
+ ${rule.light ? `
602
+ .${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
603
+ darkStyles += `}`;
604
+ schemeStyles = `@media (prefers-color-scheme: dark){
605
+ :host{
606
+ `;
607
+ schemeStyles += config.rules.map((rule) => `
608
+ ${rule.light ? `
609
+ .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
610
+ schemeStyles += `}`;
83
611
  // Set the inner HTML of the shadow root, including styles and highlighted code
84
- this.shadowRoot.innerHTML = `
85
- <style>
86
- :host {
87
- ${themeVars}
88
-
89
- font-size: var(--ax-code-fs,14px);
90
- display: block;
91
- padding: var(--ax-code-p,1em);
92
- box-sizing:border-box;
93
- line-height: var(--ax-code-lh,1.5);
94
- background-color:var(--ax-code-bg,rgba(0,0,0,0.02));
95
- color:var(--ax-code-c,#333);
96
- border:var(--ax-code-bw,1px) solid var(--ax-code-bc,rgba(0,0,0,0.08));
97
- height:${height || 'var(--ax-code-h,auto)'};
98
- max-height:${maxHeight || 'var(--ax-code-mh,500px)'};
99
- overflow:auto;
100
- transition: border-color 0.3s ease,color 0.3s ease;
101
- border-radius: var(--ax-code-r,9px);
102
- }
103
- pre,code{
104
- font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace", "microsoft yahei", "Microsoft JhengHei", "Yu Mincho", "simsun";
105
- margin:0;
106
- padding:0;
107
- }
108
-
109
-
110
-
111
- ${dynamicStyles}
112
-
113
-
114
- ${config?.internalCss || ''}
115
- </style>
116
- <pre><code>${highlightedCode}</code></pre>`;
612
+ // 2. 精确更新 DOM 节点而非重写 ShadowRoot
613
+ this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
614
+ //附加主题样式
615
+ if (config?.themeStyles) {
616
+ this.themeStylesEl.textContent = config.themeStyles;
617
+ }
618
+ }
619
+ updateName(config) {
620
+ if (this.hasAttribute('unnamed'))
621
+ return;
622
+ if (!config) {
623
+ this.codeNameEl.innerHTML = 'Plain Text';
624
+ }
625
+ else {
626
+ //更新别名
627
+ this.alias = config.alias || this.lang;
628
+ this.codeNameEl.innerHTML = this.alias;
629
+ }
630
+ }
631
+
632
+ render(code = this.source) {
633
+ //同时多次改变属性,只执行一次
634
+ // 如果已经在队列中,则直接返回
635
+ if (this._renderQueued)
636
+ return;
637
+ this._renderQueued = true;
638
+ // 使用 requestAnimationFrame 将渲染推迟到下一帧
639
+ requestAnimationFrame(async () => {
640
+ this.clear();
641
+ this.injectThemeStyles();
642
+ //一次性渲染
643
+ await this.highlight(code);
644
+ this._renderQueued = false;
645
+ });
646
+ }
647
+ clear() {
648
+ this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
649
+ }
650
+
651
+ async replace(newCode) {
652
+ this.clear();
653
+ await this.highlight(newCode);
654
+ }
655
+
656
+ async append(newCode) {
657
+ // 将新的代码追加到现有代码末尾
658
+ this.source += `\n${newCode}`;
659
+ // 高亮新的部分
660
+ await this.highlight(newCode);
661
+ }
662
+ getActiveCodeWrap() {
663
+ const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
664
+ this.createLineWrap() : lastChild;
665
+ return {
666
+ lineWrap: lastLine,
667
+ codeWrap: lastLine.lastElementChild
668
+ };
669
+ }
670
+ closeLine(lineWrap) {
671
+ lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
672
+ lineWrap && (lineWrap.completed = true);
673
+ }
674
+ openLine(lineWrap) {
675
+ lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
676
+ lineWrap && (lineWrap.completed = false);
677
+ }
678
+ stream(str, forceEnd = false) {
679
+ const { lineWrap, codeWrap } = this.getActiveCodeWrap();
680
+ this.highlightedCodeEl.appendChild(lineWrap);
681
+ //汇集
682
+ this.source += str;
683
+ //如果没有遇到换行符号,也可以强制结束
684
+ forceEnd && this.closeLine(lineWrap);
685
+ if (str.startsWith('\n') || str.startsWith('\r')) {
686
+ //标记完成
687
+ this.closeLine(lineWrap);
688
+ //渲染
689
+ this.getLineToFill(codeWrap, this.lineString);
690
+ //一行结束,清空临时行文本
691
+ this.lineString = '';
692
+ }
693
+ else {
694
+ //插入文本
695
+ codeWrap.innerHTML += str;
696
+ //临时保存行文本
697
+ this.lineString += str;
698
+ }
699
+ //滚动到最底部
700
+ this.autoScrollCode();
117
701
  }
118
702
  }
119
- customElements.define('ax-code', Coax);
120
- Coax.register('html', {
121
- rules: [
122
- { name: 'comment', reg: /&lt;!--[\s\S]*?--&gt;/ },
123
- { name: 'doctype', reg: /&lt;!DOCTYPE[\s\S]*?&gt;/i },
124
- // 匹配标签名: <div, </div
125
- { name: 'tag', reg: /&lt;\/?[a-zA-Z0-9]+/ },
126
- // 匹配属性名: class=
127
- { name: 'attr', reg: /[a-zA-Z-]+(?=\s*=\s*)/ },
128
- // 匹配属性值: "value"
129
- { name: 'string', reg: /(['"])(?:\\.|[^\\])*?\1/ },
130
- // 匹配标签闭合: >, />
131
- { name: 'bracket', reg: /\/?&gt;/ }
132
- ],
133
- //添加root变量:--ax-html-keyword
703
+
704
+ const html = [
705
+ { token: 'comment', pattern: /&lt;!--[\s\S]*?--&gt;/, light: '#999999', dark: '#6e7681' },
706
+ { token: 'doctype', pattern: /&lt;!DOCTYPE[\s\S]*?&gt;/i, light: '#6a737d', dark: '#8b949e' },
707
+ // 匹配标签名: <div, </div
708
+ { token: 'tag', pattern: /&lt;\/?[a-zA-Z0-9]+/, light: '#22863a', dark: '#7ee787' },
709
+ // 匹配属性名: class=
710
+ { token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, light: '#6f42c1', dark: '#d2a8ff' },
711
+ // 匹配属性值: "value"
712
+ { token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, light: '#032f62', dark: '#a5d6ff' },
713
+ // 匹配标签闭合: >, />
714
+ { token: 'bracket', pattern: /\/?&gt;/, light: '#24292e', dark: '#c9d1d9' }
715
+ ];
716
+
717
+ const javascript = [
718
+ { token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
719
+ { token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
720
+ { 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' },
721
+ { token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/, light: '#e36209', dark: '#ffa657' },
722
+ { token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
723
+ { token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
724
+ { token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, light: '#069598', dark: '#56b6c2' }
725
+ ];
726
+
727
+ const markdown = [
728
+ // 注释: 这是 Markdown 中的行内注释
729
+ { token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#6a737d', dark: '#8b949e' },
730
+ // 标题: 通过 `#` 来定义标题
731
+ { token: 'heading', pattern: /(^|\n)(#{1,6})\s*(.+)/, light: '#e36209', dark: '#ffa657' },
732
+ // 粗体: **text** 或 __text__
733
+ { token: 'bold', pattern: /\*\*([^*]+)\*\*|__([^_]+)__/g, light: '#d73a49', dark: '#ff7b72' },
734
+ // 斜体: *text* 或 _text_
735
+ { token: 'italic', pattern: /\*([^*]+)\*|_([^_]+)_/g, light: '#032f62', dark: '#a5d6ff' },
736
+ // 链接: [text](url)
737
+ { token: 'link', pattern: /\[([^\]]+)\]\(([^)]+)\)/g, light: '#0288d1', dark: '#80c0ff' },
738
+ // 行内代码: `code`
739
+ { token: 'inline-code', pattern: /`([^`]+)`/g, light: '#032f62', dark: '#98c379' },
740
+ // 代码块: ```code```
741
+ { token: 'code-block', pattern: /```([^\n]+)\n([\s\S]*?)```/g, light: '#24292e', dark: '#c9d1d9' },
742
+ // 列表项: - item 或 * item
743
+ { token: 'list-item', pattern: /(^|\n)([-*])\s+(.+)/g, light: '#5c6e7c', dark: '#8b949e' },
744
+ // 引用: > text
745
+ { token: 'quote', pattern: /(^|\n)>[ \t]*(.+)/g, light: '#6f42c1', dark: '#d2a8ff' },
746
+ // 图片: ![alt](url)
747
+ { token: 'image', pattern: /!\[([^\]]+)\]\(([^)]+)\)/g, light: '#d73a49', dark: '#ff7b72' },
748
+ // 分割线: ---
749
+ { token: 'hr', pattern: /^(---|___|\*\*\*)\s*$/gm, light: '#24292e', dark: '#c9d1d9' },
750
+ // 强调和删除: ~~text~~
751
+ { token: 'strikethrough', pattern: /~~([^~]+)~~/g, light: '#e36209', dark: '#ffa657' },
752
+ // 表格: | header1 | header2 |
753
+ { token: 'table', pattern: /\|([^\|]+)\|([^\|]+)\|/g, light: '#5c6e7c', dark: '#8b949e' }
754
+ ];
755
+
756
+ const typescript = [
757
+ { token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
758
+ { token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
759
+ { token: 'decorator', pattern: /@[a-zA-Z_]\w*/, light: '#953800', dark: '#ffa657' },
760
+ { 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' },
761
+ { token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/, light: '#e36209', dark: '#ffa657' },
762
+ { token: 'type', pattern: /\b[A-Z]\w*\b/, light: '#005cc5', dark: '#79c0ff' },
763
+ { token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
764
+ { token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
765
+ { token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, light: '#089ba6', dark: '#79c0ff' }
766
+ ];
767
+
768
+ const COMMA = ',';
769
+
770
+ const SPACE = ' ';
771
+
772
+ const trim = (str, placement = '') => {
773
+ if (typeof str !== 'string') {
774
+ console.warn('Expected a string input');
775
+ return '';
776
+ }
777
+ switch (placement) {
778
+ case 'start':
779
+ return str.trimStart();
780
+ case 'end':
781
+ return str.trimEnd();
782
+ case 'both':
783
+ return str.trim();
784
+ case 'global':
785
+ return str.replace(/[\s\r\n]+/g, '');
786
+ default:
787
+ return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
788
+ }
789
+ };
790
+
791
+ const parseClasses = (data) => {
792
+ let separator, result = [];
793
+ if (Array.isArray(data)) {
794
+ // If data is already an array, filter out invalid values
795
+ result = data.filter((k) => k && typeof k === 'string');
796
+ }
797
+ else {
798
+ // Trim the input string and handle multiple spaces
799
+ data = trim(data);
800
+ // Use comma as the separator if present, otherwise use space
801
+ separator = data.includes(COMMA) ? COMMA : SPACE;
802
+ result = data.split(separator);
803
+ }
804
+ // Trim each item globally and filter out any empty strings
805
+ return result.map((k) => trim(k, 'global')).filter(Boolean);
806
+ };
807
+
808
+ const rtlStyle = (name = '') => `
809
+ <style>
810
+ :where([dir="rtl"]) .icax-${name},
811
+ :where(:dir(rtl)) .icax-${name} {
812
+ transform: scaleX(-1);
813
+ transform-origin: center;
814
+ }
815
+ </style>
816
+ `;
817
+
818
+ const wrap = (content, fun, isRtl = false, options) => {
819
+ const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
820
+ // 得到 "icax-left"
821
+ origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
822
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
823
+ stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
824
+ ${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
825
+ ${content}
826
+ </svg>`;
827
+ };
828
+
829
+ const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
830
+
831
+ const icaxCopy = (options) => 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, false, options);
832
+
833
+ const copy = {
834
+ name: 'copy',
835
+ icon: icaxCopy(),
836
+ action: function (arg) {
837
+ arg.wrapEl.onclick = () => {
838
+ //this只是组件实例
839
+ navigator.clipboard.writeText(this.source)
840
+ .then(() => {
841
+ console.log('Text successfully copied to clipboard');
842
+ arg.iconEl.innerHTML = icaxCheck();
843
+ arg.iconEl.toggleAttribute('disabled', true);
844
+ setTimeout(() => {
845
+ //恢复
846
+ arg.iconEl.removeAttribute('disabled');
847
+ arg.iconEl.innerHTML = icaxCopy();
848
+ }, 2000);
849
+ })
850
+ .catch(err => {
851
+ console.error('Error copying text to clipboard: ', err);
852
+ });
853
+ };
854
+ }
855
+ };
856
+
857
+ const css = [
858
+ { token: 'comment', pattern: /\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
859
+ { token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, light: '#032f62', dark: '#a5d6ff' },
860
+ { token: 'func', pattern: /[a-z-]+\(?=/, light: '#e36209', dark: '#ffa657' },
861
+ { token: 'property', pattern: /[a-z-]+(?=\s*:)/, light: '#005cc5', dark: '#79c0ff' },
862
+ { token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, light: '#22863a', dark: '#7ee787' },
863
+ { token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, light: '#d73a49', dark: '#ff7b72' },
864
+ { token: 'number', pattern: /\b\d+(\.\d+)?\b/, light: '#005cc5', dark: '#79c0ff' },
865
+ { token: 'punct', pattern: /[{}();:]/, light: '#24292e', dark: '#c9d1d9' }
866
+ ];
867
+
868
+ const Coax = CoaxElem;
869
+ //注册语言类型
870
+ Coax.register('css', {
871
+ alias: 'CSS',
872
+ rules: css,
134
873
 
135
874
  });
136
- Coax.register('css', {
137
- rules: [
138
- { name: 'comment', reg: /\/\*[\s\S]*?\*\// },
139
- { name: 'value', reg: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/ },
140
- { name: 'func', reg: /[a-z-]+\(?=/ },
141
- { name: 'property', reg: /[a-z-]+(?=\s*:)/ },
142
- { name: 'selector', reg: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i },
143
- { name: 'unit', reg: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/ },
144
- { name: 'number', reg: /\b\d+(\.\d+)?\b/ },
145
- { name: 'punct', reg: /[{}();:]/ }
146
- ],
147
- //添加root变量:--ax-code-keyword
148
- theme: {
149
- 'type': '#61afef', // 蓝色选择器
150
- 'keyword': '#d19a66', // 橙色属性名
151
- 'string': '#98c379', // 绿色属性值
152
- 'op': '#abb2bf' // 灰色符号
153
- },
154
- internalCss: `
155
- .ax-css-string { color: var(--ax-code-string); }
156
- .ax-css-string::first-letter { color: var(--ax-code-c); }
157
- `
875
+ Coax.register('html', {
876
+ alias: 'HTML',
877
+ rules: html,
158
878
  });
159
879
  Coax.register('js', {
160
- rules: [
161
- { name: 'comment', reg: /\/\/[^\n]*|\/\*[\s\S]*?\*\// },
162
- { name: 'string', reg: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/ },
163
- { 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/ },
164
- { name: 'builtin', reg: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise)\b/ },
165
- { name: 'number', reg: /\b\d+\b/ },
166
- { name: 'func', reg: /\b[a-zA-Z_]\w*(?=\s*\()/ },
167
- { name: 'op', reg: /[+\-*/%=<>!&|^~]+/ }
168
- ],
169
-
880
+ alias: 'Javascript',
881
+ rules: javascript,
170
882
  });
171
883
  Coax.register('ts', {
172
- rules: [
173
- { name: 'comment', reg: /\/\/[^\n]*|\/\*[\s\S]*?\*\// },
174
- { name: 'string', reg: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/ },
175
- { name: 'decorator', reg: /@[a-zA-Z_]\w*/ },
176
- { 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/ },
177
- { name: 'builtin', reg: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|boolean|true|false|console|window|document)\b/ },
178
- { name: 'type', reg: /\b[A-Z]\w*\b/ },
179
- { name: 'number', reg: /\b\d+\b/ },
180
- { name: 'func', reg: /\b[a-zA-Z_]\w*(?=\s*\()/ },
181
- { name: 'op', reg: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/ }
182
- ],
183
-
884
+ alias: 'Typescript',
885
+ rules: typescript,
886
+ });
887
+ Coax.register('md', {
888
+ alias: 'Markdown',
889
+ rules: markdown
184
890
  });
891
+ //注册工具箱
892
+ Coax.addTools([copy]);
893
+ customElements.define(`${NAMESPACE}-code`, Coax);
894
+
895
+ return Coax;
185
896
 
186
897
  }));