@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/src/Coax.ts CHANGED
@@ -1,217 +1,480 @@
1
1
  /**
2
- * Last modified: 2026/01/04 15:50:55
2
+ * Last modified: 2026/01/08 16:55:35
3
3
  */
4
4
 
5
5
 
6
+ import createTools, { toolsItem } from "@codady/utils/src/createTools.js";
7
+ import icaxCopy from "@codady/icax/src/icaxCopy";
8
+ import getEl from "@codady/utils/src/getEl.js";
9
+ import createEl from "@codady/utils/src/createEl.js";
10
+ import NAMESPACE from "@codady/utils/src/namespace.js";
11
+ import icaxCheck from "@codady/icax/src/icaxCheck";
12
+ import parseClasses from "@codady/utils/src/parseClasses.js";
13
+
14
+
6
15
  // Define the structure for the language configuration
16
+ export interface LanguageRule {
17
+ token: string;
18
+ pattern: RegExp;
19
+ color?: string;
20
+ dark?: string;
21
+ }
22
+
7
23
  export interface LanguageConfig {
8
- rules: { name: string, reg: RegExp }[]; // Array of syntax highlighting rules
9
- theme?: Record<string, string>; // Optional theme with color values
10
- internalCss?: string; // Optional internal CSS for language-specific styles
11
- cssPrefix?: string; // Optional CSS prefix for class names
24
+ rules: LanguageRule[]; // Array of syntax highlighting rules
25
+ alias?: string; // Alias name
26
+ themeStyles?: string; // Optional internal CSS for language-specific styles
27
+ cssPrefix?: string; // Optional CSS prefix for class names
12
28
  }
13
29
 
