@wsxjs/wsx-base-components 0.0.16 → 0.0.18

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 (42) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +28 -28
  3. package/dist/index.cjs +14 -2
  4. package/dist/index.js +4971 -2032
  5. package/dist/style.css +1 -1
  6. package/package.json +16 -7
  7. package/src/{XyButton.css → Button.css} +1 -1
  8. package/src/{XyButton.wsx → Button.wsx} +18 -9
  9. package/src/ButtonGroup.css +30 -0
  10. package/src/{XyButtonGroup.wsx → ButtonGroup.wsx} +26 -14
  11. package/src/CodeBlock.css +275 -0
  12. package/src/CodeBlock.types.ts +25 -0
  13. package/src/CodeBlock.wsx +296 -0
  14. package/src/ColorPicker.wsx +6 -5
  15. package/src/Combobox.css +254 -0
  16. package/src/Combobox.types.ts +32 -0
  17. package/src/Combobox.wsx +352 -0
  18. package/src/Dropdown.css +178 -0
  19. package/src/Dropdown.types.ts +28 -0
  20. package/src/Dropdown.wsx +221 -0
  21. package/src/LanguageSwitcher.css +148 -0
  22. package/src/LanguageSwitcher.wsx +190 -0
  23. package/src/OverflowDetector.ts +169 -0
  24. package/src/ResponsiveNav.css +555 -0
  25. package/src/ResponsiveNav.types.ts +30 -0
  26. package/src/ResponsiveNav.wsx +450 -0
  27. package/src/SvgIcon.wsx +2 -2
  28. package/src/index.ts +17 -9
  29. package/src/types/wsx.d.ts +4 -3
  30. package/src/ReactiveCounter.css +0 -304
  31. package/src/ReactiveCounter.wsx +0 -231
  32. package/src/SimpleReactiveDemo.wsx +0 -59
  33. package/src/SvgDemo.wsx +0 -241
  34. package/src/TodoList.css +0 -197
  35. package/src/TodoList.wsx +0 -264
  36. package/src/TodoListLight.css +0 -198
  37. package/src/TodoListLight.wsx +0 -263
  38. package/src/UserProfile.css +0 -146
  39. package/src/UserProfile.wsx +0 -247
  40. package/src/UserProfileLight.css +0 -146
  41. package/src/UserProfileLight.wsx +0 -256
  42. package/src/XyButtonGroup.css +0 -30
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .todo-list-light{padding:1.5rem;max-width:600px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.todo-header{margin-bottom:1.5rem;text-align:center}.todo-header h2{margin:0 0 .5rem;color:#2196f3;font-size:1.75rem}.subtitle{margin:0;color:#666;font-size:.9rem}.todo-input-section{display:flex;gap:.5rem;margin-bottom:1rem}.todo-input{flex:1;padding:.75rem;border:2px solid #ddd;border-radius:4px;font-size:1rem;transition:border-color .2s}.todo-input:focus{outline:none;border-color:#2196f3}.todo-filters{display:flex;gap:.5rem;margin-bottom:1rem;justify-content:center}.filter-btn{padding:.5rem 1rem;border:2px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;transition:all .2s;font-size:.9rem}.filter-btn:hover{background:#f5f5f5}.filter-btn.active{background:#2196f3;color:#fff;border-color:#2196f3}.todo-list-container{min-height:200px;margin-bottom:1rem}.empty-state{text-align:center;padding:3rem 1rem;color:#999;font-style:italic}.todo-items{list-style:none;padding:0;margin:0}.todo-item{display:flex;align-items:center;gap:.75rem;padding:.75rem;margin-bottom:.5rem;background:#f0f7ff;border-radius:4px;transition:background .2s}.todo-item:hover{background:#e3f2fd}.todo-item.completed{opacity:.6}.todo-checkbox{width:1.25rem;height:1.25rem;cursor:pointer}.todo-text{flex:1;font-size:1rem}.todo-item.completed .todo-text{text-decoration:line-through;color:#999}.todo-actions{display:flex;gap:.5rem;justify-content:center;margin-top:1rem}.btn{padding:.5rem 1rem;border:none;border-radius:4px;cursor:pointer;font-size:.9rem;transition:all .2s}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background:#2196f3;color:#fff}.btn-primary:hover:not(:disabled){background:#1976d2}.btn-warning{background:#ff9800;color:#fff}.btn-warning:hover{background:#e68900}.btn-danger{background:#f44336;color:#fff}.btn-danger:hover{background:#da190b}.btn-sm{padding:.25rem .5rem;font-size:.8rem}.debug-info{margin-top:2rem;padding-top:1rem;border-top:1px solid #ddd}.debug-info summary{cursor:pointer;color:#666;font-size:.9rem}.debug-info pre{background:#f5f5f5;padding:1rem;border-radius:4px;overflow-x:auto;font-size:.85rem;margin-top:.5rem}.user-profile{padding:1.5rem;max-width:800px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.profile-header{margin-bottom:2rem;text-align:center}.profile-header h2{margin:0 0 .5rem;color:#333;font-size:1.75rem}.profile-header .subtitle{color:#666;font-size:.9rem;margin:0}.profile-content{display:flex;flex-direction:column;gap:2rem}.profile-section{background:#f8f9fa;padding:1.5rem;border-radius:8px;border:1px solid #e0e0e0}.profile-section h3{margin:0 0 1rem;color:#333;font-size:1.25rem;border-bottom:2px solid #007bff;padding-bottom:.5rem}.form-group{margin-bottom:1rem}.form-group label{display:block;margin-bottom:.5rem;color:#555;font-weight:500;font-size:.9rem}.form-group input[type=checkbox]{margin-right:.5rem}.input-field{width:100%;padding:.75rem;border:1px solid #ddd;border-radius:4px;font-size:1rem;transition:border-color .2s;box-sizing:border-box}.input-field:focus{outline:none;border-color:#007bff;box-shadow:0 0 0 3px #007bff1a}.profile-actions{display:flex;gap:1rem;flex-wrap:wrap}.btn{padding:.75rem 1.5rem;border:none;border-radius:4px;font-size:1rem;cursor:pointer;transition:all .2s;font-weight:500}.btn-primary{background:#007bff;color:#fff}.btn-primary:hover{background:#0056b3}.btn-secondary{background:#6c757d;color:#fff}.btn-secondary:hover{background:#545b62}.btn-warning{background:#ffc107;color:#212529}.btn-warning:hover{background:#e0a800}.profile-display{background:#f8f9fa;padding:1.5rem;border-radius:8px;border:1px solid #e0e0e0}.profile-display h3{margin:0 0 1rem;color:#333;font-size:1.25rem}.json-display{background:#2d2d2d;color:#f8f8f2;padding:1rem;border-radius:4px;overflow-x:auto;font-family:Courier New,monospace;font-size:.875rem;line-height:1.5;margin:0;white-space:pre-wrap;word-wrap:break-word}
1
+ code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-base-components",
3
- "version": "0.0.16",
4
- "description": "Base UI components built with WSX Framework",
3
+ "version": "0.0.18",
4
+ "description": "Base UI components built with WSXJS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -18,18 +18,23 @@
18
18
  "!**/test"
19
19
  ],
20
20
  "dependencies": {
21
- "@wsxjs/wsx-core": "0.0.16"
21
+ "prismjs": "^1.30.0",
22
+ "@wsxjs/wsx-core": "0.0.18",
23
+ "@wsxjs/wsx-logger": "0.0.18"
22
24
  },
23
25
  "devDependencies": {
26
+ "@types/prismjs": "^1.26.5",
24
27
  "@typescript-eslint/eslint-plugin": "^8.37.0",
25
28
  "@typescript-eslint/parser": "^8.37.0",
29
+ "@vitest/coverage-v8": "^2.1.8",
26
30
  "eslint": "^9.31.0",
27
31
  "http-server": "^14.1.1",
28
32
  "tsup": "^8.0.0",
29
33
  "typescript": "^5.0.0",
30
34
  "vite": "^5.4.19",
31
- "@wsxjs/eslint-plugin-wsx": "0.0.16",
32
- "@wsxjs/wsx-vite-plugin": "0.0.16"
35
+ "vitest": "^2.1.8",
36
+ "@wsxjs/eslint-plugin-wsx": "0.0.18",
37
+ "@wsxjs/wsx-vite-plugin": "0.0.18"
33
38
  },
34
39
  "keywords": [
35
40
  "wsx",
@@ -38,7 +43,7 @@
38
43
  "typescript",
39
44
  "jsx"
40
45
  ],
41
- "author": "WSX Framework Team",
46
+ "author": "WSXJS Team",
42
47
  "license": "MIT",
43
48
  "repository": {
44
49
  "type": "git",
@@ -55,6 +60,10 @@
55
60
  "clean": "rm -rf dist",
56
61
  "typecheck": "tsc --noEmit",
57
62
  "lint": "eslint .",
58
- "lint:fix": "eslint . --fix"
63
+ "lint:fix": "eslint . --fix",
64
+ "test": "vitest",
65
+ "test:run": "vitest run",
66
+ "test:coverage": "vitest run --coverage",
67
+ "test:watch": "vitest watch"
59
68
  }
60
69
  }
