@jingyi0605/codingns4dsh 0.2.1-rc.2 → 0.2.1-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.en.md +6 -4
  2. package/README.md +6 -6
  3. package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +1 -1
  4. package/data/build/dist/bootstrap/index.d.ts +1 -1
  5. package/data/build/dist/client/bundle.js +5224 -290
  6. package/data/build/dist/client/bundle.js.map +1 -1
  7. package/data/build/dist/client/client.magpie-model-tag.js +155 -5
  8. package/data/build/dist/client/client.magpie-model-tag.js.map +1 -1
  9. package/data/build/dist/client/client.native-workspace-store.js +30 -4
  10. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  11. package/data/build/dist/client/client.transport.js +747 -48
  12. package/data/build/dist/client/client.transport.js.map +1 -1
  13. package/data/build/dist/client/desktop-assistant.js +953 -185
  14. package/data/build/dist/client/feature-requirement.d.ts +43 -0
  15. package/data/build/dist/client/feature-requirement.d.ts.map +1 -0
  16. package/data/build/dist/client/feature-requirement.js +105 -0
  17. package/data/build/dist/client/feature-requirement.js.map +1 -0
  18. package/data/build/dist/client/features/index.d.ts +1 -0
  19. package/data/build/dist/client/features/index.d.ts.map +1 -1
  20. package/data/build/dist/client/features/index.js +3 -0
  21. package/data/build/dist/client/features/index.js.map +1 -1
  22. package/data/build/dist/client/features/magpie-management-controls.d.ts +46 -0
  23. package/data/build/dist/client/features/magpie-management-controls.d.ts.map +1 -0
  24. package/data/build/dist/client/features/magpie-management-controls.js +794 -0
  25. package/data/build/dist/client/features/magpie-management-controls.js.map +1 -0
  26. package/data/build/dist/client/features/magpie-management-page.d.ts +129 -0
  27. package/data/build/dist/client/features/magpie-management-page.d.ts.map +1 -0
  28. package/data/build/dist/client/features/magpie-management-page.js +1279 -0
  29. package/data/build/dist/client/features/magpie-management-page.js.map +1 -0
  30. package/data/build/dist/client/features/magpie-management.d.ts +7 -0
  31. package/data/build/dist/client/features/magpie-management.d.ts.map +1 -0
  32. package/data/build/dist/client/features/magpie-management.js +154 -0
  33. package/data/build/dist/client/features/magpie-management.js.map +1 -0
  34. package/data/build/dist/client/features/magpie-mark.d.ts +10 -0
  35. package/data/build/dist/client/features/magpie-mark.d.ts.map +1 -0
  36. package/data/build/dist/client/features/magpie-mark.js +21 -0
  37. package/data/build/dist/client/features/magpie-mark.js.map +1 -0
  38. package/data/build/dist/client/features/magpie-provider-logo.d.ts +11 -0
  39. package/data/build/dist/client/features/magpie-provider-logo.d.ts.map +1 -0
  40. package/data/build/dist/client/features/magpie-provider-logo.js +35 -0
  41. package/data/build/dist/client/features/magpie-provider-logo.js.map +1 -0
  42. package/data/build/dist/client/features/subscription-quota-groups.d.ts +193 -0
  43. package/data/build/dist/client/features/subscription-quota-groups.d.ts.map +1 -0
  44. package/data/build/dist/client/features/subscription-quota-groups.js +548 -0
  45. package/data/build/dist/client/features/subscription-quota-groups.js.map +1 -0
  46. package/data/build/dist/client/git-management.d.ts.map +1 -1
  47. package/data/build/dist/client/git-management.js +11 -9
  48. package/data/build/dist/client/git-management.js.map +1 -1
  49. package/data/build/dist/client/git-panel-styles.js +1 -0
  50. package/data/build/dist/client/git-panel-styles.js.map +1 -1
  51. package/data/build/dist/client/index.d.ts +1 -1
  52. package/data/build/dist/client/index.d.ts.map +1 -1
  53. package/data/build/dist/client/index.js +1 -1
  54. package/data/build/dist/client/index.js.map +1 -1
  55. package/data/build/dist/client/locale.d.ts.map +1 -1
  56. package/data/build/dist/client/locale.js +7 -0
  57. package/data/build/dist/client/locale.js.map +1 -1
  58. package/data/build/dist/client/locales/magpie.d.ts +4 -0
  59. package/data/build/dist/client/locales/magpie.d.ts.map +1 -0
  60. package/data/build/dist/client/locales/magpie.js +644 -0
  61. package/data/build/dist/client/locales/magpie.js.map +1 -0
  62. package/data/build/dist/client/locales/usage.d.ts.map +1 -1
  63. package/data/build/dist/client/locales/usage.js +52 -0
  64. package/data/build/dist/client/locales/usage.js.map +1 -1
  65. package/data/build/dist/client/magpie-model-tag.d.ts +9 -1
  66. package/data/build/dist/client/magpie-model-tag.d.ts.map +1 -1
  67. package/data/build/dist/client/magpie-model-tag.js +15 -3
  68. package/data/build/dist/client/magpie-model-tag.js.map +1 -1
  69. package/data/build/dist/client/provider-icons.d.ts +8 -0
  70. package/data/build/dist/client/provider-icons.d.ts.map +1 -1
  71. package/data/build/dist/client/provider-icons.js +122 -2
  72. package/data/build/dist/client/provider-icons.js.map +1 -1
  73. package/data/build/dist/client/settings-bridge.d.ts +1 -0
  74. package/data/build/dist/client/settings-bridge.d.ts.map +1 -1
  75. package/data/build/dist/client/settings-bridge.js +29 -10
  76. package/data/build/dist/client/settings-bridge.js.map +1 -1
  77. package/data/build/dist/client/settings-section.d.ts.map +1 -1
  78. package/data/build/dist/client/settings-section.js +21 -3
  79. package/data/build/dist/client/settings-section.js.map +1 -1
  80. package/data/build/dist/client/style.css +224 -221
  81. package/data/build/dist/client/subscription-slot.d.ts +22 -1
  82. package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
  83. package/data/build/dist/client/subscription-slot.js +374 -25
  84. package/data/build/dist/client/subscription-slot.js.map +1 -1
  85. package/data/build/dist/dsh-capabilities/client/global-panel-adapter.d.ts +27 -0
  86. package/data/build/dist/dsh-capabilities/client/global-panel-adapter.d.ts.map +1 -0
  87. package/data/build/dist/dsh-capabilities/client/global-panel-adapter.js +109 -0
  88. package/data/build/dist/dsh-capabilities/client/global-panel-adapter.js.map +1 -0
  89. package/data/build/dist/dsh-capabilities/host/session-title-adapter.d.ts +2 -1
  90. package/data/build/dist/dsh-capabilities/host/session-title-adapter.d.ts.map +1 -1
  91. package/data/build/dist/dsh-capabilities/host/session-title-adapter.js +32 -2
  92. package/data/build/dist/dsh-capabilities/host/session-title-adapter.js.map +1 -1
  93. package/data/build/dist/dsh-capabilities/matrix.d.ts.map +1 -1
  94. package/data/build/dist/dsh-capabilities/matrix.js +12 -11
  95. package/data/build/dist/dsh-capabilities/matrix.js.map +1 -1
  96. package/data/build/dist/dsh-capabilities/routes.d.ts.map +1 -1
  97. package/data/build/dist/dsh-capabilities/routes.js +12 -9
  98. package/data/build/dist/dsh-capabilities/routes.js.map +1 -1
  99. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  100. package/data/build/dist/dsh-capabilities/types.d.ts.map +1 -1
  101. package/data/build/dist/dsh-capabilities/types.js.map +1 -1
  102. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +27 -0
  103. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -1
  104. package/data/build/dist/host/cli-adapters/acp-cli-driver.js +88 -13
  105. package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -1
  106. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +10 -0
  107. package/data/build/dist/host/cli-adapters/codex-driver.d.ts.map +1 -1
  108. package/data/build/dist/host/cli-adapters/codex-driver.js +32 -1
  109. package/data/build/dist/host/cli-adapters/codex-driver.js.map +1 -1
  110. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts +6 -5
  111. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts.map +1 -1
  112. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js +72 -12
  113. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js.map +1 -1
  114. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  115. package/data/build/dist/host/cli-adapters/command-code-driver.js +26 -1
  116. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  117. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
  118. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +15 -2
  119. package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
  120. package/data/build/dist/host/cli-adapters/feature.d.ts +13 -0
  121. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  122. package/data/build/dist/host/cli-adapters/feature.js +438 -6
  123. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  124. package/data/build/dist/host/cli-adapters/grok-driver.js +5 -0
  125. package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
  126. package/data/build/dist/host/cli-adapters/magpie-detection.d.ts +72 -0
  127. package/data/build/dist/host/cli-adapters/magpie-detection.d.ts.map +1 -0
  128. package/data/build/dist/host/cli-adapters/magpie-detection.js +312 -0
  129. package/data/build/dist/host/cli-adapters/magpie-detection.js.map +1 -0
  130. package/data/build/dist/host/cli-adapters/magpie-management.d.ts +50 -0
  131. package/data/build/dist/host/cli-adapters/magpie-management.d.ts.map +1 -0
  132. package/data/build/dist/host/cli-adapters/magpie-management.js +1343 -0
  133. package/data/build/dist/host/cli-adapters/magpie-management.js.map +1 -0
  134. package/data/build/dist/host/cli-adapters/magpie-update.d.ts +59 -0
  135. package/data/build/dist/host/cli-adapters/magpie-update.d.ts.map +1 -0
  136. package/data/build/dist/host/cli-adapters/magpie-update.js +249 -0
  137. package/data/build/dist/host/cli-adapters/magpie-update.js.map +1 -0
  138. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts +70 -8
  139. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts.map +1 -1
  140. package/data/build/dist/host/cli-adapters/magpie-usage.js +1075 -125
  141. package/data/build/dist/host/cli-adapters/magpie-usage.js.map +1 -1
  142. package/data/build/dist/host/cli-adapters/magpie-web.d.ts +69 -0
  143. package/data/build/dist/host/cli-adapters/magpie-web.d.ts.map +1 -0
  144. package/data/build/dist/host/cli-adapters/magpie-web.js +485 -0
  145. package/data/build/dist/host/cli-adapters/magpie-web.js.map +1 -0
  146. package/data/build/dist/host/cli-adapters/process-utils.d.ts +2 -0
  147. package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
  148. package/data/build/dist/host/cli-adapters/process-utils.js +8 -0
  149. package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
  150. package/data/build/dist/host/cli-adapters/session-performance-projection.d.ts +6 -0
  151. package/data/build/dist/host/cli-adapters/session-performance-projection.d.ts.map +1 -1
  152. package/data/build/dist/host/cli-adapters/session-performance-projection.js +25 -0
  153. package/data/build/dist/host/cli-adapters/session-performance-projection.js.map +1 -1
  154. package/data/build/dist/host/dsh-gateway-feature.d.ts.map +1 -1
  155. package/data/build/dist/host/dsh-gateway-feature.js +26 -3
  156. package/data/build/dist/host/dsh-gateway-feature.js.map +1 -1
  157. package/data/build/dist/host/features/host-status.d.ts +25 -0
  158. package/data/build/dist/host/features/host-status.d.ts.map +1 -1
  159. package/data/build/dist/host/features/host-status.js +60 -6
  160. package/data/build/dist/host/features/host-status.js.map +1 -1
  161. package/data/build/dist/host/features/lan-access-dsh.d.ts.map +1 -1
  162. package/data/build/dist/host/features/lan-access-dsh.js +61 -10
  163. package/data/build/dist/host/features/lan-access-dsh.js.map +1 -1
  164. package/data/build/dist/host/features/session-translation-protection.d.ts.map +1 -1
  165. package/data/build/dist/host/features/session-translation-protection.js +2 -1
  166. package/data/build/dist/host/features/session-translation-protection.js.map +1 -1
  167. package/data/build/dist/host/lan-access-dsh.d.ts.map +1 -1
  168. package/data/build/dist/host/lan-access-dsh.js +6 -2
  169. package/data/build/dist/host/lan-access-dsh.js.map +1 -1
  170. package/data/build/dist/host/modules/pwa/pwa-session-notifications.js +2 -1
  171. package/data/build/dist/host/modules/pwa/pwa-session-notifications.js.map +1 -1
  172. package/data/build/dist/host/rpc.d.ts +15 -2
  173. package/data/build/dist/host/rpc.d.ts.map +1 -1
  174. package/data/build/dist/host/rpc.js +47 -6
  175. package/data/build/dist/host/rpc.js.map +1 -1
  176. package/data/build/dist/host/settings.d.ts.map +1 -1
  177. package/data/build/dist/host/settings.js +5 -0
  178. package/data/build/dist/host/settings.js.map +1 -1
  179. package/data/build/dist/shared/contracts/config.d.ts +18 -0
  180. package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
  181. package/data/build/dist/shared/contracts/config.js +8 -0
  182. package/data/build/dist/shared/contracts/config.js.map +1 -1
  183. package/data/build/dist/shared/contracts/errors.d.ts +2 -2
  184. package/data/build/dist/shared/contracts/feature.d.ts +7 -0
  185. package/data/build/dist/shared/contracts/feature.d.ts.map +1 -1
  186. package/data/build/dist/shared/contracts/magpie-management.d.ts +414 -0
  187. package/data/build/dist/shared/contracts/magpie-management.d.ts.map +1 -0
  188. package/data/build/dist/shared/contracts/magpie-management.js +2 -0
  189. package/data/build/dist/shared/contracts/magpie-management.js.map +1 -0
  190. package/data/build/dist/shared/contracts/subscription.d.ts +77 -1
  191. package/data/build/dist/shared/contracts/subscription.d.ts.map +1 -1
  192. package/data/build/dist/shared/contracts/subscription.js.map +1 -1
  193. package/data/build/dist/shared/contracts/version.d.ts +4 -4
  194. package/data/build/dist/shared/contracts/version.js +3 -3
  195. package/data/build/dist/shared/index.d.ts +3 -1
  196. package/data/build/dist/shared/index.d.ts.map +1 -1
  197. package/data/build/dist/shared/index.js +2 -1
  198. package/data/build/dist/shared/index.js.map +1 -1
  199. package/data/build/dist/shared/magpie-provider-id.d.ts +12 -0
  200. package/data/build/dist/shared/magpie-provider-id.d.ts.map +1 -0
  201. package/data/build/dist/shared/magpie-provider-id.js +16 -0
  202. package/data/build/dist/shared/magpie-provider-id.js.map +1 -0
  203. package/data/build/dist/shared/session-title.d.ts.map +1 -1
  204. package/data/build/dist/shared/session-title.js +10 -4
  205. package/data/build/dist/shared/session-title.js.map +1 -1
  206. package/package.json +29 -29
  207. package/profile/README.md +2 -2
  208. package/profile/package.json +3 -3
  209. package/profile/version.json +3 -3
  210. package/version.json +3 -3
@@ -0,0 +1,1279 @@
1
+ import { Fragment, createElement, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
2
+ import { CODINGNS_SUBSCRIPTION_REPORTS_FIELD, DEFAULT_SUBSCRIPTION_REPORT_SETTINGS, normalizeSubscriptionReportSettings, } from '../../shared/contracts/config.js';
3
+ import { useCodingNsTranslator } from '../locale.js';
4
+ import { callCodingNsRpcResult } from '../rpc-call.js';
5
+ import { uiFontSize } from '../font-scale.js';
6
+ import { MagpieProviderLogo } from './magpie-provider-logo.js';
7
+ import { MagpieGroupsPage, MagpieProvidersPage } from './magpie-management-controls.js';
8
+ import { groupQuotaRowsByProvider, resolveQuotaAccountBalance } from './subscription-quota-groups.js';
9
+ const PERIODS = [
10
+ { id: 'today', key: 'magpie.today' },
11
+ { id: 'week', key: 'magpie.week' },
12
+ { id: 'month', key: 'magpie.month' },
13
+ { id: 'all', key: 'magpie.all' },
14
+ ];
15
+ /**
16
+ * Top-level tab order mirrors native Magpie: Overview, Providers, Routing groups, Usage.
17
+ *
18
+ * The third tab edits the `group/<id>` entries Magpie draws under "Routing groups"; its label
19
+ * says so, because a provider editor on the Providers tab also edits that provider's routing.
20
+ */
21
+ const PAGE_TABS = [
22
+ { id: 'overview', label: 'magpie.overview', icon: '◫' },
23
+ { id: 'providers', label: 'magpie.management.providers', icon: '◈' },
24
+ { id: 'groups', label: 'magpie.management.groupsTab', icon: '⇄' },
25
+ { id: 'usage', label: 'magpie.usage', icon: '◷' },
26
+ ];
27
+ /** Native Magpie order inside Usage: Subscriptions, Sessions, Requests. */
28
+ const USAGE_TABS = [
29
+ { id: 'quotas', label: 'magpie.quotas', icon: '◉' },
30
+ { id: 'sessions', label: 'magpie.sessions', icon: '◷' },
31
+ { id: 'requests', label: 'magpie.requests', icon: '↗' },
32
+ ];
33
+ const PAGE_SIZE = 50;
34
+ /** 请求视图每页条数;底部弹层复用同一分页尺寸。 */
35
+ export const MAGPIE_REQUESTS_PAGE_SIZE = PAGE_SIZE;
36
+ /** 订阅卡显示口径的切换项;顺序与截图一致:先「已用」,再「剩余」。 */
37
+ const QUOTA_VIEWS = [
38
+ { id: 'used', label: 'magpie.quotaViewUsed' },
39
+ { id: 'remaining', label: 'magpie.quotaViewRemaining' },
40
+ ];
41
+ /**
42
+ * 读取 Host 保存的订阅卡显示口径,并返回一个写入函数。
43
+ *
44
+ * 设置只影响读法,所以读取失败或没有设置存储时静默回落到默认值:
45
+ * 页面照常显示额度,不会因为偏好读不到而变成空态。
46
+ */
47
+ export function useQuotaView(settings) {
48
+ const [saveFailed, setSaveFailed] = useState(false);
49
+ // `settings` 可能是 undefined;用稳定的空实现代替它交给 useSyncExternalStore。
50
+ const subscribe = useCallback((listener) => (settings === undefined ? () => undefined : settings.subscribe(listener)), [settings]);
51
+ const read = useCallback(() => settings === undefined
52
+ ? EMPTY_QUOTA_VIEW_SNAPSHOT
53
+ : settings.getSnapshot(), [settings]);
54
+ const snapshot = useSyncExternalStore(subscribe, read, read);
55
+ const quotaView = normalizeSubscriptionReportSettings(snapshot.value?.subscriptionReports).quotaView;
56
+ const writable = settings !== undefined && snapshot.status === 'ready' && snapshot.writable;
57
+ const selectQuotaView = useCallback((next) => {
58
+ if (settings === undefined)
59
+ return;
60
+ setSaveFailed(false);
61
+ void settings.mutate([{
62
+ op: 'set',
63
+ path: [CODINGNS_SUBSCRIPTION_REPORTS_FIELD, 'quotaView'],
64
+ value: next,
65
+ }]).then((accepted) => {
66
+ if (!accepted)
67
+ setSaveFailed(true);
68
+ }).catch(() => setSaveFailed(true));
69
+ }, [settings]);
70
+ return { quotaView, selectQuotaView, writable, saveFailed };
71
+ }
72
+ /** 没有设置存储时的稳定空快照;`useSyncExternalStore` 要求同一引用。 */
73
+ const EMPTY_QUOTA_VIEW_SNAPSHOT = {
74
+ value: undefined,
75
+ revision: undefined,
76
+ writable: false,
77
+ status: 'unavailable',
78
+ };
79
+ export function MagpieManagementPage({ rpc, locale, initialPageTab = 'overview', initialUsageTab = 'quotas', settings }) {
80
+ const t = useCodingNsTranslator(locale);
81
+ const { quotaView, selectQuotaView, writable: quotaViewWritable, saveFailed: quotaViewSaveFailed } = useQuotaView(settings);
82
+ const [pageTab, setPageTab] = useState(initialPageTab);
83
+ const [usageTab, setUsageTab] = useState(initialUsageTab);
84
+ const section = pageTab === 'usage' ? usageTab : pageTab;
85
+ const selectPageTab = (next) => setPageTab(next);
86
+ const selectUsageTab = (next) => {
87
+ setPageTab('usage');
88
+ setUsageTab(next);
89
+ };
90
+ const [period, setPeriod] = useState('today');
91
+ const [dashboard, setDashboard] = useState(null);
92
+ const [dashboardLoading, setDashboardLoading] = useState(true);
93
+ const [dashboardError, setDashboardError] = useState(false);
94
+ const [dashboardIssue, setDashboardIssue] = useState(null);
95
+ const [chartMetric, setChartMetric] = useState('calls');
96
+ const [sessionSearch, setSessionSearch] = useState('');
97
+ const [selectedSession, setSelectedSession] = useState(null);
98
+ const [requestPage, setRequestPage] = useState(null);
99
+ const [requestLoading, setRequestLoading] = useState(false);
100
+ const [requestError, setRequestError] = useState(false);
101
+ const [requestOffset, setRequestOffset] = useState(0);
102
+ const [management, setManagement] = useState(null);
103
+ const [managementLoading, setManagementLoading] = useState(true);
104
+ const [managementIssue, setManagementIssue] = useState(null);
105
+ const [managementWriting, setManagementWriting] = useState(false);
106
+ const [updateStatus, setUpdateStatus] = useState(null);
107
+ const [updateNotes, setUpdateNotes] = useState(null);
108
+ const [updateLoading, setUpdateLoading] = useState(true);
109
+ const [updateError, setUpdateError] = useState(null);
110
+ const [updateConfirming, setUpdateConfirming] = useState(false);
111
+ const [updateNotice, setUpdateNotice] = useState('');
112
+ const [updateOpen, setUpdateOpen] = useState(false);
113
+ const [refreshKey, setRefreshKey] = useState(0);
114
+ const dashboardSequence = useRef(0);
115
+ const requestSequence = useRef(0);
116
+ const managementSequence = useRef(0);
117
+ const loadDashboard = useCallback(async (signal) => {
118
+ const sequence = dashboardSequence.current + 1;
119
+ dashboardSequence.current = sequence;
120
+ setDashboardLoading(true);
121
+ setDashboardError(false);
122
+ setDashboardIssue(null);
123
+ try {
124
+ const next = await callMagpieRpc(rpc, 'usage/dashboard', { period }, signal);
125
+ if (signal.aborted || sequence !== dashboardSequence.current)
126
+ return;
127
+ setDashboard(next);
128
+ const issue = next?.availabilityIssue ?? (next === null ? 'host-rpc-outdated' : null);
129
+ setDashboardIssue(issue);
130
+ setDashboardError(issue !== null);
131
+ }
132
+ catch (error) {
133
+ if (signal.aborted || sequence !== dashboardSequence.current)
134
+ return;
135
+ const issue = magpieDashboardIssueForRpcError(error);
136
+ setDashboardIssue(issue);
137
+ setDashboardError(true);
138
+ }
139
+ finally {
140
+ if (!signal.aborted && sequence === dashboardSequence.current)
141
+ setDashboardLoading(false);
142
+ }
143
+ }, [period, rpc]);
144
+ useEffect(() => {
145
+ const controller = new AbortController();
146
+ void loadDashboard(controller.signal);
147
+ return () => controller.abort();
148
+ }, [loadDashboard, refreshKey]);
149
+ const loadManagement = useCallback(async (signal) => {
150
+ const sequence = managementSequence.current + 1;
151
+ managementSequence.current = sequence;
152
+ setManagementLoading(true);
153
+ setManagementIssue(null);
154
+ try {
155
+ const next = await callMagpieRpc(rpc, 'management/snapshot', {}, signal);
156
+ if (signal.aborted || sequence !== managementSequence.current)
157
+ return;
158
+ setManagement(next);
159
+ setManagementIssue(next?.availabilityIssue ?? (next === null ? 'host-rpc-outdated' : null));
160
+ }
161
+ catch (error) {
162
+ if (signal.aborted || sequence !== managementSequence.current)
163
+ return;
164
+ setManagementIssue(magpieManagementIssueForRpcError(error));
165
+ }
166
+ finally {
167
+ if (!signal.aborted && sequence === managementSequence.current)
168
+ setManagementLoading(false);
169
+ }
170
+ }, [rpc]);
171
+ useEffect(() => {
172
+ const controller = new AbortController();
173
+ void loadManagement(controller.signal);
174
+ return () => controller.abort();
175
+ }, [loadManagement, refreshKey]);
176
+ const loadUpdates = useCallback(async (signal) => {
177
+ setUpdateLoading(true);
178
+ setUpdateError(null);
179
+ try {
180
+ const [status, notes] = await Promise.all([
181
+ callMagpieRpc(rpc, 'update/status', {}, signal),
182
+ callMagpieRpc(rpc, 'update/notes', {}, signal),
183
+ ]);
184
+ if (signal.aborted)
185
+ return;
186
+ setUpdateStatus(status);
187
+ setUpdateNotes(notes);
188
+ }
189
+ catch (error) {
190
+ if (signal.aborted)
191
+ return;
192
+ setUpdateError(magpieUpdateIssueForRpcError(error));
193
+ }
194
+ finally {
195
+ if (!signal.aborted)
196
+ setUpdateLoading(false);
197
+ }
198
+ }, [rpc]);
199
+ useEffect(() => {
200
+ const controller = new AbortController();
201
+ void loadUpdates(controller.signal);
202
+ return () => controller.abort();
203
+ }, [loadUpdates, refreshKey]);
204
+ const checkForUpdate = useCallback(async () => {
205
+ setUpdateLoading(true);
206
+ setUpdateError(null);
207
+ setUpdateNotice('');
208
+ try {
209
+ const [status, notes] = await Promise.all([
210
+ callMagpieRpc(rpc, 'update/check', {}, undefined),
211
+ callMagpieRpc(rpc, 'update/notes', {}, undefined),
212
+ ]);
213
+ setUpdateStatus(status);
214
+ setUpdateNotes(notes);
215
+ }
216
+ catch (error) {
217
+ setUpdateError(magpieUpdateIssueForRpcError(error));
218
+ }
219
+ finally {
220
+ setUpdateLoading(false);
221
+ }
222
+ }, [rpc]);
223
+ const installUpdate = useCallback(async () => {
224
+ setUpdateLoading(true);
225
+ setUpdateError(null);
226
+ setUpdateNotice('');
227
+ try {
228
+ const result = await callMagpieRpc(rpc, 'update/install', { confirmed: true }, undefined);
229
+ if (result.status !== null)
230
+ setUpdateStatus(result.status);
231
+ setUpdateConfirming(false);
232
+ setUpdateNotice(result.restartExpected ? t('magpie.update.restartRequested') : t('magpie.update.requested'));
233
+ }
234
+ catch (error) {
235
+ setUpdateError(magpieUpdateIssueForRpcError(error));
236
+ }
237
+ finally {
238
+ setUpdateLoading(false);
239
+ }
240
+ }, [rpc, t]);
241
+ useEffect(() => {
242
+ if (!updateOpen)
243
+ return;
244
+ const closeOnEscape = (event) => {
245
+ if (event.key === 'Escape')
246
+ setUpdateOpen(false);
247
+ };
248
+ window.addEventListener('keydown', closeOnEscape);
249
+ return () => window.removeEventListener('keydown', closeOnEscape);
250
+ }, [updateOpen]);
251
+ // A write re-reads the snapshot rather than trusting the one it answered with: a provider renamed
252
+ // moves among the routing groups, which only a fresh read shows as Magpie has them now.
253
+ const updateProvider = useCallback(async (update) => {
254
+ setManagementWriting(true);
255
+ try {
256
+ const result = await callMagpieRpc(rpc, 'management/provider/update', update);
257
+ setManagement(result.snapshot);
258
+ // A re-read that fails leaves the verified snapshot in place: the save itself succeeded.
259
+ await loadManagement(new AbortController().signal).catch(() => undefined);
260
+ }
261
+ finally {
262
+ setManagementWriting(false);
263
+ }
264
+ }, [loadManagement, rpc]);
265
+ const updateGroup = useCallback(async (update) => {
266
+ setManagementWriting(true);
267
+ try {
268
+ const result = await callMagpieRpc(rpc, 'management/group/update', update);
269
+ setManagement(result.snapshot);
270
+ }
271
+ finally {
272
+ setManagementWriting(false);
273
+ }
274
+ }, [rpc]);
275
+ const updateAccount = useCallback(async (update) => {
276
+ setManagementWriting(true);
277
+ try {
278
+ const result = await callMagpieRpc(rpc, 'management/account/update', update);
279
+ setManagement(result.snapshot);
280
+ }
281
+ finally {
282
+ setManagementWriting(false);
283
+ }
284
+ }, [rpc]);
285
+ useEffect(() => {
286
+ if (section !== 'requests')
287
+ return;
288
+ const controller = new AbortController();
289
+ const sequence = requestSequence.current + 1;
290
+ requestSequence.current = sequence;
291
+ setRequestLoading(true);
292
+ setRequestError(false);
293
+ void callMagpieRpc(rpc, 'usage/requests', {
294
+ period,
295
+ offset: requestOffset,
296
+ limit: PAGE_SIZE,
297
+ ...(selectedSession === null ? {} : {
298
+ sessionId: selectedSession.id,
299
+ ...(selectedSession.sessionIds === undefined ? {} : { sessionIds: selectedSession.sessionIds }),
300
+ sessionAgent: selectedSession.agent,
301
+ }),
302
+ }, controller.signal).then((next) => {
303
+ if (controller.signal.aborted || sequence !== requestSequence.current)
304
+ return;
305
+ setRequestPage(next);
306
+ setRequestError(next === null);
307
+ }).catch(() => {
308
+ if (controller.signal.aborted || sequence !== requestSequence.current)
309
+ return;
310
+ setRequestError(true);
311
+ }).finally(() => {
312
+ if (!controller.signal.aborted && sequence === requestSequence.current)
313
+ setRequestLoading(false);
314
+ });
315
+ return () => controller.abort();
316
+ }, [period, requestOffset, rpc, section, selectedSession]);
317
+ const filteredSessions = useMemo(() => {
318
+ const query = sessionSearch.trim().toLocaleLowerCase();
319
+ const sessions = dashboard?.sessions ?? [];
320
+ if (query === '')
321
+ return sessions;
322
+ return sessions.filter((session) => session.agent.toLocaleLowerCase().includes(query) || session.id.toLocaleLowerCase().includes(query));
323
+ }, [dashboard?.sessions, sessionSearch]);
324
+ const openSession = (session) => {
325
+ setSelectedSession(session);
326
+ setRequestOffset(0);
327
+ setRequestPage(null);
328
+ selectUsageTab('requests');
329
+ };
330
+ const clearSession = () => {
331
+ setSelectedSession(null);
332
+ setRequestOffset(0);
333
+ setRequestPage(null);
334
+ };
335
+ const style = pageStyle;
336
+ const capturedAt = dashboard?.capturedAt ?? management?.capturedAt ?? null;
337
+ const usageReports = pageTab === 'usage';
338
+ /** Provider and routing-group settings are not period-scoped, so the period filter is hidden there. */
339
+ const periodScoped = pageTab === 'overview' || pageTab === 'usage';
340
+ return createElement('div', {
341
+ className: 'codingns-magpie-page',
342
+ role: 'region',
343
+ 'aria-label': t('magpie.pageTitle'),
344
+ style,
345
+ }, createElement('style', undefined, MAGPIE_PAGE_CSS), createElement('header', { className: 'codingns-magpie-topbar' }, createElement('div', { className: 'codingns-magpie-topbar-left' }, periodScoped && createElement('div', { className: 'codingns-magpie-periods', role: 'group', 'aria-label': t('magpie.periodLabel') }, ...PERIODS.map((entry) => createElement('button', {
346
+ key: entry.id,
347
+ type: 'button',
348
+ className: `codingns-magpie-period${period === entry.id ? ' is-active' : ''}`,
349
+ 'aria-pressed': period === entry.id,
350
+ onClick: () => {
351
+ setPeriod(entry.id);
352
+ setRequestOffset(0);
353
+ setRequestPage(null);
354
+ },
355
+ }, t(entry.key))))), createElement('nav', { className: 'codingns-magpie-tabs', 'aria-label': t('magpie.pageTitle') }, ...PAGE_TABS.map((entry) => createElement('button', {
356
+ key: entry.id,
357
+ type: 'button',
358
+ className: `codingns-magpie-tab${pageTab === entry.id ? ' is-active' : ''}`,
359
+ 'aria-current': pageTab === entry.id ? 'page' : undefined,
360
+ onClick: () => selectPageTab(entry.id),
361
+ }, createElement('span', { className: 'codingns-magpie-tab-icon', 'aria-hidden': true }, entry.icon), createElement('span', { className: 'codingns-magpie-tab-label' }, t(entry.label))))), createElement('div', { className: 'codingns-magpie-topbar-right' }, createElement('button', {
362
+ type: 'button',
363
+ className: 'codingns-magpie-version-button',
364
+ 'aria-haspopup': 'dialog',
365
+ 'aria-expanded': updateOpen,
366
+ onClick: () => setUpdateOpen(value => !value),
367
+ 'aria-label': t('magpie.update.open', { version: updateStatus?.current ?? '—' }),
368
+ title: t('magpie.update.open', { version: updateStatus?.current ?? '—' }),
369
+ }, updateStatus?.current ?? '—'), createElement('button', {
370
+ type: 'button',
371
+ className: 'codingns-magpie-icon-button',
372
+ disabled: dashboardLoading || managementLoading || managementWriting,
373
+ 'aria-busy': dashboardLoading || managementLoading,
374
+ onClick: () => setRefreshKey((value) => value + 1),
375
+ 'aria-label': t(dashboardLoading || managementLoading ? 'magpie.refreshing' : 'magpie.refresh'),
376
+ title: t(dashboardLoading || managementLoading ? 'magpie.refreshing' : 'magpie.refresh'),
377
+ }, createElement(RefreshIcon, { busy: dashboardLoading || managementLoading })))), updateOpen && createElement(MagpieUpdateDialog, {
378
+ status: updateStatus,
379
+ notes: updateNotes,
380
+ loading: updateLoading,
381
+ error: updateError,
382
+ confirming: updateConfirming,
383
+ notice: updateNotice,
384
+ t,
385
+ onCheck: checkForUpdate,
386
+ onConfirm: installUpdate,
387
+ onConfirmingChange: setUpdateConfirming,
388
+ onRefresh: () => {
389
+ const controller = new AbortController();
390
+ void loadUpdates(controller.signal);
391
+ },
392
+ onClose: () => setUpdateOpen(false),
393
+ }), usageReports && createElement('div', { className: 'codingns-magpie-toolbar has-tabs' }, createElement('div', { className: 'codingns-magpie-toolbar-left' }, createElement('div', { className: 'codingns-magpie-usage-tabs', role: 'group', 'aria-label': t('magpie.usage') }, ...USAGE_TABS.map((entry) => createElement('button', {
394
+ key: entry.id,
395
+ type: 'button',
396
+ className: `codingns-magpie-usage-tab${usageTab === entry.id ? ' is-active' : ''}`,
397
+ 'aria-pressed': usageTab === entry.id,
398
+ onClick: () => selectUsageTab(entry.id),
399
+ }, t(entry.label))))),
400
+ // 口径切换只作用于「订阅」页的额度卡;其他用量报表没有"剩余"这个概念。
401
+ section === 'quotas' && createElement('div', { className: 'codingns-magpie-toolbar-right' }, createElement(QuotaViewSwitch, {
402
+ value: quotaView,
403
+ // 没有设置存储(测试/静态预览)时控件只展示、不写回,因此不禁用。
404
+ disabled: settings === undefined ? false : !quotaViewWritable,
405
+ failed: quotaViewSaveFailed,
406
+ onChange: selectQuotaView,
407
+ t,
408
+ }))), createElement('main', { className: 'codingns-magpie-main' }, section === 'providers' || section === 'groups'
409
+ ? managementIssue !== null && management === null
410
+ ? createElement(EmptyState, { message: t(managementIssueLocaleKey(managementIssue)) })
411
+ : managementLoading && management === null
412
+ ? createElement(EmptyState, { message: t('magpie.management.loading'), loading: true })
413
+ : management?.availabilityIssue !== null && management?.availabilityIssue !== undefined
414
+ ? createElement(EmptyState, { message: t(managementIssueLocaleKey(management.availabilityIssue)) })
415
+ : section === 'providers'
416
+ ? createElement(MagpieProvidersPage, { snapshot: management, t, onUpdate: updateProvider, onAccountUpdate: updateAccount, writing: managementWriting })
417
+ : createElement(MagpieGroupsPage, { snapshot: management, t, onUpdate: updateGroup })
418
+ : dashboardIssue !== null
419
+ ? createElement(EmptyState, { message: t(dashboardIssueLocaleKey(dashboardIssue)) })
420
+ : dashboardLoading && dashboard === null
421
+ ? createElement(EmptyState, { message: t('magpie.loading'), loading: true })
422
+ : dashboardError && dashboard === null
423
+ ? createElement(EmptyState, { message: t('magpie.unavailable') })
424
+ : section === 'overview'
425
+ ? createElement(OverviewPage, {
426
+ dashboard, loading: dashboardLoading, error: dashboardError, chartMetric, onChartMetricChange: setChartMetric, t, onOpenSession: openSession,
427
+ })
428
+ : section === 'sessions'
429
+ ? createElement(SessionsPage, { dashboard, sessions: filteredSessions, search: sessionSearch, onSearch: setSessionSearch, onOpenSession: openSession, t })
430
+ : section === 'requests'
431
+ ? createElement(RequestsPage, { page: requestPage, loading: requestLoading, error: requestError, selectedSession, offset: requestOffset, onOffsetChange: setRequestOffset, onClearSession: clearSession, t })
432
+ : createElement(QuotasPage, { dashboard, loading: dashboardLoading, quotaView, t })));
433
+ }
434
+ export function OverviewPage({ dashboard, loading, error, chartMetric, onChartMetricChange, t, onOpenSession }) {
435
+ const [modelBreakdownMode, setModelBreakdownMode] = useState('aggregate');
436
+ const metrics = dashboard?.aggregate;
437
+ if (dashboard === null || dashboard.usageStatus === 'unavailable' || metrics == null) {
438
+ return createElement(EmptyState, { message: t(error ? 'magpie.usageUnavailable' : 'magpie.noUsage'), loading });
439
+ }
440
+ const ioTokens = sumNullable(metrics.inputTokens, metrics.outputTokens);
441
+ const stats = [
442
+ { label: t('magpie.statTokens'), value: formatCompact(ioTokens), detail: `${t('magpie.metricInput')} ${formatCompact(metrics.inputTokens)} · ${t('magpie.metricOutput')} ${formatCompact(metrics.outputTokens)}` },
443
+ { label: t('magpie.metricCacheRead'), value: formatCompact(metrics.cacheReadTokens), detail: `${t('magpie.metricCacheWrite')} ${formatCompact(metrics.cacheWriteTokens)}` },
444
+ { label: t('magpie.metricReasoning'), value: formatCompact(metrics.reasoningTokens), detail: `${t('magpie.statWriteDown')} ${formatCompact(metrics.outputTokens)}` },
445
+ { label: t('magpie.statCalls'), value: formatInteger(metrics.calls), detail: `${formatInteger(metrics.errors)} ${t('magpie.metricErrors')} · ${formatInteger(metrics.retried)} ${t('magpie.metricRetries')}` },
446
+ ];
447
+ return createElement('div', { className: 'codingns-magpie-view' }, error && createElement(InlineNotice, { message: t('magpie.usageUnavailable') }), createElement(StatStrip, { stats }), createElement('section', { className: 'codingns-magpie-panel codingns-magpie-barchart-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', { className: 'codingns-magpie-chart-switch', role: 'group', 'aria-label': t('magpie.timeline') }, ...['calls', 'tokens', 'cost'].map((metric) => createElement('button', {
448
+ key: metric,
449
+ type: 'button',
450
+ 'aria-pressed': chartMetric === metric,
451
+ className: chartMetric === metric ? 'is-active' : '',
452
+ onClick: () => onChartMetricChange(metric),
453
+ }, metric === 'calls' ? t('magpie.metricCalls') : metric === 'tokens' ? t('magpie.tokens') : t('magpie.metricCost'))))), createElement(BarChart, { points: dashboard.series, metric: chartMetric, label: t(`magpie.chart.${chartMetric}`) })), createElement(ProgressSection, { title: t('magpie.byAgent'), rows: dashboard.agents, t }), createElement(ModelProgressSection, { rows: dashboard.models, mode: modelBreakdownMode, onModeChange: setModelBreakdownMode, t }), dashboard.sessions.length > 0 && createElement(ProgressSection, {
454
+ title: t('magpie.topSessions'),
455
+ rows: dashboard.sessions.map((session) => ({
456
+ ...session,
457
+ id: `${session.agent}:${session.id}`,
458
+ name: session.dshSessionName ?? session.agent,
459
+ model: session.id,
460
+ provider: null,
461
+ account: null,
462
+ })),
463
+ t,
464
+ onOpen: (row) => {
465
+ const target = dashboard.sessions.find((item) => `${item.agent}:${item.id}` === row.id);
466
+ if (target !== undefined)
467
+ onOpenSession(target);
468
+ },
469
+ }));
470
+ }
471
+ function MagpieUpdateDialog({ status, notes, loading, error, confirming, notice, t, onCheck, onConfirm, onConfirmingChange, onRefresh, onClose }) {
472
+ return createElement('div', {
473
+ className: 'codingns-magpie-update-overlay',
474
+ role: 'presentation',
475
+ onMouseDown: (event) => {
476
+ if (event.target === event.currentTarget)
477
+ onClose();
478
+ },
479
+ }, createElement('div', {
480
+ className: 'codingns-magpie-update-dialog',
481
+ role: 'dialog',
482
+ 'aria-modal': true,
483
+ 'aria-label': t('magpie.update.title'),
484
+ onMouseDown: (event) => event.stopPropagation(),
485
+ }, createElement(MagpieUpdatePanel, {
486
+ status,
487
+ notes,
488
+ loading,
489
+ error,
490
+ confirming,
491
+ notice,
492
+ t,
493
+ onCheck,
494
+ onConfirm,
495
+ onConfirmingChange,
496
+ onRefresh,
497
+ onClose,
498
+ })));
499
+ }
500
+ function MagpieUpdatePanel({ status, notes, loading, error, confirming, notice, t, onCheck, onConfirm, onConfirmingChange, onRefresh, onClose }) {
501
+ const updateAvailable = status !== null && ['found', 'ready', 'available', 'error'].includes(status.state);
502
+ const release = notes?.releases.find(item => item.version === status?.latest) ?? notes?.releases[0];
503
+ const progress = status?.state === 'downloading' && status.total !== null && status.total > 0 && status.done !== null
504
+ ? Math.max(0, Math.min(100, Math.round(status.done / status.total * 100)))
505
+ : null;
506
+ return createElement('section', { className: 'codingns-magpie-panel codingns-magpie-update-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', undefined, createElement('h2', undefined, t('magpie.update.title')), createElement('p', undefined, t('magpie.update.description'))), createElement('div', { className: 'codingns-magpie-update-panel-actions' }, createElement('button', {
507
+ type: 'button',
508
+ className: 'codingns-magpie-text-button',
509
+ disabled: loading,
510
+ onClick: onRefresh,
511
+ }, t(loading ? 'magpie.refreshing' : 'magpie.refresh')), createElement('button', {
512
+ type: 'button',
513
+ className: 'codingns-magpie-update-close',
514
+ onClick: onClose,
515
+ 'aria-label': t('magpie.update.close'),
516
+ title: t('magpie.update.close'),
517
+ }, '×'))), error !== null
518
+ ? createElement(InlineNotice, { message: t(updateIssueLocaleKey(error)) })
519
+ : status === null
520
+ ? createElement(EmptyState, { message: t(loading ? 'magpie.loading' : 'magpie.update.unavailable'), loading })
521
+ : createElement('div', { className: 'codingns-magpie-update-body' }, createElement('div', { className: 'codingns-magpie-update-status' }, createElement('strong', undefined, updateStateLabel(status.state, t)), createElement('span', undefined, t('magpie.update.current', { version: status.current })), status.latest !== null && status.latest !== status.current && createElement('span', undefined, t('magpie.update.latest', { version: status.latest }))), status.error !== null && createElement('div', { className: 'codingns-magpie-management-message' }, status.error), progress !== null && createElement('div', { className: 'codingns-magpie-update-progress' }, createElement('span', { style: { width: `${progress}%` } }), createElement('small', undefined, `${progress}%`)), release !== undefined && release.notes.trim() !== '' && createElement('details', {
522
+ className: 'codingns-magpie-update-notes',
523
+ open: status.latest !== null && status.latest !== status.current,
524
+ }, createElement('summary', undefined, t('magpie.update.releaseNotes', { version: release.version })), createElement('div', undefined, release.notes)), confirming && updateAvailable
525
+ ? createElement('div', { className: 'codingns-magpie-confirm codingns-magpie-update-confirm' }, createElement('strong', undefined, t('magpie.update.confirmTitle')), createElement('span', undefined, t('magpie.update.confirmWarning')), createElement('div', { className: 'codingns-magpie-management-actions' }, createElement('button', { type: 'button', disabled: loading, onClick: () => onConfirmingChange(false) }, t('magpie.management.cancel')), createElement('button', { type: 'button', className: 'is-primary', disabled: loading, onClick: onConfirm }, loading ? t('magpie.refreshing') : t('magpie.update.confirm'))))
526
+ : createElement('div', { className: 'codingns-magpie-management-actions' }, updateAvailable && createElement('button', {
527
+ type: 'button',
528
+ className: 'is-primary',
529
+ disabled: loading,
530
+ onClick: () => onConfirmingChange(true),
531
+ }, t(status.state === 'error' && status.retry ? 'magpie.update.retry' : 'magpie.update.manual')), status.state === 'checking' && createElement('span', { className: 'codingns-magpie-management-message' }, t('magpie.update.checking')), status.state === 'downloading' && createElement('span', { className: 'codingns-magpie-management-message' }, t('magpie.update.downloading'))), notice !== '' && createElement('div', { className: 'codingns-magpie-management-message', role: 'status' }, notice), !confirming && createElement('div', { className: 'codingns-magpie-update-actions' }, createElement('button', { type: 'button', disabled: loading, onClick: onCheck }, loading ? t('magpie.refreshing') : t('magpie.update.check')))));
532
+ }
533
+ export function ModelProgressSection({ rows, mode, onModeChange, t }) {
534
+ const controls = createElement('div', { className: 'codingns-magpie-chart-switch', role: 'group', 'aria-label': t('magpie.modelBreakdown') }, ...['aggregate', 'account'].map((value) => createElement('button', {
535
+ key: value,
536
+ type: 'button',
537
+ className: mode === value ? 'is-active' : '',
538
+ 'aria-pressed': mode === value,
539
+ onClick: () => onModeChange(value),
540
+ }, value === 'aggregate' ? t('magpie.modelAggregate') : t('magpie.modelPerAccount'))));
541
+ const displayRows = modelBreakdownRows(rows, mode);
542
+ return createElement(ProgressSection, {
543
+ // The two modes have different row identity semantics; remount the list so React cannot
544
+ // retain an account row when returning to the aggregated view.
545
+ key: `model-breakdown-${mode}`,
546
+ title: t('magpie.byModel'),
547
+ rows: displayRows,
548
+ t,
549
+ header: controls,
550
+ ...(mode === 'aggregate' ? { limit: 8 } : {}),
551
+ });
552
+ }
553
+ export function SessionsPage({ dashboard, sessions, search, onSearch, onOpenSession, t }) {
554
+ const [agentFilter, setAgentFilter] = useState('');
555
+ const agentNames = useMemo(() => [...new Set(sessions.map((session) => session.agent))].sort(), [sessions]);
556
+ const visible = agentFilter === '' ? sessions : sessions.filter((session) => session.agent === agentFilter);
557
+ const aggregate = sumSessions(visible);
558
+ const metrics = dashboard?.aggregate;
559
+ const stats = [
560
+ { label: t('magpie.statSessions'), value: formatInteger(visible.length), detail: t('magpie.sessionsCount', { count: visible.length }) },
561
+ { label: t('magpie.statTokens'), value: formatCompact(sumNullable(aggregate.inputTokens, aggregate.outputTokens)), detail: `${t('magpie.metricInput')} ${formatCompact(aggregate.inputTokens)} · ${t('magpie.metricOutput')} ${formatCompact(aggregate.outputTokens)}` },
562
+ { label: t('magpie.metricCost'), value: formatValue(aggregate.cost), detail: `${formatInteger(aggregate.unpriced)} ${t('magpie.metricUnpriced')}` },
563
+ { label: t('magpie.metricCacheRead'), value: formatCompact(aggregate.cacheReadTokens), detail: `${t('magpie.metricCacheWrite')} ${formatCompact(aggregate.cacheWriteTokens)}` },
564
+ { label: t('magpie.statReasoning'), value: formatCompact(aggregate.reasoningTokens), detail: t('magpie.statReasoningNote') },
565
+ ];
566
+ return createElement('div', { className: 'codingns-magpie-view' }, createElement('div', { className: 'codingns-magpie-chiprow', role: 'group', 'aria-label': t('magpie.sessions') }, createElement('button', { type: 'button', className: `codingns-magpie-chip${agentFilter === '' ? ' is-active' : ''}`, onClick: () => setAgentFilter('') }, t('magpie.all')), ...agentNames.map((agent) => createElement('button', { key: agent, type: 'button', className: `codingns-magpie-chip${agentFilter === agent ? ' is-active' : ''}`, onClick: () => setAgentFilter(agent) }, createElement(MagpieProviderLogo, { identities: [agent], size: 18, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), agent))), createElement('div', { className: 'codingns-magpie-filterrow' }, createElement('label', { className: 'codingns-magpie-search codingns-magpie-grow' }, createElement(SearchIcon), createElement('input', { type: 'search', value: search, onChange: (event) => onSearch(event.currentTarget.value), placeholder: t('magpie.sessionSearch'), 'aria-label': t('magpie.sessionSearch') })), metrics != null && createElement('span', { className: 'codingns-magpie-filter-summary' }, t('magpie.sessionCountUnknown') === '' ? '' : t('magpie.sessionsCount', { count: dashboard?.sessionCount ?? visible.length }))), createElement(StatStrip, { stats }), dashboard !== null && dashboard.series.length > 1 && createElement('section', { className: 'codingns-magpie-panel codingns-magpie-barchart-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', undefined, createElement('h2', undefined, t('magpie.dailyTrend')))), createElement(BarChart, { points: dashboard.series, metric: 'tokens', label: t('magpie.chart.tokens') })), visible.length === 0
567
+ ? createElement(EmptyState, { message: t(search === '' && agentFilter === '' ? 'magpie.noSessions' : 'magpie.noRequests') })
568
+ : createElement('section', { className: 'codingns-magpie-panel codingns-magpie-toplist-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('h2', undefined, t('magpie.topSessions'))), ...visible.slice(0, 40).map((session) => createElement('button', {
569
+ key: `${session.agent}:${session.id}`,
570
+ type: 'button',
571
+ className: 'codingns-magpie-toprow',
572
+ onClick: () => onOpenSession(session),
573
+ }, createElement(MagpieProviderLogo, { identities: [session.agent], size: 28, className: 'codingns-magpie-provider-logo', fallback: 'none' }), createElement('span', { className: 'codingns-magpie-toprow-main' }, createElement('strong', undefined, session.dshSessionName ?? session.agent), session.dshSessionName !== undefined && createElement('small', { title: session.agent }, session.agent), createElement('small', { title: session.id }, session.id)), createElement('span', { className: 'codingns-magpie-toprow-metrics' }, createElement('span', undefined, formatCompact(sumNullable(session.inputTokens, session.outputTokens))), createElement('span', undefined, formatInteger(session.calls)), createElement('span', undefined, formatValue(session.cost)))))));
574
+ }
575
+ function sumSessions(sessions) {
576
+ const total = { calls: 0, errors: 0, retried: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0, cost: 0, unpriced: 0 };
577
+ let any = false;
578
+ for (const session of sessions) {
579
+ any = true;
580
+ total.calls += session.calls ?? 0;
581
+ total.errors += session.errors ?? 0;
582
+ total.retried += session.retried ?? 0;
583
+ total.inputTokens += session.inputTokens ?? 0;
584
+ total.outputTokens += session.outputTokens ?? 0;
585
+ total.cacheReadTokens += session.cacheReadTokens ?? 0;
586
+ total.cacheWriteTokens += session.cacheWriteTokens ?? 0;
587
+ total.reasoningTokens += session.reasoningTokens ?? 0;
588
+ total.cost += session.cost ?? 0;
589
+ total.unpriced += session.unpriced ?? 0;
590
+ }
591
+ return any ? total : { ...total };
592
+ }
593
+ export function RequestsPage({ page, loading, error, selectedSession, offset, onOffsetChange, onClearSession, t, append = false }) {
594
+ const items = page?.items ?? [];
595
+ const aggregate = sumRequests(items);
596
+ const stats = [
597
+ { label: t('magpie.statTokens'), value: formatCompact(sumNullable(aggregate.inputTokens, aggregate.outputTokens)), detail: `${t('magpie.metricInput')} ${formatCompact(aggregate.inputTokens)} · ${t('magpie.metricOutput')} ${formatCompact(aggregate.outputTokens)}` },
598
+ { label: t('magpie.statCalls'), value: formatInteger(page?.total ?? aggregate.calls), detail: `${formatInteger(aggregate.errors)} ${t('magpie.metricErrors')} · ${formatInteger(aggregate.retried)} ${t('magpie.metricRetries')}` },
599
+ ...(selectedSession === null ? [] : [{
600
+ label: t('magpie.statTurns'),
601
+ value: formatInteger(page?.sessionMetrics?.turns ?? null),
602
+ detail: `${t('magpie.statSteps')} ${formatInteger(page?.sessionMetrics?.steps ?? null)}`,
603
+ }]),
604
+ { label: t('magpie.metricCost'), value: formatValue(aggregate.cost), detail: `${formatInteger(aggregate.unpriced)} ${t('magpie.metricUnpriced')}` },
605
+ { label: t('magpie.statSpeed'), value: aggregate.decodeTokens === null ? '—' : `${formatValue(aggregate.decodeTokens)} token/s`, detail: aggregate.decodeCount === null ? undefined : `${formatInteger(aggregate.decodeCount)} ${t('magpie.metricDecodeCount')}` },
606
+ { label: t('magpie.statLatency'), value: aggregate.ttftMs === null ? '—' : `${formatValue(aggregate.ttftMs)} ms`, detail: t('magpie.statLatencyNote') },
607
+ ];
608
+ /** 追加列表按位置生成稳定 key,翻页列表按本页偏移生成,切换页面时行不会错位。 */
609
+ const rowKey = (index) => (append ? index : offset + index);
610
+ return createElement('div', { className: 'codingns-magpie-view' }, selectedSession !== null && createElement('div', { className: 'codingns-magpie-selection' }, createElement('span', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [selectedSession.agent], size: 20, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), selectedSession.dshSessionName ?? t('magpie.requestFor', { agent: selectedSession.agent, id: selectedSession.id })), onClearSession !== null && createElement('button', { type: 'button', className: 'codingns-magpie-text-button', onClick: onClearSession }, t('magpie.clearSession'))), error && createElement(InlineNotice, { message: t('magpie.usageUnavailable') }), loading && page === null
611
+ ? createElement(EmptyState, { message: t('magpie.loading'), loading: true })
612
+ : page === null
613
+ ? createElement(EmptyState, { message: t('magpie.noRequests') })
614
+ : createElement('div', undefined, createElement(StatStrip, { stats }), items.length === 0
615
+ ? createElement(EmptyState, { message: t('magpie.noRequests') })
616
+ : createElement('section', { className: 'codingns-magpie-panel codingns-magpie-request-panel' }, createElement('div', { className: 'codingns-magpie-reqlist' }, ...items.map((request, index) => createElement(NativeRequestCard, { key: `${request.routeId ?? rowKey(index)}:${request.time ?? ''}`, request, t }))), createElement('div', { className: 'codingns-magpie-table-scroll' }, createElement('table', { className: 'codingns-magpie-table codingns-magpie-native-table' }, createElement('thead', undefined, createElement('tr', undefined, createElement('th', undefined, t('magpie.ncolTime')), createElement('th', undefined, 'AGENT'), createElement('th', undefined, t('magpie.ncolRequested')), createElement('th', undefined, t('magpie.ncolProvider')), createElement('th', undefined, t('magpie.ncolServed')), createElement('th', undefined, t('magpie.ncolInput')), createElement('th', undefined, t('magpie.ncolOutput')), createElement('th', undefined, t('magpie.ncolCost')), createElement('th', undefined, t('magpie.ncolFirstText')), createElement('th', undefined, t('magpie.ncolDuration')), createElement('th', undefined, t('magpie.ncolSpeed')), createElement('th', undefined, t('magpie.ncolStatus')))), createElement('tbody', undefined, ...items.map((request, index) => createElement(NativeRequestRow, { key: `${request.routeId ?? rowKey(index)}:${request.time ?? ''}`, request, t }))))), createElement('div', { className: 'codingns-magpie-pagination' }, createElement('span', undefined, append
617
+ ? page.total === null
618
+ ? t('magpie.loadedCount', { count: formatInteger(items.length) })
619
+ : `${formatInteger(items.length)} / ${formatInteger(page.total)}`
620
+ : page.total === null ? t('magpie.pageOf', { page: Math.floor(offset / PAGE_SIZE) + 1 }) : `${offset + 1}–${Math.min(offset + items.length, page.total)} / ${formatInteger(page.total)}`), createElement('div', undefined, append
621
+ ? createElement('button', { type: 'button', className: 'codingns-magpie-load-more', disabled: !page.hasMore || loading, onClick: () => onOffsetChange(items.length) }, loading ? t('magpie.loading') : t('magpie.loadMore'))
622
+ : createElement(Fragment, undefined, createElement('button', { type: 'button', disabled: offset === 0 || loading, onClick: () => onOffsetChange(Math.max(0, offset - PAGE_SIZE)) }, t('magpie.previous')), createElement('button', { type: 'button', disabled: !page.hasMore || loading, onClick: () => onOffsetChange(offset + PAGE_SIZE) }, t('magpie.next'))))))));
623
+ }
624
+ function sumRequests(items) {
625
+ const total = { calls: 0, errors: 0, retried: 0, inputTokens: 0, outputTokens: 0, cost: 0, unpriced: 0 };
626
+ let ttftSum = 0;
627
+ let ttftCount = 0;
628
+ let speedSum = 0;
629
+ let speedCount = 0;
630
+ for (const item of items) {
631
+ total.calls += item.status !== null && /^2/u.test(item.status) ? 1 : 1;
632
+ if (item.status !== null && !/^2/u.test(item.status))
633
+ total.errors += 1;
634
+ total.inputTokens += item.inputTokens ?? 0;
635
+ total.outputTokens += item.outputTokens ?? 0;
636
+ total.cost += item.cost ?? 0;
637
+ if (item.priced === false)
638
+ total.unpriced += 1;
639
+ if (item.firstTextMs !== null) {
640
+ ttftSum += item.firstTextMs;
641
+ ttftCount += 1;
642
+ }
643
+ if (item.durationMs !== null && item.outputTokens !== null && item.durationMs > 0) {
644
+ speedSum += item.outputTokens / (item.durationMs / 1000);
645
+ speedCount += 1;
646
+ }
647
+ }
648
+ return {
649
+ calls: total.calls,
650
+ errors: total.errors,
651
+ retried: 0,
652
+ inputTokens: total.inputTokens,
653
+ outputTokens: total.outputTokens,
654
+ cost: total.cost,
655
+ unpriced: total.unpriced,
656
+ ttftMs: ttftCount === 0 ? null : ttftSum / ttftCount,
657
+ decodeTokens: speedCount === 0 ? null : speedSum / speedCount,
658
+ decodeCount: speedCount === 0 ? null : speedCount,
659
+ };
660
+ }
661
+ function NativeRequestRow({ request, t }) {
662
+ const speed = request.durationMs !== null && request.outputTokens !== null && request.durationMs > 0
663
+ ? `${formatValue(request.outputTokens / (request.durationMs / 1000))} token/s`
664
+ : '—';
665
+ return createElement('tr', undefined, createElement('td', { className: 'codingns-magpie-time-cell' }, formatTimestamp(request.time)), createElement('td', undefined, createElement('span', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [request.agent], size: 18, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('span', { className: 'codingns-magpie-agent-tag' }, request.agent ?? '—'))), createElement('td', undefined, createElement('span', { className: 'codingns-magpie-route-model', title: request.requestedModel ?? '' }, request.requestedModel ?? '—')), createElement('td', undefined, createElement('span', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [request.provider], size: 18, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('span', { className: 'codingns-magpie-provider-name' }, request.provider ?? '—')), request.account !== null && createElement('small', { className: 'codingns-magpie-table-secondary' }, request.account)), createElement('td', undefined, createElement('span', { className: 'codingns-magpie-route-model' }, request.servedModel ?? '—')), createElement('td', undefined, formatCompact(request.inputTokens)), createElement('td', undefined, formatCompact(request.outputTokens)), createElement('td', { className: 'codingns-magpie-cost-cell' }, formatValue(request.cost)), createElement('td', undefined, request.firstTextMs === null ? '—' : `${formatValue(request.firstTextMs)} ms`), createElement('td', undefined, request.durationMs === null ? '—' : `${formatValue(request.durationMs / 1000)} s`), createElement('td', undefined, speed), createElement('td', undefined, createElement('span', { className: `codingns-magpie-status ${request.status !== null && /^2/u.test(request.status) ? 'is-ok' : 'is-warn'}` }, request.status ?? '—')));
666
+ }
667
+ /** Compact ledger card used below 650px: same fields as the native table, stacked for narrow screens. */
668
+ function NativeRequestCard({ request, t }) {
669
+ const speed = request.durationMs !== null && request.outputTokens !== null && request.durationMs > 0
670
+ ? `${formatValue(request.outputTokens / (request.durationMs / 1000))} token/s`
671
+ : '—';
672
+ const facts = [
673
+ `${t('magpie.ncolInput')} ${formatCompact(request.inputTokens)}`,
674
+ `${t('magpie.ncolOutput')} ${formatCompact(request.outputTokens)}`,
675
+ `${t('magpie.ncolCost')} ${formatValue(request.cost)}`,
676
+ request.firstTextMs === null ? null : `${t('magpie.ncolFirstText')} ${formatValue(request.firstTextMs)} ms`,
677
+ request.durationMs === null ? null : `${t('magpie.ncolDuration')} ${formatValue(request.durationMs / 1000)} s`,
678
+ `${t('magpie.ncolSpeed')} ${speed}`,
679
+ ].filter((item) => item !== null);
680
+ return createElement('article', { className: 'codingns-magpie-reqcard' }, createElement('div', { className: 'codingns-magpie-reqcard-top' }, createElement('span', { className: 'codingns-magpie-time-cell' }, formatTimestamp(request.time)), createElement('span', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [request.agent], size: 16, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('span', { className: 'codingns-magpie-agent-tag' }, request.agent ?? '—')), createElement('span', { className: `codingns-magpie-status ${request.status !== null && /^2/u.test(request.status) ? 'is-ok' : 'is-warn'}` }, request.status ?? '—')), createElement('div', { className: 'codingns-magpie-reqcard-route' }, createElement('span', { className: 'codingns-magpie-route-model', title: request.requestedModel ?? '' }, request.requestedModel ?? '—'), createElement('span', { className: 'codingns-magpie-reqcard-arrow', 'aria-hidden': true }, '→'), createElement('span', { className: 'codingns-magpie-route-model' }, request.servedModel ?? '—')), createElement('div', { className: 'codingns-magpie-reqcard-sub' }, createElement('span', { className: 'codingns-magpie-provider-name' }, request.provider ?? '—'), request.account !== null && createElement('span', { className: 'codingns-magpie-table-secondary' }, request.account)), createElement('div', { className: 'codingns-magpie-reqcard-facts' }, ...facts.map((fact) => createElement('span', { key: fact }, fact))));
681
+ }
682
+ /**
683
+ * 订阅卡显示口径的切换控件。
684
+ *
685
+ * 与原生 Magpie 一样是一个两段式分段控件,固定贴在工具栏最右侧;选中结果写回
686
+ * Host 设置,因此刷新页面或换设备后仍保持同一读法。设置不可写时按钮禁用而不隐藏,
687
+ * 避免控件在布局里消失导致工具栏跳动。
688
+ */
689
+ export function QuotaViewSwitch({ value, disabled, failed, onChange, t }) {
690
+ const label = t('magpie.quotaViewLabel');
691
+ return createElement('div', { className: 'codingns-magpie-quota-view', role: 'group', 'aria-label': label },
692
+ // 图标只提供视觉锚点;口径名称放在 aria-label 里,读屏仍能读出整个控件。
693
+ createElement('span', { className: 'codingns-magpie-quota-view-icon', 'aria-hidden': true, title: failed ? t('magpie.quotaViewFailed') : label }, createElement(QuotaViewIcon, {})), createElement('div', { className: 'codingns-magpie-quota-view-segment' }, ...QUOTA_VIEWS.map((entry) => createElement('button', {
694
+ key: entry.id,
695
+ type: 'button',
696
+ className: `codingns-magpie-quota-view-option${value === entry.id ? ' is-active' : ''}`,
697
+ 'aria-pressed': value === entry.id,
698
+ disabled,
699
+ onClick: () => {
700
+ if (value !== entry.id)
701
+ onChange(entry.id);
702
+ },
703
+ }, t(entry.label)))));
704
+ }
705
+ /** 口径控件左侧的圆形图标:半实心圆表示"部分已用",与进度条的语义一致。 */
706
+ function QuotaViewIcon() {
707
+ return createElement('svg', {
708
+ width: 15,
709
+ height: 15,
710
+ viewBox: '0 0 16 16',
711
+ fill: 'none',
712
+ 'aria-hidden': true,
713
+ className: 'codingns-magpie-quota-view-icon-svg',
714
+ }, createElement('circle', { cx: 8, cy: 8, r: 6.25, stroke: 'currentColor', strokeWidth: 1.5, opacity: 0.45 }), createElement('path', { d: 'M8 1.75a6.25 6.25 0 0 1 0 12.5z', fill: 'currentColor' }));
715
+ }
716
+ export function QuotasPage({ dashboard, loading, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
717
+ const quotaUnavailable = dashboard?.quotaStatus !== 'available';
718
+ const quotas = dashboard?.quotas ?? [];
719
+ const quotaHistory = dashboard?.quotaHistory ?? [];
720
+ return createElement('div', { className: 'codingns-magpie-view' }, quotaUnavailable
721
+ ? createElement(EmptyState, { message: t(dashboard === null && loading ? 'magpie.loading' : 'magpie.quotaUnavailable'), loading: dashboard === null && loading })
722
+ : quotas.length === 0
723
+ ? createElement(EmptyState, { message: t('magpie.noQuotas') })
724
+ : createElement('div', { className: 'codingns-magpie-quota-grid' }, ...groupQuotaRowsByProvider(quotas).map((group) => createElement(QuotaGroupCard, { key: group.provider, group, quotaView, t }))), createElement('section', { className: 'codingns-magpie-panel codingns-magpie-history-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', undefined, createElement('h2', undefined, t('magpie.quotaHistory')), createElement('p', undefined, t('magpie.quotaHistoryNote')))), quotaHistory.length === 0
725
+ ? createElement('div', { className: 'codingns-magpie-inline-empty' }, t(dashboard === null && loading ? 'magpie.loading' : dashboard?.quotaHistoryStatus === 'available' ? 'magpie.noHistory' : 'magpie.historyUnavailable'))
726
+ : createElement('div', { className: 'codingns-magpie-history-grid' }, ...quotaHistory.map((history) => createElement(QuotaHistoryCard, { key: `${history.provider}:${history.user ?? ''}`, history })))));
727
+ }
728
+ function StatStrip({ stats }) {
729
+ return createElement('div', { className: 'codingns-magpie-statstrip' }, ...stats.map((stat) => createElement('div', { key: stat.label, className: 'codingns-magpie-statcell' }, createElement('span', { className: 'codingns-magpie-statcell-label' }, stat.label), createElement('strong', { className: 'codingns-magpie-statcell-value' }, stat.value), stat.detail !== undefined && stat.detail !== '' && createElement('small', { className: 'codingns-magpie-statcell-detail' }, stat.detail))));
730
+ }
731
+ function BarChart({ points, metric, label }) {
732
+ const values = points.map((point) => metric === 'calls'
733
+ ? point.calls
734
+ : metric === 'tokens' ? sumNullable(point.inputTokens, point.outputTokens) : point.cost);
735
+ const known = values.filter((value) => value !== null && Number.isFinite(value));
736
+ if (known.length === 0)
737
+ return createElement('div', { className: 'codingns-magpie-inline-empty' }, '—');
738
+ const max = Math.max(1, ...known);
739
+ const labelEvery = Math.max(1, Math.ceil(points.length / 12));
740
+ return createElement('div', { className: 'codingns-magpie-barchart' }, createElement('div', { className: 'codingns-magpie-barchart-plot', role: 'img', 'aria-label': label }, ...points.map((point, index) => {
741
+ const value = values[index] ?? null;
742
+ const height = value === null || !Number.isFinite(value) ? 0 : Math.max(2, (value / max) * 100);
743
+ return createElement('div', { key: `${point.time ?? point.label ?? index}`, className: 'codingns-magpie-bar-slot', title: `${point.label ?? point.time ?? ''} ${value === null ? '—' : formatValue(value)}` }, createElement('span', { className: 'codingns-magpie-bar', style: { height: `${height}%` } }, value !== null && createElement('span', { className: 'codingns-magpie-bar-cap' })));
744
+ })), createElement('div', { className: 'codingns-magpie-barchart-axis' }, ...points.flatMap((point, index) => index % labelEvery === 0
745
+ ? [createElement('span', { key: `${point.time ?? index}`, className: 'codingns-magpie-barchart-tick' }, point.label ?? '')]
746
+ : [])), createElement('div', { className: 'codingns-magpie-barchart-peak' }, formatValue(Math.max(...known))));
747
+ }
748
+ function ProgressSection({ title, rows, t, onOpen, header, limit = 8 }) {
749
+ const sorted = [...rows].sort((left, right) => (right.cost ?? -1) - (left.cost ?? -1) || (right.calls ?? -1) - (left.calls ?? -1));
750
+ const display = limit === undefined ? sorted : sorted.slice(0, limit);
751
+ const hasCost = display.some((row) => row.cost !== null);
752
+ const max = Math.max(1, ...display.map((row) => hasCost ? row.cost ?? 0 : sumNullable(row.inputTokens, row.outputTokens) ?? 0));
753
+ return createElement('section', { className: 'codingns-magpie-panel codingns-magpie-progress-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('h2', undefined, title), header), display.length === 0
754
+ ? createElement('div', { className: 'codingns-magpie-inline-empty' }, '—')
755
+ : createElement('div', { className: 'codingns-magpie-progress-list' }, ...display.map((row) => {
756
+ const label = row.label ?? row.name ?? row.model ?? row.account ?? row.provider ?? row.agent ?? row.id;
757
+ const weight = Math.max(0, (hasCost ? row.cost ?? 0 : sumNullable(row.inputTokens, row.outputTokens) ?? 0));
758
+ const body = createElement('div', { className: 'codingns-magpie-progress-row' }, createElement('div', { className: 'codingns-magpie-progress-head' }, createElement('span', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [row.agent, row.provider, row.model, row.id], size: 20, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('span', { className: 'codingns-magpie-progress-copy' }, createElement('span', { className: 'codingns-magpie-progress-name', title: label }, label), (row.account ?? row.provider) !== null && createElement('small', { className: 'codingns-magpie-progress-context', title: row.account ?? row.provider ?? '' }, row.account ?? row.provider))), createElement('strong', { className: 'codingns-magpie-progress-value' }, hasCost ? formatValue(row.cost) : formatCompact(sumNullable(row.inputTokens, row.outputTokens)))), createElement('div', { className: 'codingns-magpie-progress-track' }, createElement('span', { style: { width: `${Math.max(2, (weight / max) * 100)}%` } })), createElement('small', { className: 'codingns-magpie-progress-meta' }, `${formatInteger(row.calls)} ${t('magpie.metricCalls')} · ${formatCompact(sumNullable(row.inputTokens, row.outputTokens))} token · ${formatValue(row.cost)}`));
759
+ return onOpen === undefined
760
+ ? createElement('div', { key: row.id, className: 'codingns-magpie-progress-item' }, body)
761
+ : createElement('button', { key: row.id, type: 'button', className: 'codingns-magpie-progress-item is-button', onClick: () => onOpen(row) }, body);
762
+ })));
763
+ }
764
+ /**
765
+ * 一个厂商的订阅卡:卡头是厂商本身,卡内按账号分段列出额度。
766
+ *
767
+ * 同一厂商的多个账号(如 WorkBuddy 的 4 个)共用一张卡,避免每个账号各占一张卡导致
768
+ * 同类账号被拆散在网格各处;单账号厂商的读法与改造前一致。
769
+ */
770
+ function QuotaGroupCard({ group, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
771
+ const first = group.accounts[0];
772
+ // 默认展开:只有用户主动收起后才是折叠态。
773
+ const [collapsed, setCollapsed] = useState(false);
774
+ const multi = group.accounts.length > 1;
775
+ const visible = multi && collapsed ? group.accounts.slice(0, 1) : group.accounts;
776
+ const hidden = group.accounts.length - visible.length;
777
+ return createElement('article', { className: 'codingns-magpie-panel codingns-magpie-quota-card' }, createElement('div', { className: 'codingns-magpie-quota-topline' }, createElement(MagpieProviderLogo, { identities: [group.provider, group.name], logoDataUrl: group.logoDataUrl, size: 52, className: 'codingns-magpie-quota-icon codingns-magpie-provider-logo' }), createElement('div', undefined, createElement('h2', undefined, group.name || group.provider), createElement('p', undefined, first.plan ?? first.kind ?? group.provider)), multi && createElement('button', {
778
+ type: 'button',
779
+ className: 'codingns-magpie-quota-count',
780
+ 'aria-expanded': !collapsed,
781
+ onClick: () => setCollapsed((value) => !value),
782
+ title: collapsed ? t('magpie.quotaShowAllAccounts') : t('magpie.quotaShowMainAccount'),
783
+ }, collapsed
784
+ ? `${t('magpie.quotaAccounts', { count: group.accounts.length })} · ${t('magpie.quotaRemainingAccounts', { count: hidden })}`
785
+ : t('magpie.quotaCollapseAccounts', { count: group.accounts.length }))), ...visible.map((quota, index) => createElement(QuotaAccountSection, {
786
+ key: `${group.provider}:${quota.user ?? index}`,
787
+ quota,
788
+ quotaView,
789
+ t,
790
+ // 单账号时不重复显示账号段头,保持与改造前一致的读法。
791
+ showHeading: multi,
792
+ })));
793
+ }
794
+ /**
795
+ * 卡内单个账号:账号行、余额、窗口进度条与每日用量。
796
+ *
797
+ * 有百分比的账号走进度条;没有百分比但有余额的账号(New-API 的 `$74.14`、
798
+ * Sub2api 的钱包余额、按积分计量的 WorkBuddy)单独显示余额行,与 Magpie 原生卡片
799
+ * 一致——过去这些账号只剩一条空进度条,余额被完全藏起来。
800
+ */
801
+ function QuotaAccountSection({ quota, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, showHeading }) {
802
+ // `daily` 现在带 `since`,按日点位于 `days` 里。
803
+ const dailyValues = quota.daily.days.map((point) => point.used).filter((value) => value !== null);
804
+ const balance = resolveQuotaAccountBalance(quota);
805
+ // 只有真正带百分比的窗口才画进度条;纯金额窗口在余额行里已经有完整读法。
806
+ const percentWindows = quota.windows.filter((window) => window.usedPercent !== null || window.remainingPercent !== null);
807
+ return createElement('div', { className: 'codingns-magpie-quota-account-block' }, showHeading && createElement('div', { className: 'codingns-magpie-quota-account-head' }, createElement('strong', undefined, quota.alias ?? quota.name), quota.user !== null && createElement('span', undefined, t('magpie.account', { account: quota.user })), quota.plan !== null && createElement('small', undefined, quota.plan)), !showHeading && quota.user !== null && createElement('div', { className: 'codingns-magpie-quota-account' }, t('magpie.account', { account: quota.user })), balance !== null && createElement('div', { className: 'codingns-magpie-quota-balance' }, createElement('span', { className: 'codingns-magpie-quota-balance-label' }, t('magpie.balance')), createElement('strong', { className: balance.money ? 'codingns-magpie-quota-balance-value is-money' : 'codingns-magpie-quota-balance-value', title: balance.raw }, balance.text)), quota.windows.length === 0
808
+ ? balance === null && createElement('div', { className: 'codingns-magpie-inline-empty' }, t('magpie.noLimit'))
809
+ : percentWindows.length > 0 && percentWindows.map((window) => {
810
+ const used = window.usedPercent ?? (window.remainingPercent === null ? null : 100 - window.remainingPercent);
811
+ const remaining = window.remainingPercent ?? (used === null ? null : 100 - used);
812
+ // 口径只改读法:同一组百分比既可以读成"已用多少",也可以读成"还剩多少",
813
+ // 进度条宽度随口径取对应值,顶部标签也换成对应的本地化文案。
814
+ const barPercent = quotaView === 'used' ? used : remaining;
815
+ const headline = quotaView === 'used'
816
+ ? (used === null ? window.display ?? '—' : t('magpie.used', { percent: Math.round(used) }))
817
+ : (remaining === null ? window.display ?? '—' : t('magpie.remaining', { percent: Math.round(remaining) }));
818
+ return createElement('div', { key: window.name, className: 'codingns-magpie-quota-window' }, createElement('div', { className: 'codingns-magpie-quota-window-head' }, createElement('strong', undefined, window.name), createElement('span', undefined, headline)), createElement('div', { className: 'codingns-magpie-track quota-track' }, used === null || remaining === null
819
+ ? null
820
+ : createElement('span', { style: { width: `${Math.max(0, Math.min(100, barPercent ?? 0))}%` } })), createElement('div', { className: 'codingns-magpie-quota-foot' }, createElement('span', undefined, window.amount !== null && window.limit !== null ? `${formatValue(window.amount)} / ${formatValue(window.limit)} ${window.unit ?? ''}` : window.display ?? ''), window.resetsAt !== null && createElement('span', undefined, t('magpie.resets', { time: formatTimestamp(window.resetsAt) }))));
821
+ }),
822
+ // 有余额但没有百分比的账号,把重置时间单独留在余额下面。
823
+ balance !== null && percentWindows.length === 0 && quota.windows.some((window) => window.resetsAt !== null) && createElement('div', { className: 'codingns-magpie-quota-foot' }, ...quota.windows.filter((window) => window.resetsAt !== null).map((window) => createElement('span', { key: window.name }, t('magpie.resets', { time: formatTimestamp(window.resetsAt) })))), quota.daily.days.length > 1 && createElement('div', { className: 'codingns-magpie-daily-chart' }, createElement(Sparkline, { values: dailyValues }), createElement('small', undefined, t('magpie.dailyUsage'))));
824
+ }
825
+ function QuotaHistoryCard({ history }) {
826
+ return createElement('div', { className: 'codingns-magpie-history-card' }, createElement('div', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [history.provider], size: 22, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('strong', undefined, history.provider)), history.user !== null && createElement('small', undefined, history.user), ...history.lines.map((line) => createElement('div', { key: line.name, className: 'codingns-magpie-history-line' }, createElement('div', { className: 'codingns-magpie-history-line-head' }, createElement('span', undefined, line.name), createElement('span', undefined, line.points.at(-1)?.left === null || line.points.at(-1)?.left === undefined ? '—' : formatValue(line.points.at(-1).left))), createElement(Sparkline, { values: line.points.map((point) => point.left).filter((value) => value !== null) }))));
827
+ }
828
+ function Sparkline({ values }) {
829
+ const max = Math.max(1, ...values);
830
+ const points = values.map((value, index) => `${values.length <= 1 ? 50 : 2 + (index * 96) / (values.length - 1)},${32 - (Math.max(0, value) / max) * 28}`).join(' ');
831
+ return createElement('svg', { className: 'codingns-magpie-sparkline', viewBox: '0 0 100 36', preserveAspectRatio: 'none', 'aria-hidden': true }, createElement('polyline', { points, fill: 'none' }));
832
+ }
833
+ function EmptyState({ message, loading = false }) {
834
+ return createElement('div', { className: `codingns-magpie-empty${loading ? ' is-loading' : ''}`, role: loading ? 'status' : 'note' }, createElement('span', { className: 'codingns-magpie-empty-mark', 'aria-hidden': true }, loading ? createElement('span', { className: 'codingns-magpie-spinner' }) : '∅'), createElement('span', undefined, message));
835
+ }
836
+ function InlineNotice({ message }) {
837
+ return createElement('div', { className: 'codingns-magpie-notice', role: 'status' }, createElement('span', undefined, 'i'), message);
838
+ }
839
+ /** 刷新图标沿用 DSH 的 1.6 描边简约样式;加载时整体旋转,静默重绘状态下保持静止。 */
840
+ function RefreshIcon({ busy = false }) {
841
+ return createElement('svg', {
842
+ className: `codingns-magpie-refresh-icon${busy ? ' is-spinning' : ''}`,
843
+ width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.6,
844
+ strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true,
845
+ }, createElement('path', { d: 'M20 11a8 8 0 1 0-2.3 5.6' }), createElement('path', { d: 'M20 4.5V11h-6.5' }));
846
+ }
847
+ function SearchIcon() {
848
+ return createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', 'aria-hidden': true }, createElement('circle', { cx: 10.8, cy: 10.8, r: 6.8 }), createElement('path', { d: 'm16 16 4.4 4.4' }));
849
+ }
850
+ export async function callMagpieRpc(rpc, action, payload, signal) {
851
+ const result = await callCodingNsRpcResult(rpc, `magpie/${action}`, payload, signal);
852
+ if (!result.ok)
853
+ throw Object.assign(new Error(result.error.message), { code: result.error.code });
854
+ return result.value;
855
+ }
856
+ export function magpieUpdateIssueForRpcError(error) {
857
+ const record = error !== null && typeof error === 'object' ? error : undefined;
858
+ const code = typeof record?.code === 'string' ? record.code : '';
859
+ const message = typeof record?.message === 'string' ? record.message : '';
860
+ if (code === 'CODINGNS_MAGPIE_UPDATE_UNSUPPORTED')
861
+ return 'cli-version-unsupported';
862
+ if (code === 'CODINGNS_MAGPIE_UPDATE_INVALID')
863
+ return 'update-response-invalid';
864
+ if (code === 'CODINGNS_MAGPIE_UPDATE_CONFIRMATION_REQUIRED')
865
+ return 'host-request-failed';
866
+ if (code === 'CODINGNS_RPC_NOT_FOUND'
867
+ || (code === 'Error' && /^Unknown Magpie RPC: update\/(?:status|notes|check|install)$/u.test(message))) {
868
+ return 'host-rpc-outdated';
869
+ }
870
+ if (code === 'CODINGNS_MAGPIE_DISABLED')
871
+ return 'feature-disabled';
872
+ return 'host-request-failed';
873
+ }
874
+ export function magpieDashboardIssueForRpcError(error) {
875
+ const code = error !== null && typeof error === 'object' && 'code' in error && typeof error.code === 'string'
876
+ ? error.code
877
+ : '';
878
+ if (code === 'CODINGNS_RPC_NOT_FOUND')
879
+ return 'host-rpc-outdated';
880
+ if (code === 'CODINGNS_MAGPIE_DISABLED')
881
+ return 'feature-disabled';
882
+ return 'host-request-failed';
883
+ }
884
+ export function magpieManagementIssueForRpcError(error) {
885
+ const record = error !== null && typeof error === 'object' ? error : undefined;
886
+ const code = typeof record?.code === 'string' ? record.code : '';
887
+ const message = typeof record?.message === 'string' ? record.message : '';
888
+ if (code === 'CODINGNS_RPC_NOT_FOUND'
889
+ || (code === 'Error' && /^Unknown Magpie RPC: management\/(?:snapshot|provider\/update|group\/update|account\/update)$/u.test(message))) {
890
+ return 'host-rpc-outdated';
891
+ }
892
+ if (code === 'CODINGNS_MAGPIE_DISABLED')
893
+ return 'feature-disabled';
894
+ return 'host-request-failed';
895
+ }
896
+ function dashboardIssueLocaleKey(issue) {
897
+ switch (issue) {
898
+ case 'web-api-unavailable': return 'magpie.webApiUnavailable';
899
+ case 'cli-unavailable': return 'magpie.cliUnavailable';
900
+ case 'cli-version-unsupported': return 'magpie.cliVersionUnsupported';
901
+ case 'model-catalog-unavailable': return 'magpie.catalogUnavailable';
902
+ case 'host-rpc-outdated': return 'magpie.hostRpcOutdated';
903
+ case 'feature-disabled': return 'magpie.featureDisabled';
904
+ case 'host-request-failed': return 'magpie.hostRequestFailed';
905
+ }
906
+ }
907
+ function managementIssueLocaleKey(issue) {
908
+ switch (issue) {
909
+ case 'web-api-unavailable': return 'magpie.webApiUnavailable';
910
+ case 'cli-unavailable': return 'magpie.cliUnavailable';
911
+ case 'cli-version-unsupported': return 'magpie.management.cliVersionUnsupported';
912
+ case 'providers-unavailable': return 'magpie.management.providersUnavailable';
913
+ case 'groups-unavailable': return 'magpie.management.groupsUnavailable';
914
+ case 'accounts-unavailable': return 'magpie.management.accountsUnavailable';
915
+ case 'host-rpc-outdated': return 'magpie.hostRpcOutdated';
916
+ case 'feature-disabled': return 'magpie.featureDisabled';
917
+ case 'host-request-failed': return 'magpie.hostRequestFailed';
918
+ }
919
+ }
920
+ function updateIssueLocaleKey(issue) {
921
+ switch (issue) {
922
+ case 'web-api-unavailable': return 'magpie.webApiUnavailable';
923
+ case 'cli-unavailable': return 'magpie.cliUnavailable';
924
+ case 'cli-version-unsupported': return 'magpie.update.unsupported';
925
+ case 'update-response-invalid': return 'magpie.update.invalid';
926
+ case 'host-rpc-outdated': return 'magpie.hostRpcOutdated';
927
+ case 'feature-disabled': return 'magpie.featureDisabled';
928
+ case 'host-request-failed': return 'magpie.hostRequestFailed';
929
+ }
930
+ }
931
+ function updateStateLabel(state, t) {
932
+ return t(`magpie.update.state.${state}`);
933
+ }
934
+ function sumNullable(left, right) {
935
+ return left === null && right === null ? null : (left ?? 0) + (right ?? 0);
936
+ }
937
+ /** Collapse raw model rows while retaining an identity only when it is unambiguous. */
938
+ export function aggregateModelUsageRows(rows) {
939
+ const grouped = new Map();
940
+ const nameKeys = new Map();
941
+ for (const row of rows) {
942
+ const modelKey = modelUsageKey(row);
943
+ if (modelKey === '')
944
+ continue;
945
+ const nameKey = modelUsageNameKey(row);
946
+ // Some Magpie versions qualify the model ID differently for each account. When the
947
+ // account rows share a provider and display name, the display name is the stable model
948
+ // identity and must win over that account-specific ID.
949
+ const groupKey = nameKey === null ? modelKey : nameKeys.get(nameKey) ?? modelKey;
950
+ if (nameKey !== null)
951
+ nameKeys.set(nameKey, groupKey);
952
+ const current = grouped.get(groupKey);
953
+ if (current === undefined) {
954
+ grouped.set(groupKey, { ...row, id: `model:${groupKey}`, account: null });
955
+ continue;
956
+ }
957
+ grouped.set(groupKey, {
958
+ ...current,
959
+ name: current.name ?? row.name,
960
+ provider: mergeIdentity(current.provider, row.provider),
961
+ model: current.model ?? row.model,
962
+ agent: mergeIdentity(current.agent, row.agent),
963
+ calls: sumNullable(current.calls, row.calls),
964
+ errors: sumNullable(current.errors, row.errors),
965
+ retried: sumNullable(current.retried, row.retried),
966
+ inputTokens: sumNullable(current.inputTokens, row.inputTokens),
967
+ outputTokens: sumNullable(current.outputTokens, row.outputTokens),
968
+ cacheReadTokens: sumNullable(current.cacheReadTokens, row.cacheReadTokens),
969
+ cacheWriteTokens: sumNullable(current.cacheWriteTokens, row.cacheWriteTokens),
970
+ reasoningTokens: sumNullable(current.reasoningTokens, row.reasoningTokens),
971
+ cost: sumNullable(current.cost, row.cost),
972
+ unpriced: sumNullable(current.unpriced, row.unpriced),
973
+ credits: sumNullable(current.credits, row.credits),
974
+ credited: sumNullable(current.credited, row.credited),
975
+ timed: sumNullable(current.timed, row.timed),
976
+ ttftMs: sumNullable(current.ttftMs, row.ttftMs),
977
+ decodeMs: sumNullable(current.decodeMs, row.decodeMs),
978
+ decodeTokens: sumNullable(current.decodeTokens, row.decodeTokens),
979
+ decodeCount: sumNullable(current.decodeCount, row.decodeCount),
980
+ });
981
+ }
982
+ return [...grouped.values()];
983
+ }
984
+ /** Return rows with unique React identities so duplicate Magpie IDs cannot leak across modes. */
985
+ export function modelBreakdownRows(rows, mode) {
986
+ if (mode === 'aggregate')
987
+ return aggregateModelUsageRows(rows);
988
+ const seen = new Map();
989
+ return rows.map((row, index) => {
990
+ const baseId = row.id.trim() || `row-${index}`;
991
+ const occurrence = seen.get(baseId) ?? 0;
992
+ seen.set(baseId, occurrence + 1);
993
+ return occurrence === 0 ? row : { ...row, id: `${baseId}#${occurrence}` };
994
+ });
995
+ }
996
+ function modelUsageKey(row) {
997
+ return (row.model ?? row.name ?? row.id).trim().toLocaleLowerCase();
998
+ }
999
+ function modelUsageNameKey(row) {
1000
+ if (row.account === null)
1001
+ return null;
1002
+ const name = row.name?.trim().toLocaleLowerCase() ?? '';
1003
+ const provider = row.provider?.trim().toLocaleLowerCase() ?? '';
1004
+ return name === '' ? null : `${provider}\u0000${name}`;
1005
+ }
1006
+ function mergeIdentity(left, right) {
1007
+ if (left === null)
1008
+ return right;
1009
+ if (right === null || left === right)
1010
+ return left;
1011
+ return null;
1012
+ }
1013
+ function formatInteger(value) {
1014
+ return value === null ? '—' : new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(value);
1015
+ }
1016
+ function formatCompact(value) {
1017
+ return value === null ? '—' : new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 }).format(value);
1018
+ }
1019
+ function formatValue(value) {
1020
+ return value === null ? '—' : new Intl.NumberFormat(undefined, { maximumFractionDigits: value >= 100 ? 1 : 4 }).format(value);
1021
+ }
1022
+ function formatTimestamp(value) {
1023
+ if (value === null)
1024
+ return '—';
1025
+ const parsed = Date.parse(value);
1026
+ if (!Number.isFinite(parsed))
1027
+ return value;
1028
+ return new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(parsed);
1029
+ }
1030
+ const pageStyle = {
1031
+ display: 'flex', flexDirection: 'column', width: '100%', height: '100%', minWidth: 0, minHeight: 0, boxSizing: 'border-box',
1032
+ paddingTop: 'var(--dsh-frame-top-clearance, 0px)',
1033
+ color: 'var(--dsw-alias-label-primary,CanvasText)', background: 'var(--mp-surface)',
1034
+ border: 0, borderRadius: 0, boxShadow: 'none', overflow: 'hidden',
1035
+ };
1036
+ const MAGPIE_PAGE_CSS = `
1037
+ .codingns-magpie-page{--mp-accent:var(--dsw-alias-state-business-primary,#4176e6);--mp-chart:var(--mp-accent);--mp-ink:var(--dsw-alias-label-primary,CanvasText);--mp-muted:var(--dsw-alias-label-tertiary,GrayText);--mp-caption:var(--dsw-alias-label-caption,GrayText);--mp-line:var(--dsw-alias-border-l2,rgba(0,0,0,.1));--mp-line-strong:var(--dsw-alias-border-l3,rgba(0,0,0,.12));--mp-card:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1,transparent));--mp-layer:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06)));--mp-hover:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.1));--mp-surface:var(--dsw-alias-bg-base,Canvas);--mp-radius:var(--dsw-radius-lg,12px);position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-width:0;min-height:0;font:inherit;color:var(--mp-ink);background:var(--mp-surface)}
1038
+ .codingns-magpie-page *{box-sizing:border-box}
1039
+ .codingns-magpie-topbar{flex:0 0 auto;display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:12px;min-height:56px;padding:var(--mp-topbar-pad-y) calc(var(--mp-topbar-pad-x) + var(--mp-leading-inset));border-bottom:.5px solid var(--mp-line-strong);background:var(--mp-surface)}
1040
+ .codingns-magpie-topbar-left{display:flex;align-items:center;justify-self:start;min-width:0;max-width:100%}
1041
+ .codingns-magpie-topbar-right{display:flex;align-items:center;justify-self:end;gap:8px;min-width:0}
1042
+ .codingns-magpie-version-button{display:inline-flex;align-items:center;min-height:30px;padding:0 9px;border:.5px solid var(--mp-line-strong);border-radius:999px;background:var(--mp-card);color:var(--mp-muted);font:inherit;font-size:${uiFontSize(12)};font-variant-numeric:tabular-nums;cursor:pointer;transition:background .15s,color .15s,border-color .15s}.codingns-magpie-version-button:hover,.codingns-magpie-version-button[aria-expanded=true]{border-color:color-mix(in srgb,var(--mp-accent) 55%,var(--mp-line-strong));background:color-mix(in srgb,var(--mp-accent) 8%,var(--mp-card));color:var(--mp-ink)}.codingns-magpie-version-button:focus-visible{outline:2px solid color-mix(in srgb,var(--mp-accent) 55%,transparent);outline-offset:1px}
1043
+ .codingns-magpie-icon-button{display:grid;place-items:center;flex:0 0 auto;width:30px;height:30px;border:0;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--mp-muted);cursor:pointer;transition:background .15s,color .15s}
1044
+ .codingns-magpie-icon-button:hover{background:var(--mp-hover);color:var(--mp-ink)}
1045
+ .codingns-magpie-icon-button:active{background:var(--mp-layer)}
1046
+ .codingns-magpie-icon-button:focus-visible{outline:2px solid color-mix(in srgb,var(--mp-accent) 55%,transparent);outline-offset:1px}
1047
+ .codingns-magpie-icon-button:disabled{opacity:.5;cursor:default;background:transparent;color:var(--mp-muted)}
1048
+ .codingns-magpie-refresh-icon{display:block;flex:0 0 auto;transition:transform .2s}
1049
+ .codingns-magpie-refresh-icon.is-spinning{animation:magpie-spin .9s linear infinite}
1050
+ @media(prefers-reduced-motion:reduce){.codingns-magpie-refresh-icon.is-spinning{animation:none}}
1051
+ .codingns-magpie-tabs{display:flex;align-items:center;justify-content:center;gap:2px;justify-self:center;min-width:0;overflow-x:auto;scrollbar-width:none}
1052
+ .codingns-magpie-tabs::-webkit-scrollbar{display:none}
1053
+ .codingns-magpie-tab{position:relative;display:inline-flex;align-items:center;gap:7px;flex:0 0 auto;min-height:42px;padding:0 12px;border:0;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(13)};white-space:nowrap;cursor:pointer;transition:color .16s,background .16s}
1054
+ .codingns-magpie-tab:hover{color:var(--mp-ink);background:var(--mp-hover)}
1055
+ .codingns-magpie-tab.is-active{background:transparent;color:var(--mp-ink);font-weight:600;box-shadow:none}
1056
+ .codingns-magpie-tab.is-active:after{content:'';position:absolute;left:10px;right:10px;bottom:0;height:2px;border-radius:2px;background:var(--mp-accent)}
1057
+ .codingns-magpie-tab-icon{display:grid;place-items:center;width:15px;font-size:${uiFontSize(13)};line-height:1;opacity:.85}
1058
+ .codingns-magpie-toolbar{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:14px;min-height:46px;padding:0 20px;border-bottom:.5px solid var(--mp-line);background:var(--mp-surface)}
1059
+ .codingns-magpie-toolbar-left{display:flex;align-items:center;gap:12px;min-width:0}
1060
+ .codingns-magpie-periods{flex-direction:row;overflow:auto;display:flex;align-items:center;gap:2px;min-width:0;padding:2px;border-radius:999px;background:var(--mp-layer);scrollbar-width:none}
1061
+ .codingns-magpie-periods::-webkit-scrollbar{display:none}
1062
+ .codingns-magpie-period{flex:0 0 auto;min-height:26px;padding:0 12px;border:0;border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(13)};white-space:nowrap;cursor:pointer;transition:background .16s,color .16s}
1063
+ .codingns-magpie-period:hover{color:var(--mp-ink)}
1064
+ .codingns-magpie-period.is-active{background:var(--mp-surface);color:var(--mp-ink);font-weight:600;box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12))}
1065
+ .codingns-magpie-live-dot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#3f9d6b);box-shadow:0 0 0 4px color-mix(in srgb,var(--dsw-alias-state-success-primary,#3f9d6b) 12%,transparent)}
1066
+ .codingns-magpie-main{flex:1 1 0;min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;background:var(--mp-surface)}
1067
+ .codingns-magpie-view{max-width:1320px;margin:0 auto;padding:18px clamp(16px,2.4vw,28px) 40px}
1068
+ .codingns-magpie-panel{min-width:0;border:.5px solid var(--mp-line);border-radius:var(--mp-radius);background:var(--mp-card)}
1069
+ .codingns-magpie-panel-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:13px}
1070
+ .codingns-magpie-panel-head h2{margin:0;font-size:${uiFontSize(14)};font-weight:600}.codingns-magpie-panel-head p{margin:4px 0 0;color:var(--mp-muted);font-size:${uiFontSize(13)};line-height:1.5}
1071
+ .codingns-magpie-chart-switch{display:flex;flex-wrap:wrap;gap:2px;padding:2px;border:0;border-radius:999px;background:var(--mp-layer)}
1072
+ .codingns-magpie-chart-switch button{border:0;border-radius:999px;padding:5px 10px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(12)};cursor:pointer}.codingns-magpie-chart-switch button.is-active{background:var(--mp-surface);color:var(--mp-ink);font-weight:600;box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12))}
1073
+ .codingns-magpie-track{height:4px;margin:7px 0 5px;border-radius:99px;background:var(--mp-hover);overflow:hidden}.codingns-magpie-track span{display:block;height:100%;min-width:2px;border-radius:inherit;background:linear-gradient(90deg,var(--mp-accent),color-mix(in srgb,var(--mp-accent) 55%,transparent));transition:width .35s ease}
1074
+ .codingns-magpie-text-button{border:0;background:transparent;color:var(--dsw-alias-link,var(--mp-accent));font:inherit;font-size:${uiFontSize(13)};font-weight:600;cursor:pointer}.codingns-magpie-text-button:hover{text-decoration:underline}
1075
+ .codingns-magpie-search{display:flex;align-items:center;gap:9px;max-width:420px;margin:0 0 14px;padding:8px 12px;border:.5px solid var(--dsw-alias-border-l4,rgba(0,0,0,.16));border-radius:10px;color:var(--mp-muted);background:var(--mp-card)}.codingns-magpie-search input{width:100%;border:0;outline:0;background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(13)}}
1076
+ .codingns-magpie-request-panel{overflow:hidden}.codingns-magpie-table-scroll{overflow:auto;overscroll-behavior-inline:contain}.codingns-magpie-table{width:100%;min-width:880px;border-collapse:collapse}.codingns-magpie-table th{padding:9px 12px;border-bottom:.5px solid var(--mp-line);color:var(--mp-caption);font-size:${uiFontSize(13)};font-weight:600;text-align:left;white-space:nowrap}.codingns-magpie-table td{max-width:250px;padding:10px 12px;border-bottom:.5px solid var(--mp-line);font-size:${uiFontSize(13)};vertical-align:middle}.codingns-magpie-table tbody tr:last-child td{border-bottom:0}.codingns-magpie-table tbody tr:hover{background:var(--mp-card)}
1077
+ .codingns-magpie-time-cell{white-space:nowrap;color:var(--mp-muted)}.codingns-magpie-provider-name,.codingns-magpie-route-model{display:block;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:620}.codingns-magpie-table-secondary{display:block;max-width:220px;margin-top:3px;overflow:hidden;color:var(--mp-caption);font-size:${uiFontSize(12)};text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-cost-cell{font-variant-numeric:tabular-nums}
1078
+ .codingns-magpie-status{display:inline-block;padding:2px 7px;border-radius:6px;background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#c08a3e) 12%,transparent);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#a06a2c));font-size:${uiFontSize(12)};font-variant-numeric:tabular-nums}.codingns-magpie-status.is-ok{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3f9d6b) 12%,transparent);color:var(--dsw-alias-state-success-primary,#3f9d6b)}
1079
+ .codingns-magpie-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-top:.5px solid var(--mp-line);color:var(--mp-muted);font-size:${uiFontSize(13)}}.codingns-magpie-pagination div{display:flex;gap:6px}.codingns-magpie-pagination button{min-height:28px;padding:0 10px;border:.5px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(13)};cursor:pointer}.codingns-magpie-pagination button:disabled{opacity:.4;cursor:not-allowed}
1080
+ /* Lay the subscription cards out in a multi-column flow: a CSS Grid row is as
1081
+ tall as its tallest card, which leaves large gaps under short cards when a
1082
+ provider has several accounts but a single window. Each column fills top to
1083
+ bottom instead, matching Magpie's own layout. */
1084
+ .codingns-magpie-quota-grid{column-width:340px;column-gap:12px;margin-bottom:13px}
1085
+ /* Never split a card across two columns: its title and quota stay together. */
1086
+ .codingns-magpie-quota-card{display:inline-block;width:100%;margin:0 0 12px;vertical-align:top;padding:17px;break-inside:avoid;page-break-inside:avoid}.codingns-magpie-quota-topline{display:flex;align-items:center;gap:10px;margin-bottom:13px}.codingns-magpie-quota-icon{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;background:var(--mp-layer);color:var(--mp-accent);font-weight:700}.codingns-magpie-quota-topline h2{margin:0;font-size:${uiFontSize(14)};font-weight:600}.codingns-magpie-quota-topline p{margin:3px 0 0;color:var(--mp-caption);font-size:${uiFontSize(13)}}.codingns-magpie-quota-account{margin:-5px 0 11px;color:var(--mp-muted);font-size:${uiFontSize(13)}}.codingns-magpie-quota-count{margin-left:auto;flex:none;min-height:26px;padding:0 9px;border:1px solid var(--mp-line);border-radius:999px;background:transparent;color:var(--mp-muted);font-size:${uiFontSize(12)};cursor:pointer;white-space:nowrap}.codingns-magpie-quota-count:hover{color:var(--mp-fg);border-color:var(--mp-accent)}.codingns-magpie-quota-count:focus-visible{outline:none;box-shadow:0 0 0 2px var(--mp-accent)}.codingns-magpie-quota-account-block + .codingns-magpie-quota-account-block{margin-top:12px;padding-top:11px;border-top:1px solid var(--mp-line)}.codingns-magpie-quota-account-head{display:flex;align-items:baseline;gap:8px;min-width:0;margin-bottom:9px;font-size:${uiFontSize(13)}}.codingns-magpie-quota-account-head strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-quota-account-head span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mp-muted);font-size:${uiFontSize(12)}}.codingns-magpie-quota-account-head small{margin-left:auto;flex:none;color:var(--mp-caption);font-size:${uiFontSize(12)}}
1087
+ .codingns-magpie-quota-balance{display:flex;align-items:baseline;justify-content:space-between;gap:10px;min-width:0;margin:0 0 4px;padding:9px 0 2px}
1088
+ .codingns-magpie-quota-balance-label{flex:0 0 auto;color:var(--mp-muted);font-size:${uiFontSize(13)}}
1089
+ .codingns-magpie-quota-balance-value{min-width:0;overflow:hidden;color:var(--mp-ink);font-size:${uiFontSize(15)};font-weight:600;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}
1090
+ .codingns-magpie-quota-balance-value.is-money{font-size:${uiFontSize(17)};letter-spacing:-.01em}
1091
+ .codingns-magpie-quota-window{padding:11px 0;border-top:.5px solid var(--mp-line)}.codingns-magpie-quota-window-head,.codingns-magpie-quota-foot,.codingns-magpie-history-line-head{display:flex;align-items:center;justify-content:space-between;gap:9px}.codingns-magpie-quota-window-head{font-size:${uiFontSize(13)};font-weight:500}.codingns-magpie-quota-window-head span{color:var(--mp-muted);font-variant-numeric:tabular-nums}.quota-track{height:6px}.quota-track span{background:linear-gradient(90deg,var(--mp-accent),color-mix(in srgb,var(--mp-accent) 55%,transparent))}.codingns-magpie-quota-foot{color:var(--mp-caption);font-size:${uiFontSize(12)}}
1092
+ .codingns-magpie-daily-chart{display:grid;grid-template-columns:1fr auto;align-items:center;gap:9px;margin-top:4px;padding-top:10px;border-top:.5px solid var(--mp-line);color:var(--mp-muted);font-size:${uiFontSize(12)}}.codingns-magpie-sparkline{width:100%;height:30px;overflow:visible}.codingns-magpie-sparkline polyline{stroke:var(--mp-accent);stroke-width:2;vector-effect:non-scaling-stroke}
1093
+ .codingns-magpie-history-panel{padding:17px;margin-top:14px}.codingns-magpie-history-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}.codingns-magpie-history-card{padding:12px;border:1px solid var(--mp-line);border-radius:9px;background:var(--mp-card)}.codingns-magpie-history-card>strong,.codingns-magpie-history-card>small{display:block}.codingns-magpie-history-card>strong{font-size:${uiFontSize(13)};font-weight:600}.codingns-magpie-history-card>small{margin-top:3px;color:var(--mp-caption);font-size:${uiFontSize(12)}}.codingns-magpie-history-line{margin-top:10px}.codingns-magpie-history-line-head{font-size:${uiFontSize(12)}}.codingns-magpie-history-line-head span:last-child{font-variant-numeric:tabular-nums;font-weight:650}
1094
+ .codingns-magpie-update-overlay{position:absolute;inset:0;z-index:20;display:flex;align-items:flex-start;justify-content:center;overflow:auto;padding:calc(var(--dsh-frame-top-clearance,0px) + 68px) 18px 24px;background:color-mix(in srgb,var(--mp-surface) 72%,rgba(0,0,0,.28));backdrop-filter:blur(3px)}.codingns-magpie-update-dialog{width:min(720px,100%);max-height:100%;overflow:auto;border:.5px solid var(--mp-line-strong);border-radius:var(--mp-radius);background:var(--mp-surface);box-shadow:var(--dsw-elevation-prominent,0 18px 48px rgba(0,0,0,.22))}.codingns-magpie-update-panel{padding:16px 17px;margin-bottom:0;border:0}.codingns-magpie-update-panel .codingns-magpie-panel-head{align-items:flex-start}.codingns-magpie-update-panel-actions{display:flex;align-items:center;gap:10px;flex:0 0 auto}.codingns-magpie-update-close{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--mp-muted);font:inherit;font-size:20px;line-height:1;cursor:pointer}.codingns-magpie-update-close:hover{background:var(--mp-hover);color:var(--mp-ink)}.codingns-magpie-update-close:focus-visible{outline:2px solid color-mix(in srgb,var(--mp-accent) 55%,transparent);outline-offset:1px}.codingns-magpie-update-body{display:grid;gap:9px}.codingns-magpie-update-status{display:flex;align-items:center;flex-wrap:wrap;gap:8px;color:var(--mp-muted);font-size:${uiFontSize(12)}}.codingns-magpie-update-status strong{color:var(--mp-ink);font-size:${uiFontSize(14)}}.codingns-magpie-update-progress{position:relative;height:7px;overflow:hidden;border-radius:999px;background:var(--mp-layer)}.codingns-magpie-update-progress span{display:block;height:100%;border-radius:inherit;background:var(--mp-accent);transition:width .2s ease}.codingns-magpie-update-progress small{display:block;margin-top:4px;color:var(--mp-caption);font-size:${uiFontSize(11)}}.codingns-magpie-update-notes{padding:9px 10px;border:1px solid var(--mp-line);border-radius:8px;background:var(--mp-card);color:var(--mp-muted);font-size:${uiFontSize(12)};line-height:1.5}.codingns-magpie-update-notes summary{cursor:pointer;color:var(--mp-ink);font-weight:600}.codingns-magpie-update-notes div{white-space:pre-wrap;overflow-wrap:anywhere}.codingns-magpie-update-actions{display:flex;justify-content:flex-start}.codingns-magpie-update-actions button{min-height:30px;padding:0 11px;border:1px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(12)};cursor:pointer}.codingns-magpie-update-actions button:disabled{opacity:.45;cursor:not-allowed}
1095
+ .codingns-magpie-management-grid{display:grid;grid-template-columns:minmax(190px,260px) minmax(0,1fr);align-items:start;gap:12px}.codingns-magpie-management-list{max-height:calc(100vh - 190px);overflow:auto;padding:5px}.codingns-magpie-management-row{width:100%;display:flex;align-items:center;justify-content:space-between;gap:9px;min-height:48px;padding:8px 10px;border:0;border-bottom:.5px solid var(--mp-line);background:transparent;color:var(--mp-ink);font:inherit;text-align:left;cursor:pointer}.codingns-magpie-management-row:last-child{border-bottom:0}.codingns-magpie-management-row:hover,.codingns-magpie-management-row.is-active{background:var(--mp-hover)}.codingns-magpie-management-row.is-active{box-shadow:inset 2px 0 var(--mp-accent)}.codingns-magpie-management-row-main{display:flex;flex:1 1 auto;flex-direction:column;align-items:flex-start;gap:3px;min-width:0;text-align:left}.codingns-magpie-management-row-main strong,.codingns-magpie-management-row-main small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-management-row-main strong{max-width:100%;text-align:left;font-size:${uiFontSize(13)};font-weight:600}.codingns-magpie-management-row-main small{color:var(--mp-caption);font-size:${uiFontSize(12)}}.codingns-magpie-state{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;padding:3px 7px;border-radius:999px;background:var(--mp-layer);color:var(--mp-muted);font-size:${uiFontSize(11)};font-weight:700}.codingns-magpie-state:before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}.codingns-magpie-state.is-on{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3f9d6b) 12%,transparent);color:var(--mp-accent)}.codingns-magpie-state.is-off{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#c08a3e) 12%,transparent);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#a06a2c))}.codingns-magpie-management-editor{padding:17px}.codingns-magpie-management-statusline{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:-2px 0 13px;color:var(--mp-muted);font-size:${uiFontSize(13)}}.codingns-magpie-field{display:flex;flex:1 1 150px;flex-direction:column;gap:6px;min-width:0;margin:0 0 12px;color:var(--mp-muted);font-size:${uiFontSize(13)};font-weight:500}.codingns-magpie-field small{font-size:${uiFontSize(11)};font-weight:400;line-height:1.4}.codingns-magpie-field input,.codingns-magpie-field select,.codingns-magpie-field textarea{width:100%;min-width:0;box-sizing:border-box;padding:7px 10px;border:.5px solid var(--dsw-alias-border-l4,rgba(0,0,0,.16));border-radius:var(--dsw-radius-sm,8px);background:var(--mp-card);color:var(--mp-ink);font:inherit;font-size:${uiFontSize(13)};outline:none}.codingns-magpie-field textarea{resize:vertical;line-height:1.5;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.codingns-magpie-field-row{display:flex;gap:9px;align-items:stretch}.codingns-magpie-management-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:7px;margin-top:10px}.codingns-magpie-management-actions button{min-height:32px;padding:0 12px;border:.5px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(13)};cursor:pointer}.codingns-magpie-management-actions button.is-primary{border-color:transparent;background:var(--dsw-alias-button-primary-fill,var(--mp-accent));color:var(--dsw-alias-label-primary-foreground,#fff)}.codingns-magpie-management-actions button:disabled{opacity:.45;cursor:not-allowed}.codingns-magpie-confirm{display:grid;gap:7px;margin:12px 0;padding:11px;border:1px solid rgba(59,126,98,.24);border-radius:9px;background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3f9d6b) 8%,transparent);font-size:${uiFontSize(11)}}.codingns-magpie-confirm>span{overflow-wrap:anywhere;color:var(--mp-muted);line-height:1.5}.codingns-magpie-management-message{margin-top:10px;color:var(--mp-accent);font-size:${uiFontSize(11)}}.codingns-magpie-management-account-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.codingns-magpie-management-account{padding:16px}.codingns-magpie-management-balance{margin:8px 0;color:var(--mp-ink);font-size:${uiFontSize(14)};font-weight:700}.codingns-magpie-management-window{padding:10px 0;border-top:1px solid var(--mp-line)}.codingns-magpie-management-window>small,.codingns-magpie-management-readat{display:block;margin-top:5px;color:var(--mp-muted);font-size:${uiFontSize(11)}}.codingns-magpie-management-members{display:flex;flex-wrap:wrap;gap:5px;margin:-2px 0 13px}.codingns-magpie-member-chip{max-width:100%;overflow:hidden;padding:4px 7px;border:1px solid var(--mp-line);border-radius:6px;background:var(--mp-card);color:var(--mp-ink);font-size:${uiFontSize(11)};text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-member-chip.is-muted{opacity:.55}.codingns-magpie-management-policy{flex:0 0 auto;color:var(--mp-muted);font-size:${uiFontSize(11)}}.codingns-magpie-checkbox{display:flex;align-items:center;gap:8px;margin:0;color:var(--mp-ink);font-size:${uiFontSize(13)}}.codingns-magpie-checkbox input{accent-color:var(--mp-accent)}
1096
+ .codingns-magpie-field-checkbox{margin:2px 0 12px}.codingns-magpie-management-hint{margin:0 0 14px;color:var(--mp-caption);font-size:${uiFontSize(11)};line-height:1.5}
1097
+ .codingns-magpie-checkbox-list{display:grid;gap:4px;padding:8px;border:1px solid var(--mp-line);border-radius:8px;background:var(--mp-card)}
1098
+ .codingns-magpie-checkbox-list .codingns-magpie-model-option{min-height:34px;padding:4px 7px}
1099
+ .codingns-magpie-checkbox-list .codingns-magpie-model-id{font-weight:600;color:var(--mp-ink)}
1100
+ .codingns-magpie-segmented{display:inline-flex;flex-wrap:wrap;gap:2px;align-self:flex-start;padding:2px;border-radius:999px;background:var(--mp-layer)}
1101
+ .codingns-magpie-segmented button{min-height:26px;padding:0 12px;border:0;border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(12)};white-space:nowrap;cursor:pointer;transition:background .16s,color .16s}
1102
+ .codingns-magpie-segmented button:hover{color:var(--mp-ink)}
1103
+ .codingns-magpie-segmented button.is-active{background:var(--mp-surface);color:var(--mp-ink);font-weight:600;box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12))}
1104
+ .codingns-magpie-levels{display:flex;flex-wrap:wrap;gap:5px}
1105
+ .codingns-magpie-level{min-height:28px;padding:0 11px;border:.5px solid var(--mp-line-strong);border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(12)};cursor:pointer;transition:background .14s,color .14s,border-color .14s}
1106
+ .codingns-magpie-level:hover{color:var(--mp-ink)}
1107
+ .codingns-magpie-level.is-on{border-color:transparent;background:color-mix(in srgb,var(--mp-accent) 14%,transparent);color:var(--mp-accent);font-weight:600}
1108
+ .codingns-magpie-context-input{width:120px!important;max-width:100%;font-variant-numeric:tabular-nums}
1109
+ .codingns-magpie-field-error{margin-top:-2px;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#a06a2c));font-size:${uiFontSize(11)};line-height:1.4}.codingns-magpie-management-empty{display:flex;min-height:150px;align-items:center;justify-content:center;padding:24px;color:var(--mp-muted);font-size:${uiFontSize(13)};text-align:center}.codingns-magpie-model-editor{display:grid;gap:4px;padding:8px;border:1px solid var(--mp-line);border-radius:8px;background:var(--mp-card)}.codingns-magpie-model-actions{display:flex;gap:6px;flex-wrap:wrap}.codingns-magpie-model-actions button,.codingns-magpie-group-model-actions button{min-height:28px;padding:0 8px;border:1px solid var(--mp-line);border-radius:6px;background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(11)};cursor:pointer}.codingns-magpie-model-actions button:disabled,.codingns-magpie-group-model-actions button:disabled{opacity:.45;cursor:not-allowed}.codingns-magpie-model-option{display:flex;align-items:center;gap:9px;min-width:0;min-height:40px;padding:6px 9px;border:.5px solid transparent;border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--mp-ink);font-weight:400}.codingns-magpie-model-option:hover{border-color:var(--mp-line-strong);background:var(--mp-hover)}.codingns-magpie-model-option input,.codingns-magpie-group-model-check input{flex:0 0 auto;width:16px;height:16px;margin:0;accent-color:var(--mp-accent)}.codingns-magpie-model-option-copy{display:flex;flex:1 1 auto;min-width:0;overflow:hidden;flex-direction:column;gap:3px}.codingns-magpie-model-name{display:block;min-width:0;overflow:hidden;color:var(--mp-ink);font-size:${uiFontSize(13)};font-weight:600;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-model-meta{display:flex;align-items:center;min-width:0;gap:7px;color:var(--mp-caption);font-size:${uiFontSize(12)};line-height:1.25}.codingns-magpie-model-provider,.codingns-magpie-model-id{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codingns-magpie-model-provider{font-weight:650}.codingns-magpie-model-id{font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);color:var(--mp-muted)}.codingns-magpie-group-model-editor{display:grid;gap:6px;padding:10px;border:1px solid var(--mp-line);border-radius:9px;background:var(--mp-card)}.codingns-magpie-group-model-option{display:flex;align-items:center;gap:8px;min-width:0;min-height:45px;padding:6px 8px;border:1px solid transparent;border-radius:7px;background:var(--mp-surface);color:var(--mp-ink);transition:border-color .15s,background .15s,box-shadow .15s}.codingns-magpie-group-model-option:hover{border-color:color-mix(in srgb,var(--mp-accent) 48%,transparent);background:color-mix(in srgb,var(--mp-accent) 5%,transparent)}.codingns-magpie-group-model-option.is-selected{cursor:grab;border-color:color-mix(in srgb,var(--mp-accent) 32%,transparent);background:color-mix(in srgb,var(--mp-accent) 5%,transparent);box-shadow:inset 2px 0 var(--mp-accent)}.codingns-magpie-group-model-option.is-dragging{opacity:.55;box-shadow:inset 2px 0 var(--mp-accent),0 5px 14px rgba(30,70,50,.12)}.codingns-magpie-group-model-check{display:flex;align-items:center;flex:1 1 auto;gap:8px;min-width:0;cursor:pointer}.codingns-magpie-group-model-actions{display:flex;align-items:center;flex:0 0 auto;gap:4px}.codingns-magpie-group-model-actions button{display:grid;place-items:center;width:26px;height:26px;min-height:26px;padding:0;border-radius:6px;font-size:${uiFontSize(13)};line-height:1}.codingns-magpie-group-model-order{display:grid;place-items:center;flex:0 0 20px;width:20px;height:20px;border-radius:5px;background:var(--mp-card);color:var(--mp-muted);font-size:${uiFontSize(11)};font-variant-numeric:tabular-nums}.codingns-magpie-group-model-add{display:flex;align-items:center;gap:8px;margin-top:3px;padding-top:8px;border-top:.5px solid var(--mp-line);color:var(--mp-muted)}.codingns-magpie-group-model-add-icon{display:grid;place-items:center;flex:0 0 20px;width:20px;height:20px;border:1px dashed var(--mp-line);border-radius:5px;color:var(--mp-accent);font-size:${uiFontSize(16)};line-height:1}.codingns-magpie-group-model-add select{flex:1 1 auto;min-width:0;min-height:32px;padding:5px 8px;border:1px solid var(--mp-line);border-radius:7px;background:var(--mp-surface);color:var(--mp-ink);font:inherit;font-size:${uiFontSize(11)}}.codingns-magpie-management-empty{display:flex;min-height:150px;align-items:center;justify-content:center;padding:24px;color:var(--mp-muted);font-size:${uiFontSize(13)};text-align:center}
1110
+ .codingns-magpie-empty{min-height:180px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:13px;padding:24px;color:var(--mp-muted);font-size:${uiFontSize(13)};text-align:center}.codingns-magpie-empty-mark{display:grid;place-items:center;width:38px;height:38px;border:.5px solid var(--mp-line);border-radius:12px;color:var(--mp-muted);font-size:${uiFontSize(18)}}.codingns-magpie-spinner{width:16px;height:16px;border:2px solid var(--mp-line);border-top-color:var(--mp-accent);border-radius:50%;animation:magpie-spin .8s linear infinite}.codingns-magpie-inline-empty{padding:18px;color:var(--mp-muted);font-size:${uiFontSize(12)}}.codingns-magpie-notice{display:flex;align-items:center;gap:8px;margin:-5px 0 12px;padding:9px 11px;border:.5px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#c08a3e) 26%,transparent);border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#c08a3e) 10%,transparent);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#a06a2c));font-size:${uiFontSize(13)}}.codingns-magpie-notice span{display:grid;place-items:center;width:17px;height:17px;border-radius:50%;background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#c08a3e) 15%,transparent);font-weight:700}
1111
+ .codingns-magpie-usage-tabs{display:flex;align-items:center;gap:2px;min-width:0;padding:2px;border-radius:999px;background:var(--mp-layer);overflow-x:auto;scrollbar-width:none}
1112
+ .codingns-magpie-usage-tabs::-webkit-scrollbar{display:none}
1113
+ .codingns-magpie-usage-tab{flex:0 0 auto;min-height:26px;padding:0 13px;border:0;border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(13)};white-space:nowrap;cursor:pointer;transition:background .16s,color .16s}
1114
+ .codingns-magpie-usage-tab:hover{color:var(--mp-ink)}
1115
+ .codingns-magpie-usage-tab.is-active{background:var(--mp-surface);color:var(--mp-ink);font-weight:600;box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12))}
1116
+ /* Pin the usage-view switch to the toolbar's right edge so the icon and the
1117
+ segmented control stay together instead of wrapping with the left tabs. */
1118
+ .codingns-magpie-toolbar-right{display:flex;align-items:center;justify-content:flex-end;gap:10px;flex:0 0 auto;margin-left:auto;min-width:0}
1119
+ .codingns-magpie-quota-view{display:flex;align-items:center;gap:8px;flex:0 0 auto;min-width:0;color:var(--mp-muted);font-size:${uiFontSize(13)}}
1120
+ .codingns-magpie-quota-view-icon{display:grid;place-items:center;flex:0 0 auto;width:22px;height:22px;color:var(--mp-muted)}
1121
+ .codingns-magpie-quota-view-icon-svg{display:block}
1122
+ .codingns-magpie-quota-view-segment{display:flex;align-items:center;gap:2px;padding:2px;border-radius:999px;background:var(--mp-layer)}
1123
+ .codingns-magpie-quota-view-option{flex:0 0 auto;min-height:26px;padding:0 13px;border:0;border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(13)};white-space:nowrap;cursor:pointer;transition:background .16s,color .16s}
1124
+ .codingns-magpie-quota-view-option:hover{color:var(--mp-ink)}
1125
+ .codingns-magpie-quota-view-option.is-active{background:var(--mp-surface);color:var(--mp-ink);font-weight:600;box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12))}
1126
+ .codingns-magpie-quota-view-option:focus-visible{outline:2px solid color-mix(in srgb,var(--mp-accent) 55%,transparent);outline-offset:1px}
1127
+ .codingns-magpie-quota-view-option:disabled{opacity:.5;cursor:not-allowed}
1128
+ .codingns-magpie-statstrip{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));border:.5px solid var(--mp-line);border-radius:var(--mp-radius);background:var(--mp-card);margin-bottom:14px;overflow:hidden}
1129
+ .codingns-magpie-statcell{display:flex;flex-direction:column;gap:5px;min-width:0;padding:15px 18px;border-left:.5px solid var(--mp-line)}
1130
+ .codingns-magpie-statcell:first-child{border-left:0}
1131
+ .codingns-magpie-statcell-label{color:var(--mp-muted);font-size:${uiFontSize(13)}}
1132
+ .codingns-magpie-statcell-value{font-size:${uiFontSize(22)};line-height:1.1;font-weight:600;letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--mp-ink)}
1133
+ .codingns-magpie-statcell-detail{color:var(--mp-caption);font-size:${uiFontSize(13)};line-height:1.4}
1134
+ .codingns-magpie-barchart-panel{padding:16px 18px 14px;margin-bottom:14px}
1135
+ .codingns-magpie-barchart{position:relative;display:flex;flex-direction:column;gap:8px}
1136
+ .codingns-magpie-barchart-plot{display:flex;align-items:flex-end;gap:2px;height:170px;padding:8px 0 0;border-bottom:.5px solid var(--mp-line)}
1137
+ .codingns-magpie-bar-slot{flex:1 1 0;display:flex;align-items:flex-end;justify-content:center;height:100%;min-width:2px}
1138
+ .codingns-magpie-bar{display:block;width:100%;max-width:26px;min-height:2px;border-radius:2px 2px 0 0;background:color-mix(in srgb,var(--mp-accent) 32%,transparent);transition:height .3s ease}
1139
+ .codingns-magpie-bar-cap{display:block;width:100%;height:4px;border-radius:2px 2px 0 0;background:var(--mp-accent)}
1140
+ .codingns-magpie-barchart-axis{display:flex;justify-content:space-between;gap:4px;color:var(--mp-caption);font-size:${uiFontSize(12)}}
1141
+ .codingns-magpie-barchart-tick{flex:0 0 auto;white-space:nowrap}
1142
+ .codingns-magpie-barchart-peak{position:absolute;top:0;left:0;padding:0 4px;border-radius:4px;background:var(--mp-surface);color:var(--mp-caption);font-size:${uiFontSize(12)}}
1143
+ .codingns-magpie-progress-panel{padding:16px 18px 8px;margin-bottom:14px}
1144
+ .codingns-magpie-progress-list{display:flex;flex-direction:column}
1145
+ .codingns-magpie-progress-item{display:block;width:100%;padding:11px 0;border:0;border-top:.5px solid var(--mp-line);background:transparent;color:var(--mp-ink);font:inherit;text-align:left}
1146
+ .codingns-magpie-progress-item.is-button{cursor:pointer;border-radius:8px;padding-left:6px;padding-right:6px;transition:background .14s}
1147
+ .codingns-magpie-progress-item.is-button:hover{background:var(--mp-hover)}
1148
+ .codingns-magpie-progress-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:6px}
1149
+ .codingns-magpie-progress-copy{display:flex;flex:1 1 auto;min-width:0;overflow:hidden;flex-direction:column;gap:1px}.codingns-magpie-progress-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:${uiFontSize(13)}}
1150
+ .codingns-magpie-progress-context{min-width:0;overflow:hidden;color:var(--mp-caption);font-size:${uiFontSize(11)};text-overflow:ellipsis;white-space:nowrap}
1151
+ .codingns-magpie-progress-value{flex:0 0 auto;font-size:${uiFontSize(13)};font-weight:600;font-variant-numeric:tabular-nums}
1152
+ .codingns-magpie-progress-track{height:4px;border-radius:99px;background:var(--mp-hover);overflow:hidden}
1153
+ .codingns-magpie-progress-track span{display:block;height:100%;min-width:2px;border-radius:inherit;background:linear-gradient(90deg,var(--mp-chart,var(--mp-accent)),color-mix(in srgb,var(--mp-chart,var(--mp-accent)),transparent 45%))}
1154
+ .codingns-magpie-progress-meta{display:block;margin-top:5px;color:var(--mp-caption);font-size:${uiFontSize(12)}}
1155
+ .codingns-magpie-chiprow{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
1156
+ .codingns-magpie-chip{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;min-height:28px;padding:0 12px;border:.5px solid var(--mp-line-strong);border-radius:999px;background:transparent;color:var(--mp-muted);font:inherit;font-size:${uiFontSize(13)};cursor:pointer;transition:background .14s,color .14s,border-color .14s}
1157
+ .codingns-magpie-chip:hover{color:var(--mp-ink)}
1158
+ .codingns-magpie-chip.is-active{background:var(--dsw-alias-button-primary-fill,var(--mp-accent));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff);font-weight:600}
1159
+ .codingns-magpie-filterrow{display:flex;align-items:center;gap:10px;margin-bottom:12px}
1160
+ .codingns-magpie-grow{flex:1 1 auto;max-width:none;margin:0}
1161
+ .codingns-magpie-provider-logo{display:grid;flex:0 0 auto;place-items:center;box-sizing:border-box;object-fit:contain;padding:3px;border-radius:10px;background:var(--mp-layer);color:var(--mp-accent);font-size:${uiFontSize(12)};font-weight:750}
1162
+ .codingns-magpie-provider-logo-compact{border-radius:7px;padding:2px}
1163
+ .codingns-magpie-provider-logo[data-host-asset]{background:#fff;object-fit:contain}
1164
+ .codingns-magpie-provider-inline{display:inline-flex;align-items:center;gap:7px;min-width:0}
1165
+ .codingns-magpie-provider-heading{display:flex;align-items:center;gap:10px}
1166
+ .codingns-magpie-filter-summary{color:var(--mp-muted);font-size:${uiFontSize(12)};white-space:nowrap}
1167
+ .codingns-magpie-toplist-panel{padding:16px 18px 8px}
1168
+ .codingns-magpie-toprow{display:flex;align-items:center;gap:12px;width:100%;padding:10px 6px;border:0;border-top:.5px solid var(--mp-line);background:transparent;color:var(--mp-ink);font:inherit;text-align:left;cursor:pointer;border-radius:var(--dsw-radius-sm,8px);transition:background .14s}
1169
+ .codingns-magpie-toprow:hover{background:var(--mp-hover)}
1170
+ .codingns-magpie-toprow-main{display:flex;flex-direction:column;gap:3px;flex:1 1 auto;min-width:0}
1171
+ .codingns-magpie-toprow-main strong{font-size:${uiFontSize(13)}}
1172
+ .codingns-magpie-toprow-main small{overflow:hidden;color:var(--mp-caption);font-size:${uiFontSize(12)};text-overflow:ellipsis;white-space:nowrap}
1173
+ .codingns-magpie-toprow-metrics{display:flex;flex:0 0 auto;gap:16px;font-size:${uiFontSize(13)};font-variant-numeric:tabular-nums;color:var(--mp-muted)}
1174
+ .codingns-magpie-selection{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px;padding:9px 12px;border:.5px solid var(--mp-line);border-radius:10px;background:var(--mp-card);color:var(--mp-ink);font-size:${uiFontSize(13)}}
1175
+ .codingns-magpie-agent-tag{display:inline-block;max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:620}
1176
+ .codingns-magpie-native-table{min-width:1240px}
1177
+ .codingns-magpie-native-table th{font-size:${uiFontSize(13)};letter-spacing:.01em}
1178
+ .codingns-magpie-native-table td{max-width:none;font-size:${uiFontSize(13)};white-space:nowrap}
1179
+ @keyframes magpie-rise{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}@keyframes magpie-spin{to{transform:rotate(360deg)}}
1180
+ @media(max-width:900px){.codingns-magpie-topbar{--mp-topbar-pad-x:14px;--mp-topbar-pad-y:9px;grid-template-columns:minmax(0,1fr) auto;row-gap:8px;min-height:58px}.codingns-magpie-topbar-left{grid-column:1;grid-row:2;margin-left:calc(-1 * var(--mp-leading-inset))}.codingns-magpie-tabs{grid-column:1;grid-row:1;justify-self:start;flex-wrap:wrap;row-gap:2px;overflow:visible}.codingns-magpie-topbar-right{grid-column:2;grid-row:1}}
1181
+ @media(max-width:650px){.codingns-magpie-topbar{--mp-topbar-pad-x:12px;--mp-topbar-pad-y:8px;min-height:56px;gap:8px}.codingns-magpie-tab{min-height:32px;padding:0 9px;font-size:${uiFontSize(12)}}.codingns-magpie-tab-icon{display:none}.codingns-magpie-toolbar{min-height:44px;padding:0 12px;gap:8px}.codingns-magpie-periods{flex-direction:row;overflow:auto;max-width:100%}.codingns-magpie-period{min-height:28px;padding:0 9px}.codingns-magpie-barchart-panel,.codingns-magpie-progress-panel,.codingns-magpie-toplist-panel,.codingns-magpie-history-panel,.codingns-magpie-quota-card,.codingns-magpie-management-editor{padding:13px}.codingns-magpie-toprow-metrics{gap:10px}.codingns-magpie-chip{min-height:32px}.codingns-magpie-history-grid,.codingns-magpie-management-account-grid{grid-template-columns:1fr}.codingns-magpie-quota-grid{column-width:auto;column-count:1}.codingns-magpie-management-grid{grid-template-columns:minmax(0,1fr);gap:8px}.codingns-magpie-management-list{display:flex;max-height:none;gap:4px;overflow:auto;padding:4px}.codingns-magpie-management-row{flex:0 0 auto;width:auto;max-width:220px;min-height:42px;border:1px solid var(--mp-line);border-radius:8px}.codingns-magpie-management-row.is-active{box-shadow:inset 0 -2px var(--mp-accent)}.codingns-magpie-management-row-main{max-width:145px;text-align:left}.codingns-magpie-field-row{flex-wrap:wrap;gap:0}.codingns-magpie-chart-switch{align-self:flex-start}.codingns-magpie-page .codingns-magpie-table{min-width:900px}}
1182
+ @media(prefers-reduced-motion:reduce){.codingns-magpie-page *{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}
1183
+ .codingns-magpie-group-model-add{position:relative;display:block;margin-top:3px;padding-top:8px;border-top:1px solid var(--mp-line);color:var(--mp-muted)}
1184
+ .codingns-magpie-group-model-add-button{display:inline-flex;align-items:center;gap:8px;min-height:30px;padding:4px 10px;border:.5px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(12)};cursor:pointer}
1185
+ .codingns-magpie-group-model-add-button:hover{border-color:color-mix(in srgb,var(--mp-accent) 50%,transparent);background:color-mix(in srgb,var(--mp-accent) 6%,transparent)}
1186
+ .codingns-magpie-group-model-add-button[aria-expanded=true]{border-color:var(--mp-accent);color:var(--mp-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--mp-accent) 10%,transparent)}
1187
+ .codingns-magpie-group-model-add-icon{display:grid;place-items:center;flex:0 0 18px;width:18px;height:18px;border:1px dashed currentColor;border-radius:5px;color:var(--mp-accent);font-size:${uiFontSize(15)};line-height:1}
1188
+ .codingns-magpie-group-model-picker{position:absolute;z-index:5;top:calc(100% + 8px);left:0;width:min(520px,calc(100vw - 36px));max-width:100%;box-sizing:border-box;overflow:hidden;border:1px solid var(--mp-line);border-radius:10px;background:var(--mp-surface);box-shadow:var(--dsw-elevation-prominent,0 12px 28px rgba(0,0,0,.18))}
1189
+ .codingns-magpie-group-model-picker-search{display:flex;align-items:center;gap:8px;padding:9px 11px;border-bottom:1px solid var(--mp-line);color:var(--mp-muted);background:var(--mp-card)}
1190
+ .codingns-magpie-group-model-picker-search>span{font-size:${uiFontSize(15)};line-height:1}
1191
+ .codingns-magpie-group-model-picker-search input{width:100%;min-width:0;padding:4px 0;border:0;outline:0;background:transparent;color:var(--mp-ink);font:inherit;font-size:${uiFontSize(12)}}
1192
+ .codingns-magpie-group-model-picker-list{max-height:min(380px,50vh);overflow:auto;padding:4px 5px 7px}
1193
+ .codingns-magpie-group-model-picker-group{padding:0 0 5px}
1194
+ .codingns-magpie-group-model-picker-heading{margin:0;padding:8px 8px 5px;border-bottom:1px solid var(--mp-line);color:var(--mp-muted);font-size:${uiFontSize(11)};font-weight:750;letter-spacing:.06em;text-transform:uppercase}
1195
+ .codingns-magpie-group-model-picker-option{display:flex;align-items:center;gap:9px;width:100%;min-width:0;min-height:43px;margin-top:2px;padding:6px 8px;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--mp-ink);font:inherit;text-align:left;cursor:pointer}
1196
+ .codingns-magpie-group-model-picker-option:hover,.codingns-magpie-group-model-picker-option[aria-selected=true]{border-color:var(--mp-line-strong);background:var(--mp-hover)}
1197
+ .codingns-magpie-group-model-picker-option .codingns-magpie-model-option-copy{gap:2px}
1198
+ .codingns-magpie-group-model-picker-option .codingns-magpie-model-name{font-size:${uiFontSize(11)}}
1199
+ .codingns-magpie-group-model-picker-option .codingns-magpie-model-meta{font-size:${uiFontSize(11)}}
1200
+ .codingns-magpie-group-model-picker-mark{display:grid;place-items:center;flex:0 0 24px;width:24px;height:24px;border-radius:7px;background:color-mix(in srgb,var(--mp-accent) 10%,transparent);color:var(--mp-accent);font-size:${uiFontSize(11)};font-weight:750}
1201
+ /* -- Mobile layout --
1202
+ With the sidebar hidden DSH keeps only a floating logo button at the top-left, so the
1203
+ page stops indenting by the whole clearance width: the page fills the viewport, only the
1204
+ header's first row is pushed past the button, and the body, toolbar, and tables start at
1205
+ the left edge. This section must come after every default rule so it overrides the
1206
+ desktop paddings above. */
1207
+ .codingns-magpie-page{--mp-topbar-pad-x:16px;--mp-topbar-pad-y:0px;--mp-leading-inset:0px;padding-left:var(--dsh-frame-leading-clearance,0px)}
1208
+ .codingns-magpie-topbar{padding-right:var(--mp-topbar-pad-x);padding-left:calc(var(--mp-topbar-pad-x) + var(--mp-leading-inset))}
1209
+ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--mp-leading-inset:max(0px,calc(var(--dsh-frame-leading-clearance,0px) - 20px))}
1210
+ @media(max-width:900px){.codingns-magpie-topbar{--mp-topbar-pad-x:14px;--mp-topbar-pad-y:9px}.codingns-magpie-topbar-left{margin-left:calc(-1 * var(--mp-leading-inset))}}
1211
+ @media(max-width:650px){.codingns-magpie-topbar{--mp-topbar-pad-x:12px;--mp-topbar-pad-y:8px}.codingns-magpie-icon-button{width:36px;height:36px}.codingns-magpie-toolbar:not(.has-tabs){display:none}.codingns-magpie-view{padding:14px 12px 30px}.codingns-magpie-statstrip{grid-template-columns:repeat(2,minmax(0,1fr))}.codingns-magpie-statcell{padding:13px;border-left:0}.codingns-magpie-statcell:nth-child(2n){border-left:.5px solid var(--mp-line)}.codingns-magpie-statcell:nth-child(n+3){border-top:.5px solid var(--mp-line)}.codingns-magpie-statcell:last-child:nth-child(odd){grid-column:1/-1}.codingns-magpie-statcell-value{font-size:${uiFontSize(22)}}.codingns-magpie-barchart-plot{height:132px}.codingns-magpie-barchart-axis{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:0;overflow:hidden;font-size:${uiFontSize(9)}}.codingns-magpie-barchart-tick{overflow:hidden;text-overflow:clip}}
1212
+ .codingns-magpie-reqlist{display:none}
1213
+ .codingns-magpie-reqcard{display:flex;flex-direction:column;gap:5px;padding:11px 14px;border-top:.5px solid var(--mp-line)}
1214
+ .codingns-magpie-reqcard:first-child{border-top:0}
1215
+ .codingns-magpie-reqcard-top{display:flex;align-items:center;gap:8px;min-width:0}
1216
+ .codingns-magpie-reqcard-top .codingns-magpie-time-cell{flex:0 0 auto;font-size:${uiFontSize(12)}}
1217
+ .codingns-magpie-reqcard-top .codingns-magpie-provider-inline{flex:1 1 auto;min-width:0}
1218
+ .codingns-magpie-reqcard-route{display:flex;align-items:center;gap:6px;min-width:0;font-size:${uiFontSize(12)}}
1219
+ .codingns-magpie-reqcard-arrow{flex:0 0 auto;color:var(--mp-caption)}
1220
+ .codingns-magpie-reqcard-route .codingns-magpie-route-model{flex:0 1 auto;max-width:47%}
1221
+ .codingns-magpie-reqcard-sub{display:flex;align-items:center;gap:6px;min-width:0;font-size:${uiFontSize(11)};color:var(--mp-caption)}
1222
+ .codingns-magpie-reqcard-sub .codingns-magpie-provider-name{max-width:62%;font-weight:500;color:var(--mp-muted)}
1223
+ .codingns-magpie-reqcard-sub .codingns-magpie-table-secondary{margin-top:0}
1224
+ .codingns-magpie-reqcard-facts{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:${uiFontSize(11)};color:var(--mp-muted);font-variant-numeric:tabular-nums}
1225
+
1226
+ @media(max-width:650px){
1227
+ .codingns-magpie-view{padding:12px 12px 26px}
1228
+ .codingns-magpie-tab{min-height:38px;padding:0 10px}
1229
+ .codingns-magpie-toolbar{min-height:40px}
1230
+ .codingns-magpie-statcell-label{font-size:${uiFontSize(11)}}
1231
+ .codingns-magpie-statcell-value{font-size:${uiFontSize(19)}}
1232
+ .codingns-magpie-statcell-detail{font-size:${uiFontSize(11)};white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1233
+ .codingns-magpie-barchart-plot{height:118px}
1234
+ .codingns-magpie-barchart-panel,.codingns-magpie-progress-panel,.codingns-magpie-toplist-panel,.codingns-magpie-history-panel,.codingns-magpie-quota-card,.codingns-magpie-management-editor{padding:12px}
1235
+ .codingns-magpie-panel-head{margin-bottom:10px}
1236
+ .codingns-magpie-progress-item{padding:9px 0}
1237
+ .codingns-magpie-toprow{padding:8px 4px}
1238
+ .codingns-magpie-chip{min-height:28px;padding:0 10px}
1239
+ .codingns-magpie-quota-icon{width:28px;height:28px;border-radius:8px;font-size:${uiFontSize(12)}}
1240
+ .codingns-magpie-quota-topline{gap:8px;margin-bottom:9px}
1241
+ .codingns-magpie-quota-card{padding:11px 12px}
1242
+ .codingns-magpie-quota-topline h2{font-size:${uiFontSize(13)}}
1243
+ .codingns-magpie-quota-topline p{font-size:${uiFontSize(11)}}
1244
+ .codingns-magpie-quota-account{font-size:${uiFontSize(12)};margin:-3px 0 7px}
1245
+ .codingns-magpie-quota-window{padding:8px 0}
1246
+ .codingns-magpie-daily-chart{margin-top:2px;padding-top:6px}
1247
+ .codingns-magpie-pagination{padding:8px 10px}
1248
+ .codingns-magpie-selection{padding:8px 10px}
1249
+ }
1250
+
1251
+ @media(max-width:900px){
1252
+ .codingns-magpie-table-scroll{display:none}
1253
+ .codingns-magpie-reqlist{display:block}
1254
+ .codingns-magpie-statstrip{display:flex;flex-wrap:wrap;gap:.5px;background:var(--mp-line)}
1255
+ .codingns-magpie-statcell,.codingns-magpie-statcell:nth-child(2n),.codingns-magpie-statcell:nth-child(n+3){flex:1 1 calc(33.333% - .5px);border:0;background:var(--mp-card)}
1256
+ .codingns-magpie-statcell:last-child:nth-child(odd){grid-column:auto}
1257
+ }
1258
+
1259
+ `;
1260
+ /** 管理页样式的固定标识;重复注入时直接复用已有样式表。 */
1261
+ const MAGPIE_PAGE_STYLE_ID = 'codingns4dsh-magpie-page-style';
1262
+ /**
1263
+ * 把管理页样式注入 `document.head`(只注入一次)。
1264
+ *
1265
+ * 管理页自身在根节点里内联 `<style>`;底部弹层等嵌入式视图没有这个根节点,
1266
+ * 复用请求明细前必须先装上同一份样式表。
1267
+ */
1268
+ export function installMagpiePageStyles() {
1269
+ if (typeof document === 'undefined')
1270
+ return;
1271
+ if (document.querySelector(`style[data-plugin-css="${MAGPIE_PAGE_STYLE_ID}"]`) !== null)
1272
+ return;
1273
+ const style = document.createElement('style');
1274
+ style.dataset.plugin = 'codingns4dsh';
1275
+ style.dataset.pluginCss = MAGPIE_PAGE_STYLE_ID;
1276
+ style.textContent = MAGPIE_PAGE_CSS;
1277
+ document.head.appendChild(style);
1278
+ }
1279
+ //# sourceMappingURL=magpie-management-page.js.map