@codady/coax 0.0.1 → 0.0.2
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 +652 -127
- package/dist/coax.cjs.min.js +3 -3
- package/dist/coax.esm.js +652 -127
- package/dist/coax.esm.min.js +3 -3
- package/dist/coax.umd.js +652 -127
- package/dist/coax.umd.min.js +3 -3
- package/examples/css-highlight.html +2 -18
- package/examples/js-highlight.html +2 -17
- package/examples/plain-highlight.html +47 -0
- package/package.json +18 -2
- package/src/Coax.js +399 -145
- package/src/Coax.ts +418 -155
- package/tsconfig.json +2 -2
package/dist/coax.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2026-1-
|
|
3
|
+
* @since Last modified: 2026-1-8 16:55:44
|
|
4
4
|
* @name Coax event management system.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.2
|
|
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}
|
|
@@ -19,168 +19,693 @@
|
|
|
19
19
|
factory();
|
|
20
20
|
})((function () { 'use strict';
|
|
21
21
|
|
|
22
|
+
const getDataType = (obj) => {
|
|
23
|
+
let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
|
|
24
|
+
if (tmp === 'Function' && /^\s*class\s+/.test(obj.toString())) {
|
|
25
|
+
result = 'Class';
|
|
26
|
+
}
|
|
27
|
+
else if (tmp === 'Object' && Object.getPrototypeOf(obj) !== Object.prototype) {
|
|
28
|
+
result = 'Instance';
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
result = tmp;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
//document.createElement -> HTMLxxxElement
|
|
35
|
+
//document.createDocumentFragment() -> DocumentFragment
|
|
36
|
+
//document.createComment() -> Comment
|
|
37
|
+
//document.createTextNode -> Text
|
|
38
|
+
//document.createCDATASection() -> XMLDocument
|
|
39
|
+
//document.createProcessingInstruction() -> ProcessingInstruction
|
|
40
|
+
//document.createRange() -> Range
|
|
41
|
+
//document.createTreeWalker() -> TreeWalker
|
|
42
|
+
//document.createNodeIterator() -> NodeIterator
|
|
43
|
+
//document.createElementNS('http://www.w3.org/2000/svg', 'svg'); -> SVGSVGElement
|
|
44
|
+
//document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math'); -> MathMLElement
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const getEl = (obj, wrap = document.body) => {
|
|
48
|
+
let objType = getDataType(obj), parType = getDataType(wrap), parent = parType.includes('HTML') || parType === 'ShadowRoot' ? wrap : document.querySelector(wrap),
|
|
49
|
+
//如果parent是template节点,需要通过node.content.querySelector取得子节点
|
|
50
|
+
root = parent && parent instanceof HTMLTemplateElement ? parent.content : parent, result = null;
|
|
51
|
+
if (obj) {
|
|
52
|
+
if (objType.includes('HTML')) {
|
|
53
|
+
result = obj;
|
|
54
|
+
}
|
|
55
|
+
else if (objType === 'String') {
|
|
56
|
+
try {
|
|
57
|
+
result = (root || document).querySelector(obj.trim());
|
|
58
|
+
//可能会报错,报错则返回null
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
result = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const createEl = (name, attrs, content) => {
|
|
69
|
+
//默认为div
|
|
70
|
+
name = name || 'div';
|
|
71
|
+
//统一大小写
|
|
72
|
+
let rootName = name.toUpperCase().trim(), rootEl = document.createElement(rootName), attrsType = getDataType(attrs), loop = (host, data) => {
|
|
73
|
+
if (data === '' || data === null || data === undefined) {
|
|
74
|
+
//为空、未定义则不再执行
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
let dataType = getDataType(data);
|
|
78
|
+
//template是DocumentFragment类型不能用insertAdjacentHTML
|
|
79
|
+
if (rootName === 'TEMPLATE') {
|
|
80
|
+
host.innerHTML = data.toString();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (dataType === 'Array' && data.length > 0) {
|
|
84
|
+
//节点数组
|
|
85
|
+
//data.forEach((i: T_obj) => loop(el, i));
|
|
86
|
+
for (let k of data) {
|
|
87
|
+
let childType = getDataType(k);
|
|
88
|
+
if (childType.includes('HTML')) {
|
|
89
|
+
//是个节点
|
|
90
|
+
host.appendChild(k);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
//是个对象{name:'',attrs:{},content:''}
|
|
94
|
+
let child = createEl(k.name, k.attrs, k.content);
|
|
95
|
+
child && host.appendChild(child);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (dataType.includes('HTML')) {
|
|
100
|
+
//HTML节点
|
|
101
|
+
host.appendChild(data);
|
|
102
|
+
}
|
|
103
|
+
else if (dataType === 'String' && data.trim().startsWith('#') && data.trim().length > 1) {
|
|
104
|
+
//是字符串且是#id选择器,则取其文本
|
|
105
|
+
let el = getEl(data);
|
|
106
|
+
if (!el)
|
|
107
|
+
return;
|
|
108
|
+
//如果是template模板节点则需要特殊处理
|
|
109
|
+
el.nodeName === 'TEMPLATE' ? host.appendChild(el.content.cloneNode(true)) : host.insertAdjacentHTML('beforeEnd', el.innerHTML);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
//字符串数字等
|
|
113
|
+
host.insertAdjacentHTML('beforeEnd', data);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
//添加属性
|
|
118
|
+
if (attrs && attrsType === 'Object') {
|
|
119
|
+
for (let k in attrs) {
|
|
120
|
+
//注意,attrs[k]可能是一个{}或[],不一定是字符串
|
|
121
|
+
// JSON.stringify可以将所有格式的数据都转成文本,会忽略函数和节点数据
|
|
122
|
+
//字符串则不需要stringify,否则会将字符串的引号也一并输出
|
|
123
|
+
attrs.hasOwnProperty(k) && rootEl.setAttribute(k, typeof attrs[k] === 'string' ? attrs[k] : JSON.stringify(attrs[k]));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//执行循环创建子节点
|
|
127
|
+
loop(rootEl, content);
|
|
128
|
+
return rootEl;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const isEmpty = (data) => {
|
|
132
|
+
let type = getDataType(data), flag;
|
|
133
|
+
if (!data) {
|
|
134
|
+
//0,'',false,undefined,null
|
|
135
|
+
flag = true;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
//function(){}|()=>{}
|
|
139
|
+
//[null]|[undefined]|['']|[""]
|
|
140
|
+
//[]|{}
|
|
141
|
+
//Symbol()|Symbol.for()
|
|
142
|
+
//Set,Map
|
|
143
|
+
//Date/Regex
|
|
144
|
+
flag = (type === 'Object') ? (Object.keys(data).length === 0) :
|
|
145
|
+
(type === 'Array') ? data.join('') === '' :
|
|
146
|
+
(type === 'Function') ? (data.toString().replace(/\s+/g, '').match(/{.*}/g)[0] === '{}') :
|
|
147
|
+
(type === 'Symbol') ? (data.toString().replace(/\s+/g, '').match(/\(.*\)/g)[0] === '()') :
|
|
148
|
+
(type === 'Set' || type === 'Map') ? data.size === 0 :
|
|
149
|
+
type === 'Date' ? isNaN(data.getTime()) :
|
|
150
|
+
type === 'RegExp' ? data.source === '' :
|
|
151
|
+
type === 'ArrayBuffer' ? data.byteLength === 0 :
|
|
152
|
+
(type === 'NodeList' || type === 'HTMLCollection') ? data.length === 0 :
|
|
153
|
+
('length' in data && typeof data.length === 'number') ? data.length === 0 :
|
|
154
|
+
('size' in data && typeof data.size === 'number') ? data.size === 0 :
|
|
155
|
+
(type === 'Error' || data instanceof Error) ? data.message === '' :
|
|
156
|
+
(type.includes('Array') && (['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array', 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array'].includes(type))) ? data.length === 0 :
|
|
157
|
+
false;
|
|
158
|
+
}
|
|
159
|
+
return flag;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const ALIAS = 'rep';
|
|
163
|
+
|
|
164
|
+
const NAMESPACE = 'ax';
|
|
165
|
+
|
|
166
|
+
const COMMA$1 = ',';
|
|
167
|
+
|
|
168
|
+
const SPACE$1 = ' ';
|
|
169
|
+
|
|
170
|
+
const trim$1 = (str, placement = '') => {
|
|
171
|
+
if (typeof str !== 'string') {
|
|
172
|
+
console.warn('Expected a string input');
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
switch (placement) {
|
|
176
|
+
case 'start':
|
|
177
|
+
return str.trimStart();
|
|
178
|
+
case 'end':
|
|
179
|
+
return str.trimEnd();
|
|
180
|
+
case 'both':
|
|
181
|
+
return str.trim();
|
|
182
|
+
case 'global':
|
|
183
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
184
|
+
default:
|
|
185
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const parseClasses$1 = (data) => {
|
|
190
|
+
let separator, result = [];
|
|
191
|
+
if (Array.isArray(data)) {
|
|
192
|
+
// If data is already an array, filter out invalid values
|
|
193
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Trim the input string and handle multiple spaces
|
|
197
|
+
data = trim$1(data);
|
|
198
|
+
// Use comma as the separator if present, otherwise use space
|
|
199
|
+
separator = data.includes(COMMA$1) ? COMMA$1 : SPACE$1;
|
|
200
|
+
result = data.split(separator);
|
|
201
|
+
}
|
|
202
|
+
// Trim each item globally and filter out any empty strings
|
|
203
|
+
return result.map((k) => trim$1(k, 'global')).filter(Boolean);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const addClasses = (target, classes, intercept) => {
|
|
207
|
+
const el = getEl(target), arr = parseClasses$1(classes);
|
|
208
|
+
if (!el || arr.length === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
arr.forEach((k) => {
|
|
212
|
+
{
|
|
213
|
+
el.classList.add(k);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const createTools = (data) => {
|
|
219
|
+
|
|
220
|
+
const toolsEl = createEl('span', { class: `${NAMESPACE}-box-tools` }), renderFn = (props) => {
|
|
221
|
+
const dftAttrs = {}, arrow = props.extendable ? `<i ${ALIAS}="arrow"></i>` : '', iconStr = props.icon ? `<i ${ALIAS}="icon">${props.icon}</i>` : '', diskStr = props.disk ? `<i ${ALIAS}="disk"><img src="${props.disk}"/></i>` : '', cubeStr = props.cube ? `<i ${ALIAS}="cube"><img src="${props.cube}"/></i>` : '', imageStr = props.image ? `<i ${ALIAS}="image"><img src="${props.image}"/></i>` : '', label = props.label ? `<i ${ALIAS}="label">${props.label}</i>` : '', html = iconStr + diskStr + cubeStr + imageStr + label + arrow;
|
|
222
|
+
//使用title提示
|
|
223
|
+
props.title && (dftAttrs.title = props.title);
|
|
224
|
+
//可聚焦,增加tabindex=1
|
|
225
|
+
props.focusable && (dftAttrs.tabindex = 1);
|
|
226
|
+
//attrs是其他属性,可能会覆盖title、tabindex
|
|
227
|
+
props.wrapEl = createEl(props.nodeName || 'span', Object.assign(dftAttrs, props.attrs), html);
|
|
228
|
+
props.iconEl = props.wrapEl.querySelector(`[${ALIAS}="icon"]`);
|
|
229
|
+
props.cubeEl = props.wrapEl.querySelector(`[${ALIAS}="cube"]`);
|
|
230
|
+
props.diskEl = props.wrapEl.querySelector(`[${ALIAS}="disk"]`);
|
|
231
|
+
props.imageEl = props.wrapEl.querySelector(`[${ALIAS}="image"]`);
|
|
232
|
+
props.labelEl = props.wrapEl.querySelector(`[${ALIAS}="label"]`);
|
|
233
|
+
//增加classes和styles
|
|
234
|
+
!isEmpty(props.classes) && addClasses(props.wrapEl, props.classes);
|
|
235
|
+
!isEmpty(props.styles) && (props.wrapEl.style.cssText += props.styles);
|
|
236
|
+
};
|
|
237
|
+
//此处不用map方法,是避免改变原data的内存地址指向
|
|
238
|
+
for (let item of data) {
|
|
239
|
+
//data=[{},{},'toggle','close']
|
|
240
|
+
renderFn(item);
|
|
241
|
+
toolsEl.appendChild(item.wrapEl);
|
|
242
|
+
item?.action?.(item);
|
|
243
|
+
}
|
|
244
|
+
return toolsEl;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const COMMA = ',';
|
|
248
|
+
|
|
249
|
+
const SPACE = ' ';
|
|
250
|
+
|
|
251
|
+
const trim = (str, placement = '') => {
|
|
252
|
+
if (typeof str !== 'string') {
|
|
253
|
+
console.warn('Expected a string input');
|
|
254
|
+
return '';
|
|
255
|
+
}
|
|
256
|
+
switch (placement) {
|
|
257
|
+
case 'start':
|
|
258
|
+
return str.trimStart();
|
|
259
|
+
case 'end':
|
|
260
|
+
return str.trimEnd();
|
|
261
|
+
case 'both':
|
|
262
|
+
return str.trim();
|
|
263
|
+
case 'global':
|
|
264
|
+
return str.replace(/[\s\r\n]+/g, '');
|
|
265
|
+
default:
|
|
266
|
+
return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const parseClasses = (data) => {
|
|
271
|
+
let separator, result = [];
|
|
272
|
+
if (Array.isArray(data)) {
|
|
273
|
+
// If data is already an array, filter out invalid values
|
|
274
|
+
result = data.filter((k) => k && typeof k === 'string');
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// Trim the input string and handle multiple spaces
|
|
278
|
+
data = trim(data);
|
|
279
|
+
// Use comma as the separator if present, otherwise use space
|
|
280
|
+
separator = data.includes(COMMA) ? COMMA : SPACE;
|
|
281
|
+
result = data.split(separator);
|
|
282
|
+
}
|
|
283
|
+
// Trim each item globally and filter out any empty strings
|
|
284
|
+
return result.map((k) => trim(k, 'global')).filter(Boolean);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const rtlStyle = (name = '') => `
|
|
288
|
+
<style>
|
|
289
|
+
:where([dir="rtl"]) .icax-${name},
|
|
290
|
+
:where(:dir(rtl)) .icax-${name} {
|
|
291
|
+
transform: scaleX(-1);
|
|
292
|
+
transform-origin: center;
|
|
293
|
+
}
|
|
294
|
+
</style>
|
|
295
|
+
`;
|
|
296
|
+
|
|
297
|
+
const wrap = (content, fun, isRtl = false, options) => {
|
|
298
|
+
const size = options?.size || '1em', color = options?.color || 'currentColor', thickness = options?.thickness || 2, classes = options?.classes ? parseClasses(options.classes).join(' ') : '',
|
|
299
|
+
// 得到 "icax-left"
|
|
300
|
+
origName = fun.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
301
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="${color}"
|
|
302
|
+
stroke-width="${thickness}" stroke-linecap="round" stroke-linejoin="round" class="${origName} ${classes}">
|
|
303
|
+
${isRtl ? rtlStyle(origName.split('-')[1]) : ''}
|
|
304
|
+
${content}
|
|
305
|
+
</svg>`;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
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);
|
|
309
|
+
|
|
310
|
+
const icaxCheck = (options) => wrap(`<polyline points="20 6 9 17 4 12"></polyline>`, icaxCheck, false, options);
|
|
311
|
+
|
|
22
312
|
class Coax extends HTMLElement {
|
|
23
313
|
// A static Map to hold the configuration for different languages
|
|
24
314
|
static languages = new Map();
|
|
25
|
-
|
|
26
|
-
|
|
315
|
+
static tools = [];
|
|
316
|
+
source;
|
|
317
|
+
_renderQueued = false;
|
|
318
|
+
baseStylesEl;
|
|
319
|
+
themeStylesEl;
|
|
320
|
+
dynamicStylesEl;
|
|
321
|
+
highlightedCodeEl;
|
|
322
|
+
headerEl;
|
|
323
|
+
nameEl;
|
|
324
|
+
toolsEl;
|
|
325
|
+
alias;
|
|
27
326
|
constructor() {
|
|
28
327
|
super();
|
|
29
328
|
// Attach a Shadow DOM to the custom element
|
|
30
329
|
this.attachShadow({ mode: 'open' });
|
|
31
330
|
// Remove leading/trailing whitespace from the raw code content
|
|
32
|
-
this.
|
|
331
|
+
this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
|
|
332
|
+
this.lang = 'plain';
|
|
333
|
+
this.alias = 'Plain Text';
|
|
334
|
+
// 1. 初始化基础骨架
|
|
335
|
+
this.shadowRoot.innerHTML = `
|
|
336
|
+
<style id="base-styles">
|
|
337
|
+
:host {
|
|
338
|
+
--border-width:1px;
|
|
339
|
+
--border-style:solid;
|
|
340
|
+
--radius:9px;
|
|
341
|
+
--height:auto;
|
|
342
|
+
--max-height:500px;
|
|
343
|
+
--radius:9px;
|
|
344
|
+
--padding:1em;
|
|
345
|
+
--font-size:16px;
|
|
346
|
+
--line-height:1.8;
|
|
347
|
+
--background:rgb(247, 247, 247);
|
|
348
|
+
--border-color:rgb(224, 224, 224);
|
|
349
|
+
--color-code:rgb(51, 51, 51);
|
|
350
|
+
--color-index:rgb(153, 153, 153);
|
|
351
|
+
--color-stripe:rgb(224, 224, 224);
|
|
352
|
+
--color-hover:rgb(224, 224, 224);
|
|
353
|
+
}
|
|
354
|
+
:host([scheme="dark"]){
|
|
355
|
+
--background: #282c34;
|
|
356
|
+
--border-color: transparent;
|
|
357
|
+
--color-code: #abb2bf;
|
|
358
|
+
--color-index:rgb(153, 153, 153);
|
|
359
|
+
--color-stripe:rgb(66, 66, 66);
|
|
360
|
+
--color-hover:rgb(66, 66, 66);
|
|
361
|
+
}
|
|
362
|
+
@media (prefers-color-scheme: dark) {
|
|
363
|
+
:host{
|
|
364
|
+
--background: #282c34;
|
|
365
|
+
--border-color: transparent;
|
|
366
|
+
--color-code: #abb2bf;
|
|
367
|
+
--color-index:rgb(153, 153, 153);
|
|
368
|
+
--color-stripe:rgb(66, 66, 66);
|
|
369
|
+
--color-hover:rgb(66, 66, 66);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
:host {
|
|
373
|
+
font-size: var(--${NAMESPACE}-code-font-size,var(--font-size));
|
|
374
|
+
display: block;
|
|
375
|
+
box-sizing:border-box;
|
|
376
|
+
background:var(--${NAMESPACE}-code-background-color,var(--background));
|
|
377
|
+
color:var(--${NAMESPACE}-code-color,var(--color-code));
|
|
378
|
+
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));
|
|
379
|
+
overflow:auto;
|
|
380
|
+
transition: border-color 0.3s ease,color 0.3s ease;
|
|
381
|
+
border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
|
|
382
|
+
}
|
|
383
|
+
#code-header{
|
|
384
|
+
line-height:calc(var(--${NAMESPACE}-code-line-height,var(--line-height))*1.5);
|
|
385
|
+
padding-inline-start:var(--${NAMESPACE}-code-padding,var(--padding));
|
|
386
|
+
display:flex;
|
|
387
|
+
|
|
388
|
+
>:first-child{
|
|
389
|
+
flex:auto;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
#code-body{
|
|
393
|
+
padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
|
|
394
|
+
height:var(--${NAMESPACE}-code-height,var(--height));
|
|
395
|
+
max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
|
|
396
|
+
}
|
|
397
|
+
pre,code{
|
|
398
|
+
font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
|
|
399
|
+
margin:0; padding:0;
|
|
400
|
+
}
|
|
401
|
+
code>div{
|
|
402
|
+
display:flex;
|
|
403
|
+
padding:0 var(--${NAMESPACE}-code-padding,var(--padding));
|
|
404
|
+
line-height: var(--${NAMESPACE}-code-line-height,var(--line-height));
|
|
405
|
+
box-sizing:border-box;
|
|
406
|
+
|
|
407
|
+
>div{
|
|
408
|
+
flex:auto;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
:host([indexed]) code>div:before{
|
|
412
|
+
display:inline-flex;
|
|
413
|
+
color:var(--color-index);
|
|
414
|
+
content: attr(data-index);
|
|
415
|
+
min-width:var(--${NAMESPACE}-code-index-width,2em);
|
|
416
|
+
margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
|
|
417
|
+
}
|
|
418
|
+
:host([hoverable]) code>div:hover{
|
|
419
|
+
background-color:var(--color-hover);
|
|
420
|
+
}
|
|
421
|
+
:host([striped]) code>div:nth-child(odd){
|
|
422
|
+
background-color:var(--color-stripe);
|
|
423
|
+
}
|
|
424
|
+
:host([wrapped]) code>div{
|
|
425
|
+
white-space: pre-wrap;
|
|
426
|
+
}
|
|
427
|
+
:host([unnamed]) #code-name{
|
|
428
|
+
display:none;
|
|
429
|
+
}
|
|
430
|
+
.${NAMESPACE}-box-tools{
|
|
431
|
+
>*{
|
|
432
|
+
font-size:14px;
|
|
433
|
+
display:inline-flex;
|
|
434
|
+
align-items:center;
|
|
435
|
+
justify-content:center;
|
|
436
|
+
height:2em;
|
|
437
|
+
line-height:2em;
|
|
438
|
+
aspect-ratio:1/1;
|
|
439
|
+
margin-inline-end:8px;
|
|
440
|
+
transition:all 200ms ease;
|
|
441
|
+
border-radius:6px;
|
|
442
|
+
&:hover{
|
|
443
|
+
cursor:pointer;
|
|
444
|
+
background-color:rgba(0,0,0,.04);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
[rep=icon]{
|
|
448
|
+
display:inline-flex;
|
|
449
|
+
align-items:center;
|
|
450
|
+
justify-content:center;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
[disabled]{
|
|
454
|
+
pointer-event:none;
|
|
455
|
+
}
|
|
456
|
+
</style>
|
|
457
|
+
<style id="dynamic-styles"></style>
|
|
458
|
+
<style id="theme-styles"></style>
|
|
459
|
+
<div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
|
|
460
|
+
<div id="code-body">
|
|
461
|
+
<pre><code id="highlight-code"></code></pre>
|
|
462
|
+
</div>
|
|
463
|
+
`;
|
|
464
|
+
// 缓存引用
|
|
465
|
+
this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
|
|
466
|
+
this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
|
|
467
|
+
this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
|
|
468
|
+
this.headerEl = getEl('#code-header', this.shadowRoot);
|
|
469
|
+
this.nameEl = getEl('#code-name', this.shadowRoot);
|
|
470
|
+
this.toolsEl = getEl('#code-tools', this.shadowRoot);
|
|
471
|
+
this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
|
|
33
472
|
}
|
|
34
473
|
|
|
35
|
-
static register(name,
|
|
474
|
+
static register(name, config) {
|
|
36
475
|
// Store the language configuration in the static map
|
|
37
|
-
this.languages.set(name, {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
476
|
+
this.languages.set(name, { ...config });
|
|
477
|
+
}
|
|
478
|
+
//注册工具箱
|
|
479
|
+
static addTools(items) {
|
|
480
|
+
Coax.tools = items;
|
|
481
|
+
}
|
|
482
|
+
//加入工具箱
|
|
483
|
+
mountTools(toolItems) {
|
|
484
|
+
requestAnimationFrame(() => {
|
|
485
|
+
this.toolsEl.innerHTML = '';
|
|
486
|
+
let items = toolItems.map(k => {
|
|
487
|
+
k.action = k.action.bind(this);
|
|
488
|
+
return k;
|
|
489
|
+
});
|
|
490
|
+
this.toolsEl.appendChild(createTools(items));
|
|
42
491
|
});
|
|
43
492
|
}
|
|
44
493
|
// Called when the element is connected to the DOM
|
|
45
|
-
connectedCallback() {
|
|
46
|
-
// Observed attributes for changes
|
|
47
|
-
static get observedAttributes() { return ['lang', 'height', 'max-height']; }
|
|
48
|
-
|
|
49
|
-
attributeChangedCallback() {
|
|
494
|
+
connectedCallback() {
|
|
50
495
|
this.render();
|
|
51
496
|
}
|
|
497
|
+
// Observed attributes for changes
|
|
498
|
+
static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools']; }
|
|
52
499
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// Default to language name if no prefix is set
|
|
59
|
-
cssPrefix = config?.cssPrefix || lang, height = this.getAttribute('height'), maxHeight = this.getAttribute('max-height');
|
|
60
|
-
let highlightedCode = '', dynamicStyles = '';
|
|
61
|
-
if (config) {
|
|
62
|
-
//Generate the highlighted HTML by applying the syntax rules
|
|
63
|
-
const combinedRegex = new RegExp(config.rules.map((r) => `(${r.reg.source})`).join('|'), 'g'), escaped = this.rawCode.replace(/[&<>]/g, m => ({ '&': '&', '<': '<', '>': '>' }[m]));
|
|
64
|
-
// Replace code with highlighted syntax
|
|
65
|
-
highlightedCode = escaped.replace(combinedRegex, (match, ...args) => {
|
|
66
|
-
const index = args.findIndex(val => val !== undefined);
|
|
67
|
-
return index !== -1 && config.rules[index] ? `<span class="ax-${cssPrefix}-${config.rules[index].name}">${match}</span>` : match;
|
|
68
|
-
});
|
|
69
|
-
//Generate dynamic CSS classes for each syntax rule
|
|
70
|
-
// 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
|
|
71
|
-
dynamicStyles = config.rules.map((rule) => `
|
|
72
|
-
.ax-${cssPrefix}-${rule.name} { color: var(--ax-${cssPrefix}-${rule.name}); }
|
|
73
|
-
`).join('\n');
|
|
500
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
501
|
+
if (oldVal === newVal)
|
|
502
|
+
return;
|
|
503
|
+
if (name === 'height' || name === 'max-height') {
|
|
504
|
+
this.updateStyleByRegExp(name, newVal);
|
|
74
505
|
}
|
|
75
|
-
else {
|
|
76
|
-
|
|
77
|
-
|
|
506
|
+
else if (name === 'lang') {
|
|
507
|
+
//更新当前语言
|
|
508
|
+
this.lang = newVal;
|
|
509
|
+
this.render();
|
|
510
|
+
}
|
|
511
|
+
else if (name === 'unnamed') {
|
|
512
|
+
this.nameEl.innerHTML = newVal === null ? (this.alias || this.lang) : '';
|
|
513
|
+
this.nameEl.innerHTML = '';
|
|
78
514
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
515
|
+
else if (name === 'tools') {
|
|
516
|
+
if (!newVal)
|
|
517
|
+
this.toolsEl.innerHTML = '';
|
|
518
|
+
const tools = parseClasses$1(newVal), toolItems = Coax.tools.filter(k => tools.includes(k.name));
|
|
519
|
+
if (!toolItems.length)
|
|
520
|
+
return;
|
|
521
|
+
this.mountTools(toolItems);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
updateStyleByRegExp(prop, value) {
|
|
526
|
+
// 构建正则:匹配属性名后面跟着冒号,直到分号或换行
|
|
527
|
+
// 例如:height:\s*[^;]+;
|
|
528
|
+
const regex = new RegExp(`;\\n\\s*${prop}:\\s*[^;]+;`, 'g');
|
|
529
|
+
// 替换为新的属性值
|
|
530
|
+
this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
|
|
531
|
+
}
|
|
532
|
+
getCssPrefix(config) {
|
|
533
|
+
return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
highlight(newCode) {
|
|
537
|
+
const config = Coax.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
|
|
538
|
+
// 将新源码按行分割
|
|
539
|
+
newCodeLines = newCode.split('\n'),
|
|
540
|
+
// 如果没有找到配置,则输出原始代码,并不进行高亮处理
|
|
541
|
+
highlightedLines = newCodeLines.map((line, index) => {
|
|
542
|
+
let highlightedLine = line;
|
|
543
|
+
// 如果找到了配置,则进行高亮处理
|
|
544
|
+
if (config) {
|
|
545
|
+
const cssPrefix = this.getCssPrefix(config);
|
|
546
|
+
// 获取用于语法高亮的正则表达式
|
|
547
|
+
const combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
|
|
548
|
+
// 进行高亮替换
|
|
549
|
+
highlightedLine = line.replace(combinedRegex, (match, ...args) => {
|
|
550
|
+
const i = args.findIndex(val => val !== undefined);
|
|
551
|
+
return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
// 创建一个 div 包裹每一行
|
|
555
|
+
const lineDiv = createEl('div', { 'data-index': startIndex + index });
|
|
556
|
+
lineDiv.innerHTML = `<div>${highlightedLine}</div>`; // 将高亮后的内容填充到 div 中
|
|
557
|
+
return lineDiv;
|
|
558
|
+
});
|
|
559
|
+
//
|
|
560
|
+
// 将新生成的行节点追加到 highlightedCodeEl 节点中
|
|
561
|
+
this.highlightedCodeEl.append(...highlightedLines);
|
|
562
|
+
}
|
|
563
|
+
injectThemeStyles() {
|
|
564
|
+
const config = Coax.languages.get(this.lang);
|
|
565
|
+
if (!config)
|
|
566
|
+
return;
|
|
567
|
+
// Get language name, fallback to 'js' if not provided
|
|
568
|
+
let cssPrefix = this.getCssPrefix(config),
|
|
569
|
+
//Generate dynamic CSS classes for each syntax rule
|
|
570
|
+
// 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
|
|
571
|
+
lightStyles = config.rules.map((rule) => `
|
|
572
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.color ? ',' + rule.color : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
|
|
573
|
+
darkStyles += `:host([scheme="dark"]){`;
|
|
574
|
+
darkStyles += config.rules.map((rule) => `
|
|
575
|
+
${rule.color ? `
|
|
576
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
|
|
577
|
+
darkStyles += `}`;
|
|
578
|
+
schemeStyles = `@media (prefers-color-scheme: dark){
|
|
579
|
+
:host{
|
|
580
|
+
`;
|
|
581
|
+
schemeStyles += config.rules.map((rule) => `
|
|
582
|
+
${rule.color ? `
|
|
583
|
+
.${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
|
|
584
|
+
schemeStyles += `}`;
|
|
83
585
|
// Set the inner HTML of the shadow root, including styles and highlighted code
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
586
|
+
// 2. 精确更新 DOM 节点而非重写 ShadowRoot
|
|
587
|
+
this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
|
|
588
|
+
//附加主题样式
|
|
589
|
+
if (config?.themeStyles) {
|
|
590
|
+
this.themeStylesEl.textContent = config.themeStyles;
|
|
591
|
+
}
|
|
592
|
+
//更新别名
|
|
593
|
+
this.updateName(config);
|
|
594
|
+
}
|
|
595
|
+
updateName(config) {
|
|
596
|
+
if (this.hasAttribute('unnamed'))
|
|
597
|
+
return;
|
|
598
|
+
//更新别名
|
|
599
|
+
this.alias = config.alias || this.lang;
|
|
600
|
+
this.nameEl.innerHTML = this.alias;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
render() {
|
|
604
|
+
//同时多次改变属性,只执行一次
|
|
605
|
+
// 如果已经在队列中,则直接返回
|
|
606
|
+
if (this._renderQueued)
|
|
607
|
+
return;
|
|
608
|
+
this._renderQueued = true;
|
|
609
|
+
// 使用 requestAnimationFrame 将渲染推迟到下一帧
|
|
610
|
+
requestAnimationFrame(() => {
|
|
611
|
+
this.highlightedCodeEl.innerHTML = '';
|
|
612
|
+
//一次性渲染
|
|
613
|
+
this.highlight(this.source);
|
|
614
|
+
this.injectThemeStyles();
|
|
615
|
+
this._renderQueued = false;
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
replaceCode(newCode) {
|
|
620
|
+
// 更新原始代码为新代码
|
|
621
|
+
this.source = newCode;
|
|
622
|
+
//清空
|
|
623
|
+
this.highlightedCodeEl.innerHTML = '';
|
|
624
|
+
this.highlight(newCode);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
appendCode(newCode) {
|
|
628
|
+
// 将新的代码追加到现有代码末尾
|
|
629
|
+
this.source += `\n${newCode}`;
|
|
630
|
+
// 高亮新的部分
|
|
631
|
+
this.highlight(newCode);
|
|
117
632
|
}
|
|
118
633
|
}
|
|
119
|
-
|
|
634
|
+
Coax.register('css', {
|
|
635
|
+
rules: [
|
|
636
|
+
{ token: 'comment', pattern: /\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
|
|
637
|
+
{ token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, color: '#61afef', dark: '#a5d6ff' },
|
|
638
|
+
{ token: 'func', pattern: /[a-z-]+\(?=/, color: '#e36209', dark: '#ffa657' },
|
|
639
|
+
{ token: 'property', pattern: /[a-z-]+(?=\s*:)/, color: '#005cc5', dark: '#79c0ff' },
|
|
640
|
+
{ token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, color: '#6f42c1', dark: '#d2a8ff' },
|
|
641
|
+
{ token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, color: '#d73a49', dark: '#ff7b72' },
|
|
642
|
+
{ token: 'number', pattern: /\b\d+(\.\d+)?\b/, color: '#005cc5', dark: '#79c0ff' },
|
|
643
|
+
{ token: 'punct', pattern: /[{}();:]/, color: '#24292e', dark: '#c9d1d9' }
|
|
644
|
+
],
|
|
645
|
+
|
|
646
|
+
});
|
|
120
647
|
Coax.register('html', {
|
|
121
648
|
rules: [
|
|
122
|
-
{
|
|
123
|
-
{
|
|
649
|
+
{ token: 'comment', pattern: /<!--[\s\S]*?-->/, color: '#6a737d', dark: '#8b949e' },
|
|
650
|
+
{ token: 'doctype', pattern: /<!DOCTYPE[\s\S]*?>/i, color: '#005cc5', dark: '#56b6c2' },
|
|
124
651
|
// 匹配标签名: <div, </div
|
|
125
|
-
{
|
|
652
|
+
{ token: 'tag', pattern: /<\/?[a-zA-Z0-9]+/, color: '#22863a', dark: '#abb2bf' },
|
|
126
653
|
// 匹配属性名: class=
|
|
127
|
-
{
|
|
654
|
+
{ token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, color: '#6f42c1', dark: '#e06c75' },
|
|
128
655
|
// 匹配属性值: "value"
|
|
129
|
-
{
|
|
656
|
+
{ token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, color: '#032f62', dark: '#f39c12' },
|
|
130
657
|
// 匹配标签闭合: >, />
|
|
131
|
-
{
|
|
658
|
+
{ token: 'bracket', pattern: /\/?>/, color: '#24292e', dark: '#f1f1f1' }
|
|
132
659
|
],
|
|
133
|
-
//添加root变量:--ax-html-keyword
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
Coax.register('css', {
|
|
137
|
-
rules: [
|
|
138
|
-
{ name: 'comment', reg: /\/\*[\s\S]*?\*\// },
|
|
139
|
-
{ name: 'value', reg: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/ },
|
|
140
|
-
{ name: 'func', reg: /[a-z-]+\(?=/ },
|
|
141
|
-
{ name: 'property', reg: /[a-z-]+(?=\s*:)/ },
|
|
142
|
-
{ name: 'selector', reg: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i },
|
|
143
|
-
{ name: 'unit', reg: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/ },
|
|
144
|
-
{ name: 'number', reg: /\b\d+(\.\d+)?\b/ },
|
|
145
|
-
{ name: 'punct', reg: /[{}();:]/ }
|
|
146
|
-
],
|
|
147
|
-
//添加root变量:--ax-code-keyword
|
|
148
|
-
theme: {
|
|
149
|
-
'type': '#61afef', // 蓝色选择器
|
|
150
|
-
'keyword': '#d19a66', // 橙色属性名
|
|
151
|
-
'string': '#98c379', // 绿色属性值
|
|
152
|
-
'op': '#abb2bf' // 灰色符号
|
|
153
|
-
},
|
|
154
|
-
internalCss: `
|
|
155
|
-
.ax-css-string { color: var(--ax-code-string); }
|
|
156
|
-
.ax-css-string::first-letter { color: var(--ax-code-c); }
|
|
157
|
-
`
|
|
158
660
|
});
|
|
159
661
|
Coax.register('js', {
|
|
662
|
+
alias: 'Javascript',
|
|
160
663
|
rules: [
|
|
161
|
-
{
|
|
162
|
-
{
|
|
163
|
-
{
|
|
164
|
-
{
|
|
165
|
-
{
|
|
166
|
-
{
|
|
167
|
-
{
|
|
664
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
|
|
665
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, color: '#032f62', dark: '#61afef' },
|
|
666
|
+
{ 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/, color: '#e06c75', dark: '#d73a49' },
|
|
667
|
+
{ token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise)\b/, color: '#56b6c2', dark: '#61afef' },
|
|
668
|
+
{ token: 'number', pattern: /\b\d+\b/, color: '#61afef', dark: '#d19a66' },
|
|
669
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
|
|
670
|
+
{ token: 'op', pattern: /[+\-*/%=<>!&|^~]+/, color: '#d73a49', dark: '#e06c75' }
|
|
168
671
|
],
|
|
169
|
-
|
|
170
672
|
});
|
|
171
673
|
Coax.register('ts', {
|
|
172
674
|
rules: [
|
|
173
|
-
{
|
|
174
|
-
{
|
|
175
|
-
{
|
|
176
|
-
{
|
|
177
|
-
{
|
|
178
|
-
{
|
|
179
|
-
{
|
|
180
|
-
{
|
|
181
|
-
{
|
|
675
|
+
{ token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
|
|
676
|
+
{ token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, color: '#032f62', dark: '#61afef' },
|
|
677
|
+
{ token: 'decorator', pattern: /@[a-zA-Z_]\w*/, color: '#d19a66', dark: '#e5c07b' },
|
|
678
|
+
{ 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' },
|
|
679
|
+
{ 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' },
|
|
680
|
+
{ token: 'type', pattern: /\b[A-Z]\w*\b/, color: '#22863a', dark: '#8b949e' },
|
|
681
|
+
{ token: 'number', pattern: /\b\d+\b/, color: '#61afef', dark: '#d19a66' },
|
|
682
|
+
{ token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
|
|
683
|
+
{ token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, color: '#d73a49', dark: '#e06c75' }
|
|
182
684
|
],
|
|
183
|
-
|
|
184
685
|
});
|
|
686
|
+
Coax.addTools([{
|
|
687
|
+
name: 'copy',
|
|
688
|
+
icon: icaxCopy(),
|
|
689
|
+
action: function (arg) {
|
|
690
|
+
arg.wrapEl.onclick = () => {
|
|
691
|
+
//this只是组件实例
|
|
692
|
+
navigator.clipboard.writeText(this.source)
|
|
693
|
+
.then(() => {
|
|
694
|
+
console.log('Text successfully copied to clipboard');
|
|
695
|
+
arg.iconEl.innerHTML = icaxCheck();
|
|
696
|
+
arg.iconEl.toggleAttribute('disabled', true);
|
|
697
|
+
setTimeout(() => {
|
|
698
|
+
//恢复
|
|
699
|
+
arg.iconEl.removeAttribute('disabled');
|
|
700
|
+
arg.iconEl.innerHTML = icaxCopy();
|
|
701
|
+
}, 2000);
|
|
702
|
+
})
|
|
703
|
+
.catch(err => {
|
|
704
|
+
console.error('Error copying text to clipboard: ', err);
|
|
705
|
+
});
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}]);
|
|
709
|
+
customElements.define(`${NAMESPACE}-code`, Coax);
|
|
185
710
|
|
|
186
711
|
}));
|