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