fogact 1.1.5 → 1.1.6

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.
@@ -3,13 +3,22 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>激活 CDK | FogIDC Activator</title>
7
- <meta name="description" content="输入 FogIDC Activator 激活码,绑定用户并开通服务。" />
6
+ <title>激活 CDK | FogAct</title>
7
+ <meta name="description" content="输入 FogAct 激活码,完成服务绑定并查看额度与有效期。" />
8
+ <script>
9
+ ;(function () {
10
+ var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
11
+ var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
12
+ var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
13
+ document.documentElement.classList.toggle('dark', isDark);
14
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
15
+ })();
16
+ </script>
8
17
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
18
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
19
  <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
11
20
  <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
12
- <link rel="stylesheet" href="/assets/market-ui.css?v=20260616-upstream2" />
21
+ <link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
13
22
  </head>
14
23
  <body class="market-page">
15
24
  <span class="market-mouse-light" aria-hidden="true"></span>
@@ -19,8 +28,8 @@
19
28
  <a class="market-brand" href="/">
20
29
  <span class="market-logo">F</span>
21
30
  <span>
22
- <span class="market-brand-title">FogIDC Activator</span>
23
- <span class="market-brand-subtitle">Redeem center</span>
31
+ <span class="market-brand-title">FogAct</span>
32
+ <span class="market-brand-subtitle">输入激活码完成服务绑定</span>
24
33
  </span>
25
34
  </a>
26
35
 
@@ -29,14 +38,20 @@
29
38
  <a href="/admin/">管理后台</a>
30
39
  <a href="/">首页</a>
31
40
  </nav>
41
+
42
+ <div class="market-actions">
43
+ <button type="button" class="market-button market-theme-toggle" data-theme-toggle aria-label="切换暗黑模式">
44
+ <span class="material-symbols-outlined" data-theme-icon>light_mode</span>
45
+ </button>
46
+ </div>
32
47
  </header>
33
48
 
34
49
  <main class="market-activation">
35
50
  <section class="market-activation-copy" aria-labelledby="activation-title">
36
- <div class="market-kicker"><span class="market-kicker-dot"></span> fast activation flow</div>
51
+ <div class="market-kicker"><span class="market-kicker-dot"></span> 快速激活流程</div>
37
52
  <h1 id="activation-title" class="market-title">输入 CDK,立即开通 <span>CLI 服务</span></h1>
38
53
  <p class="market-copy">
39
- 保留原有激活接口,升级为更清晰的产品化表单。输入激活码后会自动校验额度、服务类型和有效期。
54
+ 输入 CDK 后即可绑定服务;用户名和邮箱用于后台识别,可按需填写。
40
55
  </p>
41
56
 
42
57
  <div class="market-flow" aria-label="激活流程">
@@ -50,7 +65,7 @@
50
65
  </div>
51
66
  <div class="market-flow-step">
52
67
  <span class="market-flow-index">3</span>
53
- <span><strong>点火开通</strong><span>写入服务、额度和有效期</span></span>
68
+ <span><strong>开通服务</strong><span>写入服务、额度和有效期</span></span>
54
69
  </div>
55
70
  </div>
56
71
 
@@ -61,7 +76,7 @@
61
76
  <p class="market-mini-title">安全校验</p>
62
77
  <p class="market-mini-copy">激活过程通过本地 API 完成,失败会返回明确提示。</p>
63
78
  </div>
64
- <span class="market-status-pill">Secure</span>
79
+ <span class="market-status-pill">安全</span>
65
80
  </div>
66
81
  <div class="market-mini-card">
67
82
  <span class="market-icon material-symbols-outlined">rocket_launch</span>
@@ -69,7 +84,7 @@
69
84
  <p class="market-mini-title">多服务支持</p>
70
85
  <p class="market-mini-copy">适配 Codex、Claude Code、OpenCode 与 OpenClaw 场景。</p>
71
86
  </div>
72
- <span class="market-status-pill">Multi</span>
87
+ <span class="market-status-pill">多平台</span>
73
88
  </div>
74
89
  </div>
75
90
  </section>
@@ -84,22 +99,22 @@
84
99
  <div class="market-form-header">
85
100
  <span class="market-icon material-symbols-outlined">vpn_key</span>
86
101
  <h1>激活 CDK</h1>
87
- <p>填写激活码,用户名和邮箱可选。成功后会展示服务类型、额度和过期时间。</p>
102
+ <p>输入 CDK 后即可绑定服务;用户名和邮箱用于后台识别,可按需填写。</p>
88
103
  </div>
89
104
 
90
105
  <form id="activate-form" class="market-form">
91
106
  <div class="market-field">
92
- <label for="activation-code">激活码 <span>Required</span></label>
107
+ <label for="activation-code">激活码 <span>必填</span></label>
93
108
  <input type="text" id="activation-code" placeholder="例如 XXXX-XXXX-XXXX-XXXX" required />
94
109
  </div>
95
110
 
96
111
  <div class="market-field">
97
- <label for="username">用户名 <span>Optional</span></label>
112
+ <label for="username">用户名 <span>选填</span></label>
98
113
  <input type="text" id="username" placeholder="用于后台识别的用户名" />
99
114
  </div>
100
115
 
101
116
  <div class="market-field">
