@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.
Files changed (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +331 -166
  3. package/dist/coax.cjs.js +450 -271
  4. package/dist/coax.cjs.min.js +4 -4
  5. package/dist/coax.esm.js +444 -271
  6. package/dist/coax.esm.min.js +4 -4
  7. package/dist/coax.umd.js +466 -266
  8. package/dist/coax.umd.min.js +4 -4
  9. package/examples/.htaccess +0 -0
  10. package/examples/append-highlight.html +82 -0
  11. package/examples/color-selector.html +412 -0
  12. package/examples/deepseek-highlight.html +100 -0
  13. package/examples/js-highlight.html +1 -1
  14. package/examples/md-highlight.html +60 -0
  15. package/examples/nginx.htaccess +0 -0
  16. package/examples/replace-highlight.html +69 -0
  17. package/examples/stream-highlight.html +64 -0
  18. package/examples/theme-highlight.html +69 -0
  19. package/package.json +4 -4
  20. package/rollup.config.js +3 -3
  21. package/script-note.js +2 -2
  22. package/src/Coax.js +25 -414
  23. package/src/Coax.ts +28 -443
  24. package/src/components/Coax.js +528 -0
  25. package/src/components/Coax.ts +556 -0
  26. package/src/modules.js +12 -0
  27. package/src/modules.ts +23 -0
  28. package/src/rules/css.js +11 -0
  29. package/src/rules/css.ts +11 -0
  30. package/src/rules/html.js +13 -0
  31. package/src/rules/html.ts +13 -0
  32. package/src/rules/javascript.js +10 -0
  33. package/src/rules/javascript.ts +10 -0
  34. package/src/rules/markdown.js +29 -0
  35. package/src/rules/markdown.ts +41 -0
  36. package/src/rules/ruleCss - /345/211/257/346/234/254.js" +10 -0
  37. package/src/rules/ruleHTML - /345/211/257/346/234/254.js" +12 -0
  38. package/src/rules/ruleJs - /345/211/257/346/234/254.js" +10 -0
  39. package/src/rules/ruleTs - /345/211/257/346/234/254.js" +12 -0
  40. package/src/rules/typescript.js +12 -0
  41. package/src/rules/typescript.ts +12 -0
  42. package/src/tools/copy.js +26 -0
  43. package/src/tools/copy.ts +29 -0
package/dist/coax.umd.js CHANGED
@@ -1,10 +1,10 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2026-1-8 16:55:44
3
+ * @since Last modified: 2026-1-12 14:46:3
4
4
  * @name Coax event management system.
5
- * @version 0.0.2
5
+ * @version 0.0.4
6
6
  * @author AXUI development team <3217728223@qq.com>
7
- * @description Coax is a custom web component that enables syntax highlighting for various programming languages inside your HTML documents.
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}
@@ -14,10 +14,244 @@
14
14
  * @license MIT license
15
15
  */
16
16
 