14
30
  class Coax extends HTMLElement {
15
31
  // A static Map to hold the configuration for different languages
16
- static languages = new Map();
17
- // Raw code as text from the HTML content
18
- private rawCode: string;
32
+ static languages = new Map<string, LanguageConfig>();
33
+ static tools: any[] = [];
34
+ private source: string;
35
+ private _renderQueued = false;
36
+ private baseStylesEl!: HTMLStyleElement;
37
+ private themeStylesEl!: HTMLStyleElement;
38
+ private dynamicStylesEl!: HTMLStyleElement;
39
+ private highlightedCodeEl!: HTMLElement;
40
+ private headerEl!: HTMLElement;
41
+ private nameEl!: HTMLElement;
42
+ private toolsEl!: HTMLElement;
43
+ public alias: string;
19
44
 
20
45
  constructor() {
21
46
  super();
22
47
  // Attach a Shadow DOM to the custom element
23
48
  this.attachShadow({ mode: 'open' });
24
49
  // Remove leading/trailing whitespace from the raw code content
25
- this.rawCode = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
50
+ this.source = this.textContent?.replace(/^\s*\n|\n\s*$/g, '') || '';
51
+ this.lang = 'plain';
52
+ this.alias = 'Plain Text';
53
+ // 1. 初始化基础骨架
54
+ (this.shadowRoot as any).innerHTML = `
55
+ <style id="base-styles">
56
+ :host {
57
+ --border-width:1px;
58
+ --border-style:solid;
59
+ --radius:9px;
60
+ --height:auto;
61
+ --max-height:500px;
62
+ --radius:9px;
63
+ --padding:1em;
64
+ --font-size:16px;
65
+ --line-height:1.8;
66
+ --background:rgb(247, 247, 247);
67
+ --border-color:rgb(224, 224, 224);
68
+ --color-code:rgb(51, 51, 51);
69
+ --color-index:rgb(153, 153, 153);
70
+ --color-stripe:rgb(224, 224, 224);
71
+ --color-hover:rgb(224, 224, 224);
72
+ }
73
+ :host([scheme="dark"]){
74
+ --background: #282c34;
75
+ --border-color: transparent;
76
+ --color-code: #abb2bf;
77
+ --color-index:rgb(153, 153, 153);
78
+ --color-stripe:rgb(66, 66, 66);
79
+ --color-hover:rgb(66, 66, 66);
80
+ }
81
+ @media (prefers-color-scheme: dark) {
82
+ :host{
83
+ --background: #282c34;
84
+ --border-color: transparent;
85
+ --color-code: #abb2bf;
86
+ --color-index:rgb(153, 153, 153);
87
+ --color-stripe:rgb(66, 66, 66);
88
+ --color-hover:rgb(66, 66, 66);
89
+ }
90
+ }
91
+ :host {
92
+ font-size: var(--${NAMESPACE}-code-font-size,var(--font-size));
93
+ display: block;
94
+ box-sizing:border-box;
95
+ background:var(--${NAMESPACE}-code-background-color,var(--background));
96
+ color:var(--${NAMESPACE}-code-color,var(--color-code));
97
+ 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));
98
+ overflow:auto;
99
+ transition: border-color 0.3s ease,color 0.3s ease;
100
+ border-radius: var(--${NAMESPACE}-code-radius,var(--radius));
101
+ }
102
+ #code-header{
103
+ line-height:calc(var(--${NAMESPACE}-code-line-height,var(--line-height))*1.5);
104
+ padding-inline-start:var(--${NAMESPACE}-code-padding,var(--padding));
105
+ display:flex;
106
+
107
+ >:first-child{
108
+ flex:auto;
109
+ }
110
+ }
111
+ #code-body{
112
+ padding: var(--${NAMESPACE}-code-padding,var(--padding)) 0;
113
+ height:var(--${NAMESPACE}-code-height,var(--height));
114
+ max-height:var(--${NAMESPACE}-code-max-height,var(--max-height));
115
+ }
116
+ pre,code{
117
+ font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace";
118
+ margin:0; padding:0;
119
+ }
120
+ code>div{
121
+ display:flex;
122
+ padding:0 var(--${NAMESPACE}-code-padding,var(--padding));
123
+ line-height: var(--${NAMESPACE}-code-line-height,var(--line-height));
124
+ box-sizing:border-box;
125
+
126
+ >div{
127
+ flex:auto;
128
+ }
129
+ }
130
+ :host([indexed]) code>div:before{
131
+ display:inline-flex;
132
+ color:var(--color-index);
133
+ content: attr(data-index);
134
+ min-width:var(--${NAMESPACE}-code-index-width,2em);
135
+ margin-inline-end:var(--${NAMESPACE}-code-padding,8px);
136
+ }
137
+ :host([hoverable]) code>div:hover{
138
+ background-color:var(--color-hover);
139
+ }
140
+ :host([striped]) code>div:nth-child(odd){
141
+ background-color:var(--color-stripe);
142
+ }
143
+ :host([wrapped]) code>div{
144
+ white-space: pre-wrap;
145
+ }
146
+ :host([unnamed]) #code-name{
147
+ display:none;
148
+ }
149
+ .${NAMESPACE}-box-tools{
150
+ >*{
151
+ font-size:14px;
152
+ display:inline-flex;
153
+ align-items:center;
154
+ justify-content:center;
155
+ height:2em;
156
+ line-height:2em;
157
+ aspect-ratio:1/1;
158
+ margin-inline-end:8px;
159
+ transition:all 200ms ease;
160
+ border-radius:6px;
161
+ &:hover{
162
+ cursor:pointer;
163
+ background-color:rgba(0,0,0,.04);
164
+ }
165
+ }
166
+ [rep=icon]{
167
+ display:inline-flex;
168
+ align-items:center;
169
+ justify-content:center;
170
+ }
171
+ }
172
+ [disabled]{
173
+ pointer-event:none;
174
+ }
175
+ </style>
176
+ <style id="dynamic-styles"></style>
177
+ <style id="theme-styles"></style>
178
+ <div id="code-header"><span id="code-name">${this.alias}</span><div id="code-tools"></div></div>
179
+ <div id="code-body">
180
+ <pre><code id="highlight-code"></code></pre>
181
+ </div>
182
+ `;
183
+
184
+ // 缓存引用
185
+ this.baseStylesEl = getEl('#base-styles', this.shadowRoot) as HTMLStyleElement;
186
+ this.themeStylesEl = getEl('#theme-styles', this.shadowRoot) as HTMLStyleElement;
187
+ this.dynamicStylesEl = getEl('#dynamic-styles', this.shadowRoot) as HTMLStyleElement;
188
+ this.headerEl = getEl('#code-header', this.shadowRoot) as HTMLElement;
189
+ this.nameEl = getEl('#code-name', this.shadowRoot) as HTMLElement;
190
+ this.toolsEl = getEl('#code-tools', this.shadowRoot) as HTMLElement;
191
+ this.highlightedCodeEl = getEl('#highlight-code', this.shadowRoot) as HTMLElement;
26
192
  }
27
193
  /**
28
194
  * Registers a new language with a set of syntax highlighting rules.
29
195
  * @param name - The name of the programming language.
30
196
  * @param config - Configuration for the language, including rules, theme, and optional CSS.
31
197
  */
32
- static register(name: string, { rules, theme = {}, internalCss = '', cssPrefix = '' }: LanguageConfig): void {
198
+ static register(name: string, config: LanguageConfig): void {
33
199
  // Store the language configuration in the static map
34
- this.languages.set(name, { rules, theme, internalCss, cssPrefix });
35
- // Render the highlighted code for all elements with the registered language
36
- document.querySelectorAll('ax-code').forEach(el => {
37
- if (el.getAttribute('lang') === name) (el as any).render();
200
+ this.languages.set(name, { ...config });
201
+ }
202
+ //注册工具箱
203
+ static addTools(items: toolsItem[]): void {
204
+ Coax.tools = items;
205
+ }
206
+ //加入工具箱
207
+ mountTools(toolItems: any[]) {
208
+ requestAnimationFrame(() => {
209
+ this.toolsEl.innerHTML = '';
210
+ let items = toolItems.map(k => {
211
+ k.action = k.action.bind(this);
212
+ return k;
213
+ });
214
+ this.toolsEl.appendChild(createTools(items));
38
215
  });
39
216
  }
40
217
  // Called when the element is connected to the DOM
41
- connectedCallback() { this.render(); }
218
+ connectedCallback() {
219
+ this.render();
220
+ }
42
221
 
43
222
  // Observed attributes for changes
44
- static get observedAttributes() { return ['lang', 'height', 'max-height']; }
223
+ static get observedAttributes() { return ['lang', 'height', 'max-height', 'tools']; }
45
224
 
46
225
  /**
47
226
  * Called when any of the observed attributes change.
48
227
  */
49
- attributeChangedCallback() {
50
- this.render();
228
+ attributeChangedCallback(name: string, oldVal: string, newVal: string) {
229
+ if (oldVal === newVal) return;
230
+ if (name === 'height' || name === 'max-height') {
231
+ this.updateStyleByRegExp(name, newVal);
232
+ } else if (name === 'lang') {
233
+ //更新当前语言
234
+ this.lang = newVal;
235
+ this.render();
236
+ } else if (name === 'unnamed') {
237
+ this.nameEl.innerHTML = newVal === null ? (this.alias || this.lang) : ''; this.nameEl.innerHTML = '';
238
+ } else if (name === 'tools') {
239
+ if (!newVal) this.toolsEl.innerHTML = '';
240
+ const tools = parseClasses(newVal),
241
+ toolItems = Coax.tools.filter(k => tools.includes(k.name));
242
+ if (!toolItems.length) return;
243
+ this.mountTools(toolItems);
244
+ }
51
245
  }
52
246
  /**
53
- * Renders the highlighted code within the shadow DOM.
54
- */
55
- render() {
56
- // Get language name, fallback to 'js' if not provided
57
- const lang = this.getAttribute('lang') || 'js',
58
- // Get the language configuration
59
- config = Coax.languages.get(lang),
60
- // Default to language name if no prefix is set
61
- cssPrefix = config?.cssPrefix || lang,
62
- height = this.getAttribute('height'),
63
- maxHeight = this.getAttribute('max-height');
64
-
65
- let highlightedCode = '',
66
- dynamicStyles = '';
67
-
68
- if (config) {
69
-
70
- //Generate the highlighted HTML by applying the syntax rules
71
- const combinedRegex = new RegExp(config.rules.map((r: any) => `(${r.reg.source})`).join('|'), 'g'),
72
- escaped = this.rawCode.replace(/[&<>]/g, m => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[m] as string));
73
- // Replace code with highlighted syntax
74
- highlightedCode = escaped.replace(combinedRegex, (match, ...args) => {
75
- const index = args.findIndex(val => val !== undefined);
76
- return index !== -1 && config.rules[index] ? `<span class="ax-${cssPrefix}-${config.rules[index].name}">${match}</span>` : match;
247
+ * 使用正则替换基础样式表中的特定属性,避免操作行内样式
248
+ */
249
+ private updateStyleByRegExp(prop: string, value: string) {
250
+ // 构建正则:匹配属性名后面跟着冒号,直到分号或换行
251
+ // 例如:height:\s*[^;]+;
252
+ const regex = new RegExp(`;\\n\\s*${prop}:\\s*[^;]+;`, 'g');
253
+ // 替换为新的属性值
254
+ this.baseStylesEl.textContent = this.baseStylesEl.textContent.replace(regex, `;\n${prop}: ${value};`);
255
+ }
256
+ getCssPrefix(config: LanguageConfig) {
257
+ return (config?.cssPrefix || this.lang).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
258
+ }
259
+ /**
260
+ * 处理新源码,按行高亮并追加到高亮区域
261
+ * @param newCode - 新的源码片段
262
+ */
263
+ highlight(newCode: string) {
264
+ const config = Coax.languages.get(this.lang),
265
+ startIndex = this.highlightedCodeEl.children.length,
266
+ // 将新源码按行分割
267
+ newCodeLines = newCode.split('\n'),
268
+ // 如果没有找到配置,则输出原始代码,并不进行高亮处理
269
+ highlightedLines = newCodeLines.map((line, index) => {
270
+ let highlightedLine = line;
271
+
272
+ // 如果找到了配置,则进行高亮处理
273
+ if (config) {
274
+ const cssPrefix = this.getCssPrefix(config);
275
+ // 获取用于语法高亮的正则表达式
276
+ const combinedRegex = new RegExp(config.rules.map((r: any) => `(${r.pattern.source})`).join('|'), 'g');
277
+
278
+ // 进行高亮替换
279
+ highlightedLine = line.replace(combinedRegex, (match, ...args) => {
280
+ const i = args.findIndex(val => val !== undefined);
281
+ return i !== -1 && config.rules[i] ? `<span class="${NAMESPACE}-${cssPrefix}-${config.rules[i].token}">${match}</span>` : match;
282
+ });
283
+ }
284
+
285
+ // 创建一个 div 包裹每一行
286
+ const lineDiv = createEl('div', { 'data-index': startIndex + index });
287
+ lineDiv.innerHTML = `<div>${highlightedLine}</div>`; // 将高亮后的内容填充到 div 中
288
+ return lineDiv;
77
289
  });
290
+ //
291
+ // 将新生成的行节点追加到 highlightedCodeEl 节点中
292
+ this.highlightedCodeEl.append(...highlightedLines);
293
+ }
294
+ injectThemeStyles() {
295
+ const config = Coax.languages.get(this.lang);
296
+ if (!config) return;
78
297
 
298
+ // Get language name, fallback to 'js' if not provided
299
+ let cssPrefix = this.getCssPrefix(config),
79
300
  //Generate dynamic CSS classes for each syntax rule
80
301
  // 为 rules 中的每一个 name 生成类似 .hl-name { color: var(--ax-code-name); }
81
- dynamicStyles = config.rules.map((rule: any) => `
82
- .ax-${cssPrefix}-${rule.name} { color: var(--ax-${cssPrefix}-${rule.name}); }
83
- `).join('\n');
84
- } else {
85
- // If no config, display raw code without highlighting
86
- highlightedCode = this.rawCode;
87
- }
302
+ lightStyles = config.rules.map((rule: any) => `
303
+ .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token}${rule.color ? ',' + rule.color : ''});}`).join('\n'),
304
+ darkStyles = '',
305
+ schemeStyles = '';
88
306
 
89
- //Generate CSS theme variables if a theme is provided
90
- const themeVars = config?.theme
91
- ? Object.entries(config.theme).map(([k, v]) => `--ax-${cssPrefix}-${k}: ${v};`).join('\n')
92
- : '';
307
+ darkStyles += `:host([scheme="dark"]){`;
308
+ darkStyles += config.rules.map((rule: any) => `
309
+ ${rule.color ? `
310
+ .${NAMESPACE}-${cssPrefix}-${rule.token} {color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark});}` : ``}`).join('\n');
311
+ darkStyles += `}`;
312
+ schemeStyles = `@media (prefers-color-scheme: dark){
313
+ :host{
314
+ `;
315
+ schemeStyles += config.rules.map((rule: any) => `
316
+ ${rule.color ? `
317
+ .${NAMESPACE}-${cssPrefix}-${rule.token} { color: var(--${NAMESPACE}-${cssPrefix}-${rule.token},${rule.dark}); }` : ``} `).join('\n');
318
+ schemeStyles += `}`;
93
319
  // Set the inner HTML of the shadow root, including styles and highlighted code
94
- (this.shadowRoot as any).innerHTML = `
95
- <style>
96
- :host {
97
- ${themeVars}
98
-
99
- font-size: var(--ax-code-fs,14px);
100
- display: block;
101
- padding: var(--ax-code-p,1em);
102
- box-sizing:border-box;
103
- line-height: var(--ax-code-lh,1.5);
104
- background-color:var(--ax-code-bg,rgba(0,0,0,0.02));
105
- color:var(--ax-code-c,#333);
106
- border:var(--ax-code-bw,1px) solid var(--ax-code-bc,rgba(0,0,0,0.08));
107
- height:${height || 'var(--ax-code-h,auto)'};
108
- max-height:${maxHeight || 'var(--ax-code-mh,500px)'};
109
- overflow:auto;
110
- transition: border-color 0.3s ease,color 0.3s ease;
111
- border-radius: var(--ax-code-r,9px);
112
- }
113
- pre,code{
114
- font-family:"Consolas", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace", "microsoft yahei", "Microsoft JhengHei", "Yu Mincho", "simsun";
115
- margin:0;
116
- padding:0;
117
- }
118
-
119
-
120
- /* Dynamically generated styles for syntax highlighting */
121
- ${dynamicStyles}
122
-
123
- /* Inject additional CSS specific to the language if provided */
124
- ${config?.internalCss || ''}
125
- </style>
126
- <pre><code>${highlightedCode}</code></pre>`;
320
+ // 2. 精确更新 DOM 节点而非重写 ShadowRoot
321
+ this.dynamicStylesEl.textContent = lightStyles + darkStyles + schemeStyles;
322
+
323
+ //附加主题样式
324
+ if (config?.themeStyles) {
325
+ this.themeStylesEl.textContent = config.themeStyles;
326
+ }
327
+
328
+ //更新别名
329
+ this.updateName(config)
330
+
331
+ }
332
+ updateName(config: any) {
333
+ if (this.hasAttribute('unnamed')) return;
334
+ //更新别名
335
+ this.alias = config.alias || this.lang;
336
+ this.nameEl.innerHTML = this.alias;
337
+ }
338
+ /**
339
+ * Renders the highlighted code within the shadow DOM.
340
+ */
341
+ render() {
342
+ //同时多次改变属性,只执行一次
343
+ // 如果已经在队列中,则直接返回
344
+ if (this._renderQueued) return;
345
+ this._renderQueued = true;
346
+ // 使用 requestAnimationFrame 将渲染推迟到下一帧
347
+ requestAnimationFrame(() => {
348
+ this.highlightedCodeEl.innerHTML = '';
349
+ //一次性渲染
350
+ this.highlight(this.source);
351
+ this.injectThemeStyles();
352
+ this._renderQueued = false;
353
+ });
354
+ }
355
+ /**
356
+ * 替换现有的源码并重新渲染
357
+ * @param newCode - 新的源码
358
+ */
359
+ replaceCode(newCode: string) {
360
+ // 更新原始代码为新代码
361
+ this.source = newCode;
362
+ //清空
363
+ this.highlightedCodeEl.innerHTML = '';
364
+ this.highlight(newCode);
365
+ }
366
+ /**
367
+ * 末尾新增源码,仅高亮新增的部分
368
+ * @param newCode - 新的源码片段
369
+ */
370
+ appendCode(newCode: string) {
371
+ // 将新的代码追加到现有代码末尾
372
+ this.source += `\n${newCode}`;
373
+ // 高亮新的部分
374
+ this.highlight(newCode);
127
375
  }
128
376
  }
129
377
 
130
- customElements.define('ax-code', Coax);
131
378
 
132
379
 
380
+ Coax.register('css', {
381
+ rules: [
382
+ { token: 'comment', pattern: /\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
383
+ { token: 'value', pattern: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/, color: '#61afef', dark: '#a5d6ff' },
384
+ { token: 'func', pattern: /[a-z-]+\(?=/, color: '#e36209', dark: '#ffa657' },
385
+ { token: 'property', pattern: /[a-z-]+(?=\s*:)/, color: '#005cc5', dark: '#79c0ff' },
386
+ { token: 'selector', pattern: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i, color: '#6f42c1', dark: '#d2a8ff' },
387
+ { token: 'unit', pattern: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/, color: '#d73a49', dark: '#ff7b72' },
388
+ { token: 'number', pattern: /\b\d+(\.\d+)?\b/, color: '#005cc5', dark: '#79c0ff' },
389
+ { token: 'punct', pattern: /[{}();:]/, color: '#24292e', dark: '#c9d1d9' }
390
+ ],
391
+ /* cssPrefix: 'css',
392
+ themeStyles: `
393
+ :host {
394
+ --background:rgba(0,0,0,0.02);
395
+ --border-color:rgba(0,0,0,0.08);
396
+ --color-code:#333;
397
+ }
398
+ :host([scheme="dark"]){
399
+ --background: #282c34;
400
+ --border-color: transparent;
401
+ --color-code: #abb2bf;
402
+ }
403
+ @media (prefers-color-scheme: dark) {
404
+ :host{
405
+ --background: #282c34;
406
+ --border-color: transparent;
407
+ --color-code: #abb2bf;
408
+ }
409
+ }
410
+ ` */
411
+ });
412
+
133
413
  Coax.register('html', {
134
414
  rules: [
135
- { name: 'comment', reg: /&lt;!--[\s\S]*?--&gt;/ },
136
- { name: 'doctype', reg: /&lt;!DOCTYPE[\s\S]*?&gt;/i },
415
+ { token: 'comment', pattern: /&lt;!--[\s\S]*?--&gt;/, color: '#6a737d', dark: '#8b949e' },
416
+ { token: 'doctype', pattern: /&lt;!DOCTYPE[\s\S]*?&gt;/i, color: '#005cc5', dark: '#56b6c2' },
137
417
  // 匹配标签名: <div, </div
138
- { name: 'tag', reg: /&lt;\/?[a-zA-Z0-9]+/ },
418
+ { token: 'tag', pattern: /&lt;\/?[a-zA-Z0-9]+/, color: '#22863a', dark: '#abb2bf' },
139
419
  // 匹配属性名: class=
140
- { name: 'attr', reg: /[a-zA-Z-]+(?=\s*=\s*)/ },
420
+ { token: 'attr', pattern: /[a-zA-Z-]+(?=\s*=\s*)/, color: '#6f42c1', dark: '#e06c75' },
141
421
  // 匹配属性值: "value"
142
- { name: 'string', reg: /(['"])(?:\\.|[^\\])*?\1/ },
422
+ { token: 'string', pattern: /(['"])(?:\\.|[^\\])*?\1/, color: '#032f62', dark: '#f39c12' },
143
423
  // 匹配标签闭合: >, />
144
- { name: 'bracket', reg: /\/?&gt;/ }
145
- ],
146
- //添加root变量:--ax-html-keyword
147
- /* theme: {
148
- 'tag': '#e06c75', // 红色
149
- 'attr': '#d19a66', // 橙色
150
- 'bracket': '#abb2bf', // 灰色
151
- 'doctype': '#56b6c2' // 青色
152
- } */
153
- });
154
-
155
- Coax.register('css', {
156
- rules: [
157
- { name: 'comment', reg: /\/\*[\s\S]*?\*\// },
158
- { name: 'value', reg: /(?:'|")(?:\\.|[^\\'"\b])*?(?:'|")/ },
159
- { name: 'func', reg: /[a-z-]+\(?=/ },
160
- { name: 'property', reg: /[a-z-]+(?=\s*:)/ },
161
- { name: 'selector', reg: /[.#a-z0-9, \n\t>:+()_-]+(?=\s*\{)/i },
162
- { name: 'unit', reg: /(?<=\d)(px|em|rem|%|vh|vw|ms|s|deg)/ },
163
- { name: 'number', reg: /\b\d+(\.\d+)?\b/ },
164
- { name: 'punct', reg: /[{}();:]/ }
424
+ { token: 'bracket', pattern: /\/?&gt;/, color: '#24292e', dark: '#f1f1f1' }
165
425
  ],
166
- //添加root变量:--ax-code-keyword
167
- theme: {
168
- 'type': '#61afef', // 蓝色选择器
169
- 'keyword': '#d19a66', // 橙色属性名
170
- 'string': '#98c379', // 绿色属性值
171
- 'op': '#abb2bf' // 灰色符号
172
- },
173
- internalCss: `
174
- .ax-css-string { color: var(--ax-code-string); }
175
- .ax-css-string::first-letter { color: var(--ax-code-c); }
176
- `
177
426
  });
178
427
 
179
428
  Coax.register('js', {
429
+ alias: 'Javascript',
180
430
  rules: [
181
- { name: 'comment', reg: /\/\/[^\n]*|\/\*[\s\S]*?\*\// },
182
- { name: 'string', reg: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/ },
183
- { name: 'keyword', reg: /\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/ },
184
- { name: 'builtin', reg: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise)\b/ },
185
- { name: 'number', reg: /\b\d+\b/ },
186
- { name: 'func', reg: /\b[a-zA-Z_]\w*(?=\s*\()/ },
187
- { name: 'op', reg: /[+\-*/%=<>!&|^~]+/ }
431
+ { token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
432
+ { token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, color: '#032f62', dark: '#61afef' },
433
+ { 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' },
434
+ { token: 'builtin', pattern: /\b(console|window|document|Math|JSON|true|false|null|undefined|Object|Array|Promise)\b/, color: '#56b6c2', dark: '#61afef'},
435
+ { token: 'number', pattern: /\b\d+\b/, color: '#61afef', dark: '#d19a66' },
436
+ { token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
437
+ { token: 'op', pattern: /[+\-*/%=<>!&|^~]+/,color: '#d73a49', dark: '#e06c75' }
188
438
  ],
189
- /* theme: {
190
- 'keyword': '#c678dd',
191
- 'func': '#61afef',
192
- 'builtin': '#e5c07b'
193
- } */
194
439
  });
195
440
 
196
441
  Coax.register('ts', {
197
442
  rules: [
198
- { name: 'comment', reg: /\/\/[^\n]*|\/\*[\s\S]*?\*\// },
199
- { name: 'string', reg: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/ },
200
- { name: 'decorator', reg: /@[a-zA-Z_]\w*/ },
201
- { name: 'keyword', reg: /\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/ },
202
- { name: 'builtin', reg: /\b(any|boolean|never|number|string|symbol|unknown|void|undefined|null|boolean|true|false|console|window|document)\b/ },
203
- { name: 'type', reg: /\b[A-Z]\w*\b/ },
204
- { name: 'number', reg: /\b\d+\b/ },
205
- { name: 'func', reg: /\b[a-zA-Z_]\w*(?=\s*\()/ },
206
- { name: 'op', reg: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/ }
443
+ { token: 'comment', pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//, color: '#6a737d', dark: '#8b949e' },
444
+ { token: 'string', pattern: /(?:'|"|`)(?:\\.|[^\\'"\b])*?(?:'|"|`)/, color: '#032f62', dark: '#61afef' },
445
+ { token: 'decorator', pattern: /@[a-zA-Z_]\w*/, color: '#d19a66', dark: '#e5c07b' },
446
+ { 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' },
447
+ { 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' },
448
+ { token: 'type', pattern: /\b[A-Z]\w*\b/, color: '#22863a', dark: '#8b949e' },
449
+ { token: 'number', pattern: /\b\d+\b/, color: '#61afef', dark: '#d19a66' },
450
+ { token: 'func', pattern: /\b[a-zA-Z_]\w*(?=\s*\()/, color: '#e5c07b', dark: '#98c379' },
451
+ { token: 'op', pattern: /(\?\.|![:\.]|[+\-*/%=<>!&|^~]+)/, color: '#d73a49', dark: '#e06c75' }
207
452
  ],
208
- /* theme: {
209
- 'decorator': '#e06c75',
210
- 'type': '#4dbed9',
211
- 'builtin': '#d19a66',
212
- 'keyword': '#c678dd'
213
- },
214
- internalCss: `
215
- .ax-ts-type { font-weight: bold; }
216
- ` */
217
- });
453
+ });
454
+
455
+ Coax.addTools([{
456
+ name: 'copy',
457
+ icon: icaxCopy(),
458
+ action: function (arg: any) {
459
+ arg.wrapEl.onclick = () => {
460
+ //this只是组件实例
461
+ navigator.clipboard.writeText(this.source)
462
+ .then(() => {
463
+ console.log('Text successfully copied to clipboard');
464
+ arg.iconEl.innerHTML = icaxCheck();
465
+ arg.iconEl.toggleAttribute('disabled', true);
466
+ setTimeout(() => {
467
+ //恢复
468
+ arg.iconEl.removeAttribute('disabled');
469
+ arg.iconEl.innerHTML = icaxCopy();
470
+ }, 2000);
471
+ })
472
+ .catch(err => {
473
+ console.error('Error copying text to clipboard: ', err);
474
+ });
475
+ }
476
+ }
477
+ }]);
478
+
479
+
480
+ customElements.define(`${NAMESPACE}-code`, Coax);
package/tsconfig.json CHANGED
@@ -22,9 +22,9 @@
22
22
  // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23
23
  // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24
24
  /* Modules */
25
- "module": "es2020", /* Specify what module code is generated. */
25
+ "module": "nodenext", /* Specify what module code is generated. */
26
26
  // "rootDir": "./", /* Specify the root folder within your source files. */
27
- // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
27
+ "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
28
28
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29
29
  // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30
30
  // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */