@codady/coax 0.0.3 → 0.0.5
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 +88 -75
- package/dist/coax.cjs.min.js +4 -4
- package/dist/coax.esm.js +88 -75
- package/dist/coax.esm.min.js +4 -4
- package/dist/coax.umd.js +259 -247
- package/dist/coax.umd.min.js +4 -4
- package/examples/color-selector.html +3 -4
- package/examples/deepseek-highlight.html +19 -10
- package/examples/js-highlight.html +3 -9
- package/package.json +2 -2
- package/script-note.js +2 -2
- package/src/Coax.js +2 -3
- package/src/Coax.ts +2 -3
- package/src/components/{CoaxElem.js → Coax.js} +159 -90
- package/src/components/{CoaxElem.ts → Coax.ts} +169 -103
- package/src/modules.js +3 -3
- package/src/modules.ts +3 -3
- package/examples/.htaccess +0 -0
- package/examples/nginx.htaccess +0 -0
package/dist/coax.umd.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2026-1-12
|
|
3
|
+
* @since Last modified: 2026-1-12 15:14:48
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.5
|
|
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}
|
|
@@ -22,6 +22,170 @@
|
|
|
22
22
|
|
|
23
23
|
const NAMESPACE = 'ax';
|
|
24
24
|
|
|
25
|
+
const html = [
|
|
26
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#999999', dark: '#6e7681' },
|
|
27
|
+
{ token: 'doctype', pattern: /<!DOCTYPE[\s\S]*?>/i, light: '#6a737d', dark: '#8b949e' },
|
|
28
|
+
// 匹配标签名: <div, </div
|
|
29
|
+
{ token: 'tag', pattern: /<\/?[a-zA-Z0-9]+/, light: '#22863a', dark: '#7ee787' },
|
|
30
|
+
// 匹配属性名: class=
|
|
31
|
+
{ token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
32
|
+
// 匹配属性值: "value"
|
|
33
|
+
{ token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, light: '#032f62', dark: '#a5d6ff' },
|
|
34
|
+
// 匹配标签闭合: >, />
|
|
35
|
+
{ token: 'bracket', pattern: /\/?>/, light: '#24292e', dark: '#c9d1d9' }
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const javascript = [
|
|
39
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
40
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
41
|
+
{ 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' },
|
|
42
|
+
{ token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/, light: '#e36209', dark: '#ffa657' },
|
|
43
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
44
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
45
|
+
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, light: '#069598', dark: '#56b6c2' }
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const markdown = [
|
|
49
|
+
// 注释: 这是 Markdown 中的行内注释
|
|
50
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#6a737d', dark: '#8b949e' },
|
|
51
|
+
// 标题: 通过 `#` 来定义标题
|
|
52
|
+
{ token: 'heading', pattern: /(^|\n)(#{1,6})\s*(.+)/, light: '#e36209', dark: '#ffa657' },
|
|
53
|
+
// 粗体: **text** 或 __text__
|
|
54
|
+
{ token: 'bold', pattern: /\*\*([^*]+)\*\*|__([^_]+)__/g, light: '#d73a49', dark: '#ff7b72' },
|
|
55
|
+
// 斜体: *text* 或 _text_
|
|
56
|
+
{ token: 'italic', pattern: /\*([^*]+)\*|_([^_]+)_/g, light: '#032f62', dark: '#a5d6ff' },
|
|
57
|
+
// 链接: [text](url)
|
|
58
|
+
{ token: 'link', pattern: /\[([^\]]+)\]\(([^)]+)\)/g, light: '#0288d1', dark: '#80c0ff' },
|
|
59
|
+
// 行内代码: `code`
|
|
60
|
+
{ token: 'inline-code', pattern: /`([^`]+)`/g, light: '#032f62', dark: '#98c379' },
|
|
61
|
+
// 代码块: ```code```
|
|
62
|
+
{ token: 'code-block', pattern: /```([^\n]+)\n([\s\S]*?)```/g, light: '#24292e', dark: '#c9d1d9' },
|
|
63
|
+
// 列表项: - item 或 * item
|
|
64
|
+
{ token: 'list-item', pattern: /(^|\n)([-*])\s+(.+)/g, light: '#5c6e7c', dark: '#8b949e' },
|
|
65
|
+
// 引用: > text
|
|
66
|
+
{ token: 'quote', pattern: /(^|\n)>[ \t]*(.+)/g, light: '#6f42c1', dark: '#d2a8ff' },
|
|
67
|
+
// 图片: 
|
|
68
|
+
{ token: 'image', pattern: /!\[([^\]]+)\]\(([^)]+)\)/g, light: '#d73a49', dark: '#ff7b72' },
|
|
69
|
+
// 分割线: ---
|
|
70
|
+
{ token: 'hr', pattern: /^(---|___|\*\*\*)\s*$/gm, light: '#24292e', dark: '#c9d1d9' },
|
|
71
|
+
// 强调和删除: ~~text~~
|
|
72
|
+
{ token: 'strikethrough', pattern: /~~([^~]+)~~/g, light: '#e36209', dark: '#ffa657' },
|
|
73
|
+
// 表格: | header1 | header2 |
|
|
74
|
+
{ token: 'table', pattern: /\|([^\|]+)\|([^\|]+)\|/g, light: '#5c6e7c', dark: '#8b949e' }
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const typescript = [
|
|
78
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
79
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
80
|
+
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, light: '#953800', dark: '#ffa657' },
|
|
81
|
+
{ 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' },
|
|
82
|
+
{ token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/, light: '#e36209', dark: '#ffa657' },
|
|
83
|
+
{ token: 'type', pattern: /\b[A-Z]\w*\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
84
|
+
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
85
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
86
|
+
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, light: '#089ba6', dark: '#79c0ff' }
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const COMMA$1 = ',';
|
|
90
|
+
|
|
91
|
+
const SPACE$1 = ' ';
|
|
92
|
+
|
|
93
|
+
const trim$1 = (str, placement = '') => {
|
|
94
|
+
if (typeof str !== 'string') {
|
|
95
|
+
console.warn('Expected a string input');
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
switch (placement) {
|
|
99
|
+
case 'start':
|
|
100
|
+
return str.trimStart();
|
|
101
|
+
case 'end':
|
|
102
|
+
return str.trimEnd();
|
|
103
|
+
case 'both':
|
|
104
|
+
return str.trim();
|
|
105
|
+
case 'global':
|
|
106
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
107
|
+
default:
|
|
108
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const parseClasses$1 = (data) => {
|
|
113
|
+
let separator, result = [];
|
|
114
|
+
if (Array.isArray(data)) {
|
|
115
|
+
// If data is already an array, filter out invalid values
|
|
116
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Trim the input string and handle multiple spaces
|
|
120
|
+
data = trim$1(data);
|
|
121
|
+
// Use comma as the separator if present, otherwise use space
|
|
122
|
+
separator = data.includes(COMMA$1) ? COMMA$1 : SPACE$1;
|
|
123
|
+
result = data.split(separator);
|
|
124
|
+
}
|
|
125
|
+
// Trim each item globally and filter out any empty strings
|
|
126
|
+
return result.map((k) => trim$1(k, 'global')).filter(Boolean);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const rtlStyle = (name = '') => `
|
|
130
|
+
<style>
|
|
131
|
+
:where([dir="rtl"]) .icax-${name},
|
|
132
|
+
:where(:dir(rtl)) .icax-${name} {
|
|
133
|
+
transform: scaleX(-1);
|
|
134
|
+
transform-origin: center;
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const wrap = (content, fun, isRtl = false, options) => {
|
|
140
|
+
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses$1(options.classes).join(' ') : '',
|
|
141
|
+
// 得到 "icax-left"
|
|
142
|
+
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
143
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
144
|
+
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
145
|
+
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
146
|
+
${content}
|
|
147
|
+
</svg>`;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
151
|
+
|
|
152
|
+
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);
|
|
153
|
+
|
|
154
|
+
const copy = {
|
|
155
|
+
name: 'copy',
|
|
156
|
+
icon: icaxCopy(),
|
|
157
|
+
action: function (arg) {
|
|
158
|
+
arg.wrapEl.onclick = () => {
|
|
159
|
+
//this只是组件实例
|
|
160
|
+
navigator.clipboard.writeText(this.source)
|
|
161
|
+
.then(() => {
|
|
162
|
+
console.log('Text successfully copied to clipboard');
|
|
163
|
+
arg.iconEl.innerHTML = icaxCheck();
|
|
164
|
+
arg.iconEl.toggleAttribute('disabled', true);
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
//恢复
|
|
167
|
+
arg.iconEl.removeAttribute('disabled');
|
|
168
|
+
arg.iconEl.innerHTML = icaxCopy();
|
|
169
|
+
}, 2000);
|
|
170
|
+
})
|
|
171
|
+
.catch(err => {
|
|
172
|
+
console.error('Error copying text to clipboard: ', err);
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const css = [
|
|
179
|
+
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
180
|
+
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, light: '#032f62', dark: '#a5d6ff' },
|
|
181
|
+
{ token: 'func', pattern: /[a-z-]+\(?=/, light: '#e36209', dark: '#ffa657' },
|
|
182
|
+
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, light: '#005cc5', dark: '#79c0ff' },
|
|
183
|
+
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, light: '#22863a', dark: '#7ee787' },
|
|
184
|
+
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, light: '#d73a49', dark: '#ff7b72' },
|
|
185
|
+
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
186
|
+
{ token: 'punct', pattern: /[{}();:]/, light: '#24292e', dark: '#c9d1d9' }
|
|
187
|
+
];
|
|
188
|
+
|
|
25
189
|
const typeWriter = (text, options) => {
|
|
26
190
|
const speed = options.speed || 100; // Set typing speed (default to 100ms per character)
|
|
27
191
|
return new Promise((resolve) => {
|
|
@@ -49,11 +213,11 @@
|
|
|
49
213
|
});
|
|
50
214
|
};
|
|
51
215
|
|
|
52
|
-
const COMMA
|
|
216
|
+
const COMMA = ',';
|
|
53
217
|
|
|
54
|
-
const SPACE
|
|
218
|
+
const SPACE = ' ';
|
|
55
219
|
|
|
56
|
-
const trim
|
|
220
|
+
const trim = (str, placement = 'compress') => {
|
|
57
221
|
if (typeof str !== 'string') {
|
|
58
222
|
console.warn('Expected a string input');
|
|
59
223
|
return '';
|
|
@@ -72,7 +236,7 @@
|
|
|
72
236
|
}
|
|
73
237
|
};
|
|
74
238
|
|
|
75
|
-
const parseClasses
|
|
239
|
+
const parseClasses = (data) => {
|
|
76
240
|
let separator, result = [];
|
|
77
241
|
if (Array.isArray(data)) {
|
|
78
242
|
// If data is already an array, filter out invalid values
|
|
@@ -80,13 +244,13 @@
|
|
|
80
244
|
}
|
|
81
245
|
else {
|
|
82
246
|
// Trim the input string and handle multiple spaces
|
|
83
|
-
data = trim
|
|
247
|
+
data = trim(data);
|
|
84
248
|
// Use comma as the separator if present, otherwise use space
|
|
85
|
-
separator = data.includes(COMMA
|
|
249
|
+
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
86
250
|
result = data.split(separator);
|
|
87
251
|
}
|
|
88
252
|
// Trim each item globally and filter out any empty strings
|
|
89
|
-
return result.map((k) => trim
|
|
253
|
+
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
90
254
|
};
|
|
91
255
|
|
|
92
256
|
const getDataType = (obj) => {
|
|
@@ -232,7 +396,7 @@
|
|
|
232
396
|
const ALIAS = 'rep';
|
|
233
397
|
|
|
234
398
|
const addClasses = (target, classes, intercept) => {
|
|
235
|
-
const el = getEl(target), arr = parseClasses
|
|
399
|
+
const el = getEl(target), arr = parseClasses(classes);
|
|
236
400
|
if (!el || arr.length === 0) {
|
|
237
401
|
return;
|
|
238
402
|
}
|
|
@@ -272,36 +436,35 @@
|
|
|
272
436
|
return toolsEl;
|
|
273
437
|
};
|
|
274
438
|
|
|
275
|
-
class
|
|
439
|
+
class Coax extends HTMLElement {
|
|
276
440
|
// A static Map to hold the configuration for different languages
|
|
277
441
|
static languages = new Map();
|
|
442
|
+
// A static array to hold the tools registered with the component
|
|
278
443
|
static tools = [];
|
|
279
|
-
source;
|
|
280
|
-
_renderQueued = false;
|
|
281
|
-
baseStylesEl;
|
|
282
|
-
themeStylesEl;
|
|
283
|
-
dynamicStylesEl;
|
|
284
|
-
|
|
285
|
-
headerEl;
|
|
286
|
-
codeNameEl;
|
|
287
|
-
codeToolsEl;
|
|
288
|
-
codeBodyEl;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
444
|
+
source; // Source code content to be highlighted
|
|
445
|
+
_renderQueued = false; // Flag to prevent multiple render requests
|
|
446
|
+
baseStylesEl; // Element for base styles
|
|
447
|
+
themeStylesEl; // Element for theme styles
|
|
448
|
+
dynamicStylesEl; // Element for dynamic styles
|
|
449
|
+
highlightEl; // Element that holds the highlighted code
|
|
450
|
+
headerEl; // Header element (for code name, tools, etc.)
|
|
451
|
+
codeNameEl; // Code name element (shows language or alias)
|
|
452
|
+
codeToolsEl; // Code tools element (for interactive tools like copy)
|
|
453
|
+
codeBodyEl; // Code body element (the container for the code)
|
|
454
|
+
lang = 'plain'; // Language of the code (default is plain text)
|
|
455
|
+
alias = 'Plain Text'; // Alias name for the language (default is 'Plain Text')
|
|
456
|
+
lineString = ''; // The current line's string being typed
|
|
457
|
+
lastLineString = ''; // The last line's string
|
|
458
|
+
speed = 5; // Speed of the typing effect (higher is slower)
|
|
459
|
+
autoScroll = true; // Flag to enable/disable auto-scrolling
|
|
460
|
+
canListen = true; // Flag to enable/disable auto-scrolling
|
|
293
461
|
constructor() {
|
|
294
462
|
super();
|
|
295
463
|
// Attach a Shadow DOM to the custom element
|
|
296
464
|
this.attachShadow({ mode: 'open' });
|
|
297
465
|
// Remove leading/trailing whitespace from the raw code content
|
|
298
466
|
this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
299
|
-
|
|
300
|
-
this.alias = 'Plain Text';
|
|
301
|
-
this.lineString = '';
|
|
302
|
-
this.speed = 5;
|
|
303
|
-
this.autoScroll = true;
|
|
304
|
-
// 1. 初始化基础骨架
|
|
467
|
+
// Initialize the basic structure of the component
|
|
305
468
|
this.shadowRoot.innerHTML = `
|
|
306
469
|
<style id="base-styles">
|
|
307
470
|
:host {
|
|
@@ -431,10 +594,10 @@
|
|
|
431
594
|
<style id="theme-styles"></style>
|
|
432
595
|
<div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
|
|
433
596
|
<div id="code-body">
|
|
434
|
-
<pre><code id="highlight
|
|
597
|
+
<pre><code id="highlight"></code></pre>
|
|
435
598
|
</div>
|
|
436
599
|
`;
|
|
437
|
-
//
|
|
600
|
+
// Cache references to various elements
|
|
438
601
|
this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
|
|
439
602
|
this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
|
|
440
603
|
this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
|
|
@@ -442,10 +605,10 @@
|
|
|
442
605
|
this.codeNameEl = getEl('#code-name', this.shadowRoot);
|
|
443
606
|
this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
|
|
444
607
|
this.codeBodyEl = getEl('#code-body', this.shadowRoot);
|
|
445
|
-
this.
|
|
608
|
+
this.highlightEl = getEl('#highlight', this.shadowRoot);
|
|
446
609
|
this.codeBodyEl.addEventListener('scroll', () => {
|
|
447
610
|
let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
|
|
448
|
-
//
|
|
611
|
+
// Check if the user manually scrolled
|
|
449
612
|
this.autoScroll = !flag;
|
|
450
613
|
});
|
|
451
614
|
}
|
|
@@ -454,11 +617,11 @@
|
|
|
454
617
|
// Store the language configuration in the static map
|
|
455
618
|
this.languages.set(name, { ...config });
|
|
456
619
|
}
|
|
457
|
-
|
|
620
|
+
|
|
458
621
|
static addTools(items) {
|
|
459
|
-
|
|
622
|
+
Coax.tools = items;
|
|
460
623
|
}
|
|
461
|
-
|
|
624
|
+
|
|
462
625
|
mountTools(toolItems) {
|
|
463
626
|
requestAnimationFrame(() => {
|
|
464
627
|
this.codeToolsEl.innerHTML = '';
|
|
@@ -469,31 +632,32 @@
|
|
|
469
632
|
this.codeToolsEl.appendChild(createTools(items));
|
|
470
633
|
});
|
|
471
634
|
}
|
|
472
|
-
|
|
635
|
+
|
|
473
636
|
connectedCallback() {
|
|
474
637
|
this.render();
|
|
475
638
|
}
|
|
476
|
-
|
|
639
|
+
|
|
477
640
|
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
|
|
478
641
|
|
|
479
642
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
480
|
-
if (oldVal === newVal)
|
|
643
|
+
if (oldVal === newVal || !this.canListen)
|
|
481
644
|
return;
|
|
482
645
|
if (name === 'height' || name === 'max-height') {
|
|
483
646
|
this.updateStyleByRegExp(name, newVal);
|
|
484
647
|
}
|
|
485
648
|
if (name === 'speed') {
|
|
649
|
+
// Convert to integer (0 or 1)
|
|
486
650
|
this.speed = ~~(!!newVal);
|
|
487
651
|
}
|
|
488
652
|
if (name === 'lang') {
|
|
489
|
-
|
|
653
|
+
// Update the language and re-render
|
|
490
654
|
this.lang = newVal;
|
|
491
655
|
this.render();
|
|
492
656
|
}
|
|
493
657
|
if (name === 'tools') {
|
|
494
658
|
if (!newVal)
|
|
495
659
|
this.codeToolsEl.innerHTML = '';
|
|
496
|
-
const tools = parseClasses
|
|
660
|
+
const tools = parseClasses(newVal), toolItems = Coax.tools.filter(k => tools.includes(k.name));
|
|
497
661
|
if (!toolItems.length)
|
|
498
662
|
return;
|
|
499
663
|
this.mountTools(toolItems);
|
|
@@ -507,11 +671,13 @@
|
|
|
507
671
|
// 替换为新的属性值
|
|
508
672
|
this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
|
|
509
673
|
}
|
|
674
|
+
|
|
510
675
|
getCssPrefix(config) {
|
|
511
676
|
return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
512
677
|
}
|
|
678
|
+
|
|
513
679
|
getHighLightString(string, config) {
|
|
514
|
-
config = config ||
|
|
680
|
+
config = config || Coax.languages.get(this.lang);
|
|
515
681
|
if (!config)
|
|
516
682
|
return string;
|
|
517
683
|
// 如果找到了配置,则进行高亮处理
|
|
@@ -523,19 +689,21 @@
|
|
|
523
689
|
return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
|
|
524
690
|
});
|
|
525
691
|
}
|
|
692
|
+
|
|
526
693
|
createLineWrap(index, startIndex) {
|
|
527
694
|
let dataIndex = 0;
|
|
528
695
|
if (index == null && startIndex == null) {
|
|
529
|
-
dataIndex = this.
|
|
696
|
+
dataIndex = this.highlightEl.children.length;
|
|
530
697
|
}
|
|
531
698
|
else {
|
|
532
|
-
const start = startIndex || this.
|
|
699
|
+
const start = startIndex || this.highlightEl.children.length;
|
|
533
700
|
dataIndex = start + index;
|
|
534
701
|
}
|
|
535
702
|
return createEl('div', { 'data-index': dataIndex }, '<div></div>');
|
|
536
703
|
}
|
|
704
|
+
|
|
537
705
|
getLineToFill(codeWrap, line, config) {
|
|
538
|
-
config = config ||
|
|
706
|
+
config = config || Coax.languages.get(this.lang);
|
|
539
707
|
let highlightedLine = this.getHighLightString(line, config);
|
|
540
708
|
// 将高亮后的内容填充到 div 中
|
|
541
709
|
codeWrap.innerHTML = highlightedLine;
|
|
@@ -543,7 +711,7 @@
|
|
|
543
711
|
;
|
|
544
712
|
|
|
545
713
|
async highlight(newCode) {
|
|
546
|
-
const config =
|
|
714
|
+
const config = Coax.languages.get(this.lang), startIndex = this.highlightEl.children.length,
|
|
547
715
|
// 将新源码按行分割
|
|
548
716
|
newCodeLines = newCode ? newCode.split('\n') : [];
|
|
549
717
|
//更新别名
|
|
@@ -558,9 +726,9 @@
|
|
|
558
726
|
// 创建一个 div 包裹每一行
|
|
559
727
|
const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
|
|
560
728
|
//标记完成
|
|
561
|
-
|
|
729
|
+
lineWrap.completed = true;
|
|
562
730
|
if (this.hasAttribute('speed')) {
|
|
563
|
-
this.
|
|
731
|
+
this.highlightEl.appendChild(lineWrap);
|
|
564
732
|
//流式打字
|
|
565
733
|
await typeWriter(lineString, {
|
|
566
734
|
speed: this.speed,
|
|
@@ -574,20 +742,22 @@
|
|
|
574
742
|
//直接打出
|
|
575
743
|
this.getLineToFill(codeWrap, lineString, config);
|
|
576
744
|
//
|
|
577
|
-
this.
|
|
745
|
+
this.highlightEl.appendChild(lineWrap);
|
|
578
746
|
}
|
|
579
747
|
}
|
|
580
748
|
//滚动到最底部
|
|
581
749
|
this.autoScrollCode();
|
|
582
750
|
return true;
|
|
583
751
|
}
|
|
752
|
+
|
|
584
753
|
autoScrollCode() {
|
|
585
754
|
if (this.autoScroll) {
|
|
586
755
|
this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
|
|
587
756
|
}
|
|
588
757
|
}
|
|
758
|
+
|
|
589
759
|
injectThemeStyles() {
|
|
590
|
-
const config =
|
|
760
|
+
const config = Coax.languages.get(this.lang);
|
|
591
761
|
if (!config)
|
|
592
762
|
return;
|
|
593
763
|
// Get language name, fallback to 'js' if not provided
|
|
@@ -609,13 +779,13 @@
|
|
|
609
779
|
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
|
|
610
780
|
schemeStyles += `}`;
|
|
611
781
|
// Set the inner HTML of the shadow root, including styles and highlighted code
|
|
612
|
-
// 2. 精确更新 DOM 节点而非重写 ShadowRoot
|
|
613
782
|
this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
|
|
614
783
|
//附加主题样式
|
|
615
784
|
if (config?.themeStyles) {
|
|
616
785
|
this.themeStylesEl.textContent = config.themeStyles;
|
|
617
786
|
}
|
|
618
787
|
}
|
|
788
|
+
|
|
619
789
|
updateName(config) {
|
|
620
790
|
if (this.hasAttribute('unnamed'))
|
|
621
791
|
return;
|
|
@@ -631,21 +801,18 @@
|
|
|
631
801
|
|
|
632
802
|
render(code = this.source) {
|
|
633
803
|
//同时多次改变属性,只执行一次
|
|
634
|
-
// 如果已经在队列中,则直接返回
|
|
635
804
|
if (this._renderQueued)
|
|
636
805
|
return;
|
|
637
806
|
this._renderQueued = true;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
await this.highlight(code);
|
|
644
|
-
this._renderQueued = false;
|
|
645
|
-
});
|
|
807
|
+
this.clear();
|
|
808
|
+
this.injectThemeStyles();
|
|
809
|
+
//一次性渲染
|
|
810
|
+
this.highlight(code);
|
|
811
|
+
this._renderQueued = false;
|
|
646
812
|
}
|
|
813
|
+
|
|
647
814
|
clear() {
|
|
648
|
-
this.
|
|
815
|
+
this.highlightEl.innerHTML = this.source = this.lineString = '';
|
|
649
816
|
}
|
|
650
817
|
|
|
651
818
|
async replace(newCode) {
|
|
@@ -659,36 +826,46 @@
|
|
|
659
826
|
// 高亮新的部分
|
|
660
827
|
await this.highlight(newCode);
|
|
661
828
|
}
|
|
662
|
-
|
|
663
|
-
|
|
829
|
+
|
|
830
|
+
getLastLine() {
|
|
831
|
+
const lastChild = this.highlightEl.lastElementChild, lastLine = !lastChild || this.highlightEl.lastElementChild?.completed ?
|
|
664
832
|
this.createLineWrap() : lastChild;
|
|
665
833
|
return {
|
|
666
834
|
lineWrap: lastLine,
|
|
667
835
|
codeWrap: lastLine.lastElementChild
|
|
668
836
|
};
|
|
669
837
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
838
|
+
|
|
839
|
+
close() {
|
|
840
|
+
const lineWrap = this.highlightEl.lastElementChild;
|
|
841
|
+
if (!lineWrap)
|
|
842
|
+
return;
|
|
843
|
+
lineWrap.completed = true;
|
|
844
|
+
//行结束前保存
|
|
845
|
+
this.lastLineString = this.lineString;
|
|
846
|
+
this.getLineToFill(lineWrap?.lastElementChild, this.lineString);
|
|
847
|
+
//一行结束,清空临时行文本
|
|
848
|
+
this.lineString = '';
|
|
673
849
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
850
|
+
|
|
851
|
+
open() {
|
|
852
|
+
const lineWrap = this.highlightEl.lastElementChild;
|
|
853
|
+
if (!lineWrap)
|
|
854
|
+
return;
|
|
855
|
+
lineWrap.completed = false;
|
|
856
|
+
//恢复最后一行字符串
|
|
857
|
+
(lineWrap?.lastElementChild).textContent = this.lineString = this.lastLineString;
|
|
677
858
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
this.
|
|
859
|
+
|
|
860
|
+
stream(str, forceClose = false) {
|
|
861
|
+
const { lineWrap, codeWrap } = this.getLastLine();
|
|
862
|
+
this.highlightEl.appendChild(lineWrap);
|
|
681
863
|
//汇集
|
|
682
864
|
this.source += str;
|
|
683
865
|
//如果没有遇到换行符号,也可以强制结束
|
|
684
|
-
|
|
685
|
-
if (str.startsWith('\n') || str.startsWith('\r')) {
|
|
866
|
+
if (forceClose || (str.startsWith('\n') || str.endsWith('\n'))) {
|
|
686
867
|
//标记完成
|
|
687
|
-
this.
|
|
688
|
-
//渲染
|
|
689
|
-
this.getLineToFill(codeWrap, this.lineString);
|
|
690
|
-
//一行结束,清空临时行文本
|
|
691
|
-
this.lineString = '';
|
|
868
|
+
this.close();
|
|
692
869
|
}
|
|
693
870
|
else {
|
|
694
871
|
//插入文本
|
|
@@ -701,171 +878,6 @@
|
|
|
701
878
|
}
|
|
702
879
|
}
|
|
703
880
|
|
|
704
|
-
const html = [
|
|
705
|
-
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#999999', dark: '#6e7681' },
|
|
706
|
-
{ token: 'doctype', pattern: /<!DOCTYPE[\s\S]*?>/i, light: '#6a737d', dark: '#8b949e' },
|
|
707
|
-
// 匹配标签名: <div, </div
|
|
708
|
-
{ token: 'tag', pattern: /<\/?[a-zA-Z0-9]+/, light: '#22863a', dark: '#7ee787' },
|
|
709
|
-
// 匹配属性名: class=
|
|
710
|
-
{ token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
711
|
-
// 匹配属性值: "value"
|
|
712
|
-
{ token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, light: '#032f62', dark: '#a5d6ff' },
|
|
713
|
-
// 匹配标签闭合: >, />
|
|
714
|
-
{ token: 'bracket', pattern: /\/?>/, light: '#24292e', dark: '#c9d1d9' }
|
|
715
|
-
];
|
|
716
|
-
|
|
717
|
-
const javascript = [
|
|
718
|
-
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
719
|
-
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
720
|
-
{ token: 'keyword', pattern: /\b(async|await|break|case|catch|class|const|continue|default|delete|do|else|export|extends|finally|for|function|if|import|in|instanceof|new|return|super|switch|this|throw|try|typeof|var|while|with|yield|let|static)\b/, light: '#d73a49', dark: '#ff7b72' },
|
|
721
|
-
{ token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise|Number|String|Boolean)\b/, light: '#e36209', dark: '#ffa657' },
|
|
722
|
-
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
723
|
-
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
724
|
-
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, light: '#069598', dark: '#56b6c2' }
|
|
725
|
-
];
|
|
726
|
-
|
|
727
|
-
const markdown = [
|
|
728
|
-
// 注释: 这是 Markdown 中的行内注释
|
|
729
|
-
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, light: '#6a737d', dark: '#8b949e' },
|
|
730
|
-
// 标题: 通过 `#` 来定义标题
|
|
731
|
-
{ token: 'heading', pattern: /(^|\n)(#{1,6})\s*(.+)/, light: '#e36209', dark: '#ffa657' },
|
|
732
|
-
// 粗体: **text** 或 __text__
|
|
733
|
-
{ token: 'bold', pattern: /\*\*([^*]+)\*\*|__([^_]+)__/g, light: '#d73a49', dark: '#ff7b72' },
|
|
734
|
-
// 斜体: *text* 或 _text_
|
|
735
|
-
{ token: 'italic', pattern: /\*([^*]+)\*|_([^_]+)_/g, light: '#032f62', dark: '#a5d6ff' },
|
|
736
|
-
// 链接: [text](url)
|
|
737
|
-
{ token: 'link', pattern: /\[([^\]]+)\]\(([^)]+)\)/g, light: '#0288d1', dark: '#80c0ff' },
|
|
738
|
-
// 行内代码: `code`
|
|
739
|
-
{ token: 'inline-code', pattern: /`([^`]+)`/g, light: '#032f62', dark: '#98c379' },
|
|
740
|
-
// 代码块: ```code```
|
|
741
|
-
{ token: 'code-block', pattern: /```([^\n]+)\n([\s\S]*?)```/g, light: '#24292e', dark: '#c9d1d9' },
|
|
742
|
-
// 列表项: - item 或 * item
|
|
743
|
-
{ token: 'list-item', pattern: /(^|\n)([-*])\s+(.+)/g, light: '#5c6e7c', dark: '#8b949e' },
|
|
744
|
-
// 引用: > text
|
|
745
|
-
{ token: 'quote', pattern: /(^|\n)>[ \t]*(.+)/g, light: '#6f42c1', dark: '#d2a8ff' },
|
|
746
|
-
// 图片: 
|
|
747
|
-
{ token: 'image', pattern: /!\[([^\]]+)\]\(([^)]+)\)/g, light: '#d73a49', dark: '#ff7b72' },
|
|
748
|
-
// 分割线: ---
|
|
749
|
-
{ token: 'hr', pattern: /^(---|___|\*\*\*)\s*$/gm, light: '#24292e', dark: '#c9d1d9' },
|
|
750
|
-
// 强调和删除: ~~text~~
|
|
751
|
-
{ token: 'strikethrough', pattern: /~~([^~]+)~~/g, light: '#e36209', dark: '#ffa657' },
|
|
752
|
-
// 表格: | header1 | header2 |
|
|
753
|
-
{ token: 'table', pattern: /\|([^\|]+)\|([^\|]+)\|/g, light: '#5c6e7c', dark: '#8b949e' }
|
|
754
|
-
];
|
|
755
|
-
|
|
756
|
-
const typescript = [
|
|
757
|
-
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
758
|
-
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, light: '#032f62', dark: '#98c379' },
|
|
759
|
-
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, light: '#953800', dark: '#ffa657' },
|
|
760
|
-
{ token: 'keyword', pattern: /\b(abstract|as|async|await|break|case|catch|class|const|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|package|private|protected|public|readonly|return|set|static|super|switch|this|throw|try|type|typeof|var|while|with|yield)\b/, light: '#d73a49', dark: '#ff7b72' },
|
|
761
|
-
{ token: 'builtin', pattern: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|true|false|console|window|document)\b/, light: '#e36209', dark: '#ffa657' },
|
|
762
|
-
{ token: 'type', pattern: /\b[A-Z]\w*\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
763
|
-
{ token: 'number', pattern: /\b(0x[\da-fA-F]+|0b[01]+|\d+(\.\d+)?)\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
764
|
-
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, light: '#6f42c1', dark: '#d2a8ff' },
|
|
765
|
-
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, light: '#089ba6', dark: '#79c0ff' }
|
|
766
|
-
];
|
|
767
|
-
|
|
768
|
-
const COMMA = ',';
|
|
769
|
-
|
|
770
|
-
const SPACE = ' ';
|
|
771
|
-
|
|
772
|
-
const trim = (str, placement = '') => {
|
|
773
|
-
if (typeof str !== 'string') {
|
|
774
|
-
console.warn('Expected a string input');
|
|
775
|
-
return '';
|
|
776
|
-
}
|
|
777
|
-
switch (placement) {
|
|
778
|
-
case 'start':
|
|
779
|
-
return str.trimStart();
|
|
780
|
-
case 'end':
|
|
781
|
-
return str.trimEnd();
|
|
782
|
-
case 'both':
|
|
783
|
-
return str.trim();
|
|
784
|
-
case 'global':
|
|
785
|
-
return str.replace(/[\s\r\n]+/g, '');
|
|
786
|
-
default:
|
|
787
|
-
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
const parseClasses = (data) => {
|
|
792
|
-
let separator, result = [];
|
|
793
|
-
if (Array.isArray(data)) {
|
|
794
|
-
// If data is already an array, filter out invalid values
|
|
795
|
-
result = data.filter((k) => k && typeof k === 'string');
|
|
796
|
-
}
|
|
797
|
-
else {
|
|
798
|
-
// Trim the input string and handle multiple spaces
|
|
799
|
-
data = trim(data);
|
|
800
|
-
// Use comma as the separator if present, otherwise use space
|
|
801
|
-
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
802
|
-
result = data.split(separator);
|
|
803
|
-
}
|
|
804
|
-
// Trim each item globally and filter out any empty strings
|
|
805
|
-
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
806
|
-
};
|
|
807
|
-
|
|
808
|
-
const rtlStyle = (name = '') => `
|
|
809
|
-
<style>
|
|
810
|
-
:where([dir="rtl"]) .icax-${name},
|
|
811
|
-
:where(:dir(rtl)) .icax-${name} {
|
|
812
|
-
transform: scaleX(-1);
|
|
813
|
-
transform-origin: center;
|
|
814
|
-
}
|
|
815
|
-
</style>
|
|
816
|
-
`;
|
|
817
|
-
|
|
818
|
-
const wrap = (content, fun, isRtl = false, options) => {
|
|
819
|
-
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
|
|
820
|
-
// 得到 "icax-left"
|
|
821
|
-
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
822
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
823
|
-
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
824
|
-
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
825
|
-
${content}
|
|
826
|
-
</svg>`;
|
|
827
|
-
};
|
|
828
|
-
|
|
829
|
-
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
830
|
-
|
|
831
|
-
const icaxCopy = (options) => wrap(`<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>`, icaxCopy, false, options);
|
|
832
|
-
|
|
833
|
-
const copy = {
|
|
834
|
-
name: 'copy',
|
|
835
|
-
icon: icaxCopy(),
|
|
836
|
-
action: function (arg) {
|
|
837
|
-
arg.wrapEl.onclick = () => {
|
|
838
|
-
//this只是组件实例
|
|
839
|
-
navigator.clipboard.writeText(this.source)
|
|
840
|
-
.then(() => {
|
|
841
|
-
console.log('Text successfully copied to clipboard');
|
|
842
|
-
arg.iconEl.innerHTML = icaxCheck();
|
|
843
|
-
arg.iconEl.toggleAttribute('disabled', true);
|
|
844
|
-
setTimeout(() => {
|
|
845
|
-
//恢复
|
|
846
|
-
arg.iconEl.removeAttribute('disabled');
|
|
847
|
-
arg.iconEl.innerHTML = icaxCopy();
|
|
848
|
-
}, 2000);
|
|
849
|
-
})
|
|
850
|
-
.catch(err => {
|
|
851
|
-
console.error('Error copying text to clipboard: ', err);
|
|
852
|
-
});
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
const css = [
|
|
858
|
-
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, light: '#6a737d', dark: '#8b949e' },
|
|
859
|
-
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, light: '#032f62', dark: '#a5d6ff' },
|
|
860
|
-
{ token: 'func', pattern: /[a-z-]+\(?=/, light: '#e36209', dark: '#ffa657' },
|
|
861
|
-
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, light: '#005cc5', dark: '#79c0ff' },
|
|
862
|
-
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, light: '#22863a', dark: '#7ee787' },
|
|
863
|
-
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, light: '#d73a49', dark: '#ff7b72' },
|
|
864
|
-
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, light: '#005cc5', dark: '#79c0ff' },
|
|
865
|
-
{ token: 'punct', pattern: /[{}();:]/, light: '#24292e', dark: '#c9d1d9' }
|
|
866
|
-
];
|
|
867
|
-
|
|
868
|
-
const Coax = CoaxElem;
|
|
869
881
|
//注册语言类型
|
|
870
882
|
Coax.register('css', {
|
|
871
883
|
alias: 'CSS',
|