17
- (function (factory) {
17
+ (function (global, factory) {
18
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
18
19
  typeof define === 'function' && define.amd ? define(factory) :
19
- factory();
20
- })((function () { 'use strict';
20
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.coax = factory());
21
+ })(this, (function () { 'use strict';
22
+
23
+ const NAMESPACE = 'ax';
24
+
25
+ const html = [
26
+ { token: 'comment', pattern: /&lt;!--[\s\S]*?--&gt;/, light: '#999999', dark: '#6e7681' },
27
+ { token: 'doctype', pattern: /&lt;!DOCTYPE[\s\S]*?&gt;/i, light: '#6a737d', dark: '#8b949e' },
28
+ // 匹配标签名: <div, </div
29
+ { token: 'tag', pattern: /&lt;\/?[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: /\/?&gt;/, 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
+ // 图片: ![alt](url)
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
+
189
+ const typeWriter = (text, options) => {
190
+ const speed = options.speed || 100; // Set typing speed (default to 100ms per character)
191
+ return new Promise((resolve) => {
192
+ // Callback before typing starts
193
+ options?.onBeforeType?.(text);
194
+ let index = 0;
195
+ // Timer to type the text character by character at the given speed
196
+ const timer = setInterval(() => {
197
+ if (index < text.length) {
198
+ const char = text.charAt(index); // Get the character at the current index
199
+ const typedText = text.substring(0, index + 1); // The text typed so far
200
+ // Callback during typing each character
201
+ options?.onDuringType?.(char, typedText);
202
+ index++;
203
+ }
204
+ else {
205
+ // Clear the timer once typing is complete
206
+ clearInterval(timer);
207
+ // Resolve the Promise when typing is complete
208
+ resolve(text);
209
+ // Callback after typing is finished
210
+ options?.onAfterType?.(text);
211
+ }
212
+ }, speed);
213
+ });
214
+ };
215
+
216
+ const COMMA = ',';
217
+
218
+ const SPACE = ' ';
219
+
220
+ const trim = (str, placement = 'compress') => {
221
+ if (typeof str !== 'string') {
222
+ console.warn('Expected a string input');
223
+ return '';
224
+ }
225
+ switch (placement) {
226
+ case 'start':
227
+ return str.trimStart();
228
+ case 'end':
229
+ return str.trimEnd();
230
+ case 'both':
231
+ return str.trim();
232
+ case 'global':
233
+ return str.replace(/[\s\r\n]+/g, '');
234
+ default:
235
+ return str.trim().replace(/[\s\r\n]+/g, ' '); // Default behavior, trims both ends and replaces inner spaces
236
+ }
237
+ };
238
+
239
+ const parseClasses = (data) => {
240
+ let separator, result = [];
241
+ if (Array.isArray(data)) {
242
+ // If data is already an array, filter out invalid values
243
+ result = data.filter((k) => k && typeof k === 'string');
244
+ }
245
+ else {
246
+ // Trim the input string and handle multiple spaces
247
+ data = trim(data);
248
+ // Use comma as the separator if present, otherwise use space
249
+ separator = data.includes(COMMA) ? COMMA : SPACE;
250
+ result = data.split(separator);
251
+ }
252
+ // Trim each item globally and filter out any empty strings
253
+ return result.map((k) => trim(k, 'global')).filter(Boolean);
254
+ };
21
255
 
22
256
  const getDataType = (obj) => {
23
257
  let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
@@ -161,50 +395,8 @@
161
395
 
162
396
  const ALIAS = 'rep';
163
397
 
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
398
  const addClasses = (target, classes, intercept) => {
207
- const el = getEl(target), arr = parseClasses$1(classes);
399
+ const el = getEl(target), arr = parseClasses(classes);
208
400
  if (!el || arr.length === 0) {
209
401
  return;
210
402
  }
@@ -244,94 +436,34 @@
244
436
  return toolsEl;
245
437
  };
246
438
 
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
-
312
439
  class Coax extends HTMLElement {
313
440
  // A static Map to hold the configuration for different languages
314
441
  static languages = new Map();
442
+ // A static array to hold the tools registered with the component
315
443
  static tools = [];
316
- source;
317
- _renderQueued = false;
318
- baseStylesEl;
319
- themeStylesEl;
320
- dynamicStylesEl;
321
- highlightedCodeEl;
322
- headerEl;
323
- nameEl;
324
- toolsEl;
325
- alias;
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
+ highlightedCodeEl; // 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
326
460
  constructor() {
327
461
  super();
328
462
  // Attach a Shadow DOM to the custom element
329
463
  this.attachShadow({ mode: 'open' });
330
464
  // Remove leading/trailing whitespace from the raw code content
331
465
  this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
332
- this.lang = 'plain';
333
- this.alias = 'Plain Text';
334
- // 1. 初始化基础骨架
466
+ // Initialize the basic structure of the component
335
467
  this.shadowRoot.innerHTML = `
336
468
  <style id="base-styles">
337
469
  :host {
@@ -348,16 +480,16 @@
348
480
  --border-color:rgb(224, 224, 224);
349
481
  --color-code:rgb(51, 51, 51);
350
482
  --color-index:rgb(153, 153, 153);
351
- --color-stripe:rgb(224, 224, 224);
352
- --color-hover:rgb(224, 224, 224);
483
+ --color-stripe:rgba(0,0,0,0.04);
484
+ --color-hover:rgba(0,0,0,0.06);
353
485
  }
354
486
  :host([scheme="dark"]){
355
487
  --background: #282c34;
356
488
  --border-color: transparent;
357
489
  --color-code: #abb2bf;
358
490
  --color-index:rgb(153, 153, 153);
359
- --color-stripe:rgb(66, 66, 66);
360
- --color-hover:rgb(66, 66, 66);
491
+ --color-stripe:rgba(255,255,255,0.04);
492
+ --color-hover:rgba(255,255,255,0.06);
361
493
  }
362
494
  @media (prefers-color-scheme: dark) {
363
495
  :host{
@@ -365,8 +497,8 @@
365
497
  --border-color: transparent;
366
498
  --color-code: #abb2bf;
367
499
  --color-index:rgb(153, 153, 153);
368
- --color-stripe:rgb(66, 66, 66);
369
- --color-hover:rgb(66, 66, 66);
500
+ --color-stripe:rgba(255,255,255,0.04);
501
+ --color-hover:rgba(255,255,255,0.06);
370
502
  }
371
503
  }
372
504
  :host {
@@ -376,7 +508,6 @@
376
508
  background:var(--${NAMESPACE}-code-background-color,var(--background));
377
509
  color:var(--${NAMESPACE}-code-color,var(--color-code));
378
510
  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
511
  transition: border-color 0.3s ease,color 0.3s ease;
381
512
  border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
382
513
  }
@@ -393,6 +524,7 @@
393
524
  padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
394
525
  height:var(--${NAMESPACE}-code-height,var(--height));
395
526
  max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
527
+ overflow:auto;
396
528
  }
397
529
  pre,code{
398
530
  font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
@@ -408,6 +540,9 @@
408
540
  flex:auto;
409
541
  }
410
542
  }
543
+ code>div>div:empty:before{
544
+ content:' ';
545
+ }
411
546
  :host([indexed]) code>div:before{
412
547
  display:inline-flex;
413
548
  color:var(--color-index);
@@ -415,13 +550,13 @@
415
550
  min-width:var(--${NAMESPACE}-code-index-width,2em);
416
551
  margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
417
552
  }
418
- :host([hoverable]) code>div:hover{
419
- background-color:var(--color-hover);
420
- }
421
553
  :host([striped]) code>div:nth-child(odd){
422
554
  background-color:var(--color-stripe);
423
555
  }
424
- :host([wrapped]) code>div{
556
+ :host([hoverable]) code>div:hover{
557
+ background-color:var(--color-hover);
558
+ }
559
+ :host([wrapped]) code>div>div{
425
560
  white-space: pre-wrap;
426
561
  }
427
562
  :host([unnamed]) #code-name{
@@ -461,41 +596,47 @@
461
596
  <pre><code id="highlight-code"></code></pre>
462
597
  </div>
463
598
  `;
464
- // 缓存引用
599
+ // Cache references to various elements
465
600
  this.baseStylesEl = getEl('#base-styles', this.shadowRoot);
466
601
  this.themeStylesEl = getEl('#theme-styles', this.shadowRoot);
467
602
  this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot);
468
603
  this.headerEl = getEl('#code-header', this.shadowRoot);
469
- this.nameEl = getEl('#code-name', this.shadowRoot);
470
- this.toolsEl = getEl('#code-tools', this.shadowRoot);
604
+ this.codeNameEl = getEl('#code-name', this.shadowRoot);
605
+ this.codeToolsEl = getEl('#code-tools', this.shadowRoot);
606
+ this.codeBodyEl = getEl('#code-body', this.shadowRoot);
471
607
  this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot);
608
+ this.codeBodyEl.addEventListener('scroll', () => {
609
+ let flag = this.codeBodyEl.scrollTop + this.codeBodyEl.clientHeight < this.codeBodyEl.scrollHeight;
610
+ // Check if the user manually scrolled
611
+ this.autoScroll = !flag;
612
+ });
472
613
  }
473
614
 
474
615
  static register(name, config) {
475
616
  // Store the language configuration in the static map
476
617
  this.languages.set(name, { ...config });
477
618
  }
478
- //注册工具箱
619
+
479
620
  static addTools(items) {
480
621
  Coax.tools = items;
481
622
  }
482
- //加入工具箱
623
+
483
624
  mountTools(toolItems) {
484
625
  requestAnimationFrame(() => {
485
- this.toolsEl.innerHTML = '';
626
+ this.codeToolsEl.innerHTML = '';
486
627
  let items = toolItems.map(k => {
487
628
  k.action = k.action.bind(this);
488
629
  return k;
489
630
  });
490
- this.toolsEl.appendChild(createTools(items));
631
+ this.codeToolsEl.appendChild(createTools(items));
491
632
  });
492
633
  }
493
- // Called when the element is connected to the DOM
634
+
494
635
  connectedCallback() {
495
636
  this.render();
496
637
  }
497
- // Observed attributes for changes
498
- static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools']; }
638
+
639
+ static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools', 'speed']; }
499
640
 
500
641
  attributeChangedCallback(name, oldVal, newVal) {
501
642
  if (oldVal === newVal)
@@ -503,19 +644,19 @@
503
644
  if (name === 'height' || name === 'max-height') {
504
645
  this.updateStyleByRegExp(name, newVal);
505
646
  }
506
- else if (name === 'lang') {
507
- //更新当前语言
647
+ if (name === 'speed') {
648
+ // Convert to integer (0 or 1)
649
+ this.speed = ~~(!!newVal);
650
+ }
651
+ if (name === 'lang') {
652
+ // Update the language and re-render
508
653
  this.lang = newVal;
509
654
  this.render();
510
655
  }
511
- else if (name === 'unnamed') {
512
- this.nameEl.innerHTML = newVal === null ? (this.alias || this.lang) : '';
513
- this.nameEl.innerHTML = '';
514
- }
515
- else if (name === 'tools') {
656
+ if (name === 'tools') {
516
657
  if (!newVal)
517
- this.toolsEl.innerHTML = '';
518
- const tools = parseClasses$1(newVal), toolItems = Coax.tools.filter(k => tools.includes(k.name));
658
+ this.codeToolsEl.innerHTML = '';
659
+ const tools = parseClasses(newVal), toolItems = Coax.tools.filter(k => tools.includes(k.name));
519
660
  if (!toolItems.length)
520
661
  return;
521
662
  this.mountTools(toolItems);
@@ -529,37 +670,91 @@
529
670
  // 替换为新的属性值
530
671
  this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
531
672
  }
673
+
532
674
  getCssPrefix(config) {
533
675
  return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
534
676
  }
535
677
 
536
- highlight(newCode) {
678
+ getHighLightString(string, config) {
679
+ config = config || Coax.languages.get(this.lang);
680
+ if (!config)
681
+ return string;
682
+ // 如果找到了配置,则进行高亮处理
683
+ const cssPrefix = this.getCssPrefix(config),
684
+ // 获取用于语法高亮的正则表达式
685
+ combinedRegex = new RegExp(config.rules.map((r) => `(${r.pattern.source})`).join('|'), 'g');
686
+ return string.replace(combinedRegex, (match, ...args) => {
687
+ const i = args.findIndex(val => val !== undefined);
688
+ return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
689
+ });
690
+ }
691
+
692
+ createLineWrap(index, startIndex) {
693
+ let dataIndex = 0;
694
+ if (index == null && startIndex == null) {
695
+ dataIndex = this.highlightedCodeEl.children.length;
696
+ }
697
+ else {
698
+ const start = startIndex || this.highlightedCodeEl.children.length;
699
+ dataIndex = start + index;
700
+ }
701
+ return createEl('div', { 'data-index': dataIndex }, '<div></div>');
702
+ }
703
+
704
+ getLineToFill(codeWrap, line, config) {
705
+ config = config || Coax.languages.get(this.lang);
706
+ let highlightedLine = this.getHighLightString(line, config);
707
+ // 将高亮后的内容填充到 div 中
708
+ codeWrap.innerHTML = highlightedLine;
709
+ }
710
+ ;
711
+
712
+ async highlight(newCode) {
537
713
  const config = Coax.languages.get(this.lang), startIndex = this.highlightedCodeEl.children.length,
538
714
  // 将新源码按行分割
539
- newCodeLines = newCode.split('\n'),
715
+ newCodeLines = newCode ? newCode.split('\n') : [];
716
+ //更新别名
717
+ this.updateName(config);
718
+ if (!newCodeLines.length)
719
+ return true;
540
720
  // 如果没有找到配置,则输出原始代码,并不进行高亮处理
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;
721
+ for (let [index, lineString] of newCodeLines.entries()) {
722
+ //如果是空行则跳过
723
+ if (!lineString.trim() && this.hasAttribute('sanitized'))
724
+ continue;
725
+ // 创建一个 div 包裹每一行
726
+ const lineWrap = this.createLineWrap(index, startIndex), codeWrap = lineWrap.lastElementChild;
727
+ //标记完成
728
+ lineWrap.completed = true;
729
+ if (this.hasAttribute('speed')) {
730
+ this.highlightedCodeEl.appendChild(lineWrap);
731
+ //流式打字
732
+ await typeWriter(lineString, {
733
+ speed: this.speed,
734
+ onDuringType: (char, fullText) => {
735
+ codeWrap.innerHTML = fullText;
736
+ }
552
737
  });
738
+ this.getLineToFill(codeWrap, lineString, config);
553
739
  }
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);
740
+ else {
741
+ //直接打出
742
+ this.getLineToFill(codeWrap, lineString, config);
743
+ //
744
+ this.highlightedCodeEl.appendChild(lineWrap);
745
+ }
746
+ }
747
+ //滚动到最底部
748
+ this.autoScrollCode();
749
+ return true;
750
+ }
751
+
752
+ autoScrollCode() {
753
+ if (this.autoScroll) {
754
+ this.codeBodyEl.scrollTop = this.codeBodyEl.scrollHeight;
755
+ }
562
756
  }
757
+
563
758
  injectThemeStyles() {
564
759
  const config = Coax.languages.get(this.lang);
565
760
  if (!config)
@@ -569,143 +764,148 @@
569
764
  //Generate dynamic CSS classes for each syntax rule
570
765
  // 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
571
766
  lightStyles = config.rules.map((rule) => `
572
- .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.color ? ',' + rule.color : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
767
+ .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.light ? ',' + rule.light : ''});}`).join('\n'), darkStyles = '', schemeStyles = '';
573
768
  darkStyles += `:host([scheme="dark"]){`;
574
769
  darkStyles += config.rules.map((rule) => `
575
- ${rule.color ? `
770
+ ${rule.light ? `
576
771
  .${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
577
772
  darkStyles += `}`;
578
773
  schemeStyles = `@media (prefers-color-scheme: dark){
579
774
  :host{
580
775
  `;
581
776
  schemeStyles += config.rules.map((rule) => `
582
- ${rule.color ? `
777
+ ${rule.light ? `
583
778
  .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
584
779
  schemeStyles += `}`;
585
780
  // Set the inner HTML of the shadow root, including styles and highlighted code
586
- // 2. 精确更新 DOM 节点而非重写 ShadowRoot
587
781
  this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
588
782
  //附加主题样式
589
783
  if (config?.themeStyles) {
590
784
  this.themeStylesEl.textContent = config.themeStyles;
591
785
  }
592
- //更新别名
593
- this.updateName(config);
594
786
  }
787
+
595
788
  updateName(config) {
596
789
  if (this.hasAttribute('unnamed'))
597
790
  return;
598
- //更新别名
599
- this.alias = config.alias || this.lang;
600
- this.nameEl.innerHTML = this.alias;
791
+ if (!config) {
792
+ this.codeNameEl.innerHTML = 'Plain Text';
793
+ }
794
+ else {
795
+ //更新别名
796
+ this.alias = config.alias || this.lang;
797
+ this.codeNameEl.innerHTML = this.alias;
798
+ }
601
799
  }
602
800
 
603
- render() {
801
+ render(code = this.source) {
604
802
  //同时多次改变属性,只执行一次
605
- // 如果已经在队列中,则直接返回
606
803
  if (this._renderQueued)
607
804
  return;
608
805
  this._renderQueued = true;
609
806
  // 使用 requestAnimationFrame 将渲染推迟到下一帧
610
- requestAnimationFrame(() => {
611
- this.highlightedCodeEl.innerHTML = '';
612
- //一次性渲染
613
- this.highlight(this.source);
807
+ requestAnimationFrame(async () => {
808
+ this.clear();
614
809
  this.injectThemeStyles();
810
+ //一次性渲染
811
+ await this.highlight(code);
615
812
  this._renderQueued = false;
616
813
  });
617
814
  }
618
815
 
619
- replaceCode(newCode) {
620
- // 更新原始代码为新代码
621
- this.source = newCode;
622
- //清空
623
- this.highlightedCodeEl.innerHTML = '';
624
- this.highlight(newCode);
816
+ clear() {
817
+ this.highlightedCodeEl.innerHTML = this.source = this.lineString = '';
818
+ }
819
+
820
+ async replace(newCode) {
821
+ this.clear();
822
+ await this.highlight(newCode);
625
823
  }
626
824
 
627
- appendCode(newCode) {
825
+ async append(newCode) {
628
826
  // 将新的代码追加到现有代码末尾
629
827
  this.source += `\n${newCode}`;
630
828
  // 高亮新的部分
631
- this.highlight(newCode);
829
+ await this.highlight(newCode);
830
+ }
831
+
832
+ getLastLine() {
833
+ const lastChild = this.highlightedCodeEl.lastElementChild, lastLine = !lastChild || this.highlightedCodeEl.lastElementChild?.completed ?
834
+ this.createLineWrap() : lastChild;
835
+ return {
836
+ lineWrap: lastLine,
837
+ codeWrap: lastLine.lastElementChild
838
+ };
839
+ }
840
+
841
+ close() {
842
+ const lineWrap = this.highlightedCodeEl.lastElementChild;
843
+ if (!lineWrap)
844
+ return;
845
+ lineWrap.completed = true;
846
+ //行结束前保存
847
+ this.lastLineString = this.lineString;
848
+ this.getLineToFill(lineWrap?.lastElementChild, this.lineString);
849
+ //一行结束,清空临时行文本
850
+ this.lineString = '';
851
+ }
852
+
853
+ open() {
854
+ const lineWrap = this.highlightedCodeEl.lastElementChild;
855
+ if (!lineWrap)
856
+ return;
857
+ lineWrap.completed = false;
858
+ //恢复最后一行字符串
859
+ (lineWrap?.lastElementChild).textContent = this.lineString = this.lastLineString;
860
+ }
861
+
862
+ stream(str, forceClose = false) {
863
+ const { lineWrap, codeWrap } = this.getLastLine();
864
+ this.highlightedCodeEl.appendChild(lineWrap);
865
+ //汇集
866
+ this.source += str;
867
+ //如果没有遇到换行符号,也可以强制结束
868
+ if (forceClose || (str.startsWith('\n') || str.endsWith('\n'))) {
869
+ //标记完成
870
+ this.close();
871
+ }
872
+ else {
873
+ //插入文本
874
+ codeWrap.innerHTML += str;
875
+ //临时保存行文本
876
+ this.lineString += str;
877
+ }
878
+ //滚动到最底部
879
+ this.autoScrollCode();
632
880
  }
633
881
  }
882
+
883
+ //注册语言类型
634
884
  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
- ],
885
+ alias: 'CSS',
886
+ rules: css,
645
887
 
646
888
  });
647
889
  Coax.register('html', {
648
- rules: [
649
- { token: 'comment', pattern: /&lt;!--[\s\S]*?--&gt;/, color: '#6a737d', dark: '#8b949e' },
650
- { token: 'doctype', pattern: /&lt;!DOCTYPE[\s\S]*?&gt;/i, color: '#005cc5', dark: '#56b6c2' },
651
- // 匹配标签名: <div, </div
652
- { token: 'tag', pattern: /&lt;\/?[a-zA-Z0-9]+/, color: '#22863a', dark: '#abb2bf' },
653
- // 匹配属性名: class=
654
- { token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, color: '#6f42c1', dark: '#e06c75' },
655
- // 匹配属性值: "value"
656
- { token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, color: '#032f62', dark: '#f39c12' },
657
- // 匹配标签闭合: >, />
658
- { token: 'bracket', pattern: /\/?&gt;/, color: '#24292e', dark: '#f1f1f1' }
659
- ],
890
+ alias: 'HTML',
891
+ rules: html,
660
892
  });
661
893
  Coax.register('js', {
662
894
  alias: 'Javascript',
663
- rules: [
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' }
671
- ],
895
+ rules: javascript,
672
896
  });
673
897
  Coax.register('ts', {
674
- rules: [
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' }
684
- ],
898
+ alias: 'Typescript',
899
+ rules: typescript,
685
900
  });
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
- }]);
901
+ Coax.register('md', {
902
+ alias: 'Markdown',
903
+ rules: markdown
904
+ });
905
+ //注册工具箱
906
+ Coax.addTools([copy]);
709
907
  customElements.define(`${NAMESPACE}-code`, Coax);
710
908
 
909
+ return Coax;
910
+
711
911
  }));