@lynn123411/dsh-a6api 1.1.0 → 1.3.0

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/lib/client.js CHANGED
@@ -1,8 +1,10 @@
1
1
  window.__ModuleLoader__.load({ id: "@lynn123411/dsh-a6api", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
2
  "use strict";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name2 in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/client/index.ts
@@ -26,11 +36,21 @@ __export(index_exports, {
26
36
  name: () => name
27
37
  });
28
38
  module.exports = __toCommonJS(index_exports);
39
+ var import_react7 = __toESM(require("react"), 1);
29
40
 
30
41
  // src/client/components/A6ApiSettings.tsx
31
- var import_react4 = require("react");
42
+ var import_react5 = require("react");
32
43
 
33
44
  // src/client/store.ts
45
+ function formatRelativeNow(tsSec) {
46
+ const diff = Math.floor(Date.now() / 1e3) - tsSec;
47
+ if (diff < 60) return "\u521A\u521A";
48
+ if (diff < 3600) return `${Math.floor(diff / 60)} \u5206\u949F\u524D`;
49
+ if (diff < 86400) return `${Math.floor(diff / 3600)} \u5C0F\u65F6\u524D`;
50
+ return `${Math.floor(diff / 86400)} \u5929\u524D`;
51
+ }
52
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
53
+ var RATE_LIMIT_RE = /\b429\b|Too Many Requests|rate\s*limit/i;
34
54
  var A6ApiStore = class {
35
55
  state = {
36
56
  loading: true,
@@ -45,12 +65,28 @@ var A6ApiStore = class {
45
65
  dshConfiguredModels: [],
46
66
  recentLogs: [],
47
67
  probingModelNames: /* @__PURE__ */ new Set(),
68
+ actionBusyModels: /* @__PURE__ */ new Set(),
69
+ probeAllActive: false,
70
+ probeAllTotal: 0,
71
+ probeAllDoneCount: 0,
72
+ pins: [],
73
+ catalog: [],
74
+ catalogBusy: null,
48
75
  error: null,
49
76
  priceFluctuation: { pendingCount: 0, unseenCount: 0, totalCount: 0, updatedAt: null }
50
77
  };
51
78
  listeners = /* @__PURE__ */ new Set();
52
79
  autoRefreshTimer = null;
53
- pricePollTimer = null;
80
+ /** 启动预热已触发(幂等):插件随 DSH 启动即后台拉一次完整状态 */
81
+ warmedUp = false;
82
+ /** 全量探测取消标志:置位后不再从队列取新任务,在途探测正常完成 */
83
+ probeCancelled = false;
84
+ /** 本轮全量探测的模型名快照(null = 未在运行),用于进度计数与 /state 刷新后重挂状态 */
85
+ probeAllSnapshot = null;
86
+ /** 本轮已完成探测的模型(幂等集合,驱动 probeAllDoneCount) */
87
+ probeAllDone = /* @__PURE__ */ new Set();
88
+ /** 入队前各模型的 probeError 暂存,取消时恢复历史错误提示 */
89
+ probeQueuedPrevError = /* @__PURE__ */ new Map();
54
90
  constructor() {
55
91
  this.startAutoRefresh();
56
92
  }
@@ -82,7 +118,22 @@ var A6ApiStore = class {
82
118
  this.state.config = data.config;
83
119
  this.state.balance = data.balance;
84
120
  this.state.models = data.models;
121
+ const snapshot = this.probeAllSnapshot;
122
+ if (this.state.probeAllActive && snapshot) {
123
+ this.state.models = this.state.models.map((m) => {
124
+ if (this.state.probingModelNames.has(m.model_name)) {
125
+ return { ...m, probeStatus: "probing" };
126
+ }
127
+ if (snapshot.includes(m.model_name) && !this.probeAllDone.has(m.model_name)) {
128
+ return { ...m, probeStatus: "queued", probeError: void 0 };
129
+ }
130
+ return m;
131
+ });
132
+ }
85
133
  this.state.dshConfiguredModels = data.dshConfiguredModels;
134
+ if (Array.isArray(data.pins)) {
135
+ this.state.pins = data.pins;
136
+ }
86
137
  if (data.recentLogs) {
87
138
  this.state.recentLogs = data.recentLogs;
88
139
  }
@@ -136,6 +187,120 @@ var A6ApiStore = class {
136
187
  } catch {
137
188
  }
138
189
  }
190
+ // ===== 模型目录 =====
191
+ async fetchCatalog() {
192
+ try {
193
+ const res = await fetch("/api/dsh-a6api/catalog");
194
+ if (res.ok) {
195
+ const json = await res.json();
196
+ if (Array.isArray(json?.catalog)) {
197
+ this.state.catalog = json.catalog;
198
+ this.notify();
199
+ }
200
+ }
201
+ } catch {
202
+ }
203
+ }
204
+ /** 从 A6API 市场拉取全部模型 ID 并入目录(仅新增/补品牌,不动已有参数) */
205
+ async fetchMarketModels() {
206
+ if (this.state.catalogBusy) return { ok: false, error: "\u76EE\u5F55\u64CD\u4F5C\u8FDB\u884C\u4E2D" };
207
+ this.state.catalogBusy = "fetch";
208
+ this.notify();
209
+ try {
210
+ const res = await fetch("/api/dsh-a6api/catalog/fetch-models", {
211
+ method: "POST",
212
+ headers: { "Content-Type": "application/json" },
213
+ body: "{}"
214
+ });
215
+ const json = await res.json().catch(() => null);
216
+ if (res.ok && json?.ok) {
217
+ await this.fetchCatalog();
218
+ return { ok: true, total: json.total, added: json.added, failedPages: json.failedPages || 0 };
219
+ }
220
+ const errText = json?.error || `HTTP ${res.status}`;
221
+ this.state.error = errText;
222
+ return { ok: false, error: errText };
223
+ } catch (err) {
224
+ const msg = err?.message || String(err);
225
+ this.state.error = msg;
226
+ return { ok: false, error: msg };
227
+ } finally {
228
+ this.state.catalogBusy = null;
229
+ this.notify();
230
+ }
231
+ }
232
+ /** 对全部(或指定)目录模型查 OpenRouter 并填充参数 */
233
+ async queryOpenRouter(modelIds) {
234
+ if (this.state.catalogBusy) return { ok: false, error: "\u76EE\u5F55\u64CD\u4F5C\u8FDB\u884C\u4E2D" };
235
+ this.state.catalogBusy = "query";
236
+ this.notify();
237
+ try {
238
+ const res = await fetch("/api/dsh-a6api/catalog/query-openrouter", {
239
+ method: "POST",
240
+ headers: { "Content-Type": "application/json" },
241
+ body: JSON.stringify({ modelIds })
242
+ });
243
+ const json = await res.json().catch(() => null);
244
+ if (res.ok && json?.ok) {
245
+ await this.fetchCatalog();
246
+ return { ok: true, updated: json.updated, notFound: json.notFound || [] };
247
+ }
248
+ const errText = json?.error || `HTTP ${res.status}`;
249
+ this.state.error = errText;
250
+ return { ok: false, error: errText };
251
+ } catch (err) {
252
+ const msg = err?.message || String(err);
253
+ this.state.error = msg;
254
+ return { ok: false, error: msg };
255
+ } finally {
256
+ this.state.catalogBusy = null;
257
+ this.notify();
258
+ }
259
+ }
260
+ /** 修改目录条目参数;已启用模型由服务端即时重写 settings.yaml */
261
+ async updateCatalogEntry(id, patch) {
262
+ try {
263
+ const res = await fetch("/api/dsh-a6api/catalog/update", {
264
+ method: "POST",
265
+ headers: { "Content-Type": "application/json" },
266
+ body: JSON.stringify({ id, ...patch })
267
+ });
268
+ const json = await res.json().catch(() => null);
269
+ if (res.ok && json?.ok) {
270
+ await this.fetchCatalog();
271
+ return { ok: true };
272
+ }
273
+ const errText = json?.error || `HTTP ${res.status}`;
274
+ this.state.error = errText;
275
+ return { ok: false, error: errText };
276
+ } catch (err) {
277
+ const msg = err?.message || String(err);
278
+ this.state.error = msg;
279
+ return { ok: false, error: msg };
280
+ }
281
+ }
282
+ /** 清空模型目录(随后可重新从 A6API 拉取 / OpenRouter 填充) */
283
+ async clearCatalog() {
284
+ try {
285
+ const res = await fetch("/api/dsh-a6api/catalog/clear", {
286
+ method: "POST",
287
+ headers: { "Content-Type": "application/json" },
288
+ body: "{}"
289
+ });
290
+ const json = await res.json().catch(() => null);
291
+ if (res.ok && json?.ok) {
292
+ await this.fetchCatalog();
293
+ return { ok: true };
294
+ }
295
+ const errText = json?.error || `HTTP ${res.status}`;
296
+ this.state.error = errText;
297
+ return { ok: false, error: errText };
298
+ } catch (err) {
299
+ const msg = err?.message || String(err);
300
+ this.state.error = msg;
301
+ return { ok: false, error: msg };
302
+ }
303
+ }
139
304
  async fetchPriceFluctuation() {
140
305
  try {
141
306
  const res = await fetch("/api/dsh-a6api/price-fluctuation");
@@ -168,12 +333,8 @@ var A6ApiStore = class {
168
333
  } catch {
169
334
  }
170
335
  }
171
- async probeModel(modelName) {
172
- this.state.probingModelNames.add(modelName);
173
- this.state.models = this.state.models.map(
174
- (m) => m.model_name === modelName ? { ...m, probeStatus: "probing" } : m
175
- );
176
- this.notify();
336
+ /** 单次探测请求(不修改状态,供限流重试循环复用) */
337
+ async probeOnce(modelName) {
177
338
  try {
178
339
  const res = await fetch("/api/dsh-a6api/probe", {
179
340
  method: "POST",
@@ -182,73 +343,161 @@ var A6ApiStore = class {
182
343
  });
183
344
  if (res.ok) {
184
345
  const json = await res.json();
185
- if (json?.result?.merchant) {
186
- this.state.models = this.state.models.map(
187
- (m) => m.model_name === modelName ? {
188
- ...m,
189
- merchant: json.result.merchant,
190
- probeStatus: "success",
191
- probeLatencyMs: json.result.durationMs,
192
- probeError: void 0,
193
- lastProbedAt: Date.now()
194
- } : m
195
- );
196
- } else if (json?.result?.error) {
346
+ return { kind: "ok", json };
347
+ }
348
+ return { kind: "http", status: res.status };
349
+ } catch (err) {
350
+ return { kind: "network", error: err?.message || String(err) };
351
+ }
352
+ }
353
+ isRateLimitedOutcome(o) {
354
+ return o.kind === "ok" && Boolean(o.json?.result?.error && RATE_LIMIT_RE.test(String(o.json.result.error))) || o.kind === "http" && o.status === 429;
355
+ }
356
+ /** 把一次探测结果应用到卡片(按结果类型走原有映射逻辑) */
357
+ applyProbeResult(modelName, outcome) {
358
+ const patch = (p) => {
359
+ this.state.models = this.state.models.map((m) => m.model_name === modelName ? { ...m, ...p } : m);
360
+ this.notify();
361
+ };
362
+ if (outcome.kind === "ok") {
363
+ const json = outcome.json;
364
+ if (json?.result?.merchant) {
365
+ patch({
366
+ merchant: json.result.merchant,
367
+ probeStatus: "success",
368
+ probeLatencyMs: json.result.durationMs,
369
+ probeError: void 0,
370
+ lastProbedAt: Date.now(),
371
+ // 探测请求本身会写路由日志,乐观更新路由快照时效,下次 /state 以日志为准
372
+ lastRoutedAt: Math.floor(Date.now() / 1e3),
373
+ lastRoutedText: formatRelativeNow(Math.floor(Date.now() / 1e3))
374
+ });
375
+ } else if (json?.result?.error) {
376
+ patch({
377
+ merchant: void 0,
378
+ probeStatus: "error",
379
+ probeError: json.result.error,
380
+ lastProbedAt: Date.now()
381
+ });
382
+ } else {
383
+ patch({
384
+ probeStatus: json?.result?.success ? "success" : "idle",
385
+ probeLatencyMs: json?.result?.durationMs,
386
+ probeError: json?.result?.success ? "\u63A2\u6D4B\u6210\u529F,\u4F46\u672A\u6355\u83B7\u5546\u6237\u4FE1\u606F(\u9700\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C)" : void 0,
387
+ lastProbedAt: Date.now()
388
+ });
389
+ }
390
+ } else if (outcome.kind === "http") {
391
+ patch({
392
+ merchant: void 0,
393
+ probeStatus: "error",
394
+ probeError: `HTTP ${outcome.status}`,
395
+ lastProbedAt: Date.now()
396
+ });
397
+ } else {
398
+ patch({
399
+ merchant: void 0,
400
+ probeStatus: "error",
401
+ probeError: outcome.error,
402
+ lastProbedAt: Date.now()
403
+ });
404
+ }
405
+ }
406
+ async probeModel(modelName) {
407
+ this.state.probingModelNames.add(modelName);
408
+ this.state.models = this.state.models.map(
409
+ (m) => m.model_name === modelName ? { ...m, probeStatus: "probing" } : m
410
+ );
411
+ this.notify();
412
+ try {
413
+ let outcome = await this.probeOnce(modelName);
414
+ if (this.isRateLimitedOutcome(outcome)) {
415
+ for (let attempt = 2; attempt <= 3; attempt++) {
416
+ await sleep(attempt === 2 ? 800 : 2e3);
417
+ outcome = await this.probeOnce(modelName);
418
+ if (!this.isRateLimitedOutcome(outcome)) break;
419
+ }
420
+ if (this.isRateLimitedOutcome(outcome)) {
197
421
  this.state.models = this.state.models.map(
198
422
  (m) => m.model_name === modelName ? {
199
423
  ...m,
200
424
  merchant: void 0,
201
425
  probeStatus: "error",
202
- probeError: json.result.error,
203
- lastProbedAt: Date.now()
204
- } : m
205
- );
206
- } else {
207
- this.state.models = this.state.models.map(
208
- (m) => m.model_name === modelName ? {
209
- ...m,
210
- probeStatus: json?.result?.success ? "success" : "idle",
211
- probeLatencyMs: json?.result?.durationMs,
212
- probeError: json?.result?.success ? "\u63A2\u6D4B\u6210\u529F,\u4F46\u672A\u6355\u83B7\u5546\u6237\u4FE1\u606F(\u9700\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C)" : void 0,
426
+ probeError: "\u8BF7\u6C42\u88AB\u9650\u6D41(429),\u5DF2\u81EA\u52A8\u91CD\u8BD5 3 \u6B21\u4ECD\u5931\u8D25,\u8BF7\u7A0D\u540E\u518D\u8BD5",
213
427
  lastProbedAt: Date.now()
214
428
  } : m
215
429
  );
430
+ return;
216
431
  }
217
- } else {
218
- this.state.models = this.state.models.map(
219
- (m) => m.model_name === modelName ? {
220
- ...m,
221
- merchant: void 0,
222
- probeStatus: "error",
223
- probeError: `HTTP ${res.status}`,
224
- lastProbedAt: Date.now()
225
- } : m
226
- );
227
432
  }
228
- } catch (err) {
229
- this.state.models = this.state.models.map(
230
- (m) => m.model_name === modelName ? {
231
- ...m,
232
- merchant: void 0,
233
- probeStatus: "error",
234
- probeError: err?.message || String(err),
235
- lastProbedAt: Date.now()
236
- } : m
237
- );
433
+ this.applyProbeResult(modelName, outcome);
238
434
  } finally {
239
435
  this.state.probingModelNames.delete(modelName);
436
+ if (this.probeAllSnapshot?.includes(modelName)) {
437
+ this.probeAllDone.add(modelName);
438
+ this.state.probeAllDoneCount = this.probeAllDone.size;
439
+ }
240
440
  this.notify();
241
441
  this.refreshBalance().catch(() => {
242
442
  });
243
443
  }
244
444
  }
245
445
  async probeAll() {
246
- const allNames = this.state.models.map((m) => m.model_name);
247
- const CONCURRENCY = 3;
248
- for (let i = 0; i < allNames.length; i += CONCURRENCY) {
249
- await Promise.all(allNames.slice(i, i + CONCURRENCY).map((n) => this.probeModel(n)));
446
+ if (this.state.probeAllActive) return;
447
+ const names = this.state.models.map((m) => m.model_name);
448
+ if (names.length === 0) return;
449
+ this.probeCancelled = false;
450
+ this.probeAllSnapshot = names;
451
+ this.probeAllDone.clear();
452
+ this.probeQueuedPrevError = new Map(this.state.models.map((m) => [m.model_name, m.probeError]));
453
+ this.state.probeAllActive = true;
454
+ this.state.probeAllTotal = names.length;
455
+ this.state.probeAllDoneCount = 0;
456
+ this.state.models = this.state.models.map((m) => ({
457
+ ...m,
458
+ probeStatus: "queued",
459
+ probeError: void 0
460
+ }));
461
+ this.notify();
462
+ const CONCURRENCY = Math.min(8, names.length);
463
+ let idx = 0;
464
+ const worker = async () => {
465
+ while (idx < names.length && !this.probeCancelled) {
466
+ const name2 = names[idx++];
467
+ if (this.state.probingModelNames.has(name2)) continue;
468
+ await this.probeModel(name2);
469
+ }
470
+ };
471
+ try {
472
+ await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
473
+ } finally {
474
+ this.state.probeAllActive = false;
475
+ this.probeAllSnapshot = null;
476
+ this.probeAllDone.clear();
477
+ this.state.probeAllDoneCount = 0;
478
+ this.restoreQueuedModels();
479
+ this.notify();
250
480
  }
251
481
  }
482
+ /** 把仍处于 queued 的模型复位为 idle 并恢复入队前的错误文案 */
483
+ restoreQueuedModels() {
484
+ const prev = this.probeQueuedPrevError;
485
+ this.state.models = this.state.models.map(
486
+ (m) => m.probeStatus === "queued" ? { ...m, probeStatus: "idle", probeError: prev.get(m.model_name) } : m
487
+ );
488
+ this.probeQueuedPrevError = /* @__PURE__ */ new Map();
489
+ }
490
+ /** 取消全量探测:立即复位排队模型,不再取新任务,已在途的探测正常完成并回填卡片 */
491
+ cancelProbeAll() {
492
+ if (!this.state.probeAllActive) return;
493
+ this.probeCancelled = true;
494
+ this.state.probeAllActive = false;
495
+ this.probeAllSnapshot = null;
496
+ this.probeAllDone.clear();
497
+ this.state.probeAllDoneCount = 0;
498
+ this.restoreQueuedModels();
499
+ this.notify();
500
+ }
252
501
  async toggleDshModel(modelName) {
253
502
  const currentSet = new Set(this.state.dshConfiguredModels);
254
503
  if (currentSet.has(modelName)) {
@@ -278,32 +527,89 @@ var A6ApiStore = class {
278
527
  this.notify();
279
528
  }
280
529
  }
530
+ /**
531
+ * 固定 / 取消固定 / 禁用 / 恢复 的统一执行器。
532
+ * 成功后会刷新 /state(服务端会把平台固定记录叠加回卡片,跟随官网状态)。
533
+ */
534
+ async runMarketplaceAction(modelName, endpoint, busySet) {
535
+ if (busySet.has(modelName)) return { ok: false, error: "\u64CD\u4F5C\u8FDB\u884C\u4E2D" };
536
+ busySet.add(modelName);
537
+ this.notify();
538
+ try {
539
+ const res = await fetch(`/api/dsh-a6api/${endpoint}`, {
540
+ method: "POST",
541
+ headers: { "Content-Type": "application/json" },
542
+ body: JSON.stringify({ modelName })
543
+ });
544
+ const json = await res.json().catch(() => null);
545
+ if (res.ok && json?.ok) {
546
+ if (Array.isArray(json.pins)) {
547
+ this.state.pins = json.pins;
548
+ }
549
+ await this.fetchState();
550
+ return { ok: true };
551
+ }
552
+ const errText = json?.error || `HTTP ${res.status}`;
553
+ this.state.error = errText;
554
+ this.notify();
555
+ return { ok: false, error: errText };
556
+ } catch (err) {
557
+ const msg = err?.message || String(err);
558
+ this.state.error = msg;
559
+ this.notify();
560
+ return { ok: false, error: msg };
561
+ } finally {
562
+ busySet.delete(modelName);
563
+ this.notify();
564
+ }
565
+ }
566
+ /** 固定卡片当前商家到该模型 */
567
+ pinModel(modelName) {
568
+ return this.runMarketplaceAction(modelName, "pin", this.state.actionBusyModels);
569
+ }
570
+ /** 取消该模型的固定 */
571
+ unpinModel(modelName) {
572
+ return this.runMarketplaceAction(modelName, "unpin", this.state.actionBusyModels);
573
+ }
574
+ /** 禁用卡片当前商家对该模型的服务 */
575
+ disableModel(modelName) {
576
+ return this.runMarketplaceAction(modelName, "disable", this.state.actionBusyModels);
577
+ }
578
+ /** 恢复被禁用的商家 */
579
+ restoreModel(modelName) {
580
+ return this.runMarketplaceAction(modelName, "restore", this.state.actionBusyModels);
581
+ }
582
+ /**
583
+ * 启动预热:插件随 DSH 启动即后台拉取一次完整状态。
584
+ * 侧边栏按钮在应用启动时就已挂载,预热让用户打开浮层/设置页时数据早已就绪 → 秒开无 spinner。
585
+ * 未配置凭据时 /state 返回默认回退数据,无害;后续保存配置/轮询会持续刷新。
586
+ * 目录一并预热(模型目录 tab 徽标/首屏不等待首次进入)。
587
+ */
588
+ warmUp() {
589
+ if (this.warmedUp) return;
590
+ this.warmedUp = true;
591
+ this.fetchState().catch(() => {
592
+ });
593
+ this.fetchCatalog().catch(() => {
594
+ });
595
+ }
281
596
  startAutoRefresh() {
282
597
  if (this.autoRefreshTimer) clearInterval(this.autoRefreshTimer);
283
598
  this.autoRefreshTimer = setInterval(() => {
284
- this.refreshBalance().catch(() => {
285
- });
286
- }, 6e4);
287
- if (this.pricePollTimer) clearInterval(this.pricePollTimer);
288
- this.pricePollTimer = setInterval(() => {
289
- if (this.state.config?.hasToken) {
290
- this.fetchPriceFluctuation().catch(() => {
599
+ if (this.state.config?.hasApiKey) {
600
+ this.fetchState().catch(() => {
291
601
  });
292
602
  }
293
- }, 60 * 1e3);
603
+ }, 6e4);
294
604
  }
295
605
  stopAutoRefresh() {
296
606
  if (this.autoRefreshTimer) {
297
607
  clearInterval(this.autoRefreshTimer);
298
608
  this.autoRefreshTimer = null;
299
609
  }
300
- if (this.pricePollTimer) {
301
- clearInterval(this.pricePollTimer);
302
- this.pricePollTimer = null;
303
- }
304
610
  }
305
611
  initPricePolling() {
306
- if (this.pricePollTimer) return;
612
+ if (this.autoRefreshTimer) return;
307
613
  this.startAutoRefresh();
308
614
  if (this.state.config?.hasToken) {
309
615
  this.fetchPriceFluctuation().catch(() => {
@@ -316,10 +622,33 @@ var store = new A6ApiStore();
316
622
  // src/client/components/MerchantCard.tsx
317
623
  var import_react = require("react");
318
624
  var import_jsx_runtime = require("react/jsx-runtime");
625
+ var formatAbsolute = (tsSec) => {
626
+ const d = new Date(tsSec * 1e3);
627
+ const p = (n) => String(n).padStart(2, "0");
628
+ const nowY = (/* @__PURE__ */ new Date()).getFullYear();
629
+ const y = d.getFullYear();
630
+ const md = `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
631
+ return y !== nowY ? `${y}-${md}` : md;
632
+ };
319
633
  var MerchantCard = ({ model }) => {
320
634
  const [expanded, setExpanded] = (0, import_react.useState)(false);
635
+ const [pinConfirmOpen, setPinConfirmOpen] = (0, import_react.useState)(false);
636
+ const [actionError, setActionError] = (0, import_react.useState)(null);
637
+ const errorTimerRef = (0, import_react.useRef)(null);
321
638
  const isProbing = model.probeStatus === "probing";
639
+ const isQueued = model.probeStatus === "queued";
322
640
  const merchant = model.merchant;
641
+ const isBusy = store.getState().actionBusyModels.has(model.model_name);
642
+ const canWebAction = Boolean(store.getState().config?.hasToken);
643
+ const hasMerchant = Boolean(merchant?.channel_id);
644
+ const isPinnedHere = model.pinStatus === "pin_here";
645
+ const isPinnedElsewhere = model.pinStatus === "pin_elsewhere";
646
+ const isChannelDisabled = Boolean(merchant?.user_channel_disabled);
647
+ const flashActionError = (msg) => {
648
+ if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
649
+ setActionError(msg);
650
+ errorTimerRef.current = setTimeout(() => setActionError(null), 6e3);
651
+ };
323
652
  const handleProbe = (e) => {
324
653
  e.stopPropagation();
325
654
  store.probeModel(model.model_name);
@@ -328,6 +657,38 @@ var MerchantCard = ({ model }) => {
328
657
  e.stopPropagation();
329
658
  store.toggleDshModel(model.model_name);
330
659
  };
660
+ const handleOpenPinConfirm = (e) => {
661
+ e.stopPropagation();
662
+ setActionError(null);
663
+ setPinConfirmOpen(true);
664
+ };
665
+ const handleConfirmPin = async () => {
666
+ setActionError(null);
667
+ const r = await store.pinModel(model.model_name);
668
+ if (!r.ok) {
669
+ flashActionError(r.error || "\u56FA\u5B9A\u5931\u8D25");
670
+ } else {
671
+ setPinConfirmOpen(false);
672
+ }
673
+ };
674
+ const handleUnpin = async (e) => {
675
+ e.stopPropagation();
676
+ setActionError(null);
677
+ const r = await store.unpinModel(model.model_name);
678
+ if (!r.ok) flashActionError(r.error || "\u53D6\u6D88\u56FA\u5B9A\u5931\u8D25");
679
+ };
680
+ const handleDisable = async (e) => {
681
+ e.stopPropagation();
682
+ setActionError(null);
683
+ const r = await store.disableModel(model.model_name);
684
+ if (!r.ok) flashActionError(r.error || "\u7981\u7528\u5931\u8D25");
685
+ };
686
+ const handleRestore = async (e) => {
687
+ e.stopPropagation();
688
+ setActionError(null);
689
+ const r = await store.restoreModel(model.model_name);
690
+ if (!r.ok) flashActionError(r.error || "\u6062\u590D\u5931\u8D25");
691
+ };
331
692
  const renderRealtimeDots = () => {
332
693
  if (merchant?.success_buckets && merchant.success_buckets.length > 0) {
333
694
  return merchant.success_buckets.slice(0, 10).map((b, i) => {
@@ -394,7 +755,26 @@ var MerchantCard = ({ model }) => {
394
755
  "\u5546\u6237ID ",
395
756
  merchant.channel_id
396
757
  ] })
397
- ] })
758
+ ] }),
759
+ isPinnedHere && !isChannelDisabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
760
+ "span",
761
+ {
762
+ className: "dsh-a6-pin-badge here",
763
+ "data-tooltip": `\u8BE5\u6A21\u578B\u5DF2\u56FA\u5B9A\u5230\u5F53\u524D\u5546\u5BB6${model.pinnedFallback === false ? "\uFF08\u4E25\u683C\u56FA\u5B9A\uFF09" : "\uFF0C\u5F02\u5E38\u65F6\u81EA\u52A8\u5207\u6362\u667A\u80FD\u4F18\u9009"}${model.pinTokenMatched === false ? "\uFF1B\u8BE5\u56FA\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u4EE4\u724C\uFF0C\u4EC5\u4F9B\u53C2\u8003" : ""}`,
764
+ "data-tooltip-pos": "down",
765
+ children: "\u5DF2\u56FA\u5B9A"
766
+ }
767
+ ),
768
+ isPinnedElsewhere && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
769
+ "span",
770
+ {
771
+ className: "dsh-a6-pin-badge elsewhere",
772
+ "data-tooltip": `\u8BE5\u6A21\u578B\u5DF2\u56FA\u5B9A\u5230${model.pinnedChannelId ? `\u5546\u6237 #${model.pinnedChannelId}` : "\u5176\u4ED6\u5546\u5BB6"}${model.pinnedSupplierName ? `\uFF08${model.pinnedSupplierName}\uFF09` : ""}${model.pinTokenMatched === false ? "\uFF1B\u8BE5\u56FA\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u4EE4\u724C\uFF0C\u4EC5\u4F9B\u53C2\u8003" : ""}`,
773
+ "data-tooltip-pos": "down",
774
+ children: "\u5DF2\u56FA\u5B9A\u5230\u5176\u4ED6\u5546\u5BB6"
775
+ }
776
+ ),
777
+ isChannelDisabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-a6-pin-badge disabled", "data-tooltip": "\u5F53\u524D\u5546\u5BB6\u5DF2\u5BF9\u8BE5\u6A21\u578B\u7981\u7528\uFF0C\u8DEF\u7531\u4E0D\u4F1A\u547D\u4E2D\u6B64\u6E20\u9053", "data-tooltip-pos": "down", children: "\u5DF2\u7981\u7528" })
398
778
  ] }),
399
779
  merchant?.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-sub-desc", children: merchant.description })
400
780
  ] }) }),
@@ -414,7 +794,7 @@ var MerchantCard = ({ model }) => {
414
794
  className: `dsh-a6-unprobed-hint ${model.probeError ? "error" : ""}`,
415
795
  "data-tooltip": model.probeError || void 0,
416
796
  "data-tooltip-pos": "down",
417
- children: isProbing ? "\u5546\u5BB6\u63A2\u6D4B\u4E2D..." : model.probeError ? "\u63A2\u6D4B\u5931\u8D25" : "\u5C1A\u672A\u63A2\u6D4B\u5546\u5BB6"
797
+ children: isProbing ? "\u5546\u5BB6\u63A2\u6D4B\u4E2D..." : isQueued ? "\u6392\u961F\u7B49\u5F85\u63A2\u6D4B..." : model.probeError ? "\u63A2\u6D4B\u5931\u8D25" : "\u5C1A\u672A\u63A2\u6D4B\u5546\u5BB6"
418
798
  }
419
799
  ) }),
420
800
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-bar-uptime", children: [
@@ -448,25 +828,85 @@ var MerchantCard = ({ model }) => {
448
828
  }
449
829
  ) })
450
830
  ] }) }),
451
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-bar-tags", children: (merchant?.labels || ["\u7A33\u5B9A", "\u4F4E\u4EF7", "\u9AD8\u901F", "\u9AD8\u8D28"]).map((lbl, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `dsh-a6-smart-pill ${getTagClass(lbl)}`, children: lbl }, idx)) }),
452
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-bar-actions", onClick: (e) => e.stopPropagation(), children: [
453
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
831
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-bar-tags", children: (merchant?.labels || ["\u7A33\u5B9A", "\u4F4E\u4EF7", "\u9AD8\u901F", "\u9AD8\u8D28"]).map((lbl, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `dsh-a6-smart-pill ${getTagClass(lbl)}`, children: lbl }, idx)) })
832
+ ] }),
833
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-card-footer", children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-time-stack", children: [
835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
454
836
  "span",
455
837
  {
456
838
  className: "dsh-a6-time-ago",
457
839
  "data-tooltip": "\u8BE5\u5546\u6237\u8DEF\u7EBF\u5168\u7F51\u6700\u8FD1\u4E00\u6B21\u6210\u529F\u54CD\u5E94\u65F6\u95F4",
458
- children: merchant?.last_success_text || "\u521A\u521A"
840
+ children: [
841
+ "\u5168\u7F51\u6700\u8FD1\uFF1A",
842
+ merchant?.last_success_text || "\u521A\u521A"
843
+ ]
459
844
  }
460
845
  ),
846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
847
+ "span",
848
+ {
849
+ className: `dsh-a6-time-ago dsh-a6-route-snapshot${model.lastRoutedAt ? "" : " never"}`,
850
+ "data-tooltip": model.lastRoutedAt ? `\u4E2A\u4EBA\u6700\u540E\u4E00\u6B21\u8BF7\u6C42\u8BE5\u5546\u5BB6\u7684\u8BE5\u6A21\u578B ${formatAbsolute(model.lastRoutedAt)}` : "\u65E5\u5FD7\u4E2D\u6682\u65E0\u8BE5\u5546\u5BB6\u7684\u8BE5\u6A21\u578B\u8DEF\u7531\u8BB0\u5F55",
851
+ children: [
852
+ "\u4E2A\u4EBA\u6700\u8FD1\uFF1A",
853
+ model.lastRoutedText || "\u4ECE\u672A\u8DEF\u7531"
854
+ ]
855
+ }
856
+ )
857
+ ] }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-bar-actions", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-bar-actions-btns", children: [
461
859
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
462
860
  "button",
463
861
  {
464
862
  type: "button",
465
863
  className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
466
864
  onClick: handleProbe,
467
- disabled: isProbing,
468
- "data-tooltip": "\u5411\u8BE5\u6A21\u578B\u53D1\u9001\u4E00\u6B21\u8BF7\u6C42\u4EE5\u63A2\u6D4B\u5E76\u6355\u83B7\u5176\u5B9E\u9645\u547D\u4E2D\u7684\u5546\u6237 ID\u3001\u4EF7\u683C\u53CA\u5065\u5EB7\u5EA6\u6307\u6807\uFF08\u6D88\u8017\u5C11\u91CFToken\uFF09",
469
- children: isProbing ? "\u63A2\u6D4B\u4E2D..." : "\u63A2\u6D4B\u5546\u5BB6"
865
+ disabled: isProbing || isQueued,
866
+ "data-tooltip": isQueued ? "\u6B63\u5728\u5168\u91CF\u63A2\u6D4B\u961F\u5217\u4E2D\u7B49\u5F85\uFF0C\u8BF7\u52FF\u91CD\u590D\u70B9\u51FB" : "\u5411\u8BE5\u6A21\u578B\u53D1\u9001\u4E00\u6B21\u8BF7\u6C42\u4EE5\u63A2\u6D4B\u5E76\u6355\u83B7\u5176\u5B9E\u9645\u547D\u4E2D\u7684\u5546\u6237 ID\u3001\u4EF7\u683C\u53CA\u5065\u5EB7\u5EA6\u6307\u6807\uFF08\u6D88\u8017\u5C11\u91CFToken\uFF09",
867
+ children: isProbing ? "\u63A2\u6D4B\u4E2D..." : isQueued ? "\u7B49\u5F85\u63A2\u6D4B" : "\u63A2\u6D4B\u5546\u5BB6"
868
+ }
869
+ ),
870
+ isPinnedHere ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
871
+ "button",
872
+ {
873
+ type: "button",
874
+ className: "dsh-a6-btn dsh-a6-btn-danger dsh-a6-btn-sm",
875
+ onClick: handleUnpin,
876
+ disabled: isBusy || !canWebAction || model.pinTokenMatched === false || isProbing || isQueued,
877
+ "data-tooltip": isProbing || isQueued ? "\u63A2\u6D4B\u5B8C\u6210\u540E\u518D\u53D6\u6D88\u56FA\u5B9A" : model.pinTokenMatched === false ? "\u8BE5\u56FA\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u4EE4\u724C\uFF0C\u65E0\u6CD5\u5728\u6B64\u53D6\u6D88\uFF1B\u5982\u9700\u53D6\u6D88\u8BF7\u5230\u5B98\u7F51\u6216\u5148\u4E3A\u5F53\u524D\u4EE4\u724C\u56FA\u5B9A\u6B64\u5546\u5BB6" : canWebAction ? "\u53D6\u6D88\u56FA\u5B9A\u540E\u6062\u590D\u667A\u80FD\u4F18\u9009\u8DEF\u7531\uFF0C\u53EF\u91CD\u65B0\u63A2\u6D4B\u540E\u518D\u51B3\u5B9A\u662F\u5426\u56FA\u5B9A" : "\u9700\u5148\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD",
878
+ children: isBusy ? "\u5904\u7406\u4E2D..." : "\u53D6\u6D88\u56FA\u5B9A"
879
+ }
880
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
881
+ "button",
882
+ {
883
+ type: "button",
884
+ className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
885
+ onClick: handleOpenPinConfirm,
886
+ disabled: isBusy || !hasMerchant || !canWebAction || isProbing || isQueued,
887
+ "data-tooltip": isProbing || isQueued ? "\u63A2\u6D4B\u5B8C\u6210\u540E\u518D\u56FA\u5B9A\u5546\u5BB6" : !hasMerchant ? "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" : !canWebAction ? "\u9700\u5148\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD" : "\u628A\u5F53\u524D\u5546\u5BB6\u56FA\u5B9A\u4E3A\u8BE5\u6A21\u578B\u7684\u670D\u52A1\u6E20\u9053\uFF08\u4F18\u5148\u8DEF\u7531\uFF0C\u5F02\u5E38\u65F6\u81EA\u52A8\u5207\u6362\u667A\u80FD\u4F18\u9009\uFF09",
888
+ children: isBusy ? "\u5904\u7406\u4E2D..." : "\u56FA\u5B9A\u5546\u5BB6"
889
+ }
890
+ ),
891
+ isChannelDisabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
892
+ "button",
893
+ {
894
+ type: "button",
895
+ className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
896
+ onClick: handleRestore,
897
+ disabled: isBusy || !canWebAction || isProbing || isQueued,
898
+ "data-tooltip": isProbing || isQueued ? "\u63A2\u6D4B\u5B8C\u6210\u540E\u518D\u6062\u590D" : canWebAction ? "\u6062\u590D\u8BE5\u5546\u5BB6\u5BF9\u6B64\u6A21\u578B\u7684\u670D\u52A1" : "\u9700\u5148\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD",
899
+ children: isBusy ? "\u5904\u7406\u4E2D..." : "\u6062\u590D"
900
+ }
901
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
902
+ "button",
903
+ {
904
+ type: "button",
905
+ className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
906
+ onClick: handleDisable,
907
+ disabled: isBusy || !hasMerchant || !canWebAction || isProbing || isQueued,
908
+ "data-tooltip": isProbing || isQueued ? "\u63A2\u6D4B\u5B8C\u6210\u540E\u518D\u7981\u7528" : !hasMerchant ? "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" : !canWebAction ? "\u9700\u5148\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD" : "\u7981\u7528\u5F53\u524D\u5546\u5BB6\u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1\uFF0C\u8DEF\u7531\u5C06\u4E0D\u518D\u547D\u4E2D\u6B64\u6E20\u9053",
909
+ children: isBusy ? "\u5904\u7406\u4E2D..." : "\u7981\u7528"
470
910
  }
471
911
  ),
472
912
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -490,7 +930,8 @@ var MerchantCard = ({ model }) => {
490
930
  children: expanded ? "\u6536\u8D77" : "\u8BE6\u60C5"
491
931
  }
492
932
  )
493
- ] })
933
+ ] }) }),
934
+ actionError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-action-error", role: "alert", children: actionError })
494
935
  ] }),
495
936
  expanded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-detail-container", children: [
496
937
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-detail-top-row", children: [
@@ -534,7 +975,78 @@ var MerchantCard = ({ model }) => {
534
975
  ] })
535
976
  ] })
536
977
  ] }) })
537
- ] })
978
+ ] }),
979
+ pinConfirmOpen && merchant && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
980
+ "div",
981
+ {
982
+ className: "dsh-a6-pin-modal-overlay",
983
+ onClick: (e) => {
984
+ e.stopPropagation();
985
+ setPinConfirmOpen(false);
986
+ },
987
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
988
+ "div",
989
+ {
990
+ className: "dsh-a6-pin-modal",
991
+ role: "dialog",
992
+ "aria-modal": "true",
993
+ "aria-label": "\u56FA\u5B9A\u5546\u5BB6\u786E\u8BA4",
994
+ onClick: (e) => e.stopPropagation(),
995
+ children: [
996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-pin-modal-title", children: "\u56FA\u5B9A\u5546\u5BB6" }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-pin-modal-body", children: [
998
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-pin-modal-row", children: [
999
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-a6-pin-modal-label", children: "\u6A21\u578B" }),
1000
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-a6-pin-modal-value", children: model.model_name })
1001
+ ] }),
1002
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-pin-modal-row", children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-a6-pin-modal-label", children: "\u5546\u5BB6" }),
1004
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-a6-pin-modal-value", children: [
1005
+ merchant.channel_name,
1006
+ " (ID: ",
1007
+ merchant.channel_id,
1008
+ ")"
1009
+ ] })
1010
+ ] }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-pin-modal-row", children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-a6-pin-modal-label", children: "\u5F53\u524D\u4EF7" }),
1013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-a6-pin-modal-value", children: [
1014
+ "\u8F93\u5165 ",
1015
+ merchant.input_price_cny,
1016
+ " \xB7 \u8F93\u51FA ",
1017
+ merchant.output_price_cny
1018
+ ] })
1019
+ ] }),
1020
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-a6-pin-modal-note", children: "\u56FA\u5B9A\u540E\u8BE5\u6A21\u578B\u7684\u6D41\u91CF\u4F18\u5148\u8D70\u6B64\u5546\u5BB6\uFF1B\u5546\u5BB6\u5F02\u5E38\u65F6\u81EA\u52A8\u5207\u6362\u667A\u80FD\u4F18\u9009\uFF08\u5E73\u53F0\u9ED8\u8BA4\uFF09\u3002\u56FA\u5B9A\u751F\u6548\u4E8E\u5F53\u524D API Key \u4EE4\u724C\uFF0C\u53EF\u968F\u65F6\u53D6\u6D88\u3002" }),
1021
+ actionError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-a6-action-error", children: actionError })
1022
+ ] }),
1023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-a6-pin-modal-foot", children: [
1024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1025
+ "button",
1026
+ {
1027
+ type: "button",
1028
+ className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
1029
+ onClick: () => setPinConfirmOpen(false),
1030
+ disabled: isBusy,
1031
+ children: "\u53D6\u6D88"
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1035
+ "button",
1036
+ {
1037
+ type: "button",
1038
+ className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
1039
+ onClick: handleConfirmPin,
1040
+ disabled: isBusy,
1041
+ children: isBusy ? "\u56FA\u5B9A\u4E2D..." : "\u786E\u8BA4\u56FA\u5B9A"
1042
+ }
1043
+ )
1044
+ ] })
1045
+ ]
1046
+ }
1047
+ )
1048
+ }
1049
+ )
538
1050
  ] });
539
1051
  };
540
1052
 
@@ -681,15 +1193,15 @@ var import_jsx_runtime3 = require("react/jsx-runtime");
681
1193
  var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
682
1194
  var ConfigPanel = ({ config, dshConfiguredModels }) => {
683
1195
  const [apiKey, setApiKey] = (0, import_react3.useState)(config.apiKey || "");
684
- const [accessToken, setAccessToken] = (0, import_react3.useState)(
685
- config.accessToken || config.sessionCookie || ""
686
- );
1196
+ const [accessToken, setAccessToken] = (0, import_react3.useState)(config.accessToken || "");
687
1197
  const [clearKey, setClearKey] = (0, import_react3.useState)(false);
688
1198
  const [clearToken, setClearToken] = (0, import_react3.useState)(false);
689
1199
  const [selectedNode, setSelectedNode] = (0, import_react3.useState)(
690
1200
  config.baseURL || "https://api.a6api.com"
691
1201
  );
692
- const [customNode, setCustomNode] = (0, import_react3.useState)(config.customBaseURL || "");
1202
+ const [customNode, setCustomNode] = (0, import_react3.useState)(
1203
+ config.baseURL && config.baseURL !== "https://api.a6api.com" && config.baseURL !== "https://a6.a6api.com" ? config.baseURL : ""
1204
+ );
693
1205
  const [isCustom, setIsCustom] = (0, import_react3.useState)(
694
1206
  config.baseURL !== "https://api.a6api.com" && config.baseURL !== "https://a6.a6api.com"
695
1207
  );
@@ -702,11 +1214,13 @@ var ConfigPanel = ({ config, dshConfiguredModels }) => {
702
1214
  const tokenSet = accessToken === MASK;
703
1215
  (0, import_react3.useEffect)(() => {
704
1216
  setApiKey(config.apiKey || "");
705
- setAccessToken(config.accessToken || config.sessionCookie || "");
1217
+ setAccessToken(config.accessToken || "");
706
1218
  setClearKey(false);
707
1219
  setClearToken(false);
708
1220
  setSelectedNode(config.baseURL || "https://api.a6api.com");
709
- setCustomNode(config.customBaseURL || "");
1221
+ setCustomNode(
1222
+ config.baseURL && config.baseURL !== "https://api.a6api.com" && config.baseURL !== "https://a6.a6api.com" ? config.baseURL : ""
1223
+ );
710
1224
  setIsCustom(
711
1225
  config.baseURL !== "https://api.a6api.com" && config.baseURL !== "https://a6.a6api.com"
712
1226
  );
@@ -718,9 +1232,8 @@ var ConfigPanel = ({ config, dshConfiguredModels }) => {
718
1232
  const newToken = tokenSet ? clearToken ? "" : void 0 : accessToken.trim();
719
1233
  const ok = await store.saveConfig({
720
1234
  ...newApiKey !== void 0 ? { apiKey: newApiKey } : {},
721
- ...newToken !== void 0 ? { accessToken: newToken, sessionCookie: newToken } : {},
722
- baseURL: finalBaseUrl,
723
- customBaseURL: customNode.trim()
1235
+ ...newToken !== void 0 ? { accessToken: newToken } : {},
1236
+ baseURL: finalBaseUrl
724
1237
  });
725
1238
  setSaving(false);
726
1239
  if (ok) {
@@ -937,150 +1450,714 @@ var ConfigPanel = ({ config, dshConfiguredModels }) => {
937
1450
  ] });
938
1451
  };
939
1452
 
940
- // src/client/components/A6ApiSettings.tsx
1453
+ // src/client/components/PricePill.tsx
941
1454
  var import_jsx_runtime4 = require("react/jsx-runtime");
942
- var A6ApiSettingsPanel = () => {
943
- const [state, setState] = (0, import_react4.useState)(store.getState());
944
- const [activeTab, setActiveTab] = (0, import_react4.useState)("models");
945
- const [filterMode, setFilterMode] = (0, import_react4.useState)("all");
946
- const [searchQuery, setSearchQuery] = (0, import_react4.useState)("");
947
- const [probingAll, setProbingAll] = (0, import_react4.useState)(false);
948
- const [refreshing, setRefreshing] = (0, import_react4.useState)(false);
949
- const [refreshSuccess, setRefreshSuccess] = (0, import_react4.useState)(false);
1455
+ var PricePill = ({ pf, hasToken, compact }) => {
1456
+ const n = Number(pf?.pendingCount ?? 0);
1457
+ const hasAuth = pf?.hasAuth !== false && !pf?.authError && Boolean(hasToken);
1458
+ const isAuthError = Boolean(pf?.authError);
1459
+ const isZero = n === 0;
1460
+ const isDisabled = !hasAuth || isZero;
1461
+ const compactCls = compact ? " compact" : "";
1462
+ const cls = isDisabled ? !hasAuth ? `dsh-a6-price-pill disabled${compactCls}` : `dsh-a6-price-pill is-zero is-disabled-zero${compactCls}` : `dsh-a6-price-pill has-change${compactCls}`;
1463
+ const title = !hasAuth ? isAuthError ? "\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u5DF2\u5931\u6548\uFF0C\u8BF7\u524D\u5F80\u57FA\u7840\u914D\u7F6E\u66F4\u65B0" : "\u672A\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\uFF0C\u65E0\u6CD5\u83B7\u53D6\u4EF7\u683C\u53D8\u52A8" : isZero ? "\u6682\u65E0\u4EF7\u683C\u53D8\u52A8" : `\u6709 ${n} \u6761\u4EF7\u683C\u53D8\u52A8\u5F85\u5904\u7406\uFF0C\u70B9\u51FB\u524D\u5F80\u5B98\u7F51\u5904\u7406`;
1464
+ const onClick = () => {
1465
+ if (isDisabled) return;
1466
+ window.open("https://a6api.com/console/token", "_blank", "noopener");
1467
+ };
1468
+ const onKeyDown = (e) => {
1469
+ if (isDisabled) return;
1470
+ if (e.key === "Enter" || e.key === " ") {
1471
+ e.preventDefault();
1472
+ onClick();
1473
+ }
1474
+ };
1475
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1476
+ "div",
1477
+ {
1478
+ className: cls,
1479
+ onClick: isDisabled ? void 0 : onClick,
1480
+ onKeyDown,
1481
+ tabIndex: isDisabled ? -1 : 0,
1482
+ title,
1483
+ role: "button",
1484
+ "aria-disabled": isDisabled,
1485
+ style: isDisabled ? { cursor: "not-allowed" } : void 0,
1486
+ children: [
1487
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-price-pill-label", children: "\u4EF7\u683C\u6CE2\u52A8\uFF1A" }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-price-pill-count", children: !hasAuth ? "--" : n })
1489
+ ]
1490
+ }
1491
+ );
1492
+ };
1493
+
1494
+ // src/client/components/ModelCatalogPanel.tsx
1495
+ var import_react4 = require("react");
1496
+
1497
+ // src/types.ts
1498
+ var THINKING_LEVEL_KEYS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
1499
+ function validateReasoningEfforts(value) {
1500
+ if (value === false) return { ok: true, value: false };
1501
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1502
+ return { ok: false, error: "reasoningEfforts \u5FC5\u987B\u662F\u6863\u4F4D\u5B57\u5178\u6216 false" };
1503
+ }
1504
+ const dict = value;
1505
+ const keys = Object.keys(dict);
1506
+ if (keys.length === 0) return { ok: false, error: "reasoningEfforts \u4E0D\u80FD\u4E3A\u7A7A\u5B57\u5178" };
1507
+ const out = {};
1508
+ for (const k of keys) {
1509
+ if (!THINKING_LEVEL_KEYS.includes(k)) {
1510
+ return { ok: false, error: `reasoningEfforts \u952E "${k}" \u4E0D\u662F DSH \u652F\u6301\u7684\u6863\u4F4D\uFF08off/minimal/low/medium/high/xhigh/max\uFF09` };
1511
+ }
1512
+ const v = dict[k];
1513
+ if (v === null) {
1514
+ if (k !== "off") {
1515
+ return { ok: false, error: `reasoningEfforts.${k} \u5FC5\u987B\u63D0\u4F9B wire \u503C\uFF0C\u4EC5 off \u5141\u8BB8\u7559\u7A7A` };
1516
+ }
1517
+ out[k] = null;
1518
+ } else if (typeof v === "string" && v.length > 0) {
1519
+ out[k] = v;
1520
+ } else {
1521
+ return { ok: false, error: `reasoningEfforts.${k} \u7684\u503C\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6216 null` };
1522
+ }
1523
+ }
1524
+ if (!keys.some((k) => k !== "off")) {
1525
+ return { ok: false, error: "reasoningEfforts \u9664 off \u5916\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u601D\u8003\u6863\u4F4D\uFF08\u975E\u63A8\u7406\u6A21\u578B\u8BF7\u7528 false\uFF09" };
1526
+ }
1527
+ return { ok: true, value: out };
1528
+ }
1529
+
1530
+ // src/client/components/ModelCatalogPanel.tsx
1531
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1532
+ var ModelCatalogPanel = () => {
1533
+ const [catalog, setCatalog] = (0, import_react4.useState)(store.getState().catalog);
1534
+ const [models, setModels] = (0, import_react4.useState)(store.getState().models);
1535
+ const [busy, setBusy] = (0, import_react4.useState)(store.getState().catalogBusy);
1536
+ const [search, setSearch] = (0, import_react4.useState)("");
1537
+ const [availFilter, setAvailFilter] = (0, import_react4.useState)("all");
1538
+ const [paramFilter, setParamFilter] = (0, import_react4.useState)("all");
1539
+ const [editingId, setEditingId] = (0, import_react4.useState)(null);
1540
+ const [queryingId, setQueryingId] = (0, import_react4.useState)(null);
1541
+ const [confirmClear, setConfirmClear] = (0, import_react4.useState)(false);
1542
+ const confirmClearTimer = (0, import_react4.useRef)(null);
1543
+ const [msg, setMsg] = (0, import_react4.useState)(null);
1544
+ const [draft, setDraft] = (0, import_react4.useState)({ name: "", contextWindow: "", maxTokens: "", inputText: false, inputImage: false, reasoningText: "", reasoningFalse: false });
950
1545
  (0, import_react4.useEffect)(() => {
951
1546
  const unsub = store.subscribe(() => {
952
- setState({ ...store.getState() });
1547
+ const s = store.getState();
1548
+ setCatalog(s.catalog);
1549
+ setModels(s.models);
1550
+ setBusy(s.catalogBusy);
953
1551
  });
954
- store.fetchState();
1552
+ store.fetchCatalog();
955
1553
  return unsub;
956
1554
  }, []);
957
- const handleProbeAll = async () => {
958
- setProbingAll(true);
959
- await store.probeAll();
960
- setProbingAll(false);
1555
+ const availableSet = (0, import_react4.useMemo)(
1556
+ () => new Set(models.map((m) => m.model_name.toLowerCase())),
1557
+ [models]
1558
+ );
1559
+ const flash = (kind, text) => {
1560
+ setMsg({ kind, text });
1561
+ setTimeout(() => setMsg(null), 5e3);
961
1562
  };
962
- const handleRefreshState = async () => {
963
- setRefreshing(true);
964
- await store.fetchState();
965
- setRefreshing(false);
966
- setRefreshSuccess(true);
967
- setTimeout(() => setRefreshSuccess(false), 2e3);
1563
+ const handleFetchMarket = async () => {
1564
+ const r = await store.fetchMarketModels();
1565
+ if (r.ok) {
1566
+ if (r.failedPages && r.failedPages > 0) {
1567
+ flash("err", `\u5DF2\u83B7\u53D6 ${r.total} \u4E2A\u6A21\u578B\uFF08\u65B0\u589E ${r.added} \u4E2A\uFF09\uFF0C\u4F46\u6709 ${r.failedPages} \u9875\u62C9\u53D6\u5931\u8D25\uFF0C\u76EE\u5F55\u53EF\u80FD\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u8BD5`);
1568
+ } else {
1569
+ flash("ok", `\u5DF2\u83B7\u53D6 ${r.total} \u4E2A\u6A21\u578B\uFF08\u65B0\u589E ${r.added} \u4E2A\uFF09`);
1570
+ }
1571
+ } else flash("err", r.error || "\u83B7\u53D6\u5931\u8D25");
968
1572
  };
969
- const inDshCount = state.models.filter((m) => m.inDsh).length;
970
- const probedCount = state.models.filter((m) => Boolean(m.merchant)).length;
971
- const filteredModels = state.models.filter((m) => {
972
- if (filterMode === "enabled" && !m.inDsh) return false;
973
- if (filterMode === "probed" && !m.merchant) return false;
974
- if (searchQuery.trim()) {
975
- const q = searchQuery.toLowerCase().trim();
976
- return m.model_name.toLowerCase().includes(q) || m.brand.toLowerCase().includes(q) || m.merchant?.supplier_name && m.merchant.supplier_name.toLowerCase().includes(q) || m.merchant?.channel_name && m.merchant.channel_name.toLowerCase().includes(q) || m.merchant?.description && m.merchant.description.toLowerCase().includes(q);
1573
+ const handleQueryAll = async () => {
1574
+ const r = await store.queryOpenRouter();
1575
+ if (r.ok) {
1576
+ const nf = r.notFound?.length || 0;
1577
+ flash("ok", `\u5DF2\u66F4\u65B0 ${r.updated} \u4E2A\u6A21\u578B\u53C2\u6570${nf > 0 ? `\uFF0C${nf} \u4E2A\u672A\u5728 OpenRouter \u67E5\u5230\uFF08\u53C2\u6570\u7559\u7A7A\u53EF\u624B\u52A8\u586B\u5199\uFF09` : ""}`);
1578
+ } else flash("err", r.error || "\u67E5\u8BE2\u5931\u8D25");
1579
+ };
1580
+ const handleQueryOne = async (id) => {
1581
+ setQueryingId(id);
1582
+ const r = await store.queryOpenRouter([id]);
1583
+ setQueryingId(null);
1584
+ if (r.ok) {
1585
+ if ((r.updated || 0) > 0) flash("ok", `\u300C${id}\u300D\u5DF2\u4ECE OpenRouter \u586B\u5145\u53C2\u6570`);
1586
+ else flash("ok", `\u300C${id}\u300D\u5728 OpenRouter \u672A\u67E5\u5230\uFF0C\u53EF\u624B\u52A8\u586B\u5199\u53C2\u6570`);
1587
+ } else flash("err", r.error || "\u67E5\u8BE2\u5931\u8D25");
1588
+ };
1589
+ const handleClear = async () => {
1590
+ if (!confirmClear) {
1591
+ setConfirmClear(true);
1592
+ if (confirmClearTimer.current) clearTimeout(confirmClearTimer.current);
1593
+ confirmClearTimer.current = setTimeout(() => setConfirmClear(false), 3e3);
1594
+ return;
977
1595
  }
978
- return true;
979
- });
980
- const sortedModels = [...filteredModels].sort((a, b) => {
981
- if (a.inDsh !== b.inDsh) {
1596
+ if (confirmClearTimer.current) clearTimeout(confirmClearTimer.current);
1597
+ setConfirmClear(false);
1598
+ setEditingId(null);
1599
+ const r = await store.clearCatalog();
1600
+ if (r.ok) flash("ok", "\u6A21\u578B\u76EE\u5F55\u5DF2\u6E05\u7A7A\uFF0C\u53EF\u91CD\u65B0\u300C\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B\u300D");
1601
+ else flash("err", r.error || "\u6E05\u7A7A\u5931\u8D25");
1602
+ };
1603
+ const startEdit = (entry) => {
1604
+ const re = entry.reasoningEfforts && typeof entry.reasoningEfforts === "object" ? entry.reasoningEfforts : {};
1605
+ setDraft({
1606
+ name: entry.name || "",
1607
+ contextWindow: entry.contextWindow != null ? String(entry.contextWindow) : "",
1608
+ maxTokens: entry.maxTokens != null ? String(entry.maxTokens) : "",
1609
+ inputText: entry.input ? entry.input.includes("text") : false,
1610
+ inputImage: entry.input ? entry.input.includes("image") : false,
1611
+ // 保留 off 等 null 值项(显示为 "off: "),避免编辑保存后档位静默丢失
1612
+ reasoningText: Object.entries(re).map(([k, v]) => `${k}: ${v === null ? "" : v}`).join(", "),
1613
+ reasoningFalse: entry.reasoningEfforts === false
1614
+ });
1615
+ setEditingId(entry.id);
1616
+ };
1617
+ const handleSave = async (id) => {
1618
+ const patch = {};
1619
+ const name2 = draft.name.trim();
1620
+ patch.name = name2 || null;
1621
+ const ctx = Number(draft.contextWindow);
1622
+ if (draft.contextWindow.trim() !== "") {
1623
+ if (!Number.isInteger(ctx) || ctx < 1) {
1624
+ flash("err", "contextWindow \u5FC5\u987B\u662F\u6B63\u6574\u6570");
1625
+ return;
1626
+ }
1627
+ patch.contextWindow = ctx;
1628
+ } else {
1629
+ patch.contextWindow = null;
1630
+ }
1631
+ const maxT = Number(draft.maxTokens);
1632
+ if (draft.maxTokens.trim() !== "") {
1633
+ if (!Number.isInteger(maxT) || maxT < 1) {
1634
+ flash("err", "maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570");
1635
+ return;
1636
+ }
1637
+ patch.maxTokens = maxT;
1638
+ } else {
1639
+ patch.maxTokens = null;
1640
+ }
1641
+ const mods = [];
1642
+ if (draft.inputText) mods.push("text");
1643
+ if (draft.inputImage) mods.push("image");
1644
+ patch.input = mods.length > 0 ? mods : null;
1645
+ if (draft.reasoningFalse) {
1646
+ patch.reasoningEfforts = false;
1647
+ } else {
1648
+ const text = draft.reasoningText.trim();
1649
+ if (text) {
1650
+ const parsed = {};
1651
+ let bad = false;
1652
+ for (const seg of text.split(",")) {
1653
+ const idx = seg.indexOf(":");
1654
+ if (idx < 0) {
1655
+ bad = true;
1656
+ break;
1657
+ }
1658
+ const k = seg.slice(0, idx).trim();
1659
+ const v = seg.slice(idx + 1).trim();
1660
+ if (!k) {
1661
+ bad = true;
1662
+ break;
1663
+ }
1664
+ parsed[k] = v || null;
1665
+ }
1666
+ if (bad) {
1667
+ flash("err", 'reasoningEfforts \u683C\u5F0F\u5E94\u4E3A "low: low, medium: medium"');
1668
+ return;
1669
+ }
1670
+ const checked = validateReasoningEfforts(parsed);
1671
+ if (!checked.ok) {
1672
+ flash("err", checked.error);
1673
+ return;
1674
+ }
1675
+ patch.reasoningEfforts = checked.value;
1676
+ } else {
1677
+ patch.reasoningEfforts = null;
1678
+ }
1679
+ }
1680
+ const r = await store.updateCatalogEntry(id, patch);
1681
+ if (r.ok) {
1682
+ setEditingId(null);
1683
+ flash("ok", `\u300C${id}\u300D\u5DF2\u4FDD\u5B58${store.getState().dshConfiguredModels.some((m) => m.toLowerCase() === id.toLowerCase()) ? "\uFF0C\u5E76\u5DF2\u540C\u6B65\u5230 DSH \u914D\u7F6E" : ""}`);
1684
+ } else {
1685
+ flash("err", r.error || "\u4FDD\u5B58\u5931\u8D25");
1686
+ }
1687
+ };
1688
+ const filtered = (0, import_react4.useMemo)(() => {
1689
+ const q = search.trim().toLowerCase();
1690
+ return catalog.filter((e) => {
1691
+ if (q && !e.id.toLowerCase().includes(q) && !(e.name || "").toLowerCase().includes(q)) return false;
1692
+ const isAvail = availableSet.has(e.id.toLowerCase());
1693
+ if (availFilter === "available" && !isAvail) return false;
1694
+ if (availFilter === "unavailable" && isAvail) return false;
1695
+ const filled = e.contextWindow != null || e.maxTokens != null || e.input && e.input.length > 0;
1696
+ if (paramFilter === "filled" && !filled) return false;
1697
+ if (paramFilter === "empty" && filled) return false;
1698
+ return true;
1699
+ }).sort((a, b) => a.id.localeCompare(b.id));
1700
+ }, [catalog, search, availFilter, paramFilter, availableSet]);
1701
+ const filledCount = catalog.filter((e) => e.contextWindow != null || e.maxTokens != null || e.input && e.input.length > 0).length;
1702
+ const availCount = catalog.filter((e) => availableSet.has(e.id.toLowerCase())).length;
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-page", children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-section-header", children: [
1705
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-toolbar", children: [
1706
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1707
+ "button",
1708
+ {
1709
+ type: "button",
1710
+ className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
1711
+ onClick: handleFetchMarket,
1712
+ disabled: busy !== null,
1713
+ "data-tooltip": "\u4ECE A6API \u5E02\u573A\u7FFB\u9875\u62C9\u53D6\u5168\u90E8\u652F\u6301\u6A21\u578B\u7684 ID\uFF08\u542B\u54C1\u724C\uFF09\uFF0C\u53C2\u6570\u521D\u59CB\u4E3A\u7A7A\uFF0C\u968F\u540E\u53EF\u7528 OpenRouter \u67E5\u8BE2\u586B\u5145",
1714
+ "data-tooltip-pos": "down",
1715
+ children: busy === "fetch" ? "\u83B7\u53D6\u4E2D..." : "\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B"
1716
+ }
1717
+ ),
1718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1719
+ "button",
1720
+ {
1721
+ type: "button",
1722
+ className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
1723
+ onClick: handleQueryAll,
1724
+ disabled: busy !== null || catalog.length === 0,
1725
+ "data-tooltip": "\u5BF9\u76EE\u5F55\u4E2D\u5168\u90E8\u6A21\u578B\u67E5\u8BE2 OpenRouter \u5E76\u586B\u5145 contextWindow / maxTokens / input\uFF1B\u67E5\u4E0D\u5230\u7684\u4FDD\u6301\u7559\u7A7A\u53EF\u624B\u52A8\u586B\u5199",
1726
+ "data-tooltip-pos": "down",
1727
+ children: busy === "query" ? "\u67E5\u8BE2\u4E2D..." : "\u4ECE OpenRouter \u4E00\u952E\u67E5\u8BE2"
1728
+ }
1729
+ ),
1730
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1731
+ "button",
1732
+ {
1733
+ type: "button",
1734
+ className: `dsh-a6-btn dsh-a6-btn-danger dsh-a6-btn-sm${confirmClear ? " dsh-a6-btn-clear-confirm" : ""}`,
1735
+ onClick: handleClear,
1736
+ disabled: busy !== null || catalog.length === 0,
1737
+ "data-tooltip": "\u6E05\u7A7A\u6A21\u578B\u76EE\u5F55\u5168\u90E8\u6761\u76EE\uFF0C\u53EF\u91CD\u65B0\u4ECE A6API \u83B7\u53D6\u5E76\u91CD\u65B0\u7528 OpenRouter \u586B\u5145\uFF08\u4E0D\u5F71\u54CD\u5DF2\u5199\u5165 DSH \u914D\u7F6E\u7684\u6A21\u578B\uFF09",
1738
+ "data-tooltip-pos": "down",
1739
+ children: confirmClear ? "\u786E\u8BA4\u6E05\u7A7A\uFF1F" : "\u6E05\u7A7A\u76EE\u5F55"
1740
+ }
1741
+ ),
1742
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-count", children: [
1743
+ "\u5171 ",
1744
+ catalog.length,
1745
+ " \u4E2A \xB7 \u53EF\u7528 ",
1746
+ availCount,
1747
+ " \u4E2A \xB7 \u5DF2\u586B\u53C2\u6570 ",
1748
+ filledCount,
1749
+ " \u4E2A"
1750
+ ] })
1751
+ ] }),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-filters", children: [
1753
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-filter-group", children: [
1754
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1755
+ "button",
1756
+ {
1757
+ type: "button",
1758
+ className: `dsh-a6-filter-btn ${availFilter === "all" ? "active" : ""}`,
1759
+ onClick: () => setAvailFilter("all"),
1760
+ children: [
1761
+ "\u5168\u90E8 (",
1762
+ catalog.length,
1763
+ ")"
1764
+ ]
1765
+ }
1766
+ ),
1767
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1768
+ "button",
1769
+ {
1770
+ type: "button",
1771
+ className: `dsh-a6-filter-btn ${availFilter === "available" ? "active" : ""}`,
1772
+ onClick: () => setAvailFilter("available"),
1773
+ children: [
1774
+ "\u53EF\u7528 (",
1775
+ availCount,
1776
+ ")"
1777
+ ]
1778
+ }
1779
+ ),
1780
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1781
+ "button",
1782
+ {
1783
+ type: "button",
1784
+ className: `dsh-a6-filter-btn ${availFilter === "unavailable" ? "active" : ""}`,
1785
+ onClick: () => setAvailFilter("unavailable"),
1786
+ children: [
1787
+ "\u4E0D\u53EF\u7528 (",
1788
+ catalog.length - availCount,
1789
+ ")"
1790
+ ]
1791
+ }
1792
+ )
1793
+ ] }),
1794
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-filter-group", children: [
1795
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1796
+ "button",
1797
+ {
1798
+ type: "button",
1799
+ className: `dsh-a6-filter-btn ${paramFilter === "all" ? "active" : ""}`,
1800
+ onClick: () => setParamFilter("all"),
1801
+ children: "\u5168\u90E8\u53C2\u6570"
1802
+ }
1803
+ ),
1804
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1805
+ "button",
1806
+ {
1807
+ type: "button",
1808
+ className: `dsh-a6-filter-btn ${paramFilter === "filled" ? "active" : ""}`,
1809
+ onClick: () => setParamFilter("filled"),
1810
+ children: [
1811
+ "\u5DF2\u586B (",
1812
+ filledCount,
1813
+ ")"
1814
+ ]
1815
+ }
1816
+ ),
1817
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1818
+ "button",
1819
+ {
1820
+ type: "button",
1821
+ className: `dsh-a6-filter-btn ${paramFilter === "empty" ? "active" : ""}`,
1822
+ onClick: () => setParamFilter("empty"),
1823
+ children: [
1824
+ "\u672A\u586B (",
1825
+ catalog.length - filledCount,
1826
+ ")"
1827
+ ]
1828
+ }
1829
+ )
1830
+ ] }),
1831
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-search-wrapper", children: [
1832
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1833
+ "input",
1834
+ {
1835
+ type: "text",
1836
+ className: "dsh-a6-input dsh-a6-search-input",
1837
+ placeholder: "\u641C\u7D22\u6A21\u578B ID / \u540D\u79F0...",
1838
+ value: search,
1839
+ onChange: (e) => setSearch(e.target.value)
1840
+ }
1841
+ ),
1842
+ search && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1843
+ "button",
1844
+ {
1845
+ type: "button",
1846
+ className: "dsh-a6-clear-btn",
1847
+ onClick: () => setSearch(""),
1848
+ title: "\u6E05\u7A7A\u641C\u7D22",
1849
+ children: "\xD7"
1850
+ }
1851
+ )
1852
+ ] })
1853
+ ] })
1854
+ ] }),
1855
+ msg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `dsh-a6-catalog-msg ${msg.kind}`, children: msg.text }),
1856
+ catalog.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-empty-state", children: [
1857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u6A21\u578B\u76EE\u5F55\u4E3A\u7A7A\u3002" }),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-side-popup-hint", children: "\u70B9\u51FB\u300C\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B\u300D\u62C9\u53D6\u5168\u90E8\u652F\u6301\u7684\u6A21\u578B ID\uFF0C\u518D\u7528\u300C\u4ECE OpenRouter \u4E00\u952E\u67E5\u8BE2\u300D\u81EA\u52A8\u586B\u5145\u53C2\u6570\u3002" })
1859
+ ] }) : filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-a6-empty-state", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u5F53\u524D\u7B5B\u9009\u6761\u4EF6\u4E0B\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-a6-catalog-list", children: filtered.map((entry) => {
1860
+ const editing = editingId === entry.id;
1861
+ const isAvail = availableSet.has(entry.id.toLowerCase());
1862
+ const re = entry.reasoningEfforts && typeof entry.reasoningEfforts === "object" ? entry.reasoningEfforts : null;
1863
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `dsh-a6-catalog-row${editing ? " editing" : ""}`, children: [
1864
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-row-head", children: [
1865
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-id", children: [
1866
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: entry.id }),
1867
+ isAvail && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-catalog-badge avail", children: "\u53EF\u7528" }),
1868
+ entry.name && entry.name !== entry.id && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-catalog-name", children: entry.name })
1869
+ ] }),
1870
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-row-actions", children: [
1871
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1872
+ "button",
1873
+ {
1874
+ type: "button",
1875
+ className: "dsh-a6-btn-text",
1876
+ onClick: () => handleQueryOne(entry.id),
1877
+ disabled: busy !== null,
1878
+ "data-tooltip": "\u4ECE OpenRouter \u67E5\u8BE2\u8BE5\u6A21\u578B\u53C2\u6570\u5E76\u586B\u5145",
1879
+ children: queryingId === entry.id ? "\u67E5\u8BE2\u4E2D..." : "\u67E5\u8BE2\u53C2\u6570"
1880
+ }
1881
+ ),
1882
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1883
+ "button",
1884
+ {
1885
+ type: "button",
1886
+ className: "dsh-a6-btn-text",
1887
+ onClick: () => editing ? setEditingId(null) : startEdit(entry),
1888
+ children: editing ? "\u53D6\u6D88" : "\u7F16\u8F91"
1889
+ }
1890
+ )
1891
+ ] })
1892
+ ] }),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-meta", children: [
1894
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: `dsh-a6-catalog-param${entry.contextWindow != null ? "" : " empty"}`, children: [
1895
+ "\u4E0A\u4E0B\u6587 ",
1896
+ entry.contextWindow != null ? entry.contextWindow.toLocaleString() : "\u2014"
1897
+ ] }),
1898
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: `dsh-a6-catalog-param${entry.maxTokens != null ? "" : " empty"}`, children: [
1899
+ "\u8F93\u51FA ",
1900
+ entry.maxTokens != null ? entry.maxTokens.toLocaleString() : "\u2014"
1901
+ ] }),
1902
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: `dsh-a6-catalog-param${entry.input && entry.input.length > 0 ? "" : " empty"}`, children: [
1903
+ "\u8F93\u5165 ",
1904
+ entry.input && entry.input.length > 0 ? entry.input.join("+") : "\u2014"
1905
+ ] }),
1906
+ re && Object.keys(re).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dsh-a6-catalog-param", children: [
1907
+ "\u63A8\u7406 ",
1908
+ Object.keys(re).length,
1909
+ " \u6863"
1910
+ ] }),
1911
+ entry.reasoningEfforts === false && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-catalog-param", children: "\u975E\u63A8\u7406" })
1912
+ ] }),
1913
+ editing && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-catalog-edit", children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-edit-grid", children: [
1915
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-edit-field", children: [
1916
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-label", children: "\u540D\u79F0 (name)" }),
1917
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1918
+ "input",
1919
+ {
1920
+ type: "text",
1921
+ className: "dsh-a6-input",
1922
+ value: draft.name,
1923
+ onChange: (e) => setDraft({ ...draft, name: e.target.value }),
1924
+ placeholder: "\u4EC5\u7528\u6237\u586B\u5199\uFF0C\u7559\u7A7A\u5219\u4E0D\u5199\u5165 DSH \u914D\u7F6E"
1925
+ }
1926
+ )
1927
+ ] }),
1928
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-edit-field", children: [
1929
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-label", children: "\u4E0A\u4E0B\u6587\u7A97\u53E3 (contextWindow)" }),
1930
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1931
+ "input",
1932
+ {
1933
+ type: "number",
1934
+ min: 1,
1935
+ className: "dsh-a6-input",
1936
+ value: draft.contextWindow,
1937
+ onChange: (e) => setDraft({ ...draft, contextWindow: e.target.value }),
1938
+ placeholder: "\u5982 1048576\uFF08\u7559\u7A7A = \u4E0D\u586B\uFF0CDSH \u7528\u9ED8\u8BA4\uFF09"
1939
+ }
1940
+ )
1941
+ ] }),
1942
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-edit-field", children: [
1943
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-label", children: "\u6700\u5927\u8F93\u51FA (maxTokens)" }),
1944
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1945
+ "input",
1946
+ {
1947
+ type: "number",
1948
+ min: 1,
1949
+ className: "dsh-a6-input",
1950
+ value: draft.maxTokens,
1951
+ onChange: (e) => setDraft({ ...draft, maxTokens: e.target.value }),
1952
+ placeholder: "\u5982 65536\uFF08\u7559\u7A7A = \u4E0D\u586B\uFF0CDSH \u7528\u9ED8\u8BA4\uFF09"
1953
+ }
1954
+ )
1955
+ ] }),
1956
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-edit-field", children: [
1957
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-label", children: "\u8F93\u5165\u6A21\u6001 (input)" }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-checkbox-group", children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-checkbox", children: [
1960
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1961
+ "input",
1962
+ {
1963
+ type: "checkbox",
1964
+ checked: draft.inputText,
1965
+ onChange: (e) => setDraft({ ...draft, inputText: e.target.checked })
1966
+ }
1967
+ ),
1968
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "text" })
1969
+ ] }),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-checkbox", children: [
1971
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1972
+ "input",
1973
+ {
1974
+ type: "checkbox",
1975
+ checked: draft.inputImage,
1976
+ onChange: (e) => setDraft({ ...draft, inputImage: e.target.checked })
1977
+ }
1978
+ ),
1979
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "image" })
1980
+ ] })
1981
+ ] })
1982
+ ] }),
1983
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-edit-field dsh-a6-edit-wide", children: [
1984
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dsh-a6-label", children: [
1985
+ "\u63A8\u7406\u6863\u4F4D (reasoningEfforts)",
1986
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-field-hint", style: { marginLeft: 6 }, children: "\u683C\u5F0F\uFF1Alow: low, medium: medium\uFF1B\u503C\u4E3A\u7A7A\u8868\u793A\u8BE5\u6863\u4F4D\u65E0 wire \u503C" })
1987
+ ] }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1989
+ "input",
1990
+ {
1991
+ type: "text",
1992
+ className: "dsh-a6-input",
1993
+ value: draft.reasoningText,
1994
+ disabled: draft.reasoningFalse,
1995
+ onChange: (e) => setDraft({ ...draft, reasoningText: e.target.value }),
1996
+ placeholder: "\u9ED8\u8BA4\u5DF2\u542B DSH \u5168\u90E8 7 \u6863\uFF08off/minimal/low/medium/high/xhigh/max\uFF09\uFF0C\u53EF\u4FEE\u6539\u6216\u7559\u7A7A\u5220\u9664\u8BE5\u5B57\u6BB5"
1997
+ }
1998
+ )
1999
+ ] }),
2000
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-edit-field", children: [
2001
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-a6-label", children: "\u63A8\u7406\u80FD\u529B" }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dsh-a6-checkbox", children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2004
+ "input",
2005
+ {
2006
+ type: "checkbox",
2007
+ checked: draft.reasoningFalse,
2008
+ onChange: (e) => setDraft({ ...draft, reasoningFalse: e.target.checked })
2009
+ }
2010
+ ),
2011
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u975E\u63A8\u7406\u6A21\u578B (reasoningEfforts: false)" })
2012
+ ] })
2013
+ ] })
2014
+ ] }),
2015
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-a6-edit-actions", children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2017
+ "button",
2018
+ {
2019
+ type: "button",
2020
+ className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
2021
+ onClick: () => handleSave(entry.id),
2022
+ children: "\u4FDD\u5B58"
2023
+ }
2024
+ ),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2026
+ "button",
2027
+ {
2028
+ type: "button",
2029
+ className: "dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm",
2030
+ onClick: () => setEditingId(null),
2031
+ children: "\u53D6\u6D88"
2032
+ }
2033
+ )
2034
+ ] })
2035
+ ] })
2036
+ ] }, entry.id);
2037
+ }) })
2038
+ ] });
2039
+ };
2040
+
2041
+ // src/client/components/A6ApiSettings.tsx
2042
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2043
+ var A6ApiSettingsPanel = () => {
2044
+ const [state, setState] = (0, import_react5.useState)(store.getState());
2045
+ const [activeTab, setActiveTab] = (0, import_react5.useState)("models");
2046
+ const [filterMode, setFilterMode] = (0, import_react5.useState)("all");
2047
+ const [searchQuery, setSearchQuery] = (0, import_react5.useState)("");
2048
+ const [refreshing, setRefreshing] = (0, import_react5.useState)(false);
2049
+ const [refreshSuccess, setRefreshSuccess] = (0, import_react5.useState)(false);
2050
+ (0, import_react5.useEffect)(() => {
2051
+ const unsub = store.subscribe(() => {
2052
+ setState({ ...store.getState() });
2053
+ });
2054
+ store.fetchState();
2055
+ return unsub;
2056
+ }, []);
2057
+ const handleProbeAll = () => {
2058
+ store.probeAll();
2059
+ };
2060
+ const handleCancelProbeAll = () => {
2061
+ store.cancelProbeAll();
2062
+ };
2063
+ const handleRefreshState = async () => {
2064
+ setRefreshing(true);
2065
+ await store.fetchState();
2066
+ setRefreshing(false);
2067
+ setRefreshSuccess(true);
2068
+ setTimeout(() => setRefreshSuccess(false), 2e3);
2069
+ };
2070
+ const inDshCount = state.models.filter((m) => m.inDsh).length;
2071
+ const probedCount = state.models.filter((m) => Boolean(m.merchant)).length;
2072
+ const filteredModels = state.models.filter((m) => {
2073
+ if (filterMode === "enabled" && !m.inDsh) return false;
2074
+ if (filterMode === "probed" && !m.merchant) return false;
2075
+ if (searchQuery.trim()) {
2076
+ const q = searchQuery.toLowerCase().trim();
2077
+ return m.model_name.toLowerCase().includes(q) || m.brand.toLowerCase().includes(q) || m.merchant?.supplier_name && m.merchant.supplier_name.toLowerCase().includes(q) || m.merchant?.channel_name && m.merchant.channel_name.toLowerCase().includes(q) || m.merchant?.description && m.merchant.description.toLowerCase().includes(q);
2078
+ }
2079
+ return true;
2080
+ });
2081
+ const sortedModels = [...filteredModels].sort((a, b) => {
2082
+ if (a.inDsh !== b.inDsh) {
982
2083
  return a.inDsh ? -1 : 1;
983
2084
  }
984
2085
  return a.model_name.localeCompare(b.model_name);
985
2086
  });
986
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-container", children: [
987
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-main-header", children: [
988
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-header-text", children: [
989
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "dsh-a6-main-title", children: "A6api" }),
990
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dsh-a6-main-subtitle", children: "\u805A\u5408\u5168\u7403\u4E3B\u6D41\u4E0E\u9AD8\u6027\u4EF7\u6BD4\u6A21\u578B\uFF0C\u5B9E\u65F6\u76D1\u63A7\u5546\u6237\u6307\u6807\u3001\u4EF7\u683C\u500D\u7387\u4E0E\u8D26\u6237\u8D44\u4EA7\u3002" })
2087
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-container", children: [
2088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-main-header", children: [
2089
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-header-text", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "dsh-a6-main-title", children: "A6api" }),
2091
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "dsh-a6-main-subtitle", children: "\u805A\u5408\u5168\u7403\u4E3B\u6D41\u4E0E\u9AD8\u6027\u4EF7\u6BD4\u6A21\u578B\uFF0C\u5B9E\u65F6\u76D1\u63A7\u5546\u6237\u6307\u6807\u3001\u4EF7\u683C\u500D\u7387\u4E0E\u8D26\u6237\u8D44\u4EA7\u3002" })
991
2092
  ] }),
992
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-header-badges", children: [
993
- state.balance?.hasAccountAuth && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2093
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-header-badges", children: [
2094
+ state.balance?.hasAccountAuth && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
994
2095
  "div",
995
2096
  {
996
2097
  className: "dsh-a6-header-balance-badge",
997
2098
  onClick: () => setActiveTab("account"),
998
2099
  title: "\u70B9\u51FB\u5207\u6362\u81F3\u300C\u8D26\u6237\u8D44\u4EA7\u300D\u9875\u9762",
999
2100
  children: [
1000
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-hb-label", children: "\u8D26\u6237\u4F59\u989D:" }),
1001
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-hb-amount", children: state.balance.accountBalanceFormatted })
2101
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-a6-hb-label", children: "\u8D26\u6237\u4F59\u989D:" }),
2102
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-a6-hb-amount", children: state.balance.accountBalanceFormatted })
1002
2103
  ]
1003
2104
  }
1004
2105
  ),
1005
- (() => {
1006
- const n = state.priceFluctuation?.pendingCount ?? 0;
1007
- const pf = state.priceFluctuation;
1008
- const hasAuth = pf?.hasAuth !== false && !pf?.authError && Boolean(state.config?.hasToken);
1009
- const isAuthError = Boolean(pf?.authError);
1010
- const isZero = n === 0;
1011
- const isDisabled = !hasAuth || isZero;
1012
- const cls = isDisabled ? !hasAuth ? "dsh-a6-price-pill disabled" : "dsh-a6-price-pill is-zero is-disabled-zero" : "dsh-a6-price-pill has-change";
1013
- const title = !hasAuth ? isAuthError ? "\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u5DF2\u5931\u6548\uFF0C\u8BF7\u524D\u5F80\u57FA\u7840\u914D\u7F6E\u66F4\u65B0" : "\u672A\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\uFF0C\u65E0\u6CD5\u83B7\u53D6\u4EF7\u683C\u53D8\u52A8" : isZero ? "\u6682\u65E0\u4EF7\u683C\u53D8\u52A8" : `\u6709 ${n} \u6761\u4EF7\u683C\u53D8\u52A8\u5F85\u5904\u7406\uFF0C\u70B9\u51FB\u524D\u5F80\u5B98\u7F51\u5904\u7406`;
1014
- const onClick = () => {
1015
- if (isDisabled) return;
1016
- window.open("https://a6api.com/console/token", "_blank", "noopener");
1017
- };
1018
- const onKeyDown = (e) => {
1019
- if (isDisabled) return;
1020
- if (e.key === "Enter" || e.key === " ") {
1021
- e.preventDefault();
1022
- onClick();
1023
- }
1024
- };
1025
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1026
- "div",
1027
- {
1028
- className: cls,
1029
- onClick: isDisabled ? void 0 : onClick,
1030
- onKeyDown,
1031
- tabIndex: isDisabled ? -1 : 0,
1032
- title,
1033
- role: "button",
1034
- "aria-disabled": isDisabled,
1035
- style: isDisabled ? { cursor: "not-allowed" } : void 0,
1036
- children: [
1037
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-price-pill-label", children: "\u4EF7\u683C\u6CE2\u52A8\uFF1A" }),
1038
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-price-pill-count", children: !hasAuth ? "--" : n })
1039
- ]
1040
- }
1041
- );
1042
- })()
2106
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PricePill, { pf: state.priceFluctuation, hasToken: Boolean(state.config?.hasToken) })
1043
2107
  ] })
1044
2108
  ] }),
1045
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-nav-tabs", children: [
1046
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2109
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-nav-tabs", children: [
2110
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1047
2111
  "button",
1048
2112
  {
1049
2113
  type: "button",
1050
2114
  className: `dsh-a6-nav-tab ${activeTab === "models" ? "active" : ""}`,
1051
2115
  onClick: () => setActiveTab("models"),
1052
2116
  children: [
1053
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u53EF\u7528\u6A21\u578B" }),
1054
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-tab-badge", children: state.models.length })
2117
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u53EF\u7528\u6A21\u578B" }),
2118
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-a6-tab-badge", children: state.models.length })
2119
+ ]
2120
+ }
2121
+ ),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2123
+ "button",
2124
+ {
2125
+ type: "button",
2126
+ className: `dsh-a6-nav-tab ${activeTab === "catalog" ? "active" : ""}`,
2127
+ onClick: () => setActiveTab("catalog"),
2128
+ children: [
2129
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u6A21\u578B\u76EE\u5F55" }),
2130
+ state.catalog.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-a6-tab-badge", children: state.catalog.length })
1055
2131
  ]
1056
2132
  }
1057
2133
  ),
1058
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2134
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1059
2135
  "button",
1060
2136
  {
1061
2137
  type: "button",
1062
2138
  className: `dsh-a6-nav-tab ${activeTab === "account" ? "active" : ""}`,
1063
2139
  onClick: () => setActiveTab("account"),
1064
2140
  children: [
1065
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u8D26\u6237\u8D44\u4EA7" }),
1066
- state.balance?.hasAccountAuth && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-a6-tab-badge success", children: state.balance.accountBalanceFormatted })
2141
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u8D26\u6237\u8D44\u4EA7" }),
2142
+ state.balance?.hasAccountAuth && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-a6-tab-badge success", children: state.balance.accountBalanceFormatted })
1067
2143
  ]
1068
2144
  }
1069
2145
  ),
1070
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2146
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1071
2147
  "button",
1072
2148
  {
1073
2149
  type: "button",
1074
2150
  className: `dsh-a6-nav-tab ${activeTab === "config" ? "active" : ""}`,
1075
2151
  onClick: () => setActiveTab("config"),
1076
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u57FA\u7840\u914D\u7F6E" })
2152
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u57FA\u7840\u914D\u7F6E" })
1077
2153
  }
1078
2154
  )
1079
2155
  ] }),
1080
- activeTab === "models" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-tab-page models-page", children: [
1081
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-section-header", children: [
1082
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-filter-group", children: [
1083
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2156
+ activeTab === "catalog" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-tab-page catalog-page", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ModelCatalogPanel, {}) }),
2157
+ activeTab === "models" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-tab-page models-page", children: [
2158
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-section-header", children: [
2159
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-filter-group", children: [
2160
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1084
2161
  "button",
1085
2162
  {
1086
2163
  type: "button",
@@ -1093,7 +2170,7 @@ var A6ApiSettingsPanel = () => {
1093
2170
  ]
1094
2171
  }
1095
2172
  ),
1096
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2173
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1097
2174
  "button",
1098
2175
  {
1099
2176
  type: "button",
@@ -1106,7 +2183,7 @@ var A6ApiSettingsPanel = () => {
1106
2183
  ]
1107
2184
  }
1108
2185
  ),
1109
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2186
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1110
2187
  "button",
1111
2188
  {
1112
2189
  type: "button",
@@ -1120,9 +2197,9 @@ var A6ApiSettingsPanel = () => {
1120
2197
  }
1121
2198
  )
1122
2199
  ] }),
1123
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-toolbar-right", children: [
1124
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-search-wrapper", children: [
1125
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2200
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-toolbar-right", children: [
2201
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-search-wrapper", children: [
2202
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1126
2203
  "input",
1127
2204
  {
1128
2205
  type: "text",
@@ -1132,7 +2209,7 @@ var A6ApiSettingsPanel = () => {
1132
2209
  onChange: (e) => setSearchQuery(e.target.value)
1133
2210
  }
1134
2211
  ),
1135
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2212
+ searchQuery && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1136
2213
  "button",
1137
2214
  {
1138
2215
  type: "button",
@@ -1143,42 +2220,70 @@ var A6ApiSettingsPanel = () => {
1143
2220
  }
1144
2221
  )
1145
2222
  ] }),
1146
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2223
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1147
2224
  "button",
1148
2225
  {
1149
2226
  type: "button",
1150
2227
  className: `dsh-a6-btn dsh-a6-btn-secondary dsh-a6-btn-sm ${refreshSuccess ? "dsh-a6-btn-refresh-ok" : ""}`,
1151
2228
  onClick: handleRefreshState,
1152
- disabled: refreshing,
1153
- "data-tooltip": "\u91CD\u65B0\u5411 A6API \u63A5\u53E3\u62C9\u53D6\u5F53\u524D\u4EE4\u724C\u7684\u53EF\u7528\u6A21\u578B\u5217\u8868\u53CA\u5DF2\u7F13\u5B58\u5546\u6237\u6307\u6807\uFF08\u4E0D\u6D88\u8017 Token\uFF09",
2229
+ disabled: refreshing || state.probeAllActive,
2230
+ "data-tooltip": state.probeAllActive ? "\u5168\u91CF\u63A2\u6D4B\u8FDB\u884C\u4E2D\uFF0C\u5B8C\u6210\u540E\u53EF\u5237\u65B0" : "\u91CD\u65B0\u5411 A6API \u63A5\u53E3\u62C9\u53D6\u5F53\u524D\u4EE4\u724C\u7684\u53EF\u7528\u6A21\u578B\u5217\u8868\u53CA\u5DF2\u7F13\u5B58\u5546\u6237\u6307\u6807\uFF08\u4E0D\u6D88\u8017 Token\uFF09",
1154
2231
  "data-tooltip-pos": "down",
1155
2232
  children: refreshing ? "\u5237\u65B0\u4E2D..." : refreshSuccess ? "\u5DF2\u5237\u65B0 \u2713" : "\u5237\u65B0\u5217\u8868"
1156
2233
  }
1157
2234
  ),
1158
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2235
+ state.probeAllActive ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2236
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2237
+ "button",
2238
+ {
2239
+ type: "button",
2240
+ className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
2241
+ disabled: true,
2242
+ "data-tooltip": "\u6B63\u5728\u5E76\u53D1\u63A2\u6D4B\u5168\u90E8\u6A21\u578B\uFF0C\u5361\u7247\u9010\u4E2A\u56DE\u586B\u7ED3\u679C",
2243
+ "data-tooltip-pos": "down-left",
2244
+ children: [
2245
+ "\u5168\u91CF\u63A2\u6D4B\u4E2D ",
2246
+ state.probeAllDoneCount,
2247
+ "/",
2248
+ state.probeAllTotal
2249
+ ]
2250
+ }
2251
+ ),
2252
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2253
+ "button",
2254
+ {
2255
+ type: "button",
2256
+ className: "dsh-a6-btn dsh-a6-btn-danger dsh-a6-btn-sm",
2257
+ onClick: handleCancelProbeAll,
2258
+ "data-tooltip": "\u505C\u6B62\u53D6\u65B0\u4EFB\u52A1\uFF0C\u5DF2\u5728\u63A2\u6D4B\u4E2D\u7684\u6A21\u578B\u4F1A\u6B63\u5E38\u5B8C\u6210\u5E76\u56DE\u586B",
2259
+ "data-tooltip-pos": "down-left",
2260
+ children: "\u53D6\u6D88"
2261
+ }
2262
+ )
2263
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1159
2264
  "button",
1160
2265
  {
1161
2266
  type: "button",
1162
2267
  className: "dsh-a6-btn dsh-a6-btn-primary dsh-a6-btn-sm",
1163
2268
  onClick: handleProbeAll,
1164
- disabled: probingAll || state.models.length === 0,
1165
- "data-tooltip": "\u5BF9\u5F53\u524D\u4EE4\u724C\u652F\u6301\u7684\u6240\u6709\u6A21\u578B\u9010\u4E2A\u53D1\u9001\u4E00\u6B21\u8BF7\u6C42\uFF0C\u6279\u91CF\u6355\u83B7\u5546\u6237\u8DEF\u7531\u4E0E\u6700\u65B0\u884C\u60C5\uFF08\u6BCF\u4E2A\u6A21\u578B\u6D88\u8017\u5C11\u91CFToken\uFF09",
2269
+ disabled: state.models.length === 0,
2270
+ "data-tooltip": "\u5BF9\u5F53\u524D\u4EE4\u724C\u652F\u6301\u7684\u6240\u6709\u6A21\u578B\u5E76\u53D1\u53D1\u9001\u8BF7\u6C42\uFF0C\u6279\u91CF\u6355\u83B7\u5546\u6237\u8DEF\u7531\u4E0E\u6700\u65B0\u884C\u60C5\uFF08\u6BCF\u4E2A\u6A21\u578B\u6D88\u8017\u5C11\u91CFToken\uFF0C\u9047\u9650\u6D41\u81EA\u52A8\u91CD\u8BD5\uFF09",
1166
2271
  "data-tooltip-pos": "down-left",
1167
- children: probingAll ? "\u5168\u91CF\u63A2\u6D4B\u4E2D..." : "\u4E00\u952E\u5168\u91CF\u63A2\u6D4B"
2272
+ children: "\u4E00\u952E\u5168\u91CF\u63A2\u6D4B"
1168
2273
  }
1169
2274
  )
1170
2275
  ] })
1171
2276
  ] }),
1172
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-a6-cards-list", children: state.loading && state.models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-a6-empty-state", children: [
1173
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-a6-spinner" }),
1174
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u6B63\u5728\u8FDE\u63A5 A6API \u805A\u5408\u7AD9\u5E76\u52A0\u8F7D\u6A21\u578B\u884C\u60C5..." })
1175
- ] }) : sortedModels.length > 0 ? sortedModels.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MerchantCard, { model: m }, m.model_name)) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-a6-empty-state", children: searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
2277
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-cards-list", children: state.loading && state.models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-a6-empty-state", children: [
2278
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-spinner" }),
2279
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u6B63\u5728\u8FDE\u63A5 A6API \u805A\u5408\u7AD9\u5E76\u52A0\u8F7D\u6A21\u578B\u884C\u60C5..." })
2280
+ ] }) : sortedModels.length > 0 ? sortedModels.map((m) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MerchantCard, { model: m }, m.model_name)) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-empty-state", children: searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1176
2281
  "\u672A\u641C\u7D22\u5230\u5339\u914D\u300C",
1177
2282
  searchQuery,
1178
2283
  "\u300D\u7684\u6A21\u578B"
1179
- ] }) : filterMode === "enabled" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u5F53\u524D\u5C1A\u672A\u5728 DSH \u4E2D\u542F\u7528\u4EFB\u4F55 A6API \u6A21\u578B\uFF0C\u70B9\u51FB\u6A21\u578B\u5361\u7247\u53F3\u4FA7\u300C\u6DFB\u52A0\u5230 DSH\u300D\u5373\u53EF\u542F\u7528\u3002" }) : filterMode === "probed" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u5C1A\u672A\u63A2\u6D4B\u4EFB\u4F55\u6A21\u578B\u5546\u6237\u7EBF\u8DEF\uFF0C\u70B9\u51FB\u6A21\u578B\u5361\u7247\u4E0A\u7684\u300C\u63A2\u6D4B\u5546\u5BB6\u300D\u6216\u4E0A\u65B9\u300C\u4E00\u952E\u5168\u91CF\u63A2\u6D4B\u300D\u5373\u53EF\u5F00\u59CB\u3002" }) : !state.config.hasApiKey ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u8BF7\u524D\u5F80\u300C\u57FA\u7840\u914D\u7F6E\u300D\u9875\u9762\u586B\u5165\u60A8\u7684 A6API \u4EE4\u724C (API Key) \u5E76\u4FDD\u5B58\uFF0C\u5373\u53EF\u81EA\u52A8\u52A0\u8F7D\u53EF\u7528\u6A21\u578B\u5217\u8868\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u5F53\u524D\u4EE4\u724C\u6682\u65E0\u53EF\u7528\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5 A6API \u63A7\u5236\u53F0\u4E2D\u7684\u4EE4\u724C\u9650\u5236\u8BBE\u7F6E\u3002" }) }) })
2284
+ ] }) : filterMode === "enabled" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u5F53\u524D\u5C1A\u672A\u5728 DSH \u4E2D\u542F\u7528\u4EFB\u4F55 A6API \u6A21\u578B\uFF0C\u70B9\u51FB\u6A21\u578B\u5361\u7247\u53F3\u4FA7\u300C\u6DFB\u52A0\u5230 DSH\u300D\u5373\u53EF\u542F\u7528\u3002" }) : filterMode === "probed" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u5C1A\u672A\u63A2\u6D4B\u4EFB\u4F55\u6A21\u578B\u5546\u6237\u7EBF\u8DEF\uFF0C\u70B9\u51FB\u6A21\u578B\u5361\u7247\u4E0A\u7684\u300C\u63A2\u6D4B\u5546\u5BB6\u300D\u6216\u4E0A\u65B9\u300C\u4E00\u952E\u5168\u91CF\u63A2\u6D4B\u300D\u5373\u53EF\u5F00\u59CB\u3002" }) : !state.config.hasApiKey ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u8BF7\u524D\u5F80\u300C\u57FA\u7840\u914D\u7F6E\u300D\u9875\u9762\u586B\u5165\u60A8\u7684 A6API \u4EE4\u724C (API Key) \u5E76\u4FDD\u5B58\uFF0C\u5373\u53EF\u81EA\u52A8\u52A0\u8F7D\u53EF\u7528\u6A21\u578B\u5217\u8868\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u5F53\u524D\u4EE4\u724C\u6682\u65E0\u53EF\u7528\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5 A6API \u63A7\u5236\u53F0\u4E2D\u7684\u4EE4\u724C\u9650\u5236\u8BBE\u7F6E\u3002" }) }) })
1180
2285
  ] }),
1181
- activeTab === "account" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-a6-tab-page account-page", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2286
+ activeTab === "account" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-tab-page account-page", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1182
2287
  AccountPanel,
1183
2288
  {
1184
2289
  balance: state.balance,
@@ -1187,7 +2292,7 @@ var A6ApiSettingsPanel = () => {
1187
2292
  onNavigateToConfig: () => setActiveTab("config")
1188
2293
  }
1189
2294
  ) }),
1190
- activeTab === "config" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-a6-tab-page config-page", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2295
+ activeTab === "config" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-a6-tab-page config-page", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1191
2296
  ConfigPanel,
1192
2297
  {
1193
2298
  config: state.config,
@@ -1197,55 +2302,337 @@ var A6ApiSettingsPanel = () => {
1197
2302
  ] });
1198
2303
  };
1199
2304
 
1200
- // src/client/styles/main.css
1201
- var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme Integration */
1202
-
1203
- .dsh-a6-container {
1204
- display: flex;
1205
- flex-direction: column;
1206
- color: var(--dsw-alias-label-primary, var(--ds-text-primary, #1e293b));
1207
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
1208
- padding-bottom: 28px;
1209
- }
1210
-
1211
- /* ==========================================================================
1212
- 1. Master Header & Navigation Tabs (Image 2 Style)
1213
- ========================================================================== */
1214
- .dsh-a6-main-header {
1215
- display: flex;
1216
- justify-content: space-between;
1217
- align-items: flex-start;
1218
- gap: 16px;
1219
- margin-bottom: 12px;
1220
- flex-wrap: wrap;
1221
- }
1222
-
1223
- .dsh-a6-header-text {
1224
- display: flex;
1225
- flex-direction: column;
1226
- gap: 4px;
1227
- }
1228
-
1229
- .dsh-a6-main-title {
1230
- font-size: 20px;
1231
- font-weight: 700;
1232
- color: var(--dsw-alias-label-primary, #0f172a);
1233
- margin: 0;
1234
- line-height: 1.2;
1235
- }
1236
-
1237
- .dsh-a6-main-subtitle {
1238
- font-size: 13px;
1239
- color: var(--dsw-alias-label-secondary, #64748b);
1240
- margin: 0;
1241
- line-height: 1.5;
1242
- }
1243
-
1244
- .dsh-a6-header-balance-badge {
1245
- display: inline-flex;
1246
- align-items: center;
1247
- gap: 6px;
1248
- padding: 5px 12px;
2305
+ // src/client/components/A6ApiSidebarCard.tsx
2306
+ var import_react6 = require("react");
2307
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2308
+ var POPUP_MAX_WIDTH = 500;
2309
+ var normalizeModelId = (id) => id.replace(/^[^/]+\//, "");
2310
+ var A6ApiSidebarCard = ({
2311
+ wide,
2312
+ useSessions,
2313
+ getModelDirectories
2314
+ }) => {
2315
+ if (useSessions) {
2316
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2317
+ A6ApiSidebarCardInner,
2318
+ {
2319
+ wide,
2320
+ useSessions,
2321
+ getModelDirectories
2322
+ }
2323
+ );
2324
+ }
2325
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(A6ApiSidebarCardBody, { wide, getModelDirectories });
2326
+ };
2327
+ var A6ApiSidebarCardInner = ({ wide, useSessions, getModelDirectories }) => {
2328
+ const currentId = useSessions((s) => s?.current);
2329
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2330
+ A6ApiSidebarCardBody,
2331
+ {
2332
+ wide,
2333
+ currentId,
2334
+ getModelDirectories
2335
+ }
2336
+ );
2337
+ };
2338
+ var A6ApiSidebarCardBody = ({
2339
+ wide,
2340
+ currentId,
2341
+ getModelDirectories
2342
+ }) => {
2343
+ const [open, setOpen] = (0, import_react6.useState)(false);
2344
+ const [selection, setSelection] = (0, import_react6.useState)(null);
2345
+ const [state, setState] = (0, import_react6.useState)(store.getState());
2346
+ const [pos, setPos] = (0, import_react6.useState)(null);
2347
+ const buttonRef = (0, import_react6.useRef)(null);
2348
+ const popupRef = (0, import_react6.useRef)(null);
2349
+ const flippedRef = (0, import_react6.useRef)(false);
2350
+ const fetchedRef = (0, import_react6.useRef)(false);
2351
+ const lastPosRef = (0, import_react6.useRef)(null);
2352
+ (0, import_react6.useEffect)(() => {
2353
+ let disposed = false;
2354
+ let unsub = null;
2355
+ let tries = 0;
2356
+ const connect = () => {
2357
+ if (disposed) return;
2358
+ const md = getModelDirectories?.();
2359
+ if (!md || typeof md.directoryFor !== "function") {
2360
+ if (tries++ < 6) setTimeout(connect, 500);
2361
+ return;
2362
+ }
2363
+ let dir;
2364
+ try {
2365
+ dir = md.directoryFor(currentId);
2366
+ } catch {
2367
+ dir = void 0;
2368
+ }
2369
+ if (!dir) {
2370
+ setSelection(null);
2371
+ return;
2372
+ }
2373
+ const update = () => {
2374
+ try {
2375
+ const snap = dir.store?.getSnapshot?.();
2376
+ setSelection(snap?.current ?? null);
2377
+ } catch {
2378
+ setSelection(null);
2379
+ }
2380
+ };
2381
+ update();
2382
+ try {
2383
+ unsub = dir.store?.subscribe?.(update) ?? null;
2384
+ } catch {
2385
+ unsub = null;
2386
+ }
2387
+ };
2388
+ connect();
2389
+ return () => {
2390
+ disposed = true;
2391
+ if (unsub) {
2392
+ try {
2393
+ unsub();
2394
+ } catch {
2395
+ }
2396
+ }
2397
+ };
2398
+ }, [currentId, getModelDirectories]);
2399
+ (0, import_react6.useEffect)(() => {
2400
+ const unsub = store.subscribe(() => setState({ ...store.getState() }));
2401
+ return unsub;
2402
+ }, []);
2403
+ const isA6api = Boolean(selection && selection.provider === "a6api");
2404
+ const modelName = normalizeModelId(selection?.model || "");
2405
+ const canOpen = isA6api && Boolean(modelName);
2406
+ const card = canOpen ? state.models.find((m) => m.model_name.toLowerCase() === modelName.toLowerCase()) : void 0;
2407
+ const toggle = () => {
2408
+ if (!canOpen) return;
2409
+ setOpen((v) => !v);
2410
+ };
2411
+ (0, import_react6.useEffect)(() => {
2412
+ if (open && !canOpen) setOpen(false);
2413
+ }, [open, canOpen]);
2414
+ (0, import_react6.useEffect)(() => {
2415
+ if (!open) {
2416
+ setPos(null);
2417
+ lastPosRef.current = null;
2418
+ flippedRef.current = false;
2419
+ return;
2420
+ }
2421
+ if (state.models.length === 0 && !fetchedRef.current) {
2422
+ fetchedRef.current = true;
2423
+ store.fetchState().catch(() => {
2424
+ });
2425
+ }
2426
+ const place = () => {
2427
+ const btn = buttonRef.current;
2428
+ if (!btn) return;
2429
+ const r = btn.getBoundingClientRect();
2430
+ const vw = window.innerWidth;
2431
+ const width = Math.min(POPUP_MAX_WIDTH, vw - 16);
2432
+ const left = Math.max(8, Math.min(r.left, vw - width - 8));
2433
+ const flipped = flippedRef.current || r.top < 8;
2434
+ if (flipped) flippedRef.current = true;
2435
+ const next = flipped ? { left, top: r.bottom + 8 } : { left, bottom: window.innerHeight - r.top + 8 };
2436
+ const last = lastPosRef.current;
2437
+ if (last && last.left === next.left && (last.top ?? null) === (next.top ?? null) && (last.bottom ?? null) === (next.bottom ?? null)) {
2438
+ return;
2439
+ }
2440
+ lastPosRef.current = next;
2441
+ setPos(next);
2442
+ };
2443
+ place();
2444
+ window.addEventListener("resize", place);
2445
+ window.addEventListener("scroll", place, true);
2446
+ return () => {
2447
+ window.removeEventListener("resize", place);
2448
+ window.removeEventListener("scroll", place, true);
2449
+ };
2450
+ }, [open]);
2451
+ (0, import_react6.useEffect)(() => {
2452
+ if (!open || !popupRef.current) return;
2453
+ const checkFlip = () => {
2454
+ if (flippedRef.current || !popupRef.current) return;
2455
+ const rect = popupRef.current.getBoundingClientRect();
2456
+ if (rect.top < 8) {
2457
+ flippedRef.current = true;
2458
+ const btn = buttonRef.current;
2459
+ if (btn) {
2460
+ const r = btn.getBoundingClientRect();
2461
+ setPos({ left: lastPosRef.current?.left ?? 8, top: r.bottom + 8 });
2462
+ }
2463
+ }
2464
+ };
2465
+ checkFlip();
2466
+ const ro = new ResizeObserver(checkFlip);
2467
+ ro.observe(popupRef.current);
2468
+ return () => ro.disconnect();
2469
+ }, [open, pos]);
2470
+ (0, import_react6.useEffect)(() => {
2471
+ if (!open) return;
2472
+ const onKey = (e) => {
2473
+ if (e.key === "Escape") setOpen(false);
2474
+ };
2475
+ const onPointerDown = (e) => {
2476
+ const t = e.target;
2477
+ if (popupRef.current?.contains(t)) return;
2478
+ if (buttonRef.current?.contains(t)) return;
2479
+ setOpen(false);
2480
+ };
2481
+ document.addEventListener("keydown", onKey);
2482
+ document.addEventListener("pointerdown", onPointerDown, true);
2483
+ return () => {
2484
+ document.removeEventListener("keydown", onKey);
2485
+ document.removeEventListener("pointerdown", onPointerDown, true);
2486
+ };
2487
+ }, [open]);
2488
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2489
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2490
+ "span",
2491
+ {
2492
+ className: "dsh-a6-side-btn-wrap",
2493
+ "data-tooltip": !canOpen ? "\u5F53\u524D\u4F1A\u8BDD\u672A\u4F7F\u7528 A6api \u6A21\u578B" : void 0,
2494
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2495
+ "button",
2496
+ {
2497
+ ref: buttonRef,
2498
+ type: "button",
2499
+ className: `dsh-a6-side-btn${wide ? "" : " rail"}`,
2500
+ onClick: toggle,
2501
+ disabled: !canOpen,
2502
+ "aria-expanded": open,
2503
+ "aria-label": wide ? void 0 : "A6api",
2504
+ "data-tooltip": canOpen ? open ? "\u6536\u8D77 A6api \u6A21\u578B\u5361\u7247" : "\u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u7684 A6api \u6A21\u578B\u5361\u7247" : void 0,
2505
+ children: [
2506
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2507
+ "svg",
2508
+ {
2509
+ className: "dsh-a6-side-btn-badge",
2510
+ width: "16",
2511
+ height: "16",
2512
+ viewBox: "0 0 16 16",
2513
+ fill: "none",
2514
+ xmlns: "http://www.w3.org/2000/svg",
2515
+ "aria-hidden": "true",
2516
+ children: [
2517
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2518
+ "path",
2519
+ {
2520
+ d: "M14.0861 5.51366C13.8717 5.0575 13.588 4.58542 13.2889 4.18108C13.208 4.07172 13.1596 4.04373 13.0243 4.03054C12.4277 3.97255 11.8245 4.05527 11.2269 3.9972C10.7224 3.94816 10.3133 3.71661 10.0115 3.30919C9.66986 2.84777 9.43973 2.31343 9.09824 1.85234C9.01771 1.74365 8.96805 1.71589 8.83354 1.70282C8.29432 1.65044 7.70402 1.65061 7.16656 1.70282C7.03205 1.71589 6.98239 1.74365 6.90186 1.85234C6.56067 2.31303 6.33025 2.84774 5.98855 3.30919C5.68681 3.71661 5.27774 3.94816 4.77317 3.9972C4.17564 4.05527 3.57239 3.97255 2.97585 4.03054C2.84046 4.04373 2.79208 4.07172 2.71115 4.18108C2.41212 4.58542 2.12835 5.0575 1.91403 5.51366C1.85299 5.64359 1.85286 5.7018 1.91403 5.8319C2.14865 6.33077 2.49748 6.76892 2.73237 7.26854C2.9594 7.7515 2.96041 8.24717 2.73338 8.73044C2.49837 9.23061 2.14891 9.66837 1.91403 10.1681C1.85291 10.2982 1.85299 10.3564 1.91403 10.4863C2.12856 10.9429 2.41185 11.4142 2.71115 11.8189C2.79208 11.9283 2.84046 11.9563 2.97585 11.9694C3.57239 12.0274 4.17564 11.9447 4.77317 12.0028C5.27774 12.0518 5.68681 12.2834 5.98855 12.6908C6.33024 13.1522 6.56037 13.6866 6.90186 14.1476C6.98239 14.2563 7.03205 14.2841 7.16656 14.2972C7.70402 14.3494 8.29432 14.3495 8.83354 14.2972C8.96805 14.2841 9.01771 14.2563 9.09824 14.1476C9.43944 13.687 9.66985 13.1522 10.0115 12.6908C10.3133 12.2834 10.7224 12.0518 11.2269 12.0028C11.8244 11.9447 12.4271 12.0275 13.0243 11.9694C13.1596 11.9563 13.208 11.9283 13.2889 11.8189C13.5891 11.4131 13.872 10.942 14.0861 10.4863C14.1471 10.3564 14.1472 10.2982 14.0861 10.1681C13.8513 9.66861 13.5017 9.23061 13.2667 8.73044C13.0397 8.24717 13.0407 7.7515 13.2677 7.26854C13.5026 6.7689 13.8513 6.33106 14.0861 5.8319C14.1472 5.7018 14.1471 5.64359 14.0861 5.51366ZM15.3035 6.40373C15.0685 6.90359 14.7188 7.34119 14.4841 7.84037C14.4231 7.97025 14.423 8.02855 14.4841 8.15861C14.7189 8.65833 15.0685 9.09611 15.3035 9.59626C15.5308 10.0801 15.5308 10.5744 15.3035 11.0582C15.052 11.5933 14.7225 12.1426 14.37 12.6191C14.0685 13.0265 13.6581 13.259 13.1536 13.3081C12.5566 13.366 11.9541 13.2835 11.3573 13.3414C11.2228 13.3545 11.1731 13.3823 11.0926 13.491C10.7511 13.9521 10.521 14.4864 10.1793 14.9478C9.87828 15.3542 9.46719 15.5869 8.96387 15.6358C8.34008 15.6964 7.66194 15.6966 7.03623 15.6358C6.53291 15.5869 6.12182 15.3542 5.82084 14.9478C5.47911 14.4863 5.24878 13.9517 4.90753 13.491C4.82701 13.3823 4.77734 13.3545 4.64284 13.3414C4.04647 13.2835 3.44373 13.366 2.84653 13.3081C2.34201 13.259 1.93164 13.0265 1.63013 12.6191C1.27867 12.144 0.948453 11.5941 0.696621 11.0582C0.469315 10.5744 0.469279 10.0801 0.696621 9.59626C0.931628 9.09613 1.2813 8.65807 1.51597 8.15861C1.57708 8.02855 1.57702 7.97025 1.51597 7.84037C1.28117 7.34095 0.931635 6.9036 0.696621 6.40373C0.469213 5.91992 0.469367 5.42562 0.696621 4.94183C0.948441 4.40587 1.27868 3.85598 1.63013 3.38092C1.93164 2.97349 2.34201 2.74095 2.84653 2.6919C3.44353 2.63397 4.04599 2.71649 4.64284 2.65856C4.77734 2.64549 4.82701 2.61774 4.90753 2.50904C5.24905 2.04792 5.47913 1.51362 5.82084 1.05219C6.12182 0.645806 6.53291 0.413119 7.03623 0.364178C7.66002 0.303556 8.33816 0.303369 8.96387 0.364178C9.46719 0.413119 9.87828 0.645806 10.1793 1.05219C10.521 1.51365 10.7513 2.04828 11.0926 2.50904C11.1731 2.61774 11.2228 2.64549 11.3573 2.65856C11.9541 2.71649 12.5566 2.63397 13.1536 2.6919C13.6581 2.74095 14.0685 2.97349 14.37 3.38092C14.7214 3.85598 15.0517 4.40587 15.3035 4.94183C15.5307 5.42562 15.5309 5.91992 15.3035 6.40373Z",
2521
+ fill: "currentColor"
2522
+ }
2523
+ ),
2524
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2525
+ "path",
2526
+ {
2527
+ d: "M9.13764 7.99999C9.13764 7.3715 8.62855 6.8624 8.00005 6.8624C7.37155 6.8624 6.86246 7.3715 6.86246 7.99999C6.86246 8.62849 7.37155 9.13759 8.00005 9.13759C8.62855 9.13759 9.13764 8.62849 9.13764 7.99999ZM10.4834 7.99999C10.4834 9.37126 9.37132 10.4833 8.00005 10.4833C6.62878 10.4833 5.51674 9.37126 5.51674 7.99999C5.51674 6.62873 6.62878 5.51669 8.00005 5.51669C9.37132 5.51669 10.4834 6.62873 10.4834 7.99999Z",
2528
+ fill: "currentColor"
2529
+ }
2530
+ )
2531
+ ]
2532
+ }
2533
+ ),
2534
+ wide && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-a6-side-btn-label", children: "A6api" })
2535
+ ]
2536
+ }
2537
+ )
2538
+ }
2539
+ ),
2540
+ open && pos && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2541
+ "div",
2542
+ {
2543
+ ref: popupRef,
2544
+ className: "dsh-a6-side-popup",
2545
+ role: "dialog",
2546
+ "aria-label": "\u5F53\u524D\u4F1A\u8BDD A6api \u6A21\u578B\u5361\u7247",
2547
+ style: { left: pos.left, ...pos.top !== void 0 ? { top: pos.top } : { bottom: pos.bottom } },
2548
+ children: [
2549
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-a6-side-pills", children: [
2550
+ state.balance?.hasAccountAuth && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2551
+ "div",
2552
+ {
2553
+ className: "dsh-a6-header-balance-badge dsh-a6-side-balance-pill",
2554
+ title: "\u8D26\u6237\u4F59\u989D\uFF08\u6BCF 60 \u79D2\u81EA\u52A8\u540C\u6B65\uFF09",
2555
+ children: [
2556
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-a6-hb-label", children: "\u8D26\u6237\u4F59\u989D:" }),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-a6-hb-amount", children: state.balance.accountBalanceFormatted })
2558
+ ]
2559
+ }
2560
+ ),
2561
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2562
+ PricePill,
2563
+ {
2564
+ pf: state.priceFluctuation,
2565
+ hasToken: Boolean(state.config?.hasToken),
2566
+ compact: true
2567
+ }
2568
+ )
2569
+ ] }),
2570
+ card ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MerchantCard, { model: card }) : state.loading && state.models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-a6-side-popup-empty", children: [
2571
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-a6-spinner" }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "\u6B63\u5728\u52A0\u8F7D A6api \u6570\u636E..." })
2573
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-a6-side-popup-empty", children: [
2574
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
2575
+ "\u672A\u627E\u5230\u300C",
2576
+ modelName,
2577
+ "\u300D\u7684\u5546\u6237\u6570\u636E"
2578
+ ] }),
2579
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-a6-side-popup-hint", children: "\u53EF\u5728\u300C\u8BBE\u7F6E \u2192 A6api\u300D\u4E2D\u63A2\u6D4B\u8BE5\u6A21\u578B" })
2580
+ ] })
2581
+ ]
2582
+ }
2583
+ )
2584
+ ] });
2585
+ };
2586
+
2587
+ // src/client/styles/main.css
2588
+ var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme Integration */
2589
+
2590
+ .dsh-a6-container {
2591
+ display: flex;
2592
+ flex-direction: column;
2593
+ color: var(--dsw-alias-label-primary, var(--ds-text-primary, #1e293b));
2594
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
2595
+ padding-bottom: 28px;
2596
+ }
2597
+
2598
+ /* ==========================================================================
2599
+ 1. Master Header & Navigation Tabs (Image 2 Style)
2600
+ ========================================================================== */
2601
+ .dsh-a6-main-header {
2602
+ display: flex;
2603
+ justify-content: space-between;
2604
+ align-items: flex-start;
2605
+ gap: 16px;
2606
+ margin-bottom: 12px;
2607
+ flex-wrap: wrap;
2608
+ }
2609
+
2610
+ .dsh-a6-header-text {
2611
+ display: flex;
2612
+ flex-direction: column;
2613
+ gap: 4px;
2614
+ }
2615
+
2616
+ .dsh-a6-main-title {
2617
+ font-size: 20px;
2618
+ font-weight: 700;
2619
+ color: var(--dsw-alias-label-primary, #0f172a);
2620
+ margin: 0;
2621
+ line-height: 1.2;
2622
+ }
2623
+
2624
+ .dsh-a6-main-subtitle {
2625
+ font-size: 13px;
2626
+ color: var(--dsw-alias-label-secondary, #64748b);
2627
+ margin: 0;
2628
+ line-height: 1.5;
2629
+ }
2630
+
2631
+ .dsh-a6-header-balance-badge {
2632
+ display: inline-flex;
2633
+ align-items: center;
2634
+ gap: 6px;
2635
+ padding: 5px 12px;
1249
2636
  background: var(--dsw-alias-bg-layer-2, #ffffff);
1250
2637
  border: 1px solid var(--dsw-alias-border-l2, #e2e8f0);
1251
2638
  border-radius: 16px;
@@ -1794,12 +3181,22 @@ var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme
1794
3181
  color: #059669;
1795
3182
  }
1796
3183
 
1797
- /* Right Actions Group */
3184
+ /* Right Actions Group \u2014 \u9ED8\u8BA4\u9760\u53F3\uFF0C\u7A84\u5C4F\u65F6\u5360\u6EE1\u884C\u5BBD\u4E24\u7AEF\u5BF9\u9F50 */
1798
3185
  .dsh-a6-bar-actions {
1799
3186
  display: flex;
1800
3187
  align-items: center;
1801
- gap: 8px;
1802
- flex-shrink: 0;
3188
+ gap: 12px;
3189
+ flex: 0 0 auto;
3190
+ margin-left: auto;
3191
+ }
3192
+
3193
+ @media (max-width: 900px) {
3194
+ .dsh-a6-bar-actions {
3195
+ flex: 1 1 100%;
3196
+ width: 100%;
3197
+ margin-left: 0;
3198
+ justify-content: space-between;
3199
+ }
1803
3200
  }
1804
3201
 
1805
3202
  .dsh-a6-time-ago {
@@ -1807,6 +3204,39 @@ var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme
1807
3204
  color: var(--dsw-alias-label-tertiary, #94a3b8);
1808
3205
  }
1809
3206
 
3207
+ /* \u4E24\u884C\u65F6\u95F4\u6233\u5782\u76F4\u53E0\u653E (\u5168\u7F51\u6700\u8FD1 / \u4E2A\u4EBA\u6700\u8FD1) \u2014 \u5DE6\u5BF9\u9F50 */
3208
+ .dsh-a6-time-stack {
3209
+ display: flex;
3210
+ flex-direction: column;
3211
+ align-items: flex-start;
3212
+ gap: 2px;
3213
+ flex-shrink: 0;
3214
+ text-align: left;
3215
+ }
3216
+
3217
+ /* \u5361\u7247\u5E95\u90E8\u884C\uFF1A\u65F6\u95F4\u6233\u5DE6\u4E0B\u89D2\u5DE6\u5BF9\u9F50\uFF0C\u64CD\u4F5C\u6309\u94AE\u53F3\u4E0B\u89D2\u53F3\u5BF9\u9F50 */
3218
+ .dsh-a6-card-footer {
3219
+ display: flex;
3220
+ align-items: flex-start;
3221
+ justify-content: space-between;
3222
+ gap: 12px;
3223
+ padding-top: 2px;
3224
+ flex-wrap: wrap;
3225
+ }
3226
+
3227
+ /* \u53F3\u4FA7\u4E09\u6309\u94AE\u7EC4 */
3228
+ .dsh-a6-bar-actions-btns {
3229
+ display: flex;
3230
+ align-items: center;
3231
+ gap: 8px;
3232
+ flex-shrink: 0;
3233
+ flex-wrap: wrap;
3234
+ }
3235
+
3236
+ .dsh-a6-route-snapshot.never {
3237
+ color: var(--dsw-alias-label-quaternary, #cbd5e1);
3238
+ }
3239
+
1810
3240
  .dsh-a6-btn-in-dsh {
1811
3241
  background: rgba(16, 185, 129, 0.12);
1812
3242
  border: 1px solid rgba(16, 185, 129, 0.35);
@@ -2564,19 +3994,24 @@ var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme
2564
3994
  bottom: calc(100% + 7px);
2565
3995
  left: 50%;
2566
3996
  transform: translateX(-50%);
2567
- padding: 5px 9px;
2568
- background: rgba(15, 23, 42, 0.94);
3997
+ padding: 6px 10px;
3998
+ background: rgba(15, 23, 42, 0.96);
2569
3999
  color: #f8fafc;
2570
4000
  font-size: 11px;
2571
4001
  font-weight: 400;
2572
- line-height: 1.35;
2573
- white-space: nowrap;
2574
- border-radius: 5px;
2575
- box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
4002
+ line-height: 1.4;
4003
+ white-space: normal;
4004
+ max-width: min(320px, 85vw);
4005
+ width: max-content;
4006
+ text-align: left;
4007
+ word-break: break-word;
4008
+ overflow-wrap: anywhere;
4009
+ border-radius: 6px;
4010
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
2576
4011
  pointer-events: none;
2577
4012
  opacity: 0;
2578
4013
  visibility: hidden;
2579
- transition: opacity 0.04s ease-out, visibility 0.04s ease-out;
4014
+ transition: opacity 0.08s ease-out, visibility 0.08s ease-out;
2580
4015
  z-index: 99999;
2581
4016
  border: 1px solid rgba(255, 255, 255, 0.12);
2582
4017
  }
@@ -2682,15 +4117,513 @@ var main_default = `/* A6API Plugin Styles - Clean Professional DSH Native Theme
2682
4117
  border-color: transparent transparent rgba(15, 23, 42, 0.94) transparent;
2683
4118
  }
2684
4119
 
2685
- /* Card actions tooltip default alignment */
4120
+ /* Card actions tooltip: keep centered (avoid left:0 right-edge overflow) */
2686
4121
  .dsh-a6-bar-actions [data-tooltip]:not([data-tooltip-pos])::after {
2687
- left: 0;
2688
- transform: none;
4122
+ left: 50%;
4123
+ transform: translateX(-50%);
4124
+ right: auto;
2689
4125
  }
2690
4126
 
2691
4127
  .dsh-a6-bar-actions [data-tooltip]:not([data-tooltip-pos])::before {
2692
- left: 16px;
2693
- transform: none;
4128
+ left: 50%;
4129
+ transform: translateX(-50%);
4130
+ right: auto;
4131
+ }
4132
+
4133
+ /* When JS portal tooltip is active, hide pseudo to avoid double rendering */
4134
+ body.dsh-a6api-tooltip-active [data-tooltip]::after,
4135
+ body.dsh-a6api-tooltip-active [data-tooltip]::before {
4136
+ opacity: 0 !important;
4137
+ visibility: hidden !important;
4138
+ }
4139
+
4140
+ /* ==========================================================================
4141
+ 8. \u56FA\u5B9A / \u7981\u7528 \u72B6\u6001\u5FBD\u6807\u3001\u64CD\u4F5C\u6309\u94AE\u4E0E\u56FA\u5B9A\u786E\u8BA4\u5F39\u7A97
4142
+ ========================================================================== */
4143
+ .dsh-a6-pin-badge {
4144
+ display: inline-flex;
4145
+ align-items: center;
4146
+ height: 18px;
4147
+ padding: 0 6px;
4148
+ margin-left: 6px;
4149
+ border-radius: 9px;
4150
+ font-size: 10px;
4151
+ font-weight: 600;
4152
+ line-height: 1;
4153
+ vertical-align: middle;
4154
+ white-space: nowrap;
4155
+ }
4156
+
4157
+ .dsh-a6-pin-badge.here {
4158
+ background: rgba(16, 185, 129, 0.12);
4159
+ color: #059669;
4160
+ border: 1px solid rgba(16, 185, 129, 0.35);
4161
+ }
4162
+
4163
+ .dsh-a6-pin-badge.elsewhere {
4164
+ background: rgba(245, 158, 11, 0.12);
4165
+ color: #d97706;
4166
+ border: 1px solid rgba(245, 158, 11, 0.35);
4167
+ }
4168
+
4169
+ .dsh-a6-pin-badge.disabled {
4170
+ background: rgba(100, 116, 139, 0.14);
4171
+ color: #64748b;
4172
+ border: 1px solid rgba(100, 116, 139, 0.3);
4173
+ }
4174
+
4175
+ .dsh-a6-btn-danger {
4176
+ background: #ffffff;
4177
+ border-color: #ef4444;
4178
+ color: #ef4444;
4179
+ }
4180
+
4181
+ .dsh-a6-btn-danger:hover {
4182
+ background: #fef2f2;
4183
+ border-color: #dc2626;
4184
+ color: #dc2626;
4185
+ }
4186
+
4187
+ .dsh-a6-btn:disabled {
4188
+ opacity: 0.45;
4189
+ cursor: not-allowed;
4190
+ }
4191
+
4192
+ .dsh-a6-action-error {
4193
+ /* footer \u5185\u72EC\u7ACB\u6574\u884C\u5C55\u793A\uFF08footer \u4E3A flex-wrap\uFF09\uFF0C\u4E0D\u4E0E\u6309\u94AE\u5E76\u6392\u6491\u5BBD */
4194
+ flex-basis: 100%;
4195
+ margin-top: 6px;
4196
+ padding: 4px 8px;
4197
+ border-radius: 6px;
4198
+ background: rgba(239, 68, 68, 0.08);
4199
+ border: 1px solid rgba(239, 68, 68, 0.25);
4200
+ color: #dc2626;
4201
+ font-size: 11px;
4202
+ line-height: 1.5;
4203
+ }
4204
+
4205
+ /* \u56FA\u5B9A\u786E\u8BA4\u5F39\u7A97 */
4206
+ .dsh-a6-pin-modal-overlay {
4207
+ position: fixed;
4208
+ inset: 0;
4209
+ z-index: 2147483000;
4210
+ background: rgba(15, 23, 42, 0.45);
4211
+ display: flex;
4212
+ align-items: center;
4213
+ justify-content: center;
4214
+ padding: 16px;
4215
+ }
4216
+
4217
+ .dsh-a6-pin-modal {
4218
+ width: min(420px, 92vw);
4219
+ background: var(--dsw-alias-bg-layer-2, #ffffff);
4220
+ border: 1px solid var(--dsw-alias-border-l3, #cbd5e1);
4221
+ border-radius: 10px;
4222
+ box-shadow: 0 12px 32px rgba(15, 23, 42, 0.25);
4223
+ overflow: hidden;
4224
+ }
4225
+
4226
+ .dsh-a6-pin-modal-title {
4227
+ padding: 12px 16px;
4228
+ font-size: 14px;
4229
+ font-weight: 600;
4230
+ color: var(--dsw-alias-label-primary, #1e293b);
4231
+ border-bottom: 1px solid var(--dsw-alias-border-l3, #e2e8f0);
4232
+ background: var(--dsw-alias-bg-layer-1, #f8fafc);
4233
+ }
4234
+
4235
+ .dsh-a6-pin-modal-body {
4236
+ padding: 14px 16px;
4237
+ display: flex;
4238
+ flex-direction: column;
4239
+ gap: 8px;
4240
+ }
4241
+
4242
+ .dsh-a6-pin-modal-row {
4243
+ display: flex;
4244
+ align-items: baseline;
4245
+ gap: 10px;
4246
+ font-size: 12px;
4247
+ }
4248
+
4249
+ .dsh-a6-pin-modal-label {
4250
+ flex: 0 0 48px;
4251
+ color: var(--dsw-alias-label-secondary, #64748b);
4252
+ }
4253
+
4254
+ .dsh-a6-pin-modal-value {
4255
+ color: var(--dsw-alias-label-primary, #1e293b);
4256
+ font-weight: 500;
4257
+ word-break: break-all;
4258
+ }
4259
+
4260
+ .dsh-a6-pin-modal-note {
4261
+ margin: 4px 0 0;
4262
+ font-size: 11px;
4263
+ line-height: 1.6;
4264
+ color: var(--dsw-alias-label-secondary, #64748b);
4265
+ background: rgba(59, 130, 246, 0.06);
4266
+ border: 1px solid rgba(59, 130, 246, 0.15);
4267
+ border-radius: 6px;
4268
+ padding: 6px 8px;
4269
+ }
4270
+
4271
+ .dsh-a6-pin-modal-foot {
4272
+ display: flex;
4273
+ justify-content: flex-end;
4274
+ gap: 8px;
4275
+ padding: 10px 16px;
4276
+ border-top: 1px solid var(--dsw-alias-border-l3, #e2e8f0);
4277
+ background: var(--dsw-alias-bg-layer-1, #f8fafc);
4278
+ }
4279
+
4280
+ /* ==========================================================================
4281
+ Sidebar Footer Action: \u5F53\u524D\u4F1A\u8BDD A6api \u6A21\u578B\u5361\u7247\u5165\u53E3(\u5DE6\u4E0B\u89D2\u8BBE\u7F6E\u6309\u94AE\u4E0A\u65B9)
4282
+ ========================================================================== */
4283
+ .dsh-a6-side-btn-wrap {
4284
+ display: flex;
4285
+ min-width: 0;
4286
+ }
4287
+
4288
+ /* \u539F\u751F\u98CE\u683C:\u5BF9\u9F50\u8BBE\u7F6E\u6309\u94AE(42px \u900F\u660E\u5E7D\u7075\u6309\u94AE,\u65E0\u8FB9\u6846\u65E0\u5E95\u8272,label-primary \u6587\u5B57,
4289
+ hover \u7528 shell \u4EA4\u4E92\u5E95\u8272;rail \u6298\u53E0\u6001 36px \u5706) */
4290
+ .dsh-a6-side-btn {
4291
+ box-sizing: border-box;
4292
+ display: inline-flex;
4293
+ align-items: center;
4294
+ gap: 8px;
4295
+ width: calc(100% + 4px);
4296
+ min-width: 0;
4297
+ height: 42px;
4298
+ margin: 4px -2px;
4299
+ padding: 0 10px 0 8px;
4300
+ border: none;
4301
+ border-radius: 12px;
4302
+ background: transparent;
4303
+ color: var(--dsw-alias-label-primary, #0f172a);
4304
+ cursor: pointer;
4305
+ font-family: inherit;
4306
+ font-size: 14px;
4307
+ font-weight: 500;
4308
+ line-height: 22px;
4309
+ transition: background 0.15s;
4310
+ }
4311
+
4312
+ .dsh-a6-side-btn:hover:not(:disabled) {
4313
+ background: var(--dsw-alias-interactive-bg-hover, rgba(148, 163, 184, 0.16));
4314
+ }
4315
+
4316
+ .dsh-a6-side-btn:disabled {
4317
+ cursor: default;
4318
+ opacity: 0.4;
4319
+ }
4320
+
4321
+ /* rail(\u4FA7\u8FB9\u680F\u6298\u53E0)\u6001:36px \u5706\u5F62\u5355\u8272\u56FE\u6807,\u4E0E\u8BBE\u7F6E\u6309\u94AE\u4E00\u81F4 */
4322
+ .dsh-a6-side-btn.rail {
4323
+ width: 36px;
4324
+ height: 36px;
4325
+ margin: 0;
4326
+ padding: 0;
4327
+ justify-content: center;
4328
+ gap: 0;
4329
+ border-radius: 50%;
4330
+ }
4331
+
4332
+ .dsh-a6-side-btn.rail:hover:not(:disabled) {
4333
+ background: var(--dsw-alias-interactive-bg-hover, rgba(148, 163, 184, 0.16));
4334
+ }
4335
+
4336
+ /* \u9F7F\u8F6E\u56FE\u6807:\u4E0E\u8BBE\u7F6E\u6309\u94AE\u540C\u6B3E(currentColor \u8DDF\u968F\u6587\u5B57\u8272;rail \u6001 18px \u4E0E\u8BBE\u7F6E\u4E00\u81F4) */
4337
+ .dsh-a6-side-btn-badge {
4338
+ flex: none;
4339
+ width: 16px;
4340
+ height: 16px;
4341
+ }
4342
+
4343
+ .dsh-a6-side-btn.rail .dsh-a6-side-btn-badge {
4344
+ width: 18px;
4345
+ height: 18px;
4346
+ }
4347
+
4348
+ .dsh-a6-side-btn-label {
4349
+ overflow: hidden;
4350
+ text-overflow: ellipsis;
4351
+ white-space: nowrap;
4352
+ }
4353
+
4354
+ /* \u5F39\u51FA\u5361\u7247\u6D6E\u5C42:\u951A\u5B9A\u6309\u94AE,\u5411\u4E0A\u5C55\u5F00 */
4355
+ .dsh-a6-side-popup {
4356
+ position: fixed;
4357
+ z-index: 2147483000;
4358
+ width: min(500px, calc(100vw - 16px));
4359
+ max-height: calc(100vh - 16px);
4360
+ overflow-y: auto;
4361
+ overscroll-behavior: contain;
4362
+ background: var(--dsw-alias-bg-layer-1, #ffffff);
4363
+ border: 1px solid var(--dsw-alias-border-l1, #e2e8f0);
4364
+ border-radius: 12px;
4365
+ box-shadow: 0 12px 36px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.06);
4366
+ padding: 10px;
4367
+ animation: dsh-a6-popup-up 0.18s ease-out;
4368
+ }
4369
+
4370
+ @keyframes dsh-a6-popup-up {
4371
+ from {
4372
+ opacity: 0;
4373
+ transform: translateY(10px);
4374
+ }
4375
+ to {
4376
+ opacity: 1;
4377
+ transform: none;
4378
+ }
4379
+ }
4380
+
4381
+ .dsh-a6-side-popup-empty {
4382
+ display: flex;
4383
+ flex-direction: column;
4384
+ align-items: center;
4385
+ justify-content: center;
4386
+ gap: 10px;
4387
+ padding: 28px 16px;
4388
+ color: var(--dsw-alias-label-secondary, #64748b);
4389
+ font-size: 13px;
4390
+ }
4391
+
4392
+ .dsh-a6-side-popup-hint {
4393
+ font-size: 12px;
4394
+ color: var(--dsw-alias-label-tertiary, #94a3b8);
4395
+ }
4396
+
4397
+ /* \u6D6E\u5C42\u5185\u7981\u7528 CSS \u4F2A\u5143\u7D20 tooltip:JS portal tooltip \u6709 30ms \u5EF6\u8FDF,\u907F\u514D\u53CC\u63D0\u793A\u4E14\u4F2A\u5143\u7D20\u4F1A\u88AB overflow \u88C1\u526A */
4398
+ .dsh-a6-side-popup [data-tooltip]::after,
4399
+ .dsh-a6-side-popup [data-tooltip]::before {
4400
+ display: none;
4401
+ }
4402
+
4403
+ /* \u6D6E\u5C42\u9876\u90E8\u8D26\u6237\u901F\u89C8\u80F6\u56CA\u884C:\u4E0E\u8BBE\u7F6E\u9875\u5934\u90E8\u540C\u6B3E\u89C6\u89C9,\u7F29\u7A84\u7D27\u51D1\u7248(\u4F59\u989D\u7EAF\u5C55\u793A + \u4EF7\u683C\u6CE2\u52A8\u53EF\u8DF3) */
4404
+ .dsh-a6-side-pills {
4405
+ display: flex;
4406
+ align-items: center;
4407
+ gap: 8px;
4408
+ flex-wrap: wrap;
4409
+ padding: 2px 2px 10px;
4410
+ margin-bottom: 10px;
4411
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #e2e8f0);
4412
+ }
4413
+
4414
+ .dsh-a6-side-pills .dsh-a6-header-balance-badge,
4415
+ .dsh-a6-side-pills .dsh-a6-price-pill {
4416
+ padding: 3px 10px;
4417
+ min-height: 24px;
4418
+ font-size: 11px;
4419
+ /* \u4F59\u989D\u80F6\u56CA\u57FA\u7C7B\u65E0 border-box/line-height\uFF0C\u8865\u4E0A\u4E0E\u4EF7\u683C\u80F6\u56CA\u4E00\u81F4\uFF0C\u4FDD\u8BC1\u4E24\u679A\u80F6\u56CA\u7B49\u9AD8 */
4420
+ box-sizing: border-box;
4421
+ line-height: 1;
4422
+ }
4423
+
4424
+ .dsh-a6-side-pills .dsh-a6-price-pill-count,
4425
+ .dsh-a6-side-pills .dsh-a6-price-pill-label,
4426
+ .dsh-a6-side-pills .dsh-a6-hb-amount,
4427
+ .dsh-a6-side-pills .dsh-a6-hb-label {
4428
+ font-size: 12px;
4429
+ line-height: 1;
4430
+ }
4431
+
4432
+ /* \u6D6E\u5C42\u5185\u4F59\u989D\u80F6\u56CA\u4E3A\u7EAF\u5C55\u793A:\u7981\u6389\u8BBE\u7F6E\u9875\u7684 hover \u53CD\u9988\u4E0E\u624B\u578B(\u9700\u5199\u5728\u901A\u7528 hover \u89C4\u5219\u4E4B\u540E) */
4433
+ .dsh-a6-side-balance-pill,
4434
+ .dsh-a6-side-balance-pill:hover {
4435
+ cursor: default;
4436
+ border-color: var(--dsw-alias-border-l2, #e2e8f0);
4437
+ background: var(--dsw-alias-bg-layer-2, #ffffff);
4438
+ }
4439
+
4440
+
4441
+ /* ===== \u6A21\u578B\u76EE\u5F55\u9875 ===== */
4442
+ .dsh-a6-catalog-page {
4443
+ display: flex;
4444
+ flex-direction: column;
4445
+ gap: 12px;
4446
+ }
4447
+
4448
+ .dsh-a6-catalog-toolbar {
4449
+ display: inline-flex;
4450
+ align-items: center;
4451
+ gap: 8px;
4452
+ flex-wrap: wrap;
4453
+ }
4454
+
4455
+ .dsh-a6-catalog-count {
4456
+ font-size: 12px;
4457
+ color: var(--dsw-alias-label-tertiary, #94a3b8);
4458
+ margin-left: 4px;
4459
+ }
4460
+
4461
+ .dsh-a6-catalog-msg {
4462
+ padding: 8px 12px;
4463
+ border-radius: 8px;
4464
+ font-size: 12px;
4465
+ line-height: 1.5;
4466
+ }
4467
+ .dsh-a6-catalog-msg.ok {
4468
+ background: rgba(16, 185, 129, 0.08);
4469
+ border: 1px solid rgba(16, 185, 129, 0.3);
4470
+ color: #047857;
4471
+ }
4472
+ .dsh-a6-catalog-msg.err {
4473
+ background: rgba(239, 68, 68, 0.08);
4474
+ border: 1px solid rgba(239, 68, 68, 0.3);
4475
+ color: #b91c1c;
4476
+ }
4477
+
4478
+ .dsh-a6-catalog-list {
4479
+ display: flex;
4480
+ flex-direction: column;
4481
+ gap: 8px;
4482
+ }
4483
+
4484
+ .dsh-a6-catalog-row {
4485
+ background: var(--dsw-alias-bg-layer-1, #ffffff);
4486
+ border: 1px solid var(--dsw-alias-border-l2, #e2e8f0);
4487
+ border-radius: 10px;
4488
+ padding: 10px 12px;
4489
+ display: flex;
4490
+ flex-direction: column;
4491
+ gap: 8px;
4492
+ }
4493
+ .dsh-a6-catalog-row.editing {
4494
+ border-color: #10b981;
4495
+ }
4496
+
4497
+ /* \u7B2C\u4E00\u884C\uFF1A\u6A21\u578B ID + \u53EF\u7528\u5FBD\u7AE0 + \u540D\u79F0 + \u64CD\u4F5C\uFF08\u53C2\u6570\u7EDF\u4E00\u5728 ID \u4E0B\u65B9\uFF09 */
4498
+ .dsh-a6-catalog-row-head {
4499
+ display: flex;
4500
+ align-items: center;
4501
+ justify-content: space-between;
4502
+ gap: 12px;
4503
+ flex-wrap: wrap;
4504
+ }
4505
+
4506
+ .dsh-a6-catalog-badge {
4507
+ font-size: 10px;
4508
+ font-weight: 600;
4509
+ padding: 1px 7px;
4510
+ border-radius: 9px;
4511
+ vertical-align: middle;
4512
+ white-space: nowrap;
4513
+ }
4514
+ .dsh-a6-catalog-badge.avail {
4515
+ background: rgba(16, 185, 129, 0.12);
4516
+ border: 1px solid rgba(16, 185, 129, 0.35);
4517
+ color: #047857;
4518
+ }
4519
+
4520
+ .dsh-a6-catalog-filters {
4521
+ display: flex;
4522
+ align-items: center;
4523
+ gap: 10px;
4524
+ flex-wrap: wrap;
4525
+ margin-top: 4px;
4526
+ }
4527
+
4528
+ .dsh-a6-catalog-id {
4529
+ display: inline-flex;
4530
+ align-items: baseline;
4531
+ gap: 8px;
4532
+ min-width: 0;
4533
+ }
4534
+ .dsh-a6-catalog-id code {
4535
+ font-size: 13px;
4536
+ font-weight: 600;
4537
+ color: var(--dsw-alias-label-primary, #0f172a);
4538
+ }
4539
+ .dsh-a6-catalog-name {
4540
+ font-size: 12px;
4541
+ color: var(--dsw-alias-label-secondary, #64748b);
4542
+ overflow: hidden;
4543
+ text-overflow: ellipsis;
4544
+ white-space: nowrap;
4545
+ }
4546
+
4547
+ .dsh-a6-catalog-meta {
4548
+ display: inline-flex;
4549
+ align-items: center;
4550
+ gap: 6px;
4551
+ flex-wrap: wrap;
4552
+ }
4553
+ .dsh-a6-catalog-param {
4554
+ font-size: 11px;
4555
+ padding: 2px 8px;
4556
+ border-radius: 10px;
4557
+ background: rgba(16, 185, 129, 0.08);
4558
+ border: 1px solid rgba(16, 185, 129, 0.25);
4559
+ color: #047857;
4560
+ white-space: nowrap;
4561
+ }
4562
+ .dsh-a6-catalog-param.empty {
4563
+ background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.04));
4564
+ border-color: var(--dsw-alias-border-l3, #cbd5e1);
4565
+ color: var(--dsw-alias-label-tertiary, #94a3b8);
4566
+ }
4567
+
4568
+ .dsh-a6-catalog-row-actions {
4569
+ display: inline-flex;
4570
+ align-items: center;
4571
+ gap: 12px;
4572
+ margin-left: auto;
4573
+ }
4574
+
4575
+ /* \u884C\u5185\u7F16\u8F91\u8868\u5355 */
4576
+ .dsh-a6-catalog-edit {
4577
+ border-top: 1px dashed var(--dsw-alias-border-l2, #e2e8f0);
4578
+ padding-top: 10px;
4579
+ display: flex;
4580
+ flex-direction: column;
4581
+ gap: 10px;
4582
+ }
4583
+
4584
+ .dsh-a6-edit-grid {
4585
+ display: grid;
4586
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
4587
+ gap: 10px;
4588
+ }
4589
+
4590
+ .dsh-a6-edit-field {
4591
+ display: flex;
4592
+ flex-direction: column;
4593
+ gap: 4px;
4594
+ min-width: 0;
4595
+ }
4596
+ .dsh-a6-edit-wide {
4597
+ grid-column: 1 / -1;
4598
+ }
4599
+
4600
+ .dsh-a6-checkbox-group {
4601
+ display: inline-flex;
4602
+ gap: 14px;
4603
+ align-items: center;
4604
+ padding: 6px 0;
4605
+ }
4606
+ .dsh-a6-checkbox {
4607
+ display: inline-flex;
4608
+ align-items: center;
4609
+ gap: 5px;
4610
+ font-size: 12px;
4611
+ color: var(--dsw-alias-label-primary, #0f172a);
4612
+ cursor: pointer;
4613
+ user-select: none;
4614
+ }
4615
+
4616
+ .dsh-a6-edit-actions {
4617
+ display: inline-flex;
4618
+ gap: 8px;
4619
+ justify-content: flex-end;
4620
+ }
4621
+
4622
+ /* \u6E05\u7A7A\u76EE\u5F55\u786E\u8BA4\u6001\uFF1A\u7EA2\u8272\u9AD8\u4EAE\u63D0\u793A\u4E8C\u6B21\u70B9\u51FB\u751F\u6548 */
4623
+ .dsh-a6-btn-clear-confirm {
4624
+ background: #ef4444 !important;
4625
+ border-color: #ef4444 !important;
4626
+ color: #ffffff !important;
2694
4627
  }
2695
4628
  `;
2696
4629
 
@@ -2707,11 +4640,201 @@ function injectStyles() {
2707
4640
  document.head.appendChild(style);
2708
4641
  }
2709
4642
  }
4643
+ function setupGlobalTooltip() {
4644
+ if (typeof document === "undefined" || typeof window === "undefined") return;
4645
+ if (window.__dsh_a6api_tooltip_setup) return () => {
4646
+ };
4647
+ window.__dsh_a6api_tooltip_setup = true;
4648
+ const portalId = "dsh-a6api-tooltip";
4649
+ const arrowId = "dsh-a6api-tooltip-arrow";
4650
+ let portal = document.getElementById(portalId);
4651
+ let arrow = document.getElementById(arrowId);
4652
+ if (!portal) {
4653
+ portal = document.createElement("div");
4654
+ portal.id = portalId;
4655
+ portal.setAttribute("role", "tooltip");
4656
+ portal.style.cssText = "position:fixed;left:0;top:0;transform:translate(-9999px,-9999px);padding:6px 10px;background:rgba(15,23,42,0.96);color:#f8fafc;font-size:11px;line-height:1.4;border-radius:6px;box-shadow:0 6px 18px rgba(0,0,0,0.28);border:1px solid rgba(255,255,255,0.12);max-width:min(320px,85vw);width:max-content;white-space:normal;word-break:break-word;overflow-wrap:anywhere;text-align:left;pointer-events:none;opacity:0;visibility:hidden;transition:opacity 0.08s;z-index:2147483647;";
4657
+ document.body.appendChild(portal);
4658
+ }
4659
+ if (!arrow) {
4660
+ arrow = document.createElement("div");
4661
+ arrow.id = arrowId;
4662
+ arrow.style.cssText = "position:fixed;left:0;top:0;width:8px;height:8px;background:rgba(15,23,42,0.96);transform:translate(-9999px,-9999px) rotate(45deg);border-left:1px solid rgba(255,255,255,0.12);border-top:1px solid rgba(255,255,255,0.12);pointer-events:none;opacity:0;visibility:hidden;z-index:2147483646;";
4663
+ document.body.appendChild(arrow);
4664
+ }
4665
+ let currentTarget = null;
4666
+ function hide() {
4667
+ if (!portal || !arrow) return;
4668
+ portal.style.opacity = "0";
4669
+ portal.style.visibility = "hidden";
4670
+ arrow.style.opacity = "0";
4671
+ arrow.style.visibility = "hidden";
4672
+ document.body.classList.remove("dsh-a6api-tooltip-active");
4673
+ currentTarget = null;
4674
+ }
4675
+ function position(target) {
4676
+ if (!portal || !arrow || !target) return;
4677
+ const text = target.getAttribute("data-tooltip");
4678
+ if (!text) return;
4679
+ portal.textContent = text;
4680
+ portal.style.visibility = "hidden";
4681
+ portal.style.opacity = "0";
4682
+ portal.style.transform = "translate(-9999px,-9999px)";
4683
+ portal.style.visibility = "visible";
4684
+ const ttRect = portal.getBoundingClientRect();
4685
+ portal.style.visibility = "hidden";
4686
+ const rect = target.getBoundingClientRect();
4687
+ const gap = 8;
4688
+ const vw = window.innerWidth;
4689
+ const vh = window.innerHeight;
4690
+ const margin = 8;
4691
+ let pos = target.getAttribute("data-tooltip-pos") || "";
4692
+ if (!pos) {
4693
+ const spaceAbove = rect.top;
4694
+ const spaceBelow = vh - rect.bottom;
4695
+ if (spaceAbove < ttRect.height + gap + 12 && spaceBelow > spaceAbove) pos = "down";
4696
+ else pos = "top";
4697
+ }
4698
+ let top = 0;
4699
+ let left = 0;
4700
+ let arrowTop = 0;
4701
+ let arrowLeft = 0;
4702
+ const isDown = pos.startsWith("down");
4703
+ if (isDown) {
4704
+ top = rect.bottom + gap;
4705
+ arrowTop = rect.bottom + gap - 4;
4706
+ } else {
4707
+ top = rect.top - ttRect.height - gap;
4708
+ arrowTop = rect.top - gap - 4;
4709
+ }
4710
+ if (pos === "left") {
4711
+ left = rect.right - ttRect.width;
4712
+ arrowLeft = rect.right - 14;
4713
+ } else if (pos === "right") {
4714
+ left = rect.left;
4715
+ arrowLeft = rect.left + 14;
4716
+ } else if (pos === "down-left") {
4717
+ left = rect.right - ttRect.width;
4718
+ arrowLeft = rect.right - 14;
4719
+ } else if (pos === "down-right") {
4720
+ left = rect.left;
4721
+ arrowLeft = rect.left + 14;
4722
+ } else {
4723
+ left = rect.left + rect.width / 2 - ttRect.width / 2;
4724
+ arrowLeft = rect.left + rect.width / 2 - 4;
4725
+ }
4726
+ left = Math.max(margin, Math.min(left, vw - ttRect.width - margin));
4727
+ const minArrow = left + 8;
4728
+ const maxArrow = left + ttRect.width - 12;
4729
+ arrowLeft = Math.max(minArrow, Math.min(arrowLeft, maxArrow));
4730
+ if (top < margin) top = margin;
4731
+ if (top + ttRect.height > vh - margin) top = vh - ttRect.height - margin;
4732
+ portal.style.transform = "translate(" + left + "px," + top + "px)";
4733
+ portal.style.visibility = "visible";
4734
+ portal.style.opacity = "1";
4735
+ arrow.style.transform = "translate(" + arrowLeft + "px," + arrowTop + "px) rotate(45deg)";
4736
+ arrow.style.visibility = "visible";
4737
+ arrow.style.opacity = "1";
4738
+ document.body.classList.add("dsh-a6api-tooltip-active");
4739
+ }
4740
+ let hoverTimer = null;
4741
+ const onMouseOver = (e) => {
4742
+ const target = e.target && e.target.closest ? e.target.closest("[data-tooltip]") : null;
4743
+ if (!target) return;
4744
+ currentTarget = target;
4745
+ if (hoverTimer) clearTimeout(hoverTimer);
4746
+ hoverTimer = setTimeout(() => {
4747
+ if (currentTarget === target) position(target);
4748
+ }, 30);
4749
+ };
4750
+ const onMouseOut = (e) => {
4751
+ const target = e.target && e.target.closest ? e.target.closest("[data-tooltip]") : null;
4752
+ if (!target) return;
4753
+ const related = e.relatedTarget;
4754
+ if (related && target.contains(related)) return;
4755
+ if (currentTarget === target) {
4756
+ if (hoverTimer) clearTimeout(hoverTimer);
4757
+ hide();
4758
+ }
4759
+ };
4760
+ const onFocusIn = (e) => {
4761
+ const target = e.target && e.target.closest ? e.target.closest("[data-tooltip]") : null;
4762
+ if (!target) return;
4763
+ currentTarget = target;
4764
+ position(target);
4765
+ };
4766
+ const onFocusOut = (e) => {
4767
+ const target = e.target && e.target.closest ? e.target.closest("[data-tooltip]") : null;
4768
+ if (!target) return;
4769
+ if (currentTarget === target) hide();
4770
+ };
4771
+ const onScrollOrResize = () => {
4772
+ if (currentTarget) {
4773
+ if (document.body.contains(currentTarget) && currentTarget.matches && currentTarget.matches(":hover")) {
4774
+ position(currentTarget);
4775
+ } else if (document.activeElement === currentTarget) {
4776
+ position(currentTarget);
4777
+ } else {
4778
+ hide();
4779
+ }
4780
+ }
4781
+ };
4782
+ document.addEventListener("mouseover", onMouseOver, true);
4783
+ document.addEventListener("mouseout", onMouseOut, true);
4784
+ document.addEventListener("focusin", onFocusIn, true);
4785
+ document.addEventListener("focusout", onFocusOut, true);
4786
+ window.addEventListener("scroll", onScrollOrResize, true);
4787
+ window.addEventListener("resize", onScrollOrResize);
4788
+ document.addEventListener("scroll", onScrollOrResize, true);
4789
+ return () => {
4790
+ document.removeEventListener("mouseover", onMouseOver, true);
4791
+ document.removeEventListener("mouseout", onMouseOut, true);
4792
+ document.removeEventListener("focusin", onFocusIn, true);
4793
+ document.removeEventListener("focusout", onFocusOut, true);
4794
+ window.removeEventListener("scroll", onScrollOrResize, true);
4795
+ window.removeEventListener("resize", onScrollOrResize);
4796
+ document.removeEventListener("scroll", onScrollOrResize, true);
4797
+ if (hoverTimer) clearTimeout(hoverTimer);
4798
+ try {
4799
+ portal?.remove();
4800
+ } catch {
4801
+ }
4802
+ try {
4803
+ arrow?.remove();
4804
+ } catch {
4805
+ }
4806
+ document.body.classList.remove("dsh-a6api-tooltip-active");
4807
+ try {
4808
+ delete window.__dsh_a6api_tooltip_setup;
4809
+ } catch {
4810
+ window.__dsh_a6api_tooltip_setup = void 0;
4811
+ }
4812
+ currentTarget = null;
4813
+ };
4814
+ }
2710
4815
  function apply(ctx) {
2711
4816
  injectStyles();
4817
+ if (typeof window !== "undefined") {
4818
+ try {
4819
+ ctx.effect(() => {
4820
+ const dispose = setupGlobalTooltip();
4821
+ return () => {
4822
+ try {
4823
+ if (typeof dispose === "function") dispose();
4824
+ } catch {
4825
+ }
4826
+ };
4827
+ }, "dsh-a6api: tooltip portal");
4828
+ } catch {
4829
+ }
4830
+ }
2712
4831
  if (typeof window === "undefined") return;
2713
4832
  try {
2714
4833
  setTimeout(() => {
4834
+ try {
4835
+ store.warmUp();
4836
+ } catch {
4837
+ }
2715
4838
  try {
2716
4839
  store.initPricePolling();
2717
4840
  } catch {
@@ -2733,8 +4856,23 @@ function apply(ctx) {
2733
4856
  A6ApiSettingsPanel
2734
4857
  );
2735
4858
  });
4859
+ const getModelDirectories = () => ctx && typeof ctx.get === "function" ? ctx.get("modelDirectories") : void 0;
4860
+ slots.inject("sidebar.footer.action", () => {
4861
+ return slots.register(
4862
+ {
4863
+ name: "sidebar.footer.action",
4864
+ id: "dsh-a6api-current-model",
4865
+ order: -1,
4866
+ label: () => "A6api"
4867
+ },
4868
+ (props) => import_react7.default.createElement(A6ApiSidebarCard, {
4869
+ ...props || {},
4870
+ getModelDirectories
4871
+ })
4872
+ );
4873
+ });
2736
4874
  } catch (err) {
2737
- console.warn("[dsh-a6api] Failed to inject settings section:", err);
4875
+ console.warn("[dsh-a6api] Failed to inject slots:", err);
2738
4876
  }
2739
4877
  }
2740
4878
  return module.exports; } });