@codady/coax 0.0.2 → 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.
- package/dist/coax.cjs.js +421 -257
- package/dist/coax.cjs.min.js +3 -3
- package/dist/coax.esm.js +415 -257
- package/dist/coax.esm.min.js +3 -3
- package/dist/coax.umd.js +435 -249
- package/dist/coax.umd.min.js +3 -3
- package/examples/.htaccess +0 -0
- package/examples/append-highlight.html +82 -0
- package/examples/color-selector.html +412 -0
- package/examples/deepseek-highlight.html +91 -0
- package/examples/js-highlight.html +1 -1
- package/examples/md-highlight.html +60 -0
- package/examples/nginx.htaccess +0 -0
- package/examples/replace-highlight.html +69 -0
- package/examples/stream-highlight.html +64 -0
- package/examples/theme-highlight.html +69 -0
- package/package.json +3 -3
- package/rollup.config.js +3 -3
- package/src/Coax.js +26 -414
- package/src/Coax.ts +29 -443
- package/src/components/CoaxElem.js +457 -0
- package/src/components/CoaxElem.ts +488 -0
- package/src/modules.js +12 -0
- package/src/modules.ts +23 -0
- package/src/rules/css.js +11 -0
- package/src/rules/css.ts +11 -0
- package/src/rules/html.js +13 -0
- package/src/rules/html.ts +13 -0
- package/src/rules/javascript.js +10 -0
- package/src/rules/javascript.ts +10 -0
- package/src/rules/markdown.js +29 -0
- package/src/rules/markdown.ts +41 -0
- package/src/rules/ruleCss - /345/211/257/346/234/254.js" +10 -0
- package/src/rules/ruleHTML - /345/211/257/346/234/254.js" +12 -0
- package/src/rules/ruleJs - /345/211/257/346/234/254.js" +10 -0
- package/src/rules/ruleTs - /345/211/257/346/234/254.js" +12 -0
- package/src/rules/typescript.js +12 -0
- package/src/rules/typescript.ts +12 -0
- package/src/tools/copy.js +26 -0
- package/src/tools/copy.ts +29 -0
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-12 9:47:5
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
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}
|
|
@@ -16,6 +16,75 @@
|
|
|
16
16
|
|
|
17
17
|
'use strict';
|
|
18
18
|
|
|
19
|
+
const typeWriter = (text, options) => {
|
|
20
|
+
const speed = options.speed || 100; // Set typing speed (default to 100ms per character)
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
// Callback before typing starts
|
|
23
|
+
options?.onBeforeType?.(text);
|
|
24
|
+
let index = 0;
|
|
25
|
+
// Timer to type the text character by character at the given speed
|
|
26
|
+
const timer = setInterval(() => {
|
|
27
|
+
if (index < text.length) {
|
|
28
|
+
const char = text.charAt(index); // Get the character at the current index
|
|
29
|
+
const typedText = text.substring(0, index + 1); // The text typed so far
|
|
30
|
+
// Callback during typing each character
|
|
31
|
+
options?.onDuringType?.(char, typedText);
|
|
32
|
+
index++;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Clear the timer once typing is complete
|
|
36
|
+
clearInterval(timer);
|
|
37
|
+
// Resolve the Promise when typing is complete
|
|
38
|
+
resolve(text);
|
|
39
|
+
// Callback after typing is finished
|
|
40
|
+
options?.onAfterType?.(text);
|
|
41
|
+
}
|
|
42
|
+
}, speed);
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const COMMA$1 = ',';
|
|
47
|
+
|
|
48
|
+
const SPACE$1 = ' ';
|
|
49
|
+
|
|
50
|
+
const trim$1 = (str, placement = 'compress') => {
|
|
51
|
+
if (typeof str !== 'string') {
|
|
52
|
+
console.warn('Expected a string input');
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
switch (placement) {
|
|
56
|
+
case 'start':
|
|
57
|
+
return str.trimStart();
|
|
58
|
+
case 'end':
|
|
59
|
+
return str.trimEnd();
|
|
60
|
+
case 'both':
|
|
61
|
+
return str.trim();
|
|
62
|
+
case 'global':
|
|
63
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
64
|
+
default:
|
|
65
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const parseClasses$1 = (data) => {
|
|
70
|
+
let separator, result = [];
|
|
71
|
+
if (Array.isArray(data)) {
|
|
72
|
+
// If data is already an array, filter out invalid values
|
|
73
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Trim the input string and handle multiple spaces
|
|
77
|
+
data = trim$1(data);
|
|
78
|
+
// Use comma as the separator if present, otherwise use space
|
|
79
|
+
separator = data.includes(COMMA$1) ? COMMA$1 : SPACE$1;
|
|
80
|
+
result = data.split(separator);
|
|
81
|
+
}
|
|
82
|
+
// Trim each item globally and filter out any empty strings
|
|
83
|
+
return result.map((k) => trim$1(k, 'global')).filter(Boolean);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const NAMESPACE = 'ax';
|
|
87
|
+
|
|
19
88
|
const getDataType = (obj) => {
|
|
20
89
|
let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
|
|
21
90
|
if (tmp === 'Function' && /^\s*class\s+/.test(obj.toString())) {
|
|
@@ -158,48 +227,6 @@ const isEmpty = (data) => {
|
|
|
158
227
|
|
|
159
228
|
const ALIAS = 'rep';
|
|
160
229
|
|
|
161
|
-
const NAMESPACE = 'ax';
|
|
162
|
-
|
|
163
|
-
const COMMA$1 = ',';
|
|
164
|
-
|
|
165
|
-
const SPACE$1 = ' ';
|
|
166
|
-
|
|
167
|
-
const trim$1 = (str, placement = '') => {
|
|
168
|
-
if (typeof str !== 'string') {
|
|
169
|
-
console.warn('Expected a string input');
|
|
170
|
-
return '';
|
|
171
|
-
}
|
|
172
|
-
switch (placement) {
|
|
173
|
-
case 'start':
|
|
174
|
-
return str.trimStart();
|
|
175
|
-
case 'end':
|
|
176
|
-
return str.trimEnd();
|
|
177
|
-
case 'both':
|
|
178
|
-
return str.trim();
|
|
179
|
-
case 'global':
|
|
180
|
-
return str.replace(/[\s\r\n]+/g, '');
|
|
181
|
-
default:
|
|
182
|
-
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const parseClasses$1 = (data) => {
|
|
187
|
-
let separator, result = [];
|
|
188
|
-
if (Array.isArray(data)) {
|
|
189
|
-
// If data is already an array, filter out invalid values
|
|
190
|
-
result = data.filter((k) => k && typeof k === 'string');
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
// Trim the input string and handle multiple spaces
|
|
194
|
-
data = trim$1(data);
|
|
195
|
-
// Use comma as the separator if present, otherwise use space
|
|
196
|
-
separator = data.includes(COMMA$1) ? COMMA$1 : SPACE$1;
|
|
197
|
-
result = data.split(separator);
|
|
198
|
-
}
|
|
199
|
-
// Trim each item globally and filter out any empty strings
|
|
200
|
-
return result.map((k) => trim$1(k, 'global')).filter(Boolean);
|
|
201
|
-
};
|
|
202
|
-
|
|
203
230
|
const addClasses = (target, classes, intercept) => {
|
|
204
231
|
const el = getEl(target), arr = parseClasses$1(classes);
|
|
205
232
|
if (!el || arr.length === 0) {
|
|
@@ -241,72 +268,7 @@ const createTools = (data) => {
|
|
|
241
268
|
return toolsEl;
|
|
242
269
|
};
|
|
243
270
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const SPACE = ' ';
|
|
247
|
-
|
|
248
|
-
const trim = (str, placement = '') => {
|
|
249
|
-
if (typeof str !== 'string') {
|
|
250
|
-
console.warn('Expected a string input');
|
|
251
|
-
return '';
|
|
252
|
-
}
|
|
253
|
-
switch (placement) {
|
|
254
|
-
case 'start':
|
|
255
|
-
return str.trimStart();
|
|
256
|
-
case 'end':
|
|
257
|
-
return str.trimEnd();
|
|
258
|
-
case 'both':
|
|
259
|
-
return str.trim();
|
|
260
|
-
case 'global':
|
|
261
|
-
return str.replace(/[\s\r\n]+/g, '');
|
|
262
|
-
default:
|
|
263
|
-
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
const parseClasses = (data) => {
|
|
268
|
-
let separator, result = [];
|
|
269
|
-
if (Array.isArray(data)) {
|
|
270
|
-
// If data is already an array, filter out invalid values
|
|
271
|
-
result = data.filter((k) => k && typeof k === 'string');
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
// Trim the input string and handle multiple spaces
|
|
275
|
-
data = trim(data);
|
|
276
|
-
// Use comma as the separator if present, otherwise use space
|
|
277
|
-
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
278
|
-
result = data.split(separator);
|
|
279
|
-
}
|
|
280
|
-
// Trim each item globally and filter out any empty strings
|
|
281
|
-
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const rtlStyle = (name = '') => `
|
|
285
|
-
<style>
|
|
286
|
-
:where([dir="rtl"]) .icax-${name},
|
|
287
|
-
:where(:dir(rtl)) .icax-${name} {
|
|
288
|
-
transform: scaleX(-1);
|
|
289
|
-
transform-origin: center;
|
|
290
|
-
}
|
|
291
|
-
</style>
|
|
292
|
-
`;
|
|
293
|
-
|
|
294
|
-
const wrap = (content, fun, isRtl = false, options) => {
|
|
295
|
-
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
|
|
296
|
-
// 得到 "icax-left"
|
|
297
|
-
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
298
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
299
|
-
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
300
|
-
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
301
|
-
${content}
|
|
302
|
-
</svg>`;
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
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);
|
|
306
|
-
|
|
307
|
-
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
308
|
-
|
|
309
|
-
class Coax extends HTMLElement {
|
|
271
|
+
class CoaxElem extends HTMLElement {
|
|
310
272
|
// A static Map to hold the configuration for different languages
|
|
311
273
|
static languages = new Map();
|
|
312
274
|
static tools = [];
|
|
@@ -317,9 +279,13 @@ class Coax extends HTMLElement {
|
|
|
317
279
|
dynamicStylesEl;
|
|
318
280
|
highlightedCodeEl;
|
|
319
281
|
headerEl;
|
|
320
|
-
|
|
321
|
-
|
|
282
|
+
codeNameEl;
|
|
283
|
+
codeToolsEl;
|
|
284
|
+
codeBodyEl;
|
|
322
285
|
alias;
|
|
286
|
+
lineString;
|
|
287
|
+
speed;
|
|
288
|
+
autoScroll;
|
|
323
289
|
constructor() {
|
|
324
290
|
super();
|
|
325
291
|
// Attach a Shadow DOM to the custom element
|
|
@@ -328,6 +294,9 @@ class Coax extends HTMLElement {
|
|
|
328
294
|
this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
329
295
|
this.lang = 'plain';
|
|
330
296
|
this.alias = 'Plain Text';
|
|
297
|
+
this.lineString = '';
|
|
298
|
+
this.speed = 5;
|
|
299
|
+
this.autoScroll = true;
|
|
331
300
|
// 1. 初始化基础骨架
|
|
332
301
|
this.shadowRoot.innerHTML = `
|
|
333
302
|
<style id="base-styles">
|
|
@@ -345,16 +314,16 @@ class Coax extends HTMLElement {
|
|
|
345
314
|
--border-color:rgb(224, 224, 224);
|
|
346
315
|
--color-code:rgb(51, 51, 51);
|
|
347
316
|
--color-index:rgb(153, 153, 153);
|
|
348
|
-
--color-stripe:
|
|
349
|
-
--color-hover:
|
|
317
|
+
--color-stripe:rgba(0,0,0,0.04);
|
|
318
|
+
--color-hover:rgba(0,0,0,0.06);
|
|
350
319
|
}
|
|
351
320
|
:host([scheme="dark"]){
|
|
352
321
|
--background: #282c34;
|
|
353
322
|
--border-color: transparent;
|
|
354
323
|
--color-code: #abb2bf;
|
|
355
324
|
--color-index:rgb(153, 153, 153);
|
|
356
|
-
--color-stripe:
|
|
357
|
-
--color-hover:
|
|
325
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
326
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
358
327
|
}
|
|
359
328
|
@media (prefers-color-scheme: dark) {
|
|
360
329
|
:host{
|
|
@@ -362,8 +331,8 @@ class Coax extends HTMLElement {
|
|
|
362
331
|
--border-color: transparent;
|
|
363
332
|
--color-code: #abb2bf;
|
|
364
333
|
--color-index:rgb(153, 153, 153);
|
|
365
|
-
--color-stripe:
|
|
366
|
-
--color-hover:
|
|
334
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
335
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
367
336
|
}
|
|
368
337
|
}
|
|
369
338
|
:host {
|
|
@@ -373,7 +342,6 @@ class Coax extends HTMLElement {
|
|
|
373
342
|
background:var(--${NAMESPACE}-code-background-color,var(--background));
|
|
374
343
|
color:var(--${NAMESPACE}-code-color,var(--color-code));
|
|
375
344
|
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));
|
|
376
|
-
overflow:auto;
|
|
377
345
|
transition: border-color 0.3s ease,color 0.3s ease;
|
|
378
346
|
border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
|
|
379
347
|
}
|
|
@@ -390,6 +358,7 @@ class Coax extends HTMLElement {
|
|
|
390
358
|
padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
|
|
391
359
|
height:var(--${NAMESPACE}-code-height,var(--height));
|
|
392
360
|
max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
|
|
361
|
+
overflow:auto;
|
|
393
362
|
}
|
|
394
363
|
pre,code{
|
|
395
364
|
font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
|
|
@@ -405,6 +374,9 @@ class Coax extends HTMLElement {
|
|
|
405
374
|
flex:auto;
|
|
406
375
|
}
|
|
407
376
|
}
|
|
377
|
+
code>div>div:empty:before{
|
|
378
|
+
content:' ';
|
|
379
|
+
}
|
|
408
380
|
:host([indexed]) code>div:before{
|
|
409
381
|
display:inline-flex;
|
|
410
382
|
color:var(--color-index);
|
|
@@ -412,13 +384,13 @@ class Coax extends HTMLElement {
|
|
|
412
384
|
min-width:var(--${NAMESPACE}-code-index-width,2em);
|
|
413
385
|
margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
|
|
414
386
|
}
|
|
415
|
-
:host([hoverable]) code>div:hover{
|
|
416
|
-
background-color:var(--color-hover);
|
|
417
|
-
}
|
|
418
387
|
:host([striped]) code>div:nth-child(odd){
|
|
419
388
|
background-color:var(--color-stripe);
|
|
420
389
|
}
|
|
421
|
-
:host([
|
|
390
|
+
:host([hoverable]) code>div:hover{
|
|
391
|
+
background-color:var(--color-hover);
|
|
392
|
+
}
|
|
393
|
+
:host([wrapped]) code>div>div{
|
|
422
394
|
white-space: pre-wrap;
|
|
423
395
|
}
|
|
424
396
|
:host([unnamed]) #code-name{
|
|
@@ -463,9 +435,15 @@ class Coax extends HTMLElement {
|
|
|
463
435
|
this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
|
|
464
436
|
this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
|
|
465
437
|
this.headerEl = getEl('#code-header', this.shadowRoot);
|
|
466
|
-
this.
|
|
467
|
-
this.
|
|
438
|
+
this.codeNameEl = getEl('#code-name', this.shadowRoot);
|
|
439
|
+
this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
|
|
440
|
+
this.codeBodyEl = getEl('#code-body', this.shadowRoot);
|
|
468
441
|
this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
|
|
442
|
+
this.codeBodyEl.addEventListener('scroll', () => {
|
|
443
|
+
let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
|
|
444
|
+
// 检测是否是用户手动滚动
|
|
445
|
+
this.autoScroll = !flag;
|
|
446
|
+
});
|
|
469
447
|
}
|
|
470
448
|
|
|
471
449
|
static register(name, config) {
|
|
@@ -474,17 +452,17 @@ class Coax extends HTMLElement {
|
|
|
474
452
|
}
|
|
475
453
|
//注册工具箱
|
|
476
454
|
static addTools(items) {
|
|
477
|
-
|
|
455
|
+
CoaxElem.tools = items;
|
|
478
456
|
}
|
|
479
457
|
//加入工具箱
|
|
480
458
|
mountTools(toolItems) {
|
|
481
459
|
requestAnimationFrame(() => {
|
|
482
|
-
this.
|
|
460
|
+
this.codeToolsEl.innerHTML = '';
|
|
483
461
|
let items = toolItems.map(k => {
|
|
484
462
|
k.action = k.action.bind(this);
|
|
485
463
|
return k;
|
|
486
464
|
});
|
|
487
|
-
this.
|
|
465
|
+
this.codeToolsEl.appendChild(createTools(items));
|
|
488
466
|
});
|
|
489
467
|
}
|
|
490
468
|
// Called when the element is connected to the DOM
|
|
@@ -492,7 +470,7 @@ class Coax extends HTMLElement {
|
|
|
492
470
|
this.render();
|
|
493
471
|
}
|
|
494
472
|
// Observed attributes for changes
|
|
495
|
-
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools']; }
|
|
473
|
+
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
|
|
496
474
|
|
|
497
475
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
498
476
|
if (oldVal === newVal)
|
|
@@ -500,19 +478,18 @@ class Coax extends HTMLElement {
|
|
|
500
478
|
if (name === 'height' || name === 'max-height') {
|
|
501
479
|
this.updateStyleByRegExp(name, newVal);
|
|
502
480
|
}
|
|
503
|
-
|
|
481
|
+
if (name === 'speed') {
|
|
482
|
+
this.speed = ~~(!!newVal);
|
|
483
|
+
}
|
|
484
|
+
if (name === 'lang') {
|
|
504
485
|
//更新当前语言
|
|
505
486
|
this.lang = newVal;
|
|
506
487
|
this.render();
|
|
507
488
|
}
|
|
508
|
-
|
|
509
|
-
this.nameEl.innerHTML = newVal === null ? (this.alias || this.lang) : '';
|
|
510
|
-
this.nameEl.innerHTML = '';
|
|
511
|
-
}
|
|
512
|
-
else if (name === 'tools') {
|
|
489
|
+
if (name === 'tools') {
|
|
513
490
|
if (!newVal)
|
|
514
|
-
this.
|
|
515
|
-
const tools = parseClasses$1(newVal), toolItems =
|
|
491
|
+
this.codeToolsEl.innerHTML = '';
|
|
492
|
+
const tools = parseClasses$1(newVal), toolItems = CoaxElem.tools.filter(k => tools.includes(k.name));
|
|
516
493
|
if (!toolItems.length)
|
|
517
494
|
return;
|
|
518
495
|
this.mountTools(toolItems);
|
|
@@ -529,36 +506,84 @@ class Coax extends HTMLElement {
|
|
|
529
506
|
getCssPrefix(config) {
|
|
530
507
|
return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
531
508
|
}
|
|
509
|
+
getHighLightString(string, config) {
|
|
510
|
+
config = config || CoaxElem.languages.get(this.lang);
|
|
511
|
+
if (!config)
|
|
512
|
+
return string;
|
|
513
|
+
// 如果找到了配置,则进行高亮处理
|
|
514
|
+
const cssPrefix = this.getCssPrefix(config),
|
|
515
|
+
// 获取用于语法高亮的正则表达式
|
|
516
|
+
combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
|
|
517
|
+
return string.replace(combinedRegex, (match, ...args) => {
|
|
518
|
+
const i = args.findIndex(val => val !== undefined);
|
|
519
|
+
return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
createLineWrap(index, startIndex) {
|
|
523
|
+
let dataIndex = 0;
|
|
524
|
+
if (index == null && startIndex == null) {
|
|
525
|
+
dataIndex = this.highlightedCodeEl.children.length;
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
const start = startIndex || this.highlightedCodeEl.children.length;
|
|
529
|
+
dataIndex = start + index;
|
|
530
|
+
}
|
|
531
|
+
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
532
|
+
}
|
|
533
|
+
getLineToFill(codeWrap, line, config) {
|
|
534
|
+
config = config || CoaxElem.languages.get(this.lang);
|
|
535
|
+
let highlightedLine = this.getHighLightString(line, config);
|
|
536
|
+
// 将高亮后的内容填充到 div 中
|
|
537
|
+
codeWrap.innerHTML = highlightedLine;
|
|
538
|
+
}
|
|
539
|
+
;
|
|
532
540
|
|
|
533
|
-
highlight(newCode) {
|
|
534
|
-
const config =
|
|
541
|
+
async highlight(newCode) {
|
|
542
|
+
const config = CoaxElem.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
|
|
535
543
|
// 将新源码按行分割
|
|
536
|
-
newCodeLines = newCode.split('\n')
|
|
544
|
+
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
545
|
+
//更新别名
|
|
546
|
+
this.updateName(config);
|
|
547
|
+
if (!newCodeLines.length)
|
|
548
|
+
return true;
|
|
537
549
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
550
|
+
for (let [index, lineString] of newCodeLines.entries()) {
|
|
551
|
+
//如果是空行则跳过
|
|
552
|
+
if (!lineString.trim() && this.hasAttribute('sanitized'))
|
|
553
|
+
continue;
|
|
554
|
+
// 创建一个 div 包裹每一行
|
|
555
|
+
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
556
|
+
//标记完成
|
|
557
|
+
this.closeLine(lineWrap);
|
|
558
|
+
if (this.hasAttribute('speed')) {
|
|
559
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
560
|
+
//流式打字
|
|
561
|
+
await typeWriter(lineString, {
|
|
562
|
+
speed: this.speed,
|
|
563
|
+
onDuringType: (char, fullText) => {
|
|
564
|
+
codeWrap.innerHTML = fullText;
|
|
565
|
+
}
|
|
549
566
|
});
|
|
567
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
550
568
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
569
|
+
else {
|
|
570
|
+
//直接打出
|
|
571
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
572
|
+
//
|
|
573
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
//滚动到最底部
|
|
577
|
+
this.autoScrollCode();
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
autoScrollCode() {
|
|
581
|
+
if (this.autoScroll) {
|
|
582
|
+
this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
|
|
583
|
+
}
|
|
559
584
|
}
|
|
560
585
|
injectThemeStyles() {
|
|
561
|
-
const config =
|
|
586
|
+
const config = CoaxElem.languages.get(this.lang);
|
|
562
587
|
if (!config)
|
|
563
588
|
return;
|
|
564
589
|
// Get language name, fallback to 'js' if not provided
|
|
@@ -566,17 +591,17 @@ class Coax extends HTMLElement {
|
|
|
566
591
|
//Generate dynamic CSS classes for each syntax rule
|
|
567
592
|
// 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
|
|
568
593
|
lightStyles = config.rules.map((rule) => `
|
|
569
|
-
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.
|
|
594
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.light ? ',' + rule.light : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
|
|
570
595
|
darkStyles += `:host([scheme="dark"]){`;
|
|
571
596
|
darkStyles += config.rules.map((rule) => `
|
|
572
|
-
${rule.
|
|
597
|
+
${rule.light ? `
|
|
573
598
|
.${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
|
|
574
599
|
darkStyles += `}`;
|
|
575
600
|
schemeStyles = `@media (prefers-color-scheme: dark){
|
|
576
601
|
:host{
|
|
577
602
|
`;
|
|
578
603
|
schemeStyles += config.rules.map((rule) => `
|
|
579
|
-
${rule.
|
|
604
|
+
${rule.light ? `
|
|
580
605
|
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
|
|
581
606
|
schemeStyles += `}`;
|
|
582
607
|
// Set the inner HTML of the shadow root, including styles and highlighted code
|
|
@@ -586,121 +611,260 @@ class Coax extends HTMLElement {
|
|
|
586
611
|
if (config?.themeStyles) {
|
|
587
612
|
this.themeStylesEl.textContent = config.themeStyles;
|
|
588
613
|
}
|
|
589
|
-
//更新别名
|
|
590
|
-
this.updateName(config);
|
|
591
614
|
}
|
|
592
615
|
updateName(config) {
|
|
593
616
|
if (this.hasAttribute('unnamed'))
|
|
594
617
|
return;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
618
|
+
if (!config) {
|
|
619
|
+
this.codeNameEl.innerHTML = 'Plain Text';
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
//更新别名
|
|
623
|
+
this.alias = config.alias || this.lang;
|
|
624
|
+
this.codeNameEl.innerHTML = this.alias;
|
|
625
|
+
}
|
|
598
626
|
}
|
|
599
627
|
|
|
600
|
-
render() {
|
|
628
|
+
render(code = this.source) {
|
|
601
629
|
//同时多次改变属性,只执行一次
|
|
602
630
|
// 如果已经在队列中,则直接返回
|
|
603
631
|
if (this._renderQueued)
|
|
604
632
|
return;
|
|
605
633
|
this._renderQueued = true;
|
|
606
634
|
// 使用 requestAnimationFrame 将渲染推迟到下一帧
|
|
607
|
-
requestAnimationFrame(() => {
|
|
608
|
-
this.
|
|
609
|
-
//一次性渲染
|
|
610
|
-
this.highlight(this.source);
|
|
635
|
+
requestAnimationFrame(async () => {
|
|
636
|
+
this.clear();
|
|
611
637
|
this.injectThemeStyles();
|
|
638
|
+
//一次性渲染
|
|
639
|
+
await this.highlight(code);
|
|
612
640
|
this._renderQueued = false;
|
|
613
641
|
});
|
|
614
642
|
}
|
|
643
|
+
clear() {
|
|
644
|
+
this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
|
|
645
|
+
}
|
|
615
646
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
this.
|
|
619
|
-
//清空
|
|
620
|
-
this.highlightedCodeEl.innerHTML = '';
|
|
621
|
-
this.highlight(newCode);
|
|
647
|
+
async replace(newCode) {
|
|
648
|
+
this.clear();
|
|
649
|
+
await this.highlight(newCode);
|
|
622
650
|
}
|
|
623
651
|
|
|
624
|
-
|
|
652
|
+
async append(newCode) {
|
|
625
653
|
// 将新的代码追加到现有代码末尾
|
|
626
654
|
this.source += `\n${newCode}`;
|
|
627
655
|
// 高亮新的部分
|
|
628
|
-
this.highlight(newCode);
|
|
656
|
+
await this.highlight(newCode);
|
|
629
657
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
{
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
{
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
{
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
|
|
667
|
-
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, color: '#d73a49', dark: '#e06c75' }
|
|
668
|
-
],
|
|
669
|
-
});
|
|
670
|
-
Coax.register('ts', {
|
|
671
|
-
rules: [
|
|
672
|
-
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
|
|
673
|
-
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, color: '#032f62', dark: '#61afef' },
|
|
674
|
-
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, color: '#d19a66', dark: '#e5c07b' },
|
|
675
|
-
{ 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/, color: '#e06c75', dark: '#d73a49' },
|
|
676
|
-
{ token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|boolean|true|false|console|window|document)\b/, color: '#56b6c2', dark: '#61afef' },
|
|
677
|
-
{ token: 'type', pattern: /\b[A-Z]\w*\b/, color: '#22863a', dark: '#8b949e' },
|
|
678
|
-
{ token: 'number', pattern: /\b\d+\b/, color: '#61afef', dark: '#d19a66' },
|
|
679
|
-
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
|
|
680
|
-
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, color: '#d73a49', dark: '#e06c75' }
|
|
681
|
-
],
|
|
682
|
-
});
|
|
683
|
-
Coax.addTools([{
|
|
684
|
-
name: 'copy',
|
|
685
|
-
icon: icaxCopy(),
|
|
686
|
-
action: function (arg) {
|
|
687
|
-
arg.wrapEl.onclick = () => {
|
|
688
|
-
//this只是组件实例
|
|
689
|
-
navigator.clipboard.writeText(this.source)
|
|
690
|
-
.then(() => {
|
|
691
|
-
console.log('Text successfully copied to clipboard');
|
|
692
|
-
arg.iconEl.innerHTML = icaxCheck();
|
|
693
|
-
arg.iconEl.toggleAttribute('disabled', true);
|
|
694
|
-
setTimeout(() => {
|
|
695
|
-
//恢复
|
|
696
|
-
arg.iconEl.removeAttribute('disabled');
|
|
697
|
-
arg.iconEl.innerHTML = icaxCopy();
|
|
698
|
-
}, 2000);
|
|
699
|
-
})
|
|
700
|
-
.catch(err => {
|
|
701
|
-
console.error('Error copying text to clipboard: ', err);
|
|
702
|
-
});
|
|
703
|
-
};
|
|
658
|
+
getActiveCodeWrap() {
|
|
659
|
+
const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
|
|
660
|
+
this.createLineWrap() : lastChild;
|
|
661
|
+
return {
|
|
662
|
+
lineWrap: lastLine,
|
|
663
|
+
codeWrap: lastLine.lastElementChild
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
closeLine(lineWrap) {
|
|
667
|
+
lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
|
|
668
|
+
lineWrap && (lineWrap.completed = true);
|
|
669
|
+
}
|
|
670
|
+
openLine(lineWrap) {
|
|
671
|
+
lineWrap = lineWrap || this.highlightedCodeEl.lastElementChild;
|
|
672
|
+
lineWrap && (lineWrap.completed = false);
|
|
673
|
+
}
|
|
674
|
+
stream(str, forceEnd = false) {
|
|
675
|
+
const { lineWrap, codeWrap } = this.getActiveCodeWrap();
|
|
676
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
677
|
+
//汇集
|
|
678
|
+
this.source += str;
|
|
679
|
+
//如果没有遇到换行符号,也可以强制结束
|
|
680
|
+
forceEnd && this.closeLine(lineWrap);
|
|
681
|
+
if (str.startsWith('\n') || str.startsWith('\r')) {
|
|
682
|
+
//标记完成
|
|
683
|
+
this.closeLine(lineWrap);
|
|
684
|
+
//渲染
|
|
685
|
+
this.getLineToFill(codeWrap, this.lineString);
|
|
686
|
+
//一行结束,清空临时行文本
|
|
687
|
+
this.lineString = '';
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
//插入文本
|
|
691
|
+
codeWrap.innerHTML += str;
|
|
692
|
+
//临时保存行文本
|
|
693
|
+
this.lineString += str;
|
|
704
694
|
}
|
|
705
|
-
|
|
706
|
-
|
|
695
|
+
//滚动到最底部
|
|
696
|
+
this.autoScrollCode();
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const css = [
|
|
701
|
+
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
702
|
+
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, light: '#032f62', dark: '#a5d6ff' },
|
|
703
|
+
{ token: 'func', pattern: /[a-z-]+\(?=/, light: '#e36209', dark: '#ffa657' },
|
|
704
|
+
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, light: '#005cc5', dark: '#79c0ff' },
|
|
705
|
+
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, light: '#22863a', dark: '#7ee787' },
|
|
706
|
+
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, light: '#d73a49', dark: '#ff7b72' },
|
|
707
|
+
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
708
|
+
{ token: 'punct', pattern: /[{}();:]/, light: '#24292e', dark: '#c9d1d9' }
|
|
709
|
+
];
|
|
710
|
+
|
|
711
|
+
const html = [
|
|
712
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#999999', dark: '#6e7681' },
|
|
713
|
+
{ token: 'doctype', pattern: /<!DOCTYPE[\s\S]*?>/i, light: '#6a737d', dark: '#8b949e' },
|
|
714
|
+
// 匹配标签名: <div, </div
|
|
715
|
+
{ token: 'tag', pattern: /<\/?[a-zA-Z0-9]+/, light: '#22863a', dark: '#7ee787' },
|
|
716
|
+
// 匹配属性名: class=
|
|
717
|
+
{ token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
718
|
+
// 匹配属性值: "value"
|
|
719
|
+
{ token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, light: '#032f62', dark: '#a5d6ff' },
|
|
720
|
+
// 匹配标签闭合: >, />
|
|
721
|
+
{ token: 'bracket', pattern: /\/?>/, light: '#24292e', dark: '#c9d1d9' }
|
|
722
|
+
];
|
|
723
|
+
|
|
724
|
+
const javascript = [
|
|
725
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
726
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
727
|
+
{ 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' },
|
|
728
|
+
{ token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/, light: '#e36209', dark: '#ffa657' },
|
|
729
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
730
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
731
|
+
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, light: '#069598', dark: '#56b6c2' }
|
|
732
|
+
];
|
|
733
|
+
|
|
734
|
+
const markdown = [
|
|
735
|
+
// 注释: 这是 Markdown 中的行内注释
|
|
736
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#6a737d', dark: '#8b949e' },
|
|
737
|
+
// 标题: 通过 `#` 来定义标题
|
|
738
|
+
{ token: 'heading', pattern: /(^|\n)(#{1,6})\s*(.+)/, light: '#e36209', dark: '#ffa657' },
|
|
739
|
+
// 粗体: **text** 或 __text__
|
|
740
|
+
{ token: 'bold', pattern: /\*\*([^*]+)\*\*|__([^_]+)__/g, light: '#d73a49', dark: '#ff7b72' },
|
|
741
|
+
// 斜体: *text* 或 _text_
|
|
742
|
+
{ token: 'italic', pattern: /\*([^*]+)\*|_([^_]+)_/g, light: '#032f62', dark: '#a5d6ff' },
|
|
743
|
+
// 链接: [text](url)
|
|
744
|
+
{ token: 'link', pattern: /\[([^\]]+)\]\(([^)]+)\)/g, light: '#0288d1', dark: '#80c0ff' },
|
|
745
|
+
// 行内代码: `code`
|
|
746
|
+
{ token: 'inline-code', pattern: /`([^`]+)`/g, light: '#032f62', dark: '#98c379' },
|
|
747
|
+
// 代码块: ```code```
|
|
748
|
+
{ token: 'code-block', pattern: /```([^\n]+)\n([\s\S]*?)```/g, light: '#24292e', dark: '#c9d1d9' },
|
|
749
|
+
// 列表项: - item 或 * item
|
|
750
|
+
{ token: 'list-item', pattern: /(^|\n)([-*])\s+(.+)/g, light: '#5c6e7c', dark: '#8b949e' },
|
|
751
|
+
// 引用: > text
|
|
752
|
+
{ token: 'quote', pattern: /(^|\n)>[ \t]*(.+)/g, light: '#6f42c1', dark: '#d2a8ff' },
|
|
753
|
+
// 图片: 
|
|
754
|
+
{ token: 'image', pattern: /!\[([^\]]+)\]\(([^)]+)\)/g, light: '#d73a49', dark: '#ff7b72' },
|
|
755
|
+
// 分割线: ---
|
|
756
|
+
{ token: 'hr', pattern: /^(---|___|\*\*\*)\s*$/gm, light: '#24292e', dark: '#c9d1d9' },
|
|
757
|
+
// 强调和删除: ~~text~~
|
|
758
|
+
{ token: 'strikethrough', pattern: /~~([^~]+)~~/g, light: '#e36209', dark: '#ffa657' },
|
|
759
|
+
// 表格: | header1 | header2 |
|
|
760
|
+
{ token: 'table', pattern: /\|([^\|]+)\|([^\|]+)\|/g, light: '#5c6e7c', dark: '#8b949e' }
|
|
761
|
+
];
|
|
762
|
+
|
|
763
|
+
const typescript = [
|
|
764
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
765
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
766
|
+
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, light: '#953800', dark: '#ffa657' },
|
|
767
|
+
{ 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' },
|
|
768
|
+
{ token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/, light: '#e36209', dark: '#ffa657' },
|
|
769
|
+
{ token: 'type', pattern: /\b[A-Z]\w*\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
770
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
771
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
772
|
+
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, light: '#089ba6', dark: '#79c0ff' }
|
|
773
|
+
];
|
|
774
|
+
|
|
775
|
+
const COMMA = ',';
|
|
776
|
+
|
|
777
|
+
const SPACE = ' ';
|
|
778
|
+
|
|
779
|
+
const trim = (str, placement = '') => {
|
|
780
|
+
if (typeof str !== 'string') {
|
|
781
|
+
console.warn('Expected a string input');
|
|
782
|
+
return '';
|
|
783
|
+
}
|
|
784
|
+
switch (placement) {
|
|
785
|
+
case 'start':
|
|
786
|
+
return str.trimStart();
|
|
787
|
+
case 'end':
|
|
788
|
+
return str.trimEnd();
|
|
789
|
+
case 'both':
|
|
790
|
+
return str.trim();
|
|
791
|
+
case 'global':
|
|
792
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
793
|
+
default:
|
|
794
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
const parseClasses = (data) => {
|
|
799
|
+
let separator, result = [];
|
|
800
|
+
if (Array.isArray(data)) {
|
|
801
|
+
// If data is already an array, filter out invalid values
|
|
802
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
// Trim the input string and handle multiple spaces
|
|
806
|
+
data = trim(data);
|
|
807
|
+
// Use comma as the separator if present, otherwise use space
|
|
808
|
+
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
809
|
+
result = data.split(separator);
|
|
810
|
+
}
|
|
811
|
+
// Trim each item globally and filter out any empty strings
|
|
812
|
+
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const rtlStyle = (name = '') => `
|
|
816
|
+
<style>
|
|
817
|
+
:where([dir="rtl"]) .icax-${name},
|
|
818
|
+
:where(:dir(rtl)) .icax-${name} {
|
|
819
|
+
transform: scaleX(-1);
|
|
820
|
+
transform-origin: center;
|
|
821
|
+
}
|
|
822
|
+
</style>
|
|
823
|
+
`;
|
|
824
|
+
|
|
825
|
+
const wrap = (content, fun, isRtl = false, options) => {
|
|
826
|
+
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
|
|
827
|
+
// 得到 "icax-left"
|
|
828
|
+
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
829
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
830
|
+
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
831
|
+
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
832
|
+
${content}
|
|
833
|
+
</svg>`;
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
837
|
+
|
|
838
|
+
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);
|
|
839
|
+
|
|
840
|
+
const copy = {
|
|
841
|
+
name: 'copy',
|
|
842
|
+
icon: icaxCopy(),
|
|
843
|
+
action: function (arg) {
|
|
844
|
+
arg.wrapEl.onclick = () => {
|
|
845
|
+
//this只是组件实例
|
|
846
|
+
navigator.clipboard.writeText(this.source)
|
|
847
|
+
.then(() => {
|
|
848
|
+
console.log('Text successfully copied to clipboard');
|
|
849
|
+
arg.iconEl.innerHTML = icaxCheck();
|
|
850
|
+
arg.iconEl.toggleAttribute('disabled', true);
|
|
851
|
+
setTimeout(() => {
|
|
852
|
+
//恢复
|
|
853
|
+
arg.iconEl.removeAttribute('disabled');
|
|
854
|
+
arg.iconEl.innerHTML = icaxCopy();
|
|
855
|
+
}, 2000);
|
|
856
|
+
})
|
|
857
|
+
.catch(err => {
|
|
858
|
+
console.error('Error copying text to clipboard: ', err);
|
|
859
|
+
});
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
exports.CoaxElem = CoaxElem;
|
|
865
|
+
exports.copy = copy;
|
|
866
|
+
exports.css = css;
|
|
867
|
+
exports.html = html;
|
|
868
|
+
exports.javascript = javascript;
|
|
869
|
+
exports.markdown = markdown;
|
|
870
|
+
exports.typescript = typescript;
|