102
- <label for="email">邮箱 <span>Optional</span></label>
117
+ <label for="email">邮箱 <span>选填</span></label>
103
118
  <input type="email" id="email" placeholder="name@example.com" />
104
119
  </div>
105
120
 
@@ -107,7 +122,7 @@
107
122
 
108
123
  <button type="submit" id="submit-btn" class="market-button market-button-primary" style="width: 100%; min-height: 50px;">
109
124
  <span class="material-symbols-outlined">bolt</span>
110
- 激活服务
125
+ 立即激活
111
126
  </button>
112
127
  </form>
113
128
 
@@ -127,7 +142,7 @@
127
142
  </div>
128
143
 
129
144
  <h2>激活成功</h2>
130
- <p>您的激活码已绑定,下面是本次开通详情。</p>
145
+ <p>服务已开通,以下是本次激活详情。</p>
131
146
 
132
147
  <div class="market-result-list">
133
148
  <div class="market-result-row"><span>激活码</span><span id="result-code"></span></div>
@@ -175,6 +190,8 @@
175
190
  });
176
191
  });
177
192
 
193
+ initThemeToggle();
194
+
178
195
  form.addEventListener('submit', async (event) => {
179
196
  event.preventDefault();
180
197
 
@@ -210,10 +227,32 @@
210
227
  showError('网络错误,请稍后重试');
211
228
  } finally {
212
229
  submitBtn.disabled = false;
213
- submitBtn.innerHTML = '<span class="material-symbols-outlined">bolt</span> 激活服务';
230
+ submitBtn.innerHTML = '<span class="material-symbols-outlined">bolt</span> 立即激活';
214
231
  }
215
232
  });
216
233
 
234
+ function initThemeToggle() {
235
+ const button = document.querySelector('[data-theme-toggle]');
236
+ const icon = document.querySelector('[data-theme-icon]');
237
+ if (!button || !icon) return;
238
+
239
+ const sync = () => {
240
+ const isDark = document.documentElement.classList.contains('dark');
241
+ icon.textContent = isDark ? 'dark_mode' : 'light_mode';
242
+ button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
243
+ };
244
+
245
+ button.addEventListener('click', () => {
246
+ const isDark = !document.documentElement.classList.contains('dark');
247
+ document.documentElement.classList.toggle('dark', isDark);
248
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
249
+ localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
250
+ sync();
251
+ });
252
+
253
+ sync();
254
+ }
255
+
217
256
  function showError(message) {
218
257
  errorMessage.textContent = message;
219
258
  errorMessage.classList.remove('hidden');
@@ -229,7 +229,7 @@ function getServiceOptionsForScope(currentValue = '') {
229
229
 
230
230
  const DEFAULT_SETTINGS = {
231
231
  site: {
232
- siteName: 'FogIDC Activator',
232
+ siteName: 'FogAct',
233
233
  siteDescription: '统一管理用户、激活码与订阅配置。',
234
234
  siteUrl: 'https://example.com',
235
235
  logoUrl: '',
@@ -259,7 +259,7 @@ const DEFAULT_SETTINGS = {
259
259
  email: {
260
260
  smtpHost: '',
261
261
  smtpPort: 587,
262
- senderName: 'FogIDC Activator',
262
+ senderName: 'FogAct',
263
263
  senderEmail: '',
264
264
  enableTls: true
265
265
  },
@@ -356,18 +356,22 @@ const UI = {
356
356
  initThemeToggle() {
357
357
  const button = document.getElementById('theme-toggle');
358
358
  const html = document.documentElement;
359
- const savedTheme = localStorage.getItem('admin_theme') || 'light';
359
+ const savedTheme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || 'system';
360
+ const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
361
+ const initialDark = savedTheme === 'dark' || (savedTheme === 'system' && prefersDark);
360
362
 
361
- if (savedTheme === 'dark') {
362
- html.classList.add('dark');
363
- button.querySelector('.material-symbols-outlined').textContent = 'dark_mode';
364
- }
363
+ html.classList.toggle('dark', initialDark);
364
+ html.style.colorScheme = initialDark ? 'dark' : 'light';
365
+ button.querySelector('.material-symbols-outlined').textContent = initialDark ? 'dark_mode' : 'light_mode';
366
+ button.setAttribute('aria-label', initialDark ? '切换浅色模式' : '切换暗黑模式');
365
367
 
366
368
  button.addEventListener('click', () => {
367
369
  html.classList.toggle('dark');
368
370
  const isDark = html.classList.contains('dark');
369
371
  button.querySelector('.material-symbols-outlined').textContent = isDark ? 'dark_mode' : 'light_mode';
370
- localStorage.setItem('admin_theme', isDark ? 'dark' : 'light');
372
+ button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
373
+ html.style.colorScheme = isDark ? 'dark' : 'light';
374
+ localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
371
375
  });
372
376
  },
373
377
 
@@ -375,13 +379,15 @@ const UI = {
375
379
  const palette = {
376
380
  success: 'bg-green-50 border-green-500 text-green-800',
377
381
  error: 'bg-error-container border-error text-on-error-container',
378
- info: 'bg-primary-fixed border-primary text-on-primary-fixed'
382
+ info: 'bg-primary-fixed border-primary text-on-primary-fixed',
383
+ warning: 'bg-warning-container border-warning text-on-surface'
379
384
  };
380
385
 
381
386
  const icons = {
382
387
  success: 'check_circle',
383
388
  error: 'error',
384
- info: 'info'
389
+ info: 'info',
390
+ warning: 'warning'
385
391
  };
386
392
 
387
393
  const node = document.createElement('div');
@@ -569,7 +575,7 @@ const UserManagement = {
569
575
  renderTable() {
570
576
  const container = document.getElementById('users-table-container');
571
577
  if (!AppState.users.length) {
572
- container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无用户数据</p>';
578
+ container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无用户数据,可点击右上角添加用户。</p>';
573
579
  return;
574
580
  }
575
581
 
@@ -700,7 +706,7 @@ const UserManagement = {
700
706
  try {
701
707
  const result = await API.deleteUser(id);
702
708
  if (!result.success) {
703
- UI.showNotification(result.message || '删除失败', 'error');
709
+ UI.showNotification(result.message || '用户删除失败', 'error');
704
710
  return;
705
711
  }
706
712
  UI.showNotification('用户已删除', 'success');
@@ -712,7 +718,7 @@ const UserManagement = {
712
718
  return;
713
719
  }
714
720
  console.error(error);
715
- UI.showNotification('删除失败', 'error');
721
+ UI.showNotification('用户删除失败', 'error');
716
722
  }
717
723
  }
718
724
  };
@@ -761,7 +767,7 @@ const CodeManagement = {
761
767
  renderTable() {
762
768
  const container = document.getElementById('codes-table-container');
763
769
  if (!AppState.codes.length) {
764
- container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无激活码数据</p>';
770
+ container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无激活码数据,可点击右上角生成 CDK。</p>';
765
771
  return;
766
772
  }
767
773
 
@@ -1196,7 +1202,7 @@ const CodeManagement = {
1196
1202
  try {
1197
1203
  const result = await API.createCode(payload);
1198
1204
  if (!result.success) {
1199
- UI.showNotification(result.message || '创建失败', 'error');
1205
+ UI.showNotification(result.message || '激活码创建失败', 'error');
1200
1206
  return;
1201
1207
  }
1202
1208
  UI.hideModal();
@@ -1209,7 +1215,7 @@ const CodeManagement = {
1209
1215
  return;
1210
1216
  }
1211
1217
  console.error(error);
1212
- UI.showNotification('创建失败', 'error');
1218
+ UI.showNotification('激活码创建失败', 'error');
1213
1219
  }
1214
1220
  },
1215
1221
 
@@ -1225,7 +1231,7 @@ const CodeManagement = {
1225
1231
  try {
1226
1232
  const result = await API.updateCode(id, payload);
1227
1233
  if (!result.success) {
1228
- UI.showNotification(result.message || '更新失败', 'error');
1234
+ UI.showNotification(result.message || '激活码更新失败', 'error');
1229
1235
  return;
1230
1236
  }
1231
1237
  UI.hideModal();
@@ -1238,7 +1244,7 @@ const CodeManagement = {
1238
1244
  return;
1239
1245
  }
1240
1246
  console.error(error);
1241
- UI.showNotification('更新失败', 'error');
1247
+ UI.showNotification('激活码更新失败', 'error');
1242
1248
  }
1243
1249
  },
1244
1250
 
@@ -1247,7 +1253,7 @@ const CodeManagement = {
1247
1253
  try {
1248
1254
  const result = await API.deleteCode(id);
1249
1255
  if (!result.success) {
1250
- UI.showNotification(result.message || '删除失败', 'error');
1256
+ UI.showNotification(result.message || '激活码删除失败', 'error');
1251
1257
  return;
1252
1258
  }
1253
1259
  UI.showNotification('激活码已删除', 'success');
@@ -1259,14 +1265,14 @@ const CodeManagement = {
1259
1265
  return;
1260
1266
  }
1261
1267
  console.error(error);
1262
- UI.showNotification('删除失败', 'error');
1268
+ UI.showNotification('激活码删除失败', 'error');
1263
1269
  }
1264
1270
  }
1265
1271
  };
1266
1272
 
1267
1273
  const LogsManagement = {
1268
1274
  render() {
1269
- document.getElementById('logs-container').innerHTML = '<p class="text-on-surface-variant text-center py-8">日志功能开发中...</p>';
1275
+ document.getElementById('logs-container').innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无日志记录,后续操作会在这里展示。</p>';
1270
1276
  }
1271
1277
  };
1272
1278
 
@@ -1,9 +1,19 @@
1
1
  <!DOCTYPE html>
2
- <html class="light" lang="zh-CN">
2
+ <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>管理中心 | FogIDC Activator</title>
6
+ <title>管理中心 | FogAct</title>
7
+
8
+ <script>
9
+ ;(function () {
10
+ var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
11
+ var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
12
+ var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
13
+ document.documentElement.classList.toggle('dark', isDark);
14
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
15
+ })();
16
+ </script>
7
17
 
8
18
  <!-- Fonts -->
9
19
  <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
@@ -381,7 +391,7 @@
381
391
  height: 80%;
382
392
  }
383
393
  </style>
384
- <link rel="stylesheet" href="/assets/market-ui.css?v=20260617-platform-scope1">
394
+ <link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1">
385
395
  </head>
386
396
  <body class="market-admin bg-surface text-on-surface min-h-screen flex overflow-hidden">
387
397
  <span class="market-mouse-light" aria-hidden="true"></span>
@@ -394,8 +404,8 @@
394
404
  <div class="w-16 h-16 bg-primary rounded-2xl flex items-center justify-center mx-auto mb-4">
395
405
  <span class="material-symbols-outlined text-on-primary text-3xl">shield</span>
396
406
  </div>
397
- <h1 class="text-2xl font-bold text-on-surface mb-2">管理后台登录</h1>
398
- <p class="text-sm text-on-surface-variant">FogIDC Activator 企业版</p>
407
+ <h1 class="text-2xl font-bold text-on-surface mb-2">登录管理中心</h1>
408
+ <p class="text-sm text-on-surface-variant">FogAct 管理中心</p>
399
409
  </div>
400
410
 
401
411
  <div id="login-alert" class="hidden mb-4 p-3 rounded-xl text-sm bg-error-container border border-error text-on-error-container"></div>
@@ -421,7 +431,7 @@
421
431
  </form>
422
432
 
423
433
  <div class="text-center mt-6 text-xs text-on-surface-variant">
424
- 默认密码: admin123 (可通过环境变量 ADMIN_PASSWORD 修改)
434
+ 管理密码可通过 ADMIN_PASSWORD 环境变量配置
425
435
  </div>
426
436
  </div>
427
437
  </div>
@@ -435,8 +445,8 @@
435
445
  <div class="text-xl font-black text-on-surface mb-8 px-4 flex items-center gap-3">
436
446
  <span class="market-logo">F</span>
437
447
  <span>
438
- <span class="block leading-none">FogIDC</span>
439
- <span class="block text-xs font-bold text-on-surface-variant mt-1">Admin console</span>
448
+ <span class="block leading-none">FogAct</span>
449
+ <span class="block text-xs font-bold text-on-surface-variant mt-1">管理中心</span>
440
450
  </span>
441
451
  </div>
442
452
 
@@ -661,7 +671,7 @@
661
671
  <div class="bg-surface-container-lowest rounded-2xl p-6 border border-outline-variant/20">
662
672
  <h3 class="text-lg font-bold text-on-surface mb-6">系统日志</h3>
663
673
  <div id="logs-container">
664
- <p class="text-on-surface-variant text-center py-8">日志功能开发中...</p>
674
+ <p class="text-on-surface-variant text-center py-8">暂无日志记录,后续操作会在这里展示。</p>
665
675
  </div>
666
676
  </div>
667
677
  </div>
@@ -671,7 +681,7 @@
671
681
  <div class="bg-surface-container-lowest rounded-2xl p-6 border border-outline-variant/20">
672
682
  <h3 class="text-lg font-bold text-on-surface mb-6">系统设置</h3>
673
683
  <div id="settings-container">
674
- <p class="text-on-surface-variant text-center py-8">设置功能开发中...</p>
684
+ <p class="text-on-surface-variant text-center py-8">请选择需要维护的系统配置。</p>
675
685
  </div>
676
686
  </div>
677
687
  </div>
@@ -684,7 +694,7 @@
684
694
  <div id="modal-root"></div>
685
695
 
686
696
  <!-- JavaScript -->
687
- <script src="/admin/admin-panel-v2.js?v=20260617-platform-scope1"></script>
697
+ <script src="/admin/admin-panel-v2.js?v=20260619-theme1"></script>
688
698
  <script>
689
699
  let marketPointerRaf = 0;
690
700
  window.addEventListener('load', () => {
@@ -211,6 +211,16 @@ body.market-user::after {
211
211
  box-shadow: var(--market-shadow-soft);
212
212
  }
213
213
 
214
+ .market-theme-toggle {
215
+ width: 40px;
216
+ min-width: 40px;
217
+ padding: 0;
218
+ }
219
+
220
+ .market-theme-toggle .material-symbols-outlined {
221
+ font-size: 20px;
222
+ }
223
+
214
224
  .market-button-primary {
215
225
  border-color: color-mix(in srgb, var(--market-primary) 70%, transparent);
216
226
  color: #fff;
@@ -222,6 +232,11 @@ body.market-user::after {
222
232
  color: #fff;
223
233
  }
224
234
 
235
+ html.dark .market-button:not(.market-button-primary),
236
+ body.market-dark .market-button:not(.market-button-primary) {
237
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
238
+ }
239
+
225
240
  .market-hero {
226
241
  display: grid;
227
242
  grid-template-columns: minmax(0, 1.05fr) minmax(320px, 0.75fr);
@@ -263,7 +278,13 @@ body.market-user::after {
263
278
 
264
279
  html.dark .market-hero-main::before,
265
280
  html.dark .market-card::before,
266
- html.dark .market-auth-card::before {
281
+ html.dark .market-auth-card::before,
282
+ body.market-dark .market-hero-main::before,
283
+ body.market-dark .market-card::before,
284
+ body.market-dark .market-auth-card::before {
285
+ background:
286
+ radial-gradient(circle at 12% 0%, rgba(255, 255, 255, 0.12), transparent 25%),
287
+ linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent 36%);
267
288
  opacity: 0.09;
268
289
  }
269
290
 
@@ -1041,6 +1062,59 @@ body.market-admin {
1041
1062
  background-color: color-mix(in srgb, var(--market-danger) 14%, transparent) !important;
1042
1063
  }
1043
1064
 
1065
+ html.dark .market-admin .bg-green-50,
1066
+ body.market-dark .market-admin .bg-green-50 {
1067
+ background-color: color-mix(in srgb, var(--market-success) 18%, transparent) !important;
1068
+ }
1069
+
1070
+ html.dark .market-admin .text-green-700,
1071
+ html.dark .market-admin .text-green-800,
1072
+ body.market-dark .market-admin .text-green-700,
1073
+ body.market-dark .market-admin .text-green-800 {
1074
+ color: color-mix(in srgb, var(--market-success) 82%, #fff 18%) !important;
1075
+ }
1076
+
1077
+ html.dark .market-admin .bg-amber-50,
1078
+ body.market-dark .market-admin .bg-amber-50 {
1079
+ background-color: color-mix(in srgb, var(--market-warning) 18%, transparent) !important;
1080
+ }
1081
+
1082
+ html.dark .market-admin .text-amber-700,
1083
+ body.market-dark .market-admin .text-amber-700 {
1084
+ color: color-mix(in srgb, var(--market-warning) 82%, #fff 18%) !important;
1085
+ }
1086
+
1087
+ html.dark .market-admin tbody tr,
1088
+ body.market-dark .market-admin tbody tr {
1089
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);
1090
+ }
1091
+
1092
+ html.dark .market-admin option,
1093
+ body.market-dark .market-admin option {
1094
+ color: var(--market-ink);
1095
+ background: var(--market-bg-soft);
1096
+ }
1097
+
1098
+ html.dark .market-admin .modal-backdrop,
1099
+ body.market-dark .market-admin .modal-backdrop {
1100
+ background: rgba(0, 0, 0, 0.68) !important;
1101
+ }
1102
+
1103
+ html.dark .market-admin .border-b,
1104
+ body.market-dark .market-admin .border-b {
1105
+ border-color: var(--market-line) !important;
1106
+ }
1107
+
1108
+ html.dark .market-admin [class*="bg-error-container"],
1109
+ body.market-dark .market-admin [class*="bg-error-container"] {
1110
+ background-color: color-mix(in srgb, var(--market-danger) 16%, transparent) !important;
1111
+ }
1112
+
1113
+ html.dark .market-admin [class*="text-on-error-container"],
1114
+ body.market-dark .market-admin [class*="text-on-error-container"] {
1115
+ color: color-mix(in srgb, var(--market-danger) 76%, #fff 24%) !important;
1116
+ }
1117
+
1044
1118
  /* Built user app polish */
1045
1119
  body.market-user {
1046
1120
  overflow-x: hidden;
@@ -3,13 +3,22 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>FogIDC Activator | 动态激活控制台</title>
7
- <meta name="description" content="FogIDC Activator 的用户中心、管理后台与激活码入口。" />
6
+ <title>FogAct | 多平台激活与配额管理</title>
7
+ <meta name="description" content="FogAct 提供用户中心、激活码管理、配额查看和多平台 CLI 接入入口。" />
8
+ <script>
9
+ ;(function () {
10
+ var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
11
+ var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
12
+ var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
13
+ document.documentElement.classList.toggle('dark', isDark);
14
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
15
+ })();
16
+ </script>
8
17
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
18
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
19
  <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
11
20
  <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
12
- <link rel="stylesheet" href="/assets/market-ui.css?v=20260616-upstream2" />
21
+ <link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
13
22
  </head>
14
23
  <body class="market-page">
15
24
  <span class="market-mouse-light" aria-hidden="true"></span>
@@ -19,8 +28,8 @@
19
28
  <a class="market-brand" href="/">
20
29
  <span class="market-logo">F</span>
21
30
  <span>
22
- <span class="market-brand-title">FogIDC Activator</span>
23
- <span class="market-brand-subtitle">Motion control surface</span>
31
+ <span class="market-brand-title">FogAct</span>
32
+ <span class="market-brand-subtitle">多平台激活工作台</span>
24
33
  </span>
25
34
  </a>
26
35
 
@@ -31,6 +40,9 @@
31
40
  </nav>
32
41
 
33
42
  <div class="market-actions">
43
+ <button type="button" class="market-button market-theme-toggle" data-theme-toggle aria-label="切换暗黑模式">
44
+ <span class="material-symbols-outlined" data-theme-icon>light_mode</span>
45
+ </button>
34
46
  <a class="market-button" href="/api/stats">API</a>
35
47
  <a class="market-button market-button-primary" href="/user/">
36
48
  <span class="material-symbols-outlined">bolt</span>
@@ -42,10 +54,10 @@
42
54
  <main>
43
55
  <section class="market-hero" aria-labelledby="hero-title">
44
56
  <div class="market-hero-main">
45
- <div class="market-kicker"><span class="market-kicker-dot"></span> living interface for CLI activation</div>
46
- <h1 id="hero-title" class="market-title">不是工具页,是一个会呼吸的 <span>激活工作台</span></h1>
57
+ <div class="market-kicker"><span class="market-kicker-dot"></span> CLI 激活与配额管理</div>
58
+ <h1 id="hero-title" class="market-title">统一管理 Codex、Claude 与更多 <span>CLI 服务</span></h1>
47
59
  <p class="market-copy">
48
- 参考 21st.dev 的浏览式组件结构,但不照搬卡片模板:这里用轨道、错位、光扫、浮动组件和指针跟随做出更有辨识度的首页。
60
+ 在一个入口完成激活码发放、用户绑定、额度查看和服务配置,减少来回切换。
49
61
  </p>
50
62
 
51
63
  <label class="market-search" aria-label="搜索功能入口">
@@ -70,31 +82,31 @@
70
82
  </div>
71
83
  </div>
72
84
 
73
- <aside class="market-hero-side" aria-label="动态状态概览">
85
+ <aside class="market-hero-side" aria-label="服务状态概览">
74
86
  <div class="market-preview-stack">
75
87
  <div class="market-mini-card">
76
- <span class="market-icon material-symbols-outlined">auto_awesome_motion</span>
88
+ <span class="market-icon material-symbols-outlined">hub</span>
77
89
  <div>
78
- <p class="market-mini-title">动效层已启用</p>
79
- <p class="market-mini-copy">光扫、浮动、错位网格、指针光晕、卡片内组件各自运动。</p>
90
+ <p class="market-mini-title">服务接入</p>
91
+ <p class="market-mini-copy">统一承载 Codex、Claude Code、OpenCode 与 OpenClaw 等 CLI 场景。</p>
80
92
  </div>
81
- <span class="market-status-pill">Motion</span>
93
+ <span class="market-status-pill">已接入</span>
82
94
  </div>
83
95
  <div class="market-mini-card">
84
- <span class="market-icon material-symbols-outlined">gesture</span>
96
+ <span class="market-icon material-symbols-outlined">key_vertical</span>
85
97
  <div>
86
- <p class="market-mini-title">非模板化卡片</p>
87
- <p class="market-mini-copy">每张卡片的预览内容、节奏和视觉重心都不同。</p>
98
+ <p class="market-mini-title">激活码管理</p>
99
+ <p class="market-mini-copy">支持生成、导出、绑定、过期状态追踪,方便批量运营。</p>
88
100
  </div>
89
- <span class="market-status-pill">Custom</span>
101
+ <span class="market-status-pill">CDK</span>
90
102
  </div>
91
103
  <div class="market-mini-card">
92
104
  <span class="market-icon material-symbols-outlined">speed</span>
93
105
  <div>
94
- <p class="market-mini-title">轻量 CSS 动画</p>
95
- <p class="market-mini-copy">保留静态部署,不引入新前端构建链。</p>
106
+ <p class="market-mini-title">配额监控</p>
107
+ <p class="market-mini-copy">快速查看请求、额度、用户状态和服务健康度。</p>
96
108
  </div>
97
- <span class="market-status-pill">CSS</span>
109
+ <span class="market-status-pill">监控</span>
98
110
  </div>
99
111
  </div>
100
112
 
@@ -104,7 +116,7 @@
104
116
  <span class="market-status-pill"><span class="market-kicker-dot"></span> 34020</span>
105
117
  </div>
106
118
  <p class="market-mini-copy" style="margin-top: 14px; font-size: 14px; line-height: 1.7;">
107
- 这一版把“统一样式”换成“动态产品感”:首页负责记忆点,后台和用户端继承氛围但不打断功能。
119
+ 本地 Web 服务默认运行在 34020 端口,提供首页、用户中心、管理中心和激活入口。
108
120
  </p>
109
121
  </div>
110
122
  </aside>
@@ -114,7 +126,7 @@
114
126
  <div class="market-section-head">
115
127
  <div>
116
128
  <h2 id="section-title">工作区入口</h2>
117
- <p>卡片不再是同一个模板复制三次,而是分别表达“监控、管理、激活”的动作状态。</p>
129
+ <p>按使用角色拆分入口:查看用量、管理资源、完成激活,各自保持清晰路径。</p>
118
130
  </div>
119
131
  <a class="market-button" href="/admin/">打开管理中心</a>
120
132
  </div>
@@ -126,9 +138,9 @@
126
138
  <span class="market-widget market-widget-graph"><i></i><i></i><i></i><i></i></span>
127
139
  </div>
128
140
  <div class="market-card-content">
129
- <h3>用户用量雷达</h3>
130
- <p>用浮动指标卡和脉冲柱状图表达实时额度、请求量和用户状态。</p>
131
- <div class="market-card-footer"><span class="market-tag">Portal</span><span class="market-arrow">→</span></div>
141
+ <h3>用户中心</h3>
142
+ <p>查看额度、请求趋势、公告和个人配置,适合作为用户自助入口。</p>
143
+ <div class="market-card-footer"><span class="market-tag">用户入口</span><span class="market-arrow">→</span></div>
132
144
  </div>
133
145
  </a>
134
146
 
@@ -139,8 +151,8 @@
139
151
  </div>
140
152
  <div class="market-card-content">
141
153
  <h3>管理控制台</h3>
142
- <p>像组件面板一样管理用户、激活码、日志和设置,重点是效率而不是装饰。</p>
143
- <div class="market-card-footer"><span class="market-tag">Console</span><span class="market-arrow">→</span></div>
154
+ <p>集中维护用户、激活码、日志和系统设置,适合管理员日常操作。</p>
155
+ <div class="market-card-footer"><span class="market-tag">管理入口</span><span class="market-arrow">→</span></div>
144
156
  </div>
145
157
  </a>
146
158
 
@@ -151,9 +163,9 @@
151
163
  <span class="market-widget market-widget-badge material-symbols-outlined">bolt</span>
152
164
  </div>
153
165
  <div class="market-card-content">
154
- <h3>CDK 点火流程</h3>
155
- <p>把激活动作做成“点火”体验,强调输入、校验、开通的连续反馈。</p>
156
- <div class="market-card-footer"><span class="market-tag">Activation</span><span class="market-arrow">→</span></div>
166
+ <h3>CDK 激活</h3>
167
+ <p>输入激活码即可完成服务绑定,并返回额度、服务类型和有效期。</p>
168
+ <div class="market-card-footer"><span class="market-tag">激活入口</span><span class="market-arrow">→</span></div>
157
169
  </div>
158
170
  </a>
159
171
  </div>
@@ -179,6 +191,8 @@
179
191
  });
180
192
  });
181
193
 
194
+ initThemeToggle();
195
+
182
196
  document.querySelectorAll('.market-card-lab').forEach((card) => {
183
197
  card.addEventListener('pointermove', (event) => {
184
198
  const rect = card.getBoundingClientRect();
@@ -186,6 +200,28 @@
186
200
  card.style.setProperty('--my', `${((event.clientY - rect.top) / rect.height) * 100}%`);
187
201
  });
188
202
  });
203
+
204
+ function initThemeToggle() {
205
+ const button = document.querySelector('[data-theme-toggle]');
206
+ const icon = document.querySelector('[data-theme-icon]');
207
+ if (!button || !icon) return;
208
+
209
+ const sync = () => {
210
+ const isDark = document.documentElement.classList.contains('dark');
211
+ icon.textContent = isDark ? 'dark_mode' : 'light_mode';
212
+ button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
213
+ };
214
+
215
+ button.addEventListener('click', () => {
216
+ const isDark = !document.documentElement.classList.contains('dark');
217
+ document.documentElement.classList.toggle('dark', isDark);
218
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
219
+ localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
220
+ sync();
221
+ });
222
+
223
+ sync();
224
+ }
189
225
  </script>
190
226
  </body>
191
227
  </html>
@@ -8,15 +8,15 @@
8
8
  href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z'%3E%3C/path%3E%3C/svg%3E"
9
9
  />
10
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
- <title>CLIProxy - API 使用监控</title>
12
- <meta name="description" content="CLIProxy API 使用量监控平台" />
11
+ <title>FogAct 用户中心 | 用量与配额监控</title>
12
+ <meta name="description" content="FogAct 用户中心用于查看 API 用量、配额状态、公告和个人配置。" />
13
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
15
15
  <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
16
16
  <script>
17
17
  // 阻塞式主题初始化 — 防止 FOUC + 设置内联背景色
18
18
  ;(function () {
19
- var t = localStorage.getItem('yunyi_user_theme') || 'system'
19
+ var t = localStorage.getItem('fogact_theme') || localStorage.getItem('yunyi_user_theme') || 'system'
20
20
  var d = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)
21
21
  var el = document.documentElement
22
22
  if (d) {
@@ -31,7 +31,7 @@
31
31
  <script type="module" crossorigin src="assets/index-Da98HOxL.js"></script>
32
32
  <link rel="modulepreload" crossorigin href="assets/chart-vendor-CULJE59K.js">
33
33
  <link rel="stylesheet" crossorigin href="assets/index-B8QSyYhS.css">
34
- <link rel="stylesheet" href="/assets/market-ui.css?v=20260616-upstream2" />
34
+ <link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
35
35
  </head>
36
36
  <body class="market-user min-h-screen">
37
37
  <span class="market-mouse-light" aria-hidden="true"></span>
package/lib/index.js CHANGED
@@ -8,51 +8,129 @@ const { runTestCommand } = require("./commands/test");
8
8
  const { runRestoreCommand } = require("./commands/restore");
9
9
  const { runActivationWizard } = require("./services/activation-orchestrator");
10
10
 
11
- function printBanner() {
12
- const version = packageJson.version;
13
- const title = `FogAct 激活器 v${version}`;
14
- const displayWidth = (value) => Array.from(value).reduce((width, char) => {
11
+ const MENU_CHOICES = [
12
+ { title: "1. 激活服务", value: "activate" },
13
+ { title: "2. 测试节点", value: "test" },
14
+ { title: "3. 恢复备份", value: "restore" },
15
+ { title: "4. 退出", value: "exit" },
16
+ ];
17
+
18
+ function displayWidth(value) {
19
+ return Array.from(value).reduce((width, char) => {
15
20
  return width + (char.charCodeAt(0) > 0xff ? 2 : 1);
16
21
  }, 0);
17
- const padLine = (value, width) => {
18
- const padding = Math.max(0, width - displayWidth(value));
19
- const left = Math.floor(padding / 2);
20
- const right = padding - left;
21
- return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
22
- };
23
- console.log("");
24
- console.log(" ╭─────────────────────────────────────╮");
25
- console.log(` │${padLine(title, 37)}│`);
26
- console.log(" │ Claude Code / Codex 配置工具 │");
27
- console.log(" ╰─────────────────────────────────────╯");
28
- console.log("");
22
+ }
23
+
24
+ function padLine(value, width) {
25
+ const padding = Math.max(0, width - displayWidth(value));
26
+ const left = Math.floor(padding / 2);
27
+ const right = padding - left;
28
+ return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
29
+ }
30
+
31
+ function renderMenu(cursor = 0) {
32
+ const version = packageJson.version;
33
+ const title = `FogAct 激活器 v${version}`;
34
+ const lines = [
35
+ "",
36
+ " ╭─────────────────────────────────────╮",
37
+ ` │${padLine(title, 37)}│`,
38
+ " │ Claude Code / Codex 配置工具 │",
39
+ " ╰─────────────────────────────────────╯",
40
+ "",
41
+ "? 请选择操作:",
42
+ ];
43
+
44
+ MENU_CHOICES.forEach((choice, index) => {
45
+ lines.push(`${index === cursor ? "❯" : " "} ${choice.title}`);
46
+ });
47
+
48
+ lines.push("");
49
+ lines.push("↑↓ navigate • ⏎ select");
50
+ return lines.join("\n");
51
+ }
52
+
53
+ function printBanner() {
54
+ console.log(renderMenu(0));
29
55
  }
30
56
 
31
57
  async function runInteractiveMenu() {
32
58
  await runActivationWizard();
33
59
  }
34
60
 
61
+ function selectMenuAction() {
62
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
63
+ printBanner();
64
+ return Promise.resolve(null);
65
+ }
66
+
67
+ return new Promise((resolve) => {
68
+ let cursor = 0;
69
+ let renderedLines = 0;
70
+ const stdin = process.stdin;
71
+
72
+ const render = () => {
73
+ if (renderedLines > 0) {
74
+ process.stdout.write(`\x1b[${renderedLines}A`);
75
+ process.stdout.write("\x1b[J");
76
+ }
77
+ const output = renderMenu(cursor);
78
+ renderedLines = output.split("\n").length;
79
+ process.stdout.write(output);
80
+ };
81
+
82
+ const cleanup = () => {
83
+ stdin.off("data", onData);
84
+ if (stdin.isRaw) stdin.setRawMode(false);
85
+ stdin.pause();
86
+ process.stdout.write("\x1b[?25h\n");
87
+ };
88
+
89
+ const submit = () => {
90
+ const choice = MENU_CHOICES[cursor];
91
+ cleanup();
92
+ resolve(choice.value);
93
+ };
94
+
95
+ const cancel = () => {
96
+ cleanup();
97
+ resolve("exit");
98
+ };
99
+
100
+ const onData = (data) => {
101
+ const key = data.toString("utf8");
102
+ if (key === "\u0003" || key === "\u001b") {
103
+ cancel();
104
+ return;
105
+ }
106
+ if (key === "\r" || key === "\n") {
107
+ submit();
108
+ return;
109
+ }
110
+ if (key === "\u001b[A") {
111
+ cursor = cursor === 0 ? MENU_CHOICES.length - 1 : cursor - 1;
112
+ render();
113
+ return;
114
+ }
115
+ if (key === "\u001b[B") {
116
+ cursor = cursor === MENU_CHOICES.length - 1 ? 0 : cursor + 1;
117
+ render();
118
+ }
119
+ };
120
+
121
+ process.stdout.write("\x1b[?25l");
122
+ render();
123
+ stdin.resume();
124
+ stdin.setEncoding("utf8");
125
+ stdin.setRawMode(true);
126
+ stdin.on("data", onData);
127
+ });
128
+ }
129
+
35
130
  async function runToolsMenu() {
36
- printBanner();
37
-
38
- const response = await prompts(
39
- {
40
- type: "select",
41
- name: "action",
42
- message: "请选择操作:",
43
- hint: "↑↓ 选择,回车确认",
44
- choices: [
45
- { title: "1. 激活服务", value: "activate" },
46
- { title: "2. 测试节点", value: "test" },
47
- { title: "3. 恢复备份", value: "restore" },
48
- { title: "4. 退出", value: "exit" },
49
- ],
50
- initial: 0,
51
- },
52
- { onCancel: () => false }
53
- );
54
-
55
- switch (response.action) {
131
+ const action = await selectMenuAction();
132
+
133
+ switch (action) {
56
134
  case "activate":
57
135
  await runActivationWizard();
58
136
  break;
@@ -19,7 +19,7 @@ function makeRequest(path, options = {}) {
19
19
  method: options.method || "GET",
20
20
  headers: {
21
21
  "Content-Type": "application/json",
22
- "User-Agent": "fogact/1.1.5",
22
+ "User-Agent": "fogact/1.1.6",
23
23
  ...options.headers,
24
24
  },
25
25
  };
@@ -58,7 +58,7 @@ async function verifyNewApiKey(config, apiKey) {
58
58
  headers: {
59
59
  Authorization: `Bearer ${apiKey}`,
60
60
  Accept: "application/json",
61
- "User-Agent": "fogact/1.1.5",
61
+ "User-Agent": "fogact/1.1.6",
62
62
  },
63
63
  });
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fogact",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "FogAct activation helper for Claude Code and Codex",
5
5
  "keywords": [
6
6
  "fogact",