@codady/coax 0.0.2 → 0.0.4
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/LICENSE +1 -1
- package/README.md +331 -166
- package/dist/coax.cjs.js +450 -271
- package/dist/coax.cjs.min.js +4 -4
- package/dist/coax.esm.js +444 -271
- package/dist/coax.esm.min.js +4 -4
- package/dist/coax.umd.js +466 -266
- package/dist/coax.umd.min.js +4 -4
- package/examples/.htaccess +0 -0
- package/examples/append-highlight.html +82 -0
- package/examples/color-selector.html +412 -0
- package/examples/deepseek-highlight.html +100 -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 +4 -4
- package/rollup.config.js +3 -3
- package/script-note.js +2 -2
- package/src/Coax.js +25 -414
- package/src/Coax.ts +28 -443
- package/src/components/Coax.js +528 -0
- package/src/components/Coax.ts +556 -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,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2026-1-
|
|
3
|
+
* @since Last modified: 2026-1-12 14:46:3
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.4
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
|
-
* @description Coax is a
|
|
7
|
+
* @description Coax is a lightweight web component for elegant code display with syntax highlighting, typewriter effects, and theme switching. Supports JavaScript, HTML, CSS, TypeScript, and Markdown with copy tools and customization.
|
|
8
8
|
* @see {@link https://coax.axui.cn|Official website}
|
|
9
9
|
* @see {@link https://github.com/codady/coax/issues|github issues}
|
|
10
10
|
* @see {@link https://gitee.com/codady/coax/issues|Gitee issues}
|
|
@@ -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,94 +268,34 @@ const createTools = (data) => {
|
|
|
241
268
|
return toolsEl;
|
|
242
269
|
};
|
|
243
270
|
|
|
244
|
-
const COMMA = ',';
|
|
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
271
|
class Coax extends HTMLElement {
|
|
310
272
|
// A static Map to hold the configuration for different languages
|
|
311
273
|
static languages = new Map();
|
|
274
|
+
// A static array to hold the tools registered with the component
|
|
312
275
|
static tools = [];
|
|
313
|
-
source;
|
|
314
|
-
_renderQueued = false;
|
|
315
|
-
baseStylesEl;
|
|
316
|
-
themeStylesEl;
|
|
317
|
-
dynamicStylesEl;
|
|
318
|
-
highlightedCodeEl;
|
|
319
|
-
headerEl;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
276
|
+
source; // Source code content to be highlighted
|
|
277
|
+
_renderQueued = false; // Flag to prevent multiple render requests
|
|
278
|
+
baseStylesEl; // Element for base styles
|
|
279
|
+
themeStylesEl; // Element for theme styles
|
|
280
|
+
dynamicStylesEl; // Element for dynamic styles
|
|
281
|
+
highlightedCodeEl; // Element that holds the highlighted code
|
|
282
|
+
headerEl; // Header element (for code name, tools, etc.)
|
|
283
|
+
codeNameEl; // Code name element (shows language or alias)
|
|
284
|
+
codeToolsEl; // Code tools element (for interactive tools like copy)
|
|
285
|
+
codeBodyEl; // Code body element (the container for the code)
|
|
286
|
+
lang = 'plain'; // Language of the code (default is plain text)
|
|
287
|
+
alias = 'Plain Text'; // Alias name for the language (default is 'Plain Text')
|
|
288
|
+
lineString = ''; // The current line's string being typed
|
|
289
|
+
lastLineString = ''; // The last line's string
|
|
290
|
+
speed = 5; // Speed of the typing effect (higher is slower)
|
|
291
|
+
autoScroll = true; // Flag to enable/disable auto-scrolling
|
|
323
292
|
constructor() {
|
|
324
293
|
super();
|
|
325
294
|
// Attach a Shadow DOM to the custom element
|
|
326
295
|
this.attachShadow({ mode: 'open' });
|
|
327
296
|
// Remove leading/trailing whitespace from the raw code content
|
|
328
297
|
this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
329
|
-
|
|
330
|
-
this.alias = 'Plain Text';
|
|
331
|
-
// 1. 初始化基础骨架
|
|
298
|
+
// Initialize the basic structure of the component
|
|
332
299
|
this.shadowRoot.innerHTML = `
|
|
333
300
|
<style id="base-styles">
|
|
334
301
|
:host {
|
|
@@ -345,16 +312,16 @@ class Coax extends HTMLElement {
|
|
|
345
312
|
--border-color:rgb(224, 224, 224);
|
|
346
313
|
--color-code:rgb(51, 51, 51);
|
|
347
314
|
--color-index:rgb(153, 153, 153);
|
|
348
|
-
--color-stripe:
|
|
349
|
-
--color-hover:
|
|
315
|
+
--color-stripe:rgba(0,0,0,0.04);
|
|
316
|
+
--color-hover:rgba(0,0,0,0.06);
|
|
350
317
|
}
|
|
351
318
|
:host([scheme="dark"]){
|
|
352
319
|
--background: #282c34;
|
|
353
320
|
--border-color: transparent;
|
|
354
321
|
--color-code: #abb2bf;
|
|
355
322
|
--color-index:rgb(153, 153, 153);
|
|
356
|
-
--color-stripe:
|
|
357
|
-
--color-hover:
|
|
323
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
324
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
358
325
|
}
|
|
359
326
|
@media (prefers-color-scheme: dark) {
|
|
360
327
|
:host{
|
|
@@ -362,8 +329,8 @@ class Coax extends HTMLElement {
|
|
|
362
329
|
--border-color: transparent;
|
|
363
330
|
--color-code: #abb2bf;
|
|
364
331
|
--color-index:rgb(153, 153, 153);
|
|
365
|
-
--color-stripe:
|
|
366
|
-
--color-hover:
|
|
332
|
+
--color-stripe:rgba(255,255,255,0.04);
|
|
333
|
+
--color-hover:rgba(255,255,255,0.06);
|
|
367
334
|
}
|
|
368
335
|
}
|
|
369
336
|
:host {
|
|
@@ -373,7 +340,6 @@ class Coax extends HTMLElement {
|
|
|
373
340
|
background:var(--${NAMESPACE}-code-background-color,var(--background));
|
|
374
341
|
color:var(--${NAMESPACE}-code-color,var(--color-code));
|
|
375
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));
|
|
376
|
-
overflow:auto;
|
|
377
343
|
transition: border-color 0.3s ease,color 0.3s ease;
|
|
378
344
|
border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
|
|
379
345
|
}
|
|
@@ -390,6 +356,7 @@ class Coax extends HTMLElement {
|
|
|
390
356
|
padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
|
|
391
357
|
height:var(--${NAMESPACE}-code-height,var(--height));
|
|
392
358
|
max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
|
|
359
|
+
overflow:auto;
|
|
393
360
|
}
|
|
394
361
|
pre,code{
|
|
395
362
|
font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
|
|
@@ -405,6 +372,9 @@ class Coax extends HTMLElement {
|
|
|
405
372
|
flex:auto;
|
|
406
373
|
}
|
|
407
374
|
}
|
|
375
|
+
code>div>div:empty:before{
|
|
376
|
+
content:' ';
|
|
377
|
+
}
|
|
408
378
|
:host([indexed]) code>div:before{
|
|
409
379
|
display:inline-flex;
|
|
410
380
|
color:var(--color-index);
|
|
@@ -412,13 +382,13 @@ class Coax extends HTMLElement {
|
|
|
412
382
|
min-width:var(--${NAMESPACE}-code-index-width,2em);
|
|
413
383
|
margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
|
|
414
384
|
}
|
|
415
|
-
:host([hoverable]) code>div:hover{
|
|
416
|
-
background-color:var(--color-hover);
|
|
417
|
-
}
|
|
418
385
|
:host([striped]) code>div:nth-child(odd){
|
|
419
386
|
background-color:var(--color-stripe);
|
|
420
387
|
}
|
|
421
|
-
:host([
|
|
388
|
+
:host([hoverable]) code>div:hover{
|
|
389
|
+
background-color:var(--color-hover);
|
|
390
|
+
}
|
|
391
|
+
:host([wrapped]) code>div>div{
|
|
422
392
|
white-space: pre-wrap;
|
|
423
393
|
}
|
|
424
394
|
:host([unnamed]) #code-name{
|
|
@@ -458,41 +428,47 @@ class Coax extends HTMLElement {
|
|
|
458
428
|
<pre><code id="highlight-code"></code></pre>
|
|
459
429
|
</div>
|
|
460
430
|
`;
|
|
461
|
-
//
|
|
431
|
+
// Cache references to various elements
|
|
462
432
|
this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
|
|
463
433
|
this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
|
|
464
434
|
this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
|
|
465
435
|
this.headerEl = getEl('#code-header', this.shadowRoot);
|
|
466
|
-
this.
|
|
467
|
-
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);
|
|
468
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
|
+
// Check if the user manually scrolled
|
|
443
|
+
this.autoScroll = !flag;
|
|
444
|
+
});
|
|
469
445
|
}
|
|
470
446
|
|
|
471
447
|
static register(name, config) {
|
|
472
448
|
// Store the language configuration in the static map
|
|
473
449
|
this.languages.set(name, { ...config });
|
|
474
450
|
}
|
|
475
|
-
|
|
451
|
+
|
|
476
452
|
static addTools(items) {
|
|
477
453
|
Coax.tools = items;
|
|
478
454
|
}
|
|
479
|
-
|
|
455
|
+
|
|
480
456
|
mountTools(toolItems) {
|
|
481
457
|
requestAnimationFrame(() => {
|
|
482
|
-
this.
|
|
458
|
+
this.codeToolsEl.innerHTML = '';
|
|
483
459
|
let items = toolItems.map(k => {
|
|
484
460
|
k.action = k.action.bind(this);
|
|
485
461
|
return k;
|
|
486
462
|
});
|
|
487
|
-
this.
|
|
463
|
+
this.codeToolsEl.appendChild(createTools(items));
|
|
488
464
|
});
|
|
489
465
|
}
|
|
490
|
-
|
|
466
|
+
|
|
491
467
|
connectedCallback() {
|
|
492
468
|
this.render();
|
|
493
469
|
}
|
|
494
|
-
|
|
495
|
-
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools']; }
|
|
470
|
+
|
|
471
|
+
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
|
|
496
472
|
|
|
497
473
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
498
474
|
if (oldVal === newVal)
|
|
@@ -500,18 +476,18 @@ class Coax extends HTMLElement {
|
|
|
500
476
|
if (name === 'height' || name === 'max-height') {
|
|
501
477
|
this.updateStyleByRegExp(name, newVal);
|
|
502
478
|
}
|
|
503
|
-
|
|
504
|
-
|
|
479
|
+
if (name === 'speed') {
|
|
480
|
+
// Convert to integer (0 or 1)
|
|
481
|
+
this.speed = ~~(!!newVal);
|
|
482
|
+
}
|
|
483
|
+
if (name === 'lang') {
|
|
484
|
+
// Update the language and re-render
|
|
505
485
|
this.lang = newVal;
|
|
506
486
|
this.render();
|
|
507
487
|
}
|
|
508
|
-
|
|
509
|
-
this.nameEl.innerHTML = newVal === null ? (this.alias || this.lang) : '';
|
|
510
|
-
this.nameEl.innerHTML = '';
|
|
511
|
-
}
|
|
512
|
-
else if (name === 'tools') {
|
|
488
|
+
if (name === 'tools') {
|
|
513
489
|
if (!newVal)
|
|
514
|
-
this.
|
|
490
|
+
this.codeToolsEl.innerHTML = '';
|
|
515
491
|
const tools = parseClasses$1(newVal), toolItems = Coax.tools.filter(k => tools.includes(k.name));
|
|
516
492
|
if (!toolItems.length)
|
|
517
493
|
return;
|
|
@@ -526,37 +502,91 @@ class Coax extends HTMLElement {
|
|
|
526
502
|
// 替换为新的属性值
|
|
527
503
|
this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
|
|
528
504
|
}
|
|
505
|
+
|
|
529
506
|
getCssPrefix(config) {
|
|
530
507
|
return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
531
508
|
}
|
|
532
509
|
|
|
533
|
-
|
|
510
|
+
getHighLightString(string, config) {
|
|
511
|
+
config = config || Coax.languages.get(this.lang);
|
|
512
|
+
if (!config)
|
|
513
|
+
return string;
|
|
514
|
+
// 如果找到了配置,则进行高亮处理
|
|
515
|
+
const cssPrefix = this.getCssPrefix(config),
|
|
516
|
+
// 获取用于语法高亮的正则表达式
|
|
517
|
+
combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
|
|
518
|
+
return string.replace(combinedRegex, (match, ...args) => {
|
|
519
|
+
const i = args.findIndex(val => val !== undefined);
|
|
520
|
+
return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
createLineWrap(index, startIndex) {
|
|
525
|
+
let dataIndex = 0;
|
|
526
|
+
if (index == null && startIndex == null) {
|
|
527
|
+
dataIndex = this.highlightedCodeEl.children.length;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
const start = startIndex || this.highlightedCodeEl.children.length;
|
|
531
|
+
dataIndex = start + index;
|
|
532
|
+
}
|
|
533
|
+
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
getLineToFill(codeWrap, line, config) {
|
|
537
|
+
config = config || Coax.languages.get(this.lang);
|
|
538
|
+
let highlightedLine = this.getHighLightString(line, config);
|
|
539
|
+
// 将高亮后的内容填充到 div 中
|
|
540
|
+
codeWrap.innerHTML = highlightedLine;
|
|
541
|
+
}
|
|
542
|
+
;
|
|
543
|
+
|
|
544
|
+
async highlight(newCode) {
|
|
534
545
|
const config = Coax.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
|
|
535
546
|
// 将新源码按行分割
|
|
536
|
-
newCodeLines = newCode.split('\n')
|
|
547
|
+
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
548
|
+
//更新别名
|
|
549
|
+
this.updateName(config);
|
|
550
|
+
if (!newCodeLines.length)
|
|
551
|
+
return true;
|
|
537
552
|
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
553
|
+
for (let [index, lineString] of newCodeLines.entries()) {
|
|
554
|
+
//如果是空行则跳过
|
|
555
|
+
if (!lineString.trim() && this.hasAttribute('sanitized'))
|
|
556
|
+
continue;
|
|
557
|
+
// 创建一个 div 包裹每一行
|
|
558
|
+
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
559
|
+
//标记完成
|
|
560
|
+
lineWrap.completed = true;
|
|
561
|
+
if (this.hasAttribute('speed')) {
|
|
562
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
563
|
+
//流式打字
|
|
564
|
+
await typeWriter(lineString, {
|
|
565
|
+
speed: this.speed,
|
|
566
|
+
onDuringType: (char, fullText) => {
|
|
567
|
+
codeWrap.innerHTML = fullText;
|
|
568
|
+
}
|
|
549
569
|
});
|
|
570
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
550
571
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
572
|
+
else {
|
|
573
|
+
//直接打出
|
|
574
|
+
this.getLineToFill(codeWrap, lineString, config);
|
|
575
|
+
//
|
|
576
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
//滚动到最底部
|
|
580
|
+
this.autoScrollCode();
|
|
581
|
+
return true;
|
|
559
582
|
}
|
|
583
|
+
|
|
584
|
+
autoScrollCode() {
|
|
585
|
+
if (this.autoScroll) {
|
|
586
|
+
this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
560
590
|
injectThemeStyles() {
|
|
561
591
|
const config = Coax.languages.get(this.lang);
|
|
562
592
|
if (!config)
|
|
@@ -566,141 +596,290 @@ class Coax extends HTMLElement {
|
|
|
566
596
|
//Generate dynamic CSS classes for each syntax rule
|
|
567
597
|
// 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
|
|
568
598
|
lightStyles = config.rules.map((rule) => `
|
|
569
|
-
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.
|
|
599
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.light ? ',' + rule.light : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
|
|
570
600
|
darkStyles += `:host([scheme="dark"]){`;
|
|
571
601
|
darkStyles += config.rules.map((rule) => `
|
|
572
|
-
${rule.
|
|
602
|
+
${rule.light ? `
|
|
573
603
|
.${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
|
|
574
604
|
darkStyles += `}`;
|
|
575
605
|
schemeStyles = `@media (prefers-color-scheme: dark){
|
|
576
606
|
:host{
|
|
577
607
|
`;
|
|
578
608
|
schemeStyles += config.rules.map((rule) => `
|
|
579
|
-
${rule.
|
|
609
|
+
${rule.light ? `
|
|
580
610
|
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
|
|
581
611
|
schemeStyles += `}`;
|
|
582
612
|
// Set the inner HTML of the shadow root, including styles and highlighted code
|
|
583
|
-
// 2. 精确更新 DOM 节点而非重写 ShadowRoot
|
|
584
613
|
this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
|
|
585
614
|
//附加主题样式
|
|
586
615
|
if (config?.themeStyles) {
|
|
587
616
|
this.themeStylesEl.textContent = config.themeStyles;
|
|
588
617
|
}
|
|
589
|
-
//更新别名
|
|
590
|
-
this.updateName(config);
|
|
591
618
|
}
|
|
619
|
+
|
|
592
620
|
updateName(config) {
|
|
593
621
|
if (this.hasAttribute('unnamed'))
|
|
594
622
|
return;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
623
|
+
if (!config) {
|
|
624
|
+
this.codeNameEl.innerHTML = 'Plain Text';
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
//更新别名
|
|
628
|
+
this.alias = config.alias || this.lang;
|
|
629
|
+
this.codeNameEl.innerHTML = this.alias;
|
|
630
|
+
}
|
|
598
631
|
}
|
|
599
632
|
|
|
600
|
-
render() {
|
|
633
|
+
render(code = this.source) {
|
|
601
634
|
//同时多次改变属性,只执行一次
|
|
602
|
-
// 如果已经在队列中,则直接返回
|
|
603
635
|
if (this._renderQueued)
|
|
604
636
|
return;
|
|
605
637
|
this._renderQueued = true;
|
|
606
638
|
// 使用 requestAnimationFrame 将渲染推迟到下一帧
|
|
607
|
-
requestAnimationFrame(() => {
|
|
608
|
-
this.
|
|
609
|
-
//一次性渲染
|
|
610
|
-
this.highlight(this.source);
|
|
639
|
+
requestAnimationFrame(async () => {
|
|
640
|
+
this.clear();
|
|
611
641
|
this.injectThemeStyles();
|
|
642
|
+
//一次性渲染
|
|
643
|
+
await this.highlight(code);
|
|
612
644
|
this._renderQueued = false;
|
|
613
645
|
});
|
|
614
646
|
}
|
|
615
647
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
this.source = newCode;
|
|
619
|
-
//清空
|
|
620
|
-
this.highlightedCodeEl.innerHTML = '';
|
|
621
|
-
this.highlight(newCode);
|
|
648
|
+
clear() {
|
|
649
|
+
this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
|
|
622
650
|
}
|
|
623
651
|
|
|
624
|
-
|
|
652
|
+
async replace(newCode) {
|
|
653
|
+
this.clear();
|
|
654
|
+
await this.highlight(newCode);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
async append(newCode) {
|
|
625
658
|
// 将新的代码追加到现有代码末尾
|
|
626
659
|
this.source += `\n${newCode}`;
|
|
627
660
|
// 高亮新的部分
|
|
628
|
-
this.highlight(newCode);
|
|
661
|
+
await this.highlight(newCode);
|
|
629
662
|
}
|
|
630
|
-
}
|
|
631
|
-
Coax.register('css', {
|
|
632
|
-
rules: [
|
|
633
|
-
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
|
|
634
|
-
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, color: '#61afef', dark: '#a5d6ff' },
|
|
635
|
-
{ token: 'func', pattern: /[a-z-]+\(?=/, color: '#e36209', dark: '#ffa657' },
|
|
636
|
-
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, color: '#005cc5', dark: '#79c0ff' },
|
|
637
|
-
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, color: '#6f42c1', dark: '#d2a8ff' },
|
|
638
|
-
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, color: '#d73a49', dark: '#ff7b72' },
|
|
639
|
-
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, color: '#005cc5', dark: '#79c0ff' },
|
|
640
|
-
{ token: 'punct', pattern: /[{}();:]/, color: '#24292e', dark: '#c9d1d9' }
|
|
641
|
-
],
|
|
642
663
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
{
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
{
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
-
};
|
|
664
|
+
getLastLine() {
|
|
665
|
+
const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
|
|
666
|
+
this.createLineWrap() : lastChild;
|
|
667
|
+
return {
|
|
668
|
+
lineWrap: lastLine,
|
|
669
|
+
codeWrap: lastLine.lastElementChild
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
close() {
|
|
674
|
+
const lineWrap = this.highlightedCodeEl.lastElementChild;
|
|
675
|
+
if (!lineWrap)
|
|
676
|
+
return;
|
|
677
|
+
lineWrap.completed = true;
|
|
678
|
+
//行结束前保存
|
|
679
|
+
this.lastLineString = this.lineString;
|
|
680
|
+
this.getLineToFill(lineWrap?.lastElementChild, this.lineString);
|
|
681
|
+
//一行结束,清空临时行文本
|
|
682
|
+
this.lineString = '';
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
open() {
|
|
686
|
+
const lineWrap = this.highlightedCodeEl.lastElementChild;
|
|
687
|
+
if (!lineWrap)
|
|
688
|
+
return;
|
|
689
|
+
lineWrap.completed = false;
|
|
690
|
+
//恢复最后一行字符串
|
|
691
|
+
(lineWrap?.lastElementChild).textContent = this.lineString = this.lastLineString;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
stream(str, forceClose = false) {
|
|
695
|
+
const { lineWrap, codeWrap } = this.getLastLine();
|
|
696
|
+
this.highlightedCodeEl.appendChild(lineWrap);
|
|
697
|
+
//汇集
|
|
698
|
+
this.source += str;
|
|
699
|
+
//如果没有遇到换行符号,也可以强制结束
|
|
700
|
+
if (forceClose || (str.startsWith('\n') || str.endsWith('\n'))) {
|
|
701
|
+
//标记完成
|
|
702
|
+
this.close();
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
//插入文本
|
|
706
|
+
codeWrap.innerHTML += str;
|
|
707
|
+
//临时保存行文本
|
|
708
|
+
this.lineString += str;
|
|
704
709
|
}
|
|
705
|
-
|
|
706
|
-
|
|
710
|
+
//滚动到最底部
|
|
711
|
+
this.autoScrollCode();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const css = [
|
|
716
|
+
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
717
|
+
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, light: '#032f62', dark: '#a5d6ff' },
|
|
718
|
+
{ token: 'func', pattern: /[a-z-]+\(?=/, light: '#e36209', dark: '#ffa657' },
|
|
719
|
+
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, light: '#005cc5', dark: '#79c0ff' },
|
|
720
|
+
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, light: '#22863a', dark: '#7ee787' },
|
|
721
|
+
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, light: '#d73a49', dark: '#ff7b72' },
|
|
722
|
+
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
723
|
+
{ token: 'punct', pattern: /[{}();:]/, light: '#24292e', dark: '#c9d1d9' }
|
|
724
|
+
];
|
|
725
|
+
|
|
726
|
+
const html = [
|
|
727
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#999999', dark: '#6e7681' },
|
|
728
|
+
{ token: 'doctype', pattern: /<!DOCTYPE[\s\S]*?>/i, light: '#6a737d', dark: '#8b949e' },
|
|
729
|
+
// 匹配标签名: <div, </div
|
|
730
|
+
{ token: 'tag', pattern: /<\/?[a-zA-Z0-9]+/, light: '#22863a', dark: '#7ee787' },
|
|
731
|
+
// 匹配属性名: class=
|
|
732
|
+
{ token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
733
|
+
// 匹配属性值: "value"
|
|
734
|
+
{ token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, light: '#032f62', dark: '#a5d6ff' },
|
|
735
|
+
// 匹配标签闭合: >, />
|
|
736
|
+
{ token: 'bracket', pattern: /\/?>/, light: '#24292e', dark: '#c9d1d9' }
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
const javascript = [
|
|
740
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
741
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
742
|
+
{ 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' },
|
|
743
|
+
{ token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/, light: '#e36209', dark: '#ffa657' },
|
|
744
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
745
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
746
|
+
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, light: '#069598', dark: '#56b6c2' }
|
|
747
|
+
];
|
|
748
|
+
|
|
749
|
+
const markdown = [
|
|
750
|
+
// 注释: 这是 Markdown 中的行内注释
|
|
751
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#6a737d', dark: '#8b949e' },
|
|
752
|
+
// 标题: 通过 `#` 来定义标题
|
|
753
|
+
{ token: 'heading', pattern: /(^|\n)(#{1,6})\s*(.+)/, light: '#e36209', dark: '#ffa657' },
|
|
754
|
+
// 粗体: **text** 或 __text__
|
|
755
|
+
{ token: 'bold', pattern: /\*\*([^*]+)\*\*|__([^_]+)__/g, light: '#d73a49', dark: '#ff7b72' },
|
|
756
|
+
// 斜体: *text* 或 _text_
|
|
757
|
+
{ token: 'italic', pattern: /\*([^*]+)\*|_([^_]+)_/g, light: '#032f62', dark: '#a5d6ff' },
|
|
758
|
+
// 链接: [text](url)
|
|
759
|
+
{ token: 'link', pattern: /\[([^\]]+)\]\(([^)]+)\)/g, light: '#0288d1', dark: '#80c0ff' },
|
|
760
|
+
// 行内代码: `code`
|
|
761
|
+
{ token: 'inline-code', pattern: /`([^`]+)`/g, light: '#032f62', dark: '#98c379' },
|
|
762
|
+
// 代码块: ```code```
|
|
763
|
+
{ token: 'code-block', pattern: /```([^\n]+)\n([\s\S]*?)```/g, light: '#24292e', dark: '#c9d1d9' },
|
|
764
|
+
// 列表项: - item 或 * item
|
|
765
|
+
{ token: 'list-item', pattern: /(^|\n)([-*])\s+(.+)/g, light: '#5c6e7c', dark: '#8b949e' },
|
|
766
|
+
// 引用: > text
|
|
767
|
+
{ token: 'quote', pattern: /(^|\n)>[ \t]*(.+)/g, light: '#6f42c1', dark: '#d2a8ff' },
|
|
768
|
+
// 图片: 
|
|
769
|
+
{ token: 'image', pattern: /!\[([^\]]+)\]\(([^)]+)\)/g, light: '#d73a49', dark: '#ff7b72' },
|
|
770
|
+
// 分割线: ---
|
|
771
|
+
{ token: 'hr', pattern: /^(---|___|\*\*\*)\s*$/gm, light: '#24292e', dark: '#c9d1d9' },
|
|
772
|
+
// 强调和删除: ~~text~~
|
|
773
|
+
{ token: 'strikethrough', pattern: /~~([^~]+)~~/g, light: '#e36209', dark: '#ffa657' },
|
|
774
|
+
// 表格: | header1 | header2 |
|
|
775
|
+
{ token: 'table', pattern: /\|([^\|]+)\|([^\|]+)\|/g, light: '#5c6e7c', dark: '#8b949e' }
|
|
776
|
+
];
|
|
777
|
+
|
|
778
|
+
const typescript = [
|
|
779
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
780
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
781
|
+
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, light: '#953800', dark: '#ffa657' },
|
|
782
|
+
{ 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' },
|
|
783
|
+
{ token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/, light: '#e36209', dark: '#ffa657' },
|
|
784
|
+
{ token: 'type', pattern: /\b[A-Z]\w*\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
785
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
786
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
787
|
+
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, light: '#089ba6', dark: '#79c0ff' }
|
|
788
|
+
];
|
|
789
|
+
|
|
790
|
+
const COMMA = ',';
|
|
791
|
+
|
|
792
|
+
const SPACE = ' ';
|
|
793
|
+
|
|
794
|
+
const trim = (str, placement = '') => {
|
|
795
|
+
if (typeof str !== 'string') {
|
|
796
|
+
console.warn('Expected a string input');
|
|
797
|
+
return '';
|
|
798
|
+
}
|
|
799
|
+
switch (placement) {
|
|
800
|
+
case 'start':
|
|
801
|
+
return str.trimStart();
|
|
802
|
+
case 'end':
|
|
803
|
+
return str.trimEnd();
|
|
804
|
+
case 'both':
|
|
805
|
+
return str.trim();
|
|
806
|
+
case 'global':
|
|
807
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
808
|
+
default:
|
|
809
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const parseClasses = (data) => {
|
|
814
|
+
let separator, result = [];
|
|
815
|
+
if (Array.isArray(data)) {
|
|
816
|
+
// If data is already an array, filter out invalid values
|
|
817
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
// Trim the input string and handle multiple spaces
|
|
821
|
+
data = trim(data);
|
|
822
|
+
// Use comma as the separator if present, otherwise use space
|
|
823
|
+
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
824
|
+
result = data.split(separator);
|
|
825
|
+
}
|
|
826
|
+
// Trim each item globally and filter out any empty strings
|
|
827
|
+
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
const rtlStyle = (name = '') => `
|
|
831
|
+
<style>
|
|
832
|
+
:where([dir="rtl"]) .icax-${name},
|
|
833
|
+
:where(:dir(rtl)) .icax-${name} {
|
|
834
|
+
transform: scaleX(-1);
|
|
835
|
+
transform-origin: center;
|
|
836
|
+
}
|
|
837
|
+
</style>
|
|
838
|
+
`;
|
|
839
|
+
|
|
840
|
+
const wrap = (content, fun, isRtl = false, options) => {
|
|
841
|
+
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
|
|
842
|
+
// 得到 "icax-left"
|
|
843
|
+
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
844
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
845
|
+
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
846
|
+
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
847
|
+
${content}
|
|
848
|
+
</svg>`;
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
852
|
+
|
|
853
|
+
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);
|
|
854
|
+
|
|
855
|
+
const copy = {
|
|
856
|
+
name: 'copy',
|
|
857
|
+
icon: icaxCopy(),
|
|
858
|
+
action: function (arg) {
|
|
859
|
+
arg.wrapEl.onclick = () => {
|
|
860
|
+
//this只是组件实例
|
|
861
|
+
navigator.clipboard.writeText(this.source)
|
|
862
|
+
.then(() => {
|
|
863
|
+
console.log('Text successfully copied to clipboard');
|
|
864
|
+
arg.iconEl.innerHTML = icaxCheck();
|
|
865
|
+
arg.iconEl.toggleAttribute('disabled', true);
|
|
866
|
+
setTimeout(() => {
|
|
867
|
+
//恢复
|
|
868
|
+
arg.iconEl.removeAttribute('disabled');
|
|
869
|
+
arg.iconEl.innerHTML = icaxCopy();
|
|
870
|
+
}, 2000);
|
|
871
|
+
})
|
|
872
|
+
.catch(err => {
|
|
873
|
+
console.error('Error copying text to clipboard: ', err);
|
|
874
|
+
});
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
exports.Coax = Coax;
|
|
880
|
+
exports.copy = copy;
|
|
881
|
+
exports.css = css;
|
|
882
|
+
exports.html = html;
|
|
883
|
+
exports.javascript = javascript;
|
|
884
|
+
exports.markdown = markdown;
|
|
885
|
+
exports.typescript = typescript;
|