@@ -1,4 +1,4 @@
1
- /* XyButton Component Styles - Migrated from xy-button.js */
1
+ /* Button Component Styles */
2
2
  :host {
3
3
  position: relative;
4
4
  display: inline-flex;
@@ -1,6 +1,6 @@
1
1
  /** @jsxImportSource @wsxjs/wsx-core */
2
2
  /**
3
- * XyButton WSX Component - 迁移自xy-button.js
3
+ * Button WSX Component
4
4
  *
5
5
  * 特性:
6
6
  * - 支持多种按钮类型:primary、danger、flat、dashed
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { WebComponent, autoRegister } from "@wsxjs/wsx-core";
14
14
 
15
- export interface XyButtonConfig {
15
+ export interface ButtonConfig {
16
16
  disabled?: boolean;
17
17
  icon?: string;
18
18
  loading?: boolean;
@@ -29,8 +29,8 @@ export interface XyButtonConfig {
29
29
  size?: "xxxs" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl";
30
30
  }
31
31
 
32
- @autoRegister({ tagName: "xy-button" })
33
- export default class XyButton extends WebComponent {
32
+ @autoRegister({ tagName: "wsx-button" })
33
+ export default class Button extends WebComponent {
34
34
  // 状态属性
35
35
  private disabled: boolean = false;
36
36
  private loading: boolean = false;
@@ -68,9 +68,9 @@ export default class XyButton extends WebComponent {
68
68
  ];
69
69
  }
70
70
 
71
- constructor(config: XyButtonConfig = {}) {
71
+ constructor(config: ButtonConfig = {}) {
72
72
  super({
73
- styleName: "xy-button",
73
+ styleName: "wsx-button",
74
74
  ...config,
75
75
  });
76
76
 
@@ -94,7 +94,7 @@ export default class XyButton extends WebComponent {
94
94
 
95
95
  const linkElement = (
96
96
  <a
97
- href={this.href}
97
+ href={this.disabled || this.loading ? undefined : this.href}
98
98
  target={this.target}
99
99
  rel={this.rel || undefined}
100
100
  download={this.download || undefined}
@@ -103,6 +103,10 @@ export default class XyButton extends WebComponent {
103
103
  onKeyDown={this.handleKeyDown}
104
104
  ref={(el: HTMLAnchorElement) => {
105
105
  this.btnElement = el;
106
+ // Update button state after element is set
107
+ if (el) {
108
+ setTimeout(() => this.updateButtonState(), 0);
109
+ }
106
110
  }}
107
111
  >
108
112
  {this.renderContent()}
@@ -118,13 +122,17 @@ export default class XyButton extends WebComponent {
118
122
  onKeyDown={this.handleKeyDown}
119
123
  ref={(el: HTMLButtonElement) => {
120
124
  this.btnElement = el;
125
+ // Update button state after element is set
126
+ if (el) {
127
+ setTimeout(() => this.updateButtonState(), 0);
128
+ }
121
129
  }}
122
130
  >
123
131
  {this.renderContent()}
124
132
  </button>
125
133
  );
126
134
 
127
- return <div className="xy-button-container">{isLink ? linkElement : buttonElement}</div>;
135
+ return <div className="wsx-button-container">{isLink ? linkElement : buttonElement}</div>;
128
136
  }
129
137
 
130
138
  private renderContent(): (HTMLElement | string)[] {
@@ -228,7 +236,8 @@ export default class XyButton extends WebComponent {
228
236
  switch (name) {
229
237
  case "disabled":
230
238
  this.disabled = newValue !== null;
231
- this.updateButtonState();
239
+ this.rerender();
240
+ // updateButtonState will be called in ref callback after rerender
232
241
  break;
233
242
  case "loading":
234
243
  this.loading = newValue !== null;
@@ -0,0 +1,30 @@
1
+ /* ButtonGroup Component Styles */
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+
6
+ ::slotted(wsx-button:not(:first-of-type):not(:last-of-type)) {
7
+ border-radius: 0;
8
+ }
9
+
10
+ ::slotted(wsx-button) {
11
+ margin: 0 !important;
12
+ }
13
+
14
+ ::slotted(wsx-button:not(:first-of-type)) {
15
+ margin-left: -1px !important;
16
+ }
17
+
18
+ ::slotted(wsx-button[type]:not([type="dashed"]):not(:first-of-type)) {
19
+ margin-left: 1px !important;
20
+ }
21
+
22
+ ::slotted(wsx-button:first-of-type) {
23
+ border-top-right-radius: 0;
24
+ border-bottom-right-radius: 0px;
25
+ }
26
+
27
+ ::slotted(wsx-button:last-of-type) {
28
+ border-top-left-radius: 0;
29
+ border-bottom-left-radius: 0;
30
+ }
@@ -1,28 +1,28 @@
1
1
  /** @jsxImportSource @wsxjs/wsx-core */
2
2
  /**
3
- * XyButtonGroup WSX Component - 迁移自xy-button.js
3
+ * ButtonGroup WSX Component
4
4
  *
5
- * 用于将多个xy-button组合成一个按钮组,
5
+ * 用于将多个wsx-button组合成一个按钮组,
6
6
  * 提供统一的样式和布局管理
7
7
  */
8
8
 
9
9
  import { WebComponent, autoRegister } from "@wsxjs/wsx-core";
10
10
 
11
- export interface XyButtonGroupConfig {
11
+ export interface ButtonGroupConfig {
12
12
  disabled?: boolean;
13
13
  }
14
14
 
15
- @autoRegister({ tagName: "xy-button-group" })
16
- export default class XyButtonGroup extends WebComponent {
15
+ @autoRegister({ tagName: "wsx-button-group" })
16
+ export default class ButtonGroup extends WebComponent {
17
17
  private disabled: boolean = false;
18
18
 
19
19
  static get observedAttributes(): string[] {
20
20
  return ["disabled"];
21
21
  }
22
22
 
23
- constructor(config: XyButtonGroupConfig = {}) {
23
+ constructor(config: ButtonGroupConfig = {}) {
24
24
  super({
25
- styleName: "xy-button-group",
25
+ styleName: "wsx-button-group",
26
26
  ...config,
27
27
  });
28
28
 
@@ -48,7 +48,10 @@ export default class XyButtonGroup extends WebComponent {
48
48
  switch (name) {
49
49
  case "disabled":
50
50
  this.disabled = newValue !== null;
51
- this.updateChildrenDisabledState();
51
+ // Use setTimeout to ensure DOM is updated
52
+ setTimeout(() => {
53
+ this.updateChildrenDisabledState();
54
+ }, 0);
52
55
  break;
53
56
  }
54
57
  }
@@ -65,10 +68,10 @@ export default class XyButtonGroup extends WebComponent {
65
68
  * 更新所有子按钮的disabled状态
66
69
  */
67
70
  private updateChildrenDisabledState(): void {
68
- if (!this.disabled) return;
69
-
70
- // 获取所有xy-button子元素
71
- const buttons = this.querySelectorAll("xy-button");
71
+ // 获取所有wsx-button子元素(在 light DOM 中,通过 slot)
72
+ const buttons = Array.from(this.children).filter(
73
+ (child) => child.tagName === "WSX-BUTTON"
74
+ ) as Element[];
72
75
  buttons.forEach((button: Element) => {
73
76
  if (this.disabled) {
74
77
  button.setAttribute("disabled", "");
@@ -96,8 +99,17 @@ export default class XyButtonGroup extends WebComponent {
96
99
  /**
97
100
  * 公共API:获取组内的所有按钮
98
101
  */
99
- public getButtons(): NodeListOf<Element> {
100
- return this.querySelectorAll("xy-button");
102
+ public getButtons(): Element[] {
103
+ // 获取所有wsx-button子元素(在 light DOM 中,通过 slot)
104
+ // Try querySelectorAll first, then fallback to children array
105
+ const buttons = this.querySelectorAll("wsx-button");
106
+ if (buttons.length > 0) {
107
+ return Array.from(buttons);
108
+ }
109
+ // Fallback to children array
110
+ return Array.from(this.children).filter(
111
+ (child) => child.tagName === "WSX-BUTTON"
112
+ ) as Element[];
101
113
  }
102
114
 
103
115
  /**
@@ -0,0 +1,275 @@
1
+ /* Code Block Component Styles */
2
+ .code-block {
3
+ background: var(--bg-secondary, #f9fafb);
4
+ border-radius: 12px;
5
+ overflow: hidden;
6
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
7
+ border: 1px solid var(--border-color, #e5e7eb);
8
+ }
9
+
10
+ .code-header {
11
+ display: flex;
12
+ justify-content: space-between;
13
+ align-items: center;
14
+ padding: 1rem 1.5rem;
15
+ background: var(--bg-primary, #ffffff);
16
+ border-bottom: 1px solid var(--border-color, #e5e7eb);
17
+ }
18
+
19
+ .code-title {
20
+ color: var(--text-primary, #111827);
21
+ font-weight: 600;
22
+ font-size: 0.9rem;
23
+ }
24
+
25
+ .code-actions {
26
+ display: flex;
27
+ gap: 0.75rem;
28
+ }
29
+
30
+ .btn-copy,
31
+ .btn-try {
32
+ padding: 0.5rem 1rem;
33
+ border-radius: 6px;
34
+ font-size: 0.875rem;
35
+ font-weight: 600;
36
+ cursor: pointer;
37
+ transition: all 0.2s ease;
38
+ border: none;
39
+ font-family: inherit;
40
+ }
41
+
42
+ .btn-copy {
43
+ background: var(--border-color, #e5e7eb);
44
+ color: var(--text-primary, #111827);
45
+ }
46
+
47
+ .btn-copy:hover {
48
+ background: var(--text-secondary, #6b7280);
49
+ color: var(--bg-primary, #ffffff);
50
+ }
51
+
52
+ .btn-copy.copied {
53
+ background: #10b981;
54
+ color: white;
55
+ }
56
+
57
+ .btn-try {
58
+ background: linear-gradient(135deg, #ea580c, #f97316);
59
+ color: white;
60
+ }
61
+
62
+ .btn-try:hover {
63
+ transform: translateY(-2px);
64
+ box-shadow: 0 4px 15px rgba(234, 88, 12, 0.3);
65
+ }
66
+
67
+ .code-content {
68
+ padding: 2rem;
69
+ margin: 0;
70
+ /* 浅色模式默认 */
71
+ background: #f8f9fa;
72
+ color: #24292e;
73
+ font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
74
+ font-size: 0.9rem;
75
+ line-height: 1.6;
76
+ overflow-x: auto;
77
+ position: relative;
78
+ }
79
+
80
+ /* 深色模式 */
81
+ @media (prefers-color-scheme: dark) {
82
+ .code-content {
83
+ background: #2d2d2d;
84
+ color: #f8f8f2;
85
+ }
86
+ }
87
+
88
+ .code-segment {
89
+ margin: 0;
90
+ padding: 0;
91
+ background: transparent;
92
+ }
93
+
94
+ .code-segment:not(:last-child) {
95
+ margin-bottom: 1.5rem;
96
+ }
97
+
98
+ .code-content code {
99
+ display: block;
100
+ white-space: pre;
101
+ word-wrap: normal;
102
+ word-break: normal;
103
+ tab-size: 4;
104
+ -moz-tab-size: 4;
105
+ -o-tab-size: 4;
106
+ }
107
+
108
+ /* Prism.js 语法高亮样式 - 确保主题样式正确应用 */
109
+ .code-content code[class*="language-"] {
110
+ background: transparent;
111
+ }
112
+
113
+ /* 浅色模式 - 使用更深的颜色,确保 WCAG AA 对比度 (4.5:1+) */
114
+ /* 背景: #f8f9fa (RGB: 248, 249, 250) */
115
+ .code-content .token.comment,
116
+ .code-content .token.prolog,
117
+ .code-content .token.doctype,
118
+ .code-content .token.cdata {
119
+ color: #5a6268 !important; /* 深灰色注释 - 对比度 7.2:1 */
120
+ font-style: italic;
121
+ }
122
+
123
+ .code-content .token.punctuation {
124
+ color: #1a1a1a !important; /* 接近黑色标点符号 - 对比度 15.8:1 */
125
+ }
126
+
127
+ .code-content .token.property,
128
+ .code-content .token.tag,
129
+ .code-content .token.boolean,
130
+ .code-content .token.number,
131
+ .code-content .token.constant,
132
+ .code-content .token.symbol,
133
+ .code-content .token.deleted {
134
+ color: #c7254e !important; /* 深红色 - 对比度 5.8:1 */
135
+ }
136
+
137
+ .code-content .token.selector,
138
+ .code-content .token.attr-name,
139
+ .code-content .token.string,
140
+ .code-content .token.char,
141
+ .code-content .token.builtin,
142
+ .code-content .token.inserted {
143
+ color: #005cc5 !important; /* 深蓝色字符串 - 对比度 7.1:1 */
144
+ }
145
+
146
+ .code-content .token.operator,
147
+ .code-content .token.entity,
148
+ .code-content .token.url,
149
+ .code-content .language-css .token.string,
150
+ .code-content .style .token.string {
151
+ color: #1a1a1a !important; /* 接近黑色操作符 - 对比度 15.8:1 */
152
+ }
153
+
154
+ .code-content .token.atrule,
155
+ .code-content .token.attr-value,
156
+ .code-content .token.keyword {
157
+ color: #d73a49 !important; /* 深红色关键字 - 对比度 5.2:1 */
158
+ font-weight: 600;
159
+ }
160
+
161
+ .code-content .token.function,
162
+ .code-content .token.class-name {
163
+ color: #6f42c1 !important; /* 深紫色函数/类名 - 对比度 6.1:1 */
164
+ }
165
+
166
+ .code-content .token.regex,
167
+ .code-content .token.important,
168
+ .code-content .token.variable {
169
+ color: #e36209 !important; /* 深橙色变量 - 对比度 4.8:1 */
170
+ }
171
+
172
+ /* 默认文字颜色 */
173
+ .code-content code[class*="language-"] {
174
+ color: #24292e !important; /* 深色文字 - 对比度 12.6:1 */
175
+ }
176
+
177
+ /* 深色模式 - 使用更鲜明的颜色 */
178
+ @media (prefers-color-scheme: dark) {
179
+ .code-content code[class*="language-"] {
180
+ color: #f8f8f2 !important; /* 默认文字颜色 - 更亮的白色 */
181
+ }
182
+
183
+ .code-content .token.comment,
184
+ .code-content .token.prolog,
185
+ .code-content .token.doctype,
186
+ .code-content .token.cdata {
187
+ color: #75715e !important; /* 深灰色注释 */
188
+ font-style: italic;
189
+ }
190
+
191
+ .code-content .token.punctuation {
192
+ color: #f8f8f2 !important; /* 白色标点符号 */
193
+ }
194
+
195
+ .code-content .token.property,
196
+ .code-content .token.tag,
197
+ .code-content .token.boolean,
198
+ .code-content .token.number,
199
+ .code-content .token.constant,
200
+ .code-content .token.symbol,
201
+ .code-content .token.deleted {
202
+ color: #f92672 !important; /* 深粉色/红色 */
203
+ }
204
+
205
+ .code-content .token.selector,
206
+ .code-content .token.attr-name,
207
+ .code-content .token.string,
208
+ .code-content .token.char,
209
+ .code-content .token.builtin,
210
+ .code-content .token.inserted {
211
+ color: #e6db74 !important; /* 深黄色字符串 */
212
+ }
213
+
214
+ .code-content .token.operator,
215
+ .code-content .token.entity,
216
+ .code-content .token.url,
217
+ .code-content .language-css .token.string,
218
+ .code-content .style .token.string {
219
+ color: #f8f8f2 !important; /* 白色操作符 */
220
+ }
221
+
222
+ .code-content .token.atrule,
223
+ .code-content .token.attr-value,
224
+ .code-content .token.keyword {
225
+ color: #66d9ef !important; /* 深青色关键字 */
226
+ font-weight: 500;
227
+ }
228
+
229
+ .code-content .token.function,
230
+ .code-content .token.class-name {
231
+ color: #a6e22e !important; /* 深绿色函数/类名 */
232
+ }
233
+
234
+ .code-content .token.regex,
235
+ .code-content .token.important,
236
+ .code-content .token.variable {
237
+ color: #fd971f !important; /* 深橙色变量 */
238
+ }
239
+ }
240
+
241
+ /* 特殊 token 类型 */
242
+ .code-content .token.namespace {
243
+ opacity: 0.7;
244
+ }
245
+
246
+ .code-content .token.important {
247
+ font-weight: bold;
248
+ }
249
+
250
+ .code-content .token.bold {
251
+ font-weight: bold;
252
+ }
253
+
254
+ .code-content .token.italic {
255
+ font-style: italic;
256
+ }
257
+
258
+ /* 响应式设计 */
259
+ @media (max-width: 768px) {
260
+ .code-header {
261
+ flex-direction: column;
262
+ gap: 1rem;
263
+ align-items: flex-start;
264
+ }
265
+
266
+ .code-actions {
267
+ width: 100%;
268
+ justify-content: flex-end;
269
+ }
270
+
271
+ .code-content {
272
+ padding: 1.5rem;
273
+ font-size: 0.85rem;
274
+ }
275
+ }
@@ -0,0 +1,25 @@
1
+ export interface CodeSegment {
2
+ /** 代码内容 */
3
+ code: string;
4
+ /** 编程语言(用于语法高亮) */
5
+ language: string;
6
+ }
7
+
8
+ export interface CodeBlockConfig {
9
+ /** 代码内容(单个代码块时使用) */
10
+ code?: string;
11
+ /** 代码段数组(多个代码块时使用,优先级高于 code) */
12
+ segments?: CodeSegment[];
13
+ /** 代码标题(可选) */
14
+ title?: string;
15
+ /** 编程语言(用于语法高亮,默认为 'typescript',仅在单个代码块时使用) */
16
+ language?: string;
17
+ /** 是否显示复制按钮 */
18
+ showCopy?: boolean;
19
+ /** 是否显示"在线体验"按钮 */
20
+ showTryOnline?: boolean;
21
+ /** "在线体验"按钮的 URL(优先级低于 onTryOnline) */
22
+ tryOnlineUrl?: string;
23
+ /** "在线体验"按钮的点击回调(优先级高于 tryOnlineUrl) */
24
+ onTryOnline?: () => void;
25
+ }