@kenz1117/dsh-ui-usage-billing 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +487 -275
- package/lib/index.js +303 -28
- package/lib/types/balance.d.ts +19 -0
- package/lib/types/client/TrendChart.d.ts +12 -10
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/pricing.d.ts +23 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/pricing-fetch.d.ts +17 -0
- package/lib/types/pricing-shared.d.ts +50 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
|
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region \0dsh-css:/Users/ken/deepseek-harness/packages/client/ui-usage-billing/src/client/UsageBilling.module.css.mjs
|
|
26
|
-
const css = ".VWh0dG_railButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:36px;height:36px;color:var(--dsw-static-blue-500);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;padding:0;transition:background-color .14s,border-color .14s,color .14s;display:flex}.VWh0dG_railButton:hover,.VWh0dG_railButton:focus-visible{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3);color:var(--dsw-static-blue-400);outline:none}.VWh0dG_railButton svg{stroke-width:2px;width:18px;height:18px}.VWh0dG_trigger{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;border-radius:12px;align-items:center;gap:10px;padding:9px 12px;transition:border-color .14s,background-color .14s,transform .12s;display:flex}.VWh0dG_trigger:hover,.VWh0dG_trigger:focus-visible{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover);outline:none}.VWh0dG_trigger:active{transform:scale(.985)}.VWh0dG_triggerIcon{background:linear-gradient(135deg, var(--dsw-static-blue-500), var(--dsw-static-deepseek-500));width:30px;height:30px;color:var(--dsw-static-neutral-bluish-00);border-radius:9px;flex:none;justify-content:center;align-items:center;display:inline-flex}.VWh0dG_triggerIcon svg{stroke-width:2.2px;width:15px;height:15px}.VWh0dG_triggerText{flex-direction:column;gap:1px;min-width:0;display:flex}.VWh0dG_triggerLabel{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:16px}.VWh0dG_triggerMeta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;align-items:baseline;gap:6px;font-size:11px;line-height:15px;display:flex}.VWh0dG_triggerMeta strong{color:var(--dsw-static-blue-500);font-variant-numeric:tabular-nums;font-size:13px;font-weight:600}.VWh0dG_triggerMeta em{color:var(--dsw-alias-label-caption);font-style:normal}.VWh0dG_triggerChevron{width:14px;height:14px;color:var(--dsw-alias-label-caption);flex:none;margin-left:auto}.VWh0dG_dashboardModal{width:min(760px,100vw - 48px);max-height:min(760px,88vh)}.VWh0dG_dashboard{flex-direction:column;width:100%;max-height:min(760px,88vh);display:flex}.VWh0dG_dashboardHead{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:flex-start;gap:12px;padding:20px 24px 14px;display:flex}.VWh0dG_dashboardTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:17px;font-weight:600;line-height:24px}.VWh0dG_dashboardSubtitle{color:var(--dsw-alias-label-caption);margin:3px 0 0;font-size:12px;line-height:17px}.VWh0dG_closeButton{width:30px;height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;transition:background-color .14s;display:inline-flex}.VWh0dG_closeButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_closeButton svg{stroke-width:2px;width:16px;height:16px}.VWh0dG_dashboardBody{flex-direction:column;gap:14px;padding:16px 24px 24px;display:flex;overflow-y:auto}.VWh0dG_hero{background:linear-gradient(135deg, var(--dsw-static-blue-800) 0%, var(--dsw-static-deepseek-600) 55%, var(--dsw-static-deepseek-500) 100%);min-height:118px;color:var(--dsw-static-neutral-bluish-00);border-radius:16px;align-items:center;gap:24px;padding:18px 22px;display:flex;position:relative;overflow:hidden}.VWh0dG_hero:after{content:\"\";pointer-events:none;background:radial-gradient(circle,#ffffff29 0%,#fff0 70%);border-radius:50%;width:200px;height:200px;position:absolute;top:-60px;right:-40px}.VWh0dG_heroMain{flex-direction:column;flex:auto;justify-content:center;gap:3px;min-width:0;display:flex}.VWh0dG_heroLabel{opacity:.82;white-space:nowrap;font-size:12px;font-weight:500;line-height:17px}.VWh0dG_heroValue{letter-spacing:-.02em;font-variant-numeric:tabular-nums;white-space:nowrap;font-size:38px;font-weight:700;line-height:46px}.VWh0dG_heroMeta{opacity:.82;white-space:nowrap;align-items:center;gap:6px;font-size:12px;line-height:17px;display:flex}.VWh0dG_heroMeta em{background:#ffffff29;border-radius:6px;margin-left:2px;padding:1px 6px;font-style:normal}.VWh0dG_heroDivider{background:#ffffff38;flex:none;align-self:stretch;width:1px}.VWh0dG_heroSide{flex:none;align-items:center;gap:28px;min-width:140px;display:flex}.VWh0dG_heroSideItem{flex-direction:column;justify-content:center;gap:3px;display:flex}.VWh0dG_heroSideLabel{opacity:.82;white-space:nowrap;font-size:12px;line-height:17px}.VWh0dG_heroSideValue{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:22px;font-weight:600;line-height:28px}.VWh0dG_delta{align-items:center;gap:2px;font-size:12px;font-weight:600;line-height:17px;display:inline-flex}.VWh0dG_deltaUp{color:#9ff0b8}.VWh0dG_deltaDown{color:#ffb4b4}.VWh0dG_kpiGrid{grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;display:grid}.VWh0dG_kpiTile{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:14px;flex-direction:column;gap:3px;padding:14px 15px;transition:border-color .14s,background-color .14s;display:flex}.VWh0dG_kpiTile:hover{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_kpiLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:15px}.VWh0dG_kpiValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:21px;font-weight:600;line-height:27px}.VWh0dG_kpiGreen{color:var(--dsw-static-green-500)}.VWh0dG_kpiDetail{color:var(--dsw-alias-label-caption);white-space:nowrap;text-overflow:ellipsis;font-size:11px;line-height:15px;overflow:hidden}.VWh0dG_panel{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;gap:10px;padding:16px 18px;display:flex}.VWh0dG_panelHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.VWh0dG_panelTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;font-weight:600;line-height:20px}.VWh0dG_panelHint{color:var(--dsw-alias-label-caption);white-space:nowrap;font-size:11px;line-height:16px}.VWh0dG_chartWrap{width:100%;position:relative}.VWh0dG_chartSvg{width:100%;height:auto;display:block}.VWh0dG_chartEmpty{height:140px;color:var(--dsw-alias-label-caption);justify-content:center;align-items:center;font-size:13px;display:flex}.VWh0dG_emptyRow{text-align:center;color:var(--dsw-alias-label-caption);padding:28px 0;font-size:13px}.VWh0dG_chartGrid{stroke:var(--dsw-alias-border-l1);stroke-width:1px}.VWh0dG_chartAxisLabel{fill:var(--dsw-alias-label-tertiary);font-size:10px}.VWh0dG_chartBar{fill:var(--dsw-static-blue-500);opacity:.35}.VWh0dG_chartBar:hover{opacity:.6}.VWh0dG_chartStack{stroke:var(--dsw-alias-bg-layer-1);stroke-width:.75px}.VWh0dG_chartStack:hover{opacity:.85}.VWh0dG_chartLine{stroke:var(--dsw-static-blue-500);stroke-linecap:round;stroke-linejoin:round}.VWh0dG_chartCrosshair{stroke:var(--dsw-alias-label-dimmed);stroke-width:1px;stroke-dasharray:3 3}.VWh0dG_chartDot{fill:var(--dsw-static-neutral-bluish-00);stroke:var(--dsw-static-blue-500);stroke-width:2.5px}.VWh0dG_chartTooltip{pointer-events:none;z-index:2;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);box-shadow:var(--dsw-shadow-lv2);white-space:nowrap;border-radius:10px;padding:8px 10px;position:absolute;transform:translate(-50%,calc(-100% - 10px))}.VWh0dG_chartTooltipDate{color:var(--dsw-alias-label-primary);margin-bottom:3px;font-size:11px;font-weight:600}.VWh0dG_chartTooltipRow{color:var(--dsw-alias-label-secondary);align-items:center;gap:5px;font-size:11px;line-height:17px;display:flex}.VWh0dG_chartTooltipRow strong{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-weight:600}.VWh0dG_chartLegendLine{background:var(--dsw-static-blue-500);border-radius:2px;width:10px;height:3px;display:inline-block}.VWh0dG_chartLegendBar{background:var(--dsw-static-blue-500);opacity:.5;border-radius:2px;width:7px;height:7px;display:inline-block}.VWh0dG_chartTooltipSwatch{border-radius:3px;flex:none;width:8px;height:8px;display:inline-block}.VWh0dG_chartLegend{color:var(--dsw-alias-label-tertiary);justify-content:flex-end;gap:14px;margin-top:2px;font-size:11px;display:flex}.VWh0dG_chartLegend span{align-items:center;gap:5px;display:inline-flex}.VWh0dG_tableScroll{border:1px solid var(--dsw-alias-border-l1);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;overflow:auto}.VWh0dG_modelTable,.VWh0dG_pricingTable{border-collapse:collapse;width:100%;font-size:12.5px}.VWh0dG_modelTable th,.VWh0dG_modelTable td,.VWh0dG_pricingTable th,.VWh0dG_pricingTable td{border-bottom:1px solid var(--dsw-alias-border-l1);text-align:left;white-space:nowrap;padding:9px 12px}.VWh0dG_modelTable tbody tr:last-child td,.VWh0dG_pricingTable tbody tr:last-child td{border-bottom:0}.VWh0dG_modelTable thead th,.VWh0dG_pricingTable thead th{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-module-platform);font-size:11px;font-weight:500}.VWh0dG_modelTable tbody tr,.VWh0dG_pricingTable tbody tr{transition:background-color .12s}.VWh0dG_modelTable tbody tr:hover,.VWh0dG_pricingTable tbody tr:hover{background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_numCol{text-align:right;font-variant-numeric:tabular-nums}.VWh0dG_costCol{color:var(--dsw-static-blue-500);font-weight:600}.VWh0dG_na{color:var(--dsw-alias-label-dimmed)}.VWh0dG_modelCell{align-items:center;gap:8px;display:inline-flex}.VWh0dG_modelDot{width:9px;height:9px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-bg-layer-1) 60%, transparent);border-radius:50%;flex:none}.VWh0dG_modelName{color:var(--dsw-alias-label-primary);font-weight:500;line-height:16px;display:block}.VWh0dG_modelProvider{color:var(--dsw-alias-label-caption);font-size:10.5px;line-height:14px;display:block}.VWh0dG_pricingToggle{cursor:pointer;text-align:left;background:0 0;border:none;justify-content:space-between;align-items:center;gap:10px;width:100%;padding:0;display:flex}.VWh0dG_pricingToggle:hover .VWh0dG_panelTitle,.VWh0dG_pricingToggle:focus-visible .VWh0dG_panelTitle{color:var(--dsw-static-blue-500)}.VWh0dG_pricingToggleText{align-items:baseline;gap:10px;min-width:0;display:flex}.VWh0dG_pricingChevron{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.VWh0dG_pricingChevronOpen{transform:rotate(180deg)}.VWh0dG_bandPrice{align-items:baseline;gap:6px;display:inline-flex}.VWh0dG_bandPriceOff{color:var(--dsw-alias-label-caption)}.VWh0dG_bandPriceOff:before{content:\"/\";color:var(--dsw-alias-label-dimmed);margin-right:4px}.VWh0dG_bandTag{color:var(--dsw-alias-label-secondary);flex-direction:column;gap:1px;font-size:10.5px;line-height:14px;display:inline-flex}.VWh0dG_bandTagOff{color:var(--dsw-static-green-500);font-weight:600}.VWh0dG_flatTag{color:var(--dsw-alias-label-caption);font-size:10.5px;line-height:14px}.VWh0dG_healthDot{border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.VWh0dG_healthOk{background:var(--dsw-static-green-500);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-static-green-500) 18%, transparent)}.VWh0dG_healthBad{background:var(--dsw-static-red-500);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-static-red-500) 18%, transparent)}.VWh0dG_healthIdle{background:var(--dsw-static-neutral-bluish-400)}.VWh0dG_dashboardRight{align-items:center;gap:8px;min-width:0;display:flex}.VWh0dG_healthBadge{white-space:nowrap;border-radius:999px;align-items:center;gap:6px;padding:3px 9px;font-size:11px;line-height:16px;display:inline-flex}.VWh0dG_healthBadgeOk{color:var(--dsw-static-green-500);background:color-mix(in srgb, var(--dsw-static-green-500) 10%, transparent)}.VWh0dG_healthBadgeBad{color:var(--dsw-static-red-500);background:color-mix(in srgb, var(--dsw-static-red-500) 10%, transparent)}.VWh0dG_planTag{color:var(--dsw-static-green-500);background:color-mix(in srgb, var(--dsw-static-green-500) 12%, transparent);border-radius:999px;align-items:center;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;display:inline-flex}@media (width<=640px){.VWh0dG_kpiGrid{grid-template-columns:repeat(2,minmax(0,1fr))}.VWh0dG_hero{flex-direction:column;align-items:flex-start;gap:12px}.VWh0dG_heroMain{width:100%;min-width:0}.VWh0dG_heroDivider{align-self:stretch;width:100%;height:1px}.VWh0dG_heroSide{min-width:0}}";
|
|
26
|
+
const css = ".VWh0dG_railButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:36px;height:36px;color:var(--dsw-static-blue-500);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;padding:0;transition:background-color .14s,border-color .14s,color .14s;display:flex}.VWh0dG_railButton:hover,.VWh0dG_railButton:focus-visible{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3);color:var(--dsw-static-blue-400);outline:none}.VWh0dG_railButton svg{stroke-width:2px;width:18px;height:18px}.VWh0dG_trigger{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;border-radius:12px;align-items:center;gap:12px;padding:10px 14px;transition:border-color .14s,background-color .14s;display:flex}.VWh0dG_trigger:hover,.VWh0dG_trigger:focus-visible{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover);outline:none}.VWh0dG_triggerIcon{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.VWh0dG_triggerIcon svg{stroke-width:1.8px;width:15px;height:15px}.VWh0dG_triggerToday,.VWh0dG_triggerMonth{align-items:baseline;gap:4px;min-width:0;display:flex}.VWh0dG_triggerDivider{background:var(--dsw-alias-border-l1);align-self:stretch;width:1px}.VWh0dG_triggerAmount{color:var(--dsw-alias-label-primary);letter-spacing:-.01em;font-variant-numeric:tabular-nums;white-space:nowrap;font-size:17px;font-weight:600;line-height:22px}.VWh0dG_triggerAmountSub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:13px;font-weight:600;line-height:18px}.VWh0dG_triggerMeta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:10.5px;line-height:14px}.VWh0dG_dashboardModal{width:min(760px,100vw - 48px);max-height:min(760px,88vh)}.VWh0dG_dashboard{flex-direction:column;width:100%;max-height:min(760px,88vh);display:flex}.VWh0dG_dashboardHead{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:flex-start;gap:12px;padding:20px 24px 14px;display:flex}.VWh0dG_dashboardTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:17px;font-weight:600;line-height:24px}.VWh0dG_dashboardSubtitle{color:var(--dsw-alias-label-caption);margin:3px 0 0;font-size:12px;line-height:17px}.VWh0dG_closeButton{width:30px;height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;transition:background-color .14s;display:inline-flex}.VWh0dG_closeButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_closeButton svg{stroke-width:2px;width:16px;height:16px}.VWh0dG_dashboardBody{flex-direction:column;gap:14px;padding:16px 24px 24px;display:flex;overflow-y:auto}.VWh0dG_hero{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:16px;justify-content:space-between;align-items:center;gap:24px;padding:20px 24px;display:flex}.VWh0dG_heroMain{flex-direction:column;gap:4px;min-width:0;display:flex}.VWh0dG_heroLabel{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;line-height:17px}.VWh0dG_heroValue{letter-spacing:-.02em;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:40px;font-weight:600;line-height:48px}.VWh0dG_heroMeta{color:var(--dsw-alias-label-caption);white-space:nowrap;font-size:12px;line-height:17px}.VWh0dG_heroSide{flex-direction:column;flex:none;gap:10px;display:flex}.VWh0dG_heroSideItem{align-items:baseline;gap:8px;display:flex}.VWh0dG_heroSideLabel{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;line-height:17px}.VWh0dG_heroSideValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:16px;font-weight:600;line-height:22px}.VWh0dG_delta{align-items:center;gap:2px;margin-left:4px;font-size:11px;font-weight:600;line-height:17px;display:inline-flex}.VWh0dG_deltaUp{color:var(--dsw-static-green-500)}.VWh0dG_deltaDown{color:var(--dsw-static-red-500)}.VWh0dG_kpiGrid{grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;display:grid}.VWh0dG_kpiTile{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:14px;flex-direction:column;gap:3px;padding:14px 15px;transition:border-color .14s,background-color .14s;display:flex}.VWh0dG_kpiTile:hover{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_kpiLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:15px}.VWh0dG_kpiValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:21px;font-weight:600;line-height:27px}.VWh0dG_kpiGreen{color:var(--dsw-static-green-500)}.VWh0dG_kpiDetail{color:var(--dsw-alias-label-caption);white-space:nowrap;text-overflow:ellipsis;font-size:11px;line-height:15px;overflow:hidden}.VWh0dG_panel{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;gap:10px;padding:16px 18px;display:flex}.VWh0dG_panelHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.VWh0dG_panelTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;font-weight:600;line-height:20px}.VWh0dG_panelHint{color:var(--dsw-alias-label-caption);white-space:nowrap;font-size:11px;line-height:16px}.VWh0dG_chartWrap{width:100%;position:relative}.VWh0dG_chartSvg{width:100%;height:auto;display:block}.VWh0dG_chartEmpty{height:140px;color:var(--dsw-alias-label-caption);justify-content:center;align-items:center;font-size:13px;display:flex}.VWh0dG_emptyRow{text-align:center;color:var(--dsw-alias-label-caption);padding:28px 0;font-size:13px}.VWh0dG_chartGrid{stroke:var(--dsw-alias-border-l1);stroke-width:1px}.VWh0dG_chartAxisLabel{fill:var(--dsw-alias-label-tertiary);font-size:10px}.VWh0dG_chartBar{fill:var(--dsw-static-blue-500);opacity:.35}.VWh0dG_chartBar:hover{opacity:.6}.VWh0dG_chartStack{stroke:var(--dsw-alias-bg-layer-1);stroke-width:.75px}.VWh0dG_chartStack:hover{opacity:.85}.VWh0dG_chartLine{stroke:var(--dsw-static-blue-500);stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.VWh0dG_chartCrosshair{stroke:var(--dsw-alias-label-dimmed);stroke-width:1px;stroke-dasharray:3 3}.VWh0dG_chartDot{fill:var(--dsw-static-neutral-bluish-00);stroke:var(--dsw-static-blue-500);stroke-width:2.5px}.VWh0dG_chartTooltip{pointer-events:none;z-index:2;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);box-shadow:var(--dsw-shadow-lv2);white-space:nowrap;border-radius:10px;padding:8px 10px;position:absolute;transform:translate(-50%,calc(-100% - 10px))}.VWh0dG_chartTooltipDate{color:var(--dsw-alias-label-primary);margin-bottom:3px;font-size:11px;font-weight:600}.VWh0dG_chartTooltipRow{color:var(--dsw-alias-label-secondary);align-items:center;gap:5px;font-size:11px;line-height:17px;display:flex}.VWh0dG_chartTooltipRow strong{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-weight:600}.VWh0dG_chartLegendLine{background:var(--dsw-static-blue-500);border-radius:2px;width:10px;height:3px;display:inline-block}.VWh0dG_chartLegendBar{background:var(--dsw-static-blue-500);opacity:.5;border-radius:2px;width:7px;height:7px;display:inline-block}.VWh0dG_chartTooltipSwatch{border-radius:3px;flex:none;width:8px;height:8px;display:inline-block}.VWh0dG_chartLegend{color:var(--dsw-alias-label-tertiary);justify-content:flex-end;gap:14px;margin-top:2px;font-size:11px;display:flex}.VWh0dG_chartLegend span{align-items:center;gap:5px;display:inline-flex}.VWh0dG_tableScroll{border:1px solid var(--dsw-alias-border-l1);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;overflow:auto}.VWh0dG_modelTable,.VWh0dG_pricingTable{border-collapse:collapse;width:100%;font-size:12.5px}.VWh0dG_modelTable th,.VWh0dG_modelTable td,.VWh0dG_pricingTable th,.VWh0dG_pricingTable td{border-bottom:1px solid var(--dsw-alias-border-l1);text-align:left;white-space:nowrap;padding:9px 12px}.VWh0dG_modelTable tbody tr:last-child td,.VWh0dG_pricingTable tbody tr:last-child td{border-bottom:0}.VWh0dG_modelTable thead th,.VWh0dG_pricingTable thead th{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-module-platform);font-size:11px;font-weight:500}.VWh0dG_modelTable tbody tr,.VWh0dG_pricingTable tbody tr{transition:background-color .12s}.VWh0dG_modelTable tbody tr:hover,.VWh0dG_pricingTable tbody tr:hover{background:var(--dsw-alias-interactive-bg-hover)}.VWh0dG_numCol{text-align:right;font-variant-numeric:tabular-nums}.VWh0dG_costCol{color:var(--dsw-static-blue-500);font-weight:600}.VWh0dG_na{color:var(--dsw-alias-label-dimmed)}.VWh0dG_modelCell{align-items:center;gap:8px;display:inline-flex}.VWh0dG_modelDot{width:9px;height:9px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-bg-layer-1) 60%, transparent);border-radius:50%;flex:none}.VWh0dG_modelName{color:var(--dsw-alias-label-primary);font-weight:500;line-height:16px;display:block}.VWh0dG_modelProvider{color:var(--dsw-alias-label-caption);font-size:10.5px;line-height:14px;display:block}.VWh0dG_pricingToggle{cursor:pointer;text-align:left;background:0 0;border:none;justify-content:space-between;align-items:center;gap:10px;width:100%;padding:0;display:flex}.VWh0dG_pricingToggle:hover .VWh0dG_panelTitle,.VWh0dG_pricingToggle:focus-visible .VWh0dG_panelTitle{color:var(--dsw-static-blue-500)}.VWh0dG_rateBadge{vertical-align:1px;white-space:nowrap;border-radius:999px;align-items:center;margin-left:6px;padding:1px 6px;font-size:10px;font-weight:500;line-height:14px;display:inline-flex}.VWh0dG_rateBadgeLive{color:var(--dsw-static-green-500);background:color-mix(in srgb, var(--dsw-static-green-500) 10%, transparent)}.VWh0dG_rateBadgeBuiltin{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-module-platform)}.VWh0dG_pricingToggleText{align-items:baseline;gap:10px;min-width:0;display:flex}.VWh0dG_pricingChevron{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.VWh0dG_pricingChevronOpen{transform:rotate(180deg)}.VWh0dG_bandPrice{align-items:baseline;gap:6px;display:inline-flex}.VWh0dG_bandPriceOff{color:var(--dsw-alias-label-caption)}.VWh0dG_bandPriceOff:before{content:\"/\";color:var(--dsw-alias-label-dimmed);margin-right:4px}.VWh0dG_bandTag{color:var(--dsw-alias-label-secondary);flex-direction:column;gap:1px;font-size:10.5px;line-height:14px;display:inline-flex}.VWh0dG_bandTagOff{color:var(--dsw-static-green-500);font-weight:600}.VWh0dG_flatTag{color:var(--dsw-alias-label-caption);font-size:10.5px;line-height:14px}.VWh0dG_healthDot{border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.VWh0dG_healthOk{background:var(--dsw-static-green-500);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-static-green-500) 18%, transparent)}.VWh0dG_healthBad{background:var(--dsw-static-red-500);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-static-red-500) 18%, transparent)}.VWh0dG_healthIdle{background:var(--dsw-static-neutral-bluish-400)}.VWh0dG_dashboardRight{align-items:center;gap:8px;min-width:0;display:flex}.VWh0dG_healthBadge{white-space:nowrap;border-radius:999px;align-items:center;gap:6px;padding:3px 9px;font-size:11px;line-height:16px;display:inline-flex}.VWh0dG_healthBadgeOk{color:var(--dsw-static-green-500);background:color-mix(in srgb, var(--dsw-static-green-500) 10%, transparent)}.VWh0dG_healthBadgeBad{color:var(--dsw-static-red-500);background:color-mix(in srgb, var(--dsw-static-red-500) 10%, transparent)}.VWh0dG_planTag{color:var(--dsw-static-green-500);background:color-mix(in srgb, var(--dsw-static-green-500) 12%, transparent);border-radius:999px;align-items:center;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;display:inline-flex}@media (width<=640px){.VWh0dG_kpiGrid{grid-template-columns:repeat(2,minmax(0,1fr))}.VWh0dG_hero{flex-direction:column;align-items:flex-start;gap:14px}.VWh0dG_heroSide{flex-direction:row;gap:20px}}";
|
|
27
27
|
const tagId = "@kenz1117/dsh-ui-usage-billing/UsageBilling.module.css";
|
|
28
28
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
29
29
|
const tag = document.createElement("style");
|
|
@@ -33,112 +33,120 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var UsageBilling_module_css_default = {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
36
|
+
"panelTitle": "VWh0dG_panelTitle",
|
|
37
|
+
"chartLegendLine": "VWh0dG_chartLegendLine",
|
|
38
|
+
"dashboardModal": "VWh0dG_dashboardModal",
|
|
39
|
+
"emptyRow": "VWh0dG_emptyRow",
|
|
40
|
+
"heroValue": "VWh0dG_heroValue",
|
|
41
|
+
"modelDot": "VWh0dG_modelDot",
|
|
42
|
+
"chartLegendBar": "VWh0dG_chartLegendBar",
|
|
43
|
+
"rateBadge": "VWh0dG_rateBadge",
|
|
44
|
+
"bandTagOff": "VWh0dG_bandTagOff",
|
|
45
|
+
"heroSideLabel": "VWh0dG_heroSideLabel",
|
|
46
|
+
"triggerAmountSub": "VWh0dG_triggerAmountSub",
|
|
47
|
+
"healthIdle": "VWh0dG_healthIdle",
|
|
48
|
+
"chartGrid": "VWh0dG_chartGrid",
|
|
49
|
+
"deltaUp": "VWh0dG_deltaUp",
|
|
50
|
+
"healthBadgeOk": "VWh0dG_healthBadgeOk",
|
|
51
|
+
"kpiValue": "VWh0dG_kpiValue",
|
|
52
|
+
"kpiGrid": "VWh0dG_kpiGrid",
|
|
53
|
+
"heroSide": "VWh0dG_heroSide",
|
|
47
54
|
"chartDot": "VWh0dG_chartDot",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
55
|
+
"heroSideItem": "VWh0dG_heroSideItem",
|
|
56
|
+
"chartTooltipSwatch": "VWh0dG_chartTooltipSwatch",
|
|
50
57
|
"deltaDown": "VWh0dG_deltaDown",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"chartLine": "VWh0dG_chartLine",
|
|
54
|
-
"pricingChevronOpen": "VWh0dG_pricingChevronOpen",
|
|
58
|
+
"rateBadgeLive": "VWh0dG_rateBadgeLive",
|
|
59
|
+
"pricingChevron": "VWh0dG_pricingChevron",
|
|
55
60
|
"heroLabel": "VWh0dG_heroLabel",
|
|
56
|
-
"
|
|
57
|
-
"heroValue": "VWh0dG_heroValue",
|
|
61
|
+
"trigger": "VWh0dG_trigger",
|
|
58
62
|
"chartAxisLabel": "VWh0dG_chartAxisLabel",
|
|
59
|
-
"heroMain": "VWh0dG_heroMain",
|
|
60
63
|
"modelTable": "VWh0dG_modelTable",
|
|
61
|
-
"
|
|
62
|
-
"na": "VWh0dG_na",
|
|
64
|
+
"chartBar": "VWh0dG_chartBar",
|
|
63
65
|
"modelProvider": "VWh0dG_modelProvider",
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
66
|
+
"dashboardBody": "VWh0dG_dashboardBody",
|
|
67
|
+
"chartCrosshair": "VWh0dG_chartCrosshair",
|
|
68
|
+
"triggerAmount": "VWh0dG_triggerAmount",
|
|
69
|
+
"chartTooltip": "VWh0dG_chartTooltip",
|
|
70
|
+
"pricingToggleText": "VWh0dG_pricingToggleText",
|
|
71
|
+
"closeButton": "VWh0dG_closeButton",
|
|
72
|
+
"costCol": "VWh0dG_costCol",
|
|
73
|
+
"bandPriceOff": "VWh0dG_bandPriceOff",
|
|
74
|
+
"hero": "VWh0dG_hero",
|
|
75
|
+
"heroMain": "VWh0dG_heroMain",
|
|
70
76
|
"chartEmpty": "VWh0dG_chartEmpty",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"dashboardRight": "VWh0dG_dashboardRight",
|
|
77
|
+
"triggerMeta": "VWh0dG_triggerMeta",
|
|
78
|
+
"kpiDetail": "VWh0dG_kpiDetail",
|
|
79
|
+
"pricingChevronOpen": "VWh0dG_pricingChevronOpen",
|
|
75
80
|
"dashboardTitle": "VWh0dG_dashboardTitle",
|
|
81
|
+
"healthDot": "VWh0dG_healthDot",
|
|
82
|
+
"planTag": "VWh0dG_planTag",
|
|
83
|
+
"heroSideValue": "VWh0dG_heroSideValue",
|
|
84
|
+
"pricingToggle": "VWh0dG_pricingToggle",
|
|
85
|
+
"triggerDivider": "VWh0dG_triggerDivider",
|
|
76
86
|
"panelHint": "VWh0dG_panelHint",
|
|
77
|
-
"
|
|
78
|
-
"tableScroll": "VWh0dG_tableScroll",
|
|
79
|
-
"heroSideLabel": "VWh0dG_heroSideLabel",
|
|
80
|
-
"panel": "VWh0dG_panel",
|
|
81
|
-
"closeButton": "VWh0dG_closeButton",
|
|
82
|
-
"triggerMeta": "VWh0dG_triggerMeta",
|
|
83
|
-
"modelName": "VWh0dG_modelName",
|
|
84
|
-
"pricingChevron": "VWh0dG_pricingChevron",
|
|
85
|
-
"deltaUp": "VWh0dG_deltaUp",
|
|
86
|
-
"emptyRow": "VWh0dG_emptyRow",
|
|
87
|
+
"modelCell": "VWh0dG_modelCell",
|
|
87
88
|
"healthOk": "VWh0dG_healthOk",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"dashboardHead": "VWh0dG_dashboardHead",
|
|
91
|
-
"dashboardModal": "VWh0dG_dashboardModal",
|
|
92
|
-
"kpiGrid": "VWh0dG_kpiGrid",
|
|
89
|
+
"chartWrap": "VWh0dG_chartWrap",
|
|
90
|
+
"bandTag": "VWh0dG_bandTag",
|
|
93
91
|
"kpiGreen": "VWh0dG_kpiGreen",
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"planTag": "VWh0dG_planTag",
|
|
97
|
-
"pricingToggleText": "VWh0dG_pricingToggleText",
|
|
98
|
-
"heroMeta": "VWh0dG_heroMeta",
|
|
99
|
-
"chartGrid": "VWh0dG_chartGrid",
|
|
100
|
-
"healthDot": "VWh0dG_healthDot",
|
|
101
|
-
"chartTooltipSwatch": "VWh0dG_chartTooltipSwatch",
|
|
102
|
-
"dashboardSubtitle": "VWh0dG_dashboardSubtitle",
|
|
92
|
+
"flatTag": "VWh0dG_flatTag",
|
|
93
|
+
"dashboardHead": "VWh0dG_dashboardHead",
|
|
103
94
|
"pricingTable": "VWh0dG_pricingTable",
|
|
104
|
-
"
|
|
105
|
-
"
|
|
95
|
+
"kpiLabel": "VWh0dG_kpiLabel",
|
|
96
|
+
"numCol": "VWh0dG_numCol",
|
|
97
|
+
"tableScroll": "VWh0dG_tableScroll",
|
|
98
|
+
"chartStack": "VWh0dG_chartStack",
|
|
99
|
+
"modelName": "VWh0dG_modelName",
|
|
100
|
+
"dashboard": "VWh0dG_dashboard",
|
|
101
|
+
"rateBadgeBuiltin": "VWh0dG_rateBadgeBuiltin",
|
|
102
|
+
"kpiTile": "VWh0dG_kpiTile",
|
|
103
|
+
"panel": "VWh0dG_panel",
|
|
104
|
+
"bandPrice": "VWh0dG_bandPrice",
|
|
106
105
|
"healthBad": "VWh0dG_healthBad",
|
|
107
|
-
"bandTag": "VWh0dG_bandTag",
|
|
108
|
-
"chartTooltipRow": "VWh0dG_chartTooltipRow",
|
|
109
106
|
"chartLegend": "VWh0dG_chartLegend",
|
|
107
|
+
"chartLine": "VWh0dG_chartLine",
|
|
108
|
+
"chartTooltipRow": "VWh0dG_chartTooltipRow",
|
|
109
|
+
"triggerMonth": "VWh0dG_triggerMonth",
|
|
110
|
+
"healthBadgeBad": "VWh0dG_healthBadgeBad",
|
|
111
|
+
"railButton": "VWh0dG_railButton",
|
|
112
|
+
"triggerIcon": "VWh0dG_triggerIcon",
|
|
113
|
+
"healthBadge": "VWh0dG_healthBadge",
|
|
110
114
|
"chartSvg": "VWh0dG_chartSvg",
|
|
111
|
-
"
|
|
115
|
+
"panelHead": "VWh0dG_panelHead",
|
|
116
|
+
"heroMeta": "VWh0dG_heroMeta",
|
|
117
|
+
"delta": "VWh0dG_delta",
|
|
112
118
|
"chartTooltipDate": "VWh0dG_chartTooltipDate",
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"trigger": "VWh0dG_trigger",
|
|
118
|
-
"flatTag": "VWh0dG_flatTag"
|
|
119
|
+
"triggerToday": "VWh0dG_triggerToday",
|
|
120
|
+
"dashboardRight": "VWh0dG_dashboardRight",
|
|
121
|
+
"dashboardSubtitle": "VWh0dG_dashboardSubtitle",
|
|
122
|
+
"na": "VWh0dG_na"
|
|
119
123
|
};
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
/** 运行时实时覆盖:undefined = 用内置目录与内置汇率(默认值降级)。 */
|
|
125
|
+
let liveRate;
|
|
126
|
+
let livePrices;
|
|
122
127
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* (DeepSeek, 智谱, 通义…) publish RMB prices and store them directly;
|
|
127
|
-
* overseas providers (OpenAI, Google, xAI, Meta) publish USD.
|
|
128
|
-
* Cost is always computed and displayed in CNY — only USD-priced models go
|
|
129
|
-
* through the exchange rate, never domestic ones.
|
|
130
|
-
*
|
|
131
|
-
* Google-style two-band billing is modeled per model: Gemini's Flex tier
|
|
132
|
-
* prices spare-capacity traffic at -50%; DeepSeek V4 splits peak
|
|
133
|
-
* (09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate. The estimator
|
|
134
|
-
* mixes both bands by a configured peak share ({@link DEFAULT_PEAK_SHARE}).
|
|
128
|
+
* Apply the node half's live pricing snapshot. Absent fields keep the
|
|
129
|
+
* built-in catalog and rate; callers never fabricate values.
|
|
130
|
+
* @param pricing - the `/api/billing/pricing` response.
|
|
135
131
|
*/
|
|
132
|
+
function applyLivePricing(pricing) {
|
|
133
|
+
liveRate = pricing.rate;
|
|
134
|
+
livePrices = pricing.prices;
|
|
135
|
+
}
|
|
136
|
+
/** 当前汇率:实时覆盖优先,缺省回退内置固定值。 */
|
|
137
|
+
function currentRate() {
|
|
138
|
+
return liveRate ?? 6.79;
|
|
139
|
+
}
|
|
136
140
|
/**
|
|
137
|
-
* USD → CNY
|
|
138
|
-
*
|
|
139
|
-
* USD-priced models — domestic models never pass through this rate.
|
|
141
|
+
* 当前生效的 USD → CNY 汇率及其来源:live = 启动时实时拉取成功,
|
|
142
|
+
* builtin = 实时拉取失败、正在用内置默认值。
|
|
140
143
|
*/
|
|
141
|
-
|
|
144
|
+
function getRateInfo() {
|
|
145
|
+
return {
|
|
146
|
+
rate: currentRate(),
|
|
147
|
+
live: liveRate !== void 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
142
150
|
/** Default share of traffic assumed to fall in the peak band (0..1). */
|
|
143
151
|
const DEFAULT_PEAK_SHARE = .5;
|
|
144
152
|
/**
|
|
@@ -617,11 +625,22 @@ window.__ModuleLoader__.load({
|
|
|
617
625
|
];
|
|
618
626
|
/** Lookup a model by its stats key; falls back to the generic `other` entry. */
|
|
619
627
|
function modelOf(key) {
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
628
|
+
const base = MODEL_CATALOG.find((entry) => entry.key === key) ?? (() => {
|
|
629
|
+
const fallback = MODEL_CATALOG.at(-1);
|
|
630
|
+
if (fallback !== void 0) return fallback;
|
|
631
|
+
throw new Error("MODEL_CATALOG must not be empty");
|
|
632
|
+
})();
|
|
633
|
+
const live = livePrices?.[key];
|
|
634
|
+
if (live === void 0) return base;
|
|
635
|
+
return {
|
|
636
|
+
...base,
|
|
637
|
+
price: {
|
|
638
|
+
currency: "USD",
|
|
639
|
+
input: live.input,
|
|
640
|
+
cacheHit: live.cacheHit,
|
|
641
|
+
output: live.output
|
|
642
|
+
}
|
|
643
|
+
};
|
|
625
644
|
}
|
|
626
645
|
/** Resolve a price-table row by its CSS variable name (theme token or fallback color). */
|
|
627
646
|
function resolveToken(name) {
|
|
@@ -640,12 +659,18 @@ window.__ModuleLoader__.load({
|
|
|
640
659
|
const miss = buckets.cacheMiss > 0 ? buckets.cacheMiss : Math.max(0, buckets.input - buckets.cacheHit);
|
|
641
660
|
const hit = Math.min(buckets.cacheHit, buckets.input);
|
|
642
661
|
const raw = (miss * (band.cacheMiss ?? band.input) + hit * band.cacheHit + buckets.output * band.output) / 1e6;
|
|
643
|
-
return currency === "USD" ? raw *
|
|
662
|
+
return currency === "USD" ? raw * currentRate() : raw;
|
|
644
663
|
}
|
|
645
664
|
/**
|
|
646
665
|
* Estimate the CNY cost of one model's token usage, mixing the peak and
|
|
647
666
|
* off-peak bands by the given peak share (flat-priced models cost the same in
|
|
648
667
|
* both bands).
|
|
668
|
+
*
|
|
669
|
+
* 计费维度是「缓存命中价 × 时段价」的交叉:每个时段档内部分别按缓存命中
|
|
670
|
+
* 价(cacheHit)与未命中价(input/cacheMiss)计价,两个时段档再按
|
|
671
|
+
* peakShare 混合。时段定义以北京时间为准(如 DeepSeek V4 高峰
|
|
672
|
+
* 09:00-12:00 / 14:00-18:00)。因聚合只有按日 token 量、没有请求级时间戳,
|
|
673
|
+
* 时段只能按比例估算,而非逐请求判定。
|
|
649
674
|
* @param entry - the catalog entry whose prices apply.
|
|
650
675
|
* @param buckets - token usage counts.
|
|
651
676
|
* @param peakShare - share of traffic in the peak band (0..1); defaults to {@link DEFAULT_PEAK_SHARE}.
|
|
@@ -659,10 +684,12 @@ window.__ModuleLoader__.load({
|
|
|
659
684
|
}
|
|
660
685
|
/** Format a CNY amount with adaptive precision. */
|
|
661
686
|
function formatMoney(cny) {
|
|
662
|
-
|
|
663
|
-
if (
|
|
664
|
-
if (
|
|
665
|
-
return `¥${
|
|
687
|
+
const value = Number(cny);
|
|
688
|
+
if (!Number.isFinite(value)) return "¥0";
|
|
689
|
+
if (value >= 1e3) return `¥${value.toFixed(0)}`;
|
|
690
|
+
if (value >= 10) return `¥${value.toFixed(1)}`;
|
|
691
|
+
if (value >= .1) return `¥${value.toFixed(2)}`;
|
|
692
|
+
return `¥${value.toFixed(3)}`;
|
|
666
693
|
}
|
|
667
694
|
/**
|
|
668
695
|
* Format a per-1M-token price in its native currency (free when the rate is
|
|
@@ -686,23 +713,27 @@ window.__ModuleLoader__.load({
|
|
|
686
713
|
}
|
|
687
714
|
/** Format a percentage. */
|
|
688
715
|
function formatPercent(value) {
|
|
689
|
-
|
|
716
|
+
const normalized = Number(value);
|
|
717
|
+
if (!Number.isFinite(normalized)) return "0.0%";
|
|
718
|
+
return `${normalized.toFixed(1)}%`;
|
|
690
719
|
}
|
|
691
720
|
//#endregion
|
|
692
721
|
//#region src/client/TrendChart.tsx
|
|
693
722
|
/**
|
|
694
|
-
* TrendChart: dependency-free SVG
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
723
|
+
* TrendChart: dependency-free SVG chart of daily cost + calls.
|
|
724
|
+
*
|
|
725
|
+
* The columns are GROUPED per model — one bar per model per day, each in its
|
|
726
|
+
* brand color, so per-model cost is directly comparable. The blue line is the
|
|
727
|
+
* total call volume across all models, plotted on its own right-hand axis.
|
|
728
|
+
* A hover crosshair shows the day's model breakdown. No chart library — the
|
|
729
|
+
* surface stays self-contained and offline.
|
|
699
730
|
*/
|
|
700
731
|
/** Fixed viewBox; the SVG scales to its container. */
|
|
701
732
|
const W = 680;
|
|
702
733
|
const H = 220;
|
|
703
734
|
const PAD = {
|
|
704
735
|
top: 18,
|
|
705
|
-
right:
|
|
736
|
+
right: 40,
|
|
706
737
|
bottom: 26,
|
|
707
738
|
left: 46
|
|
708
739
|
};
|
|
@@ -711,6 +742,12 @@ window.__ModuleLoader__.load({
|
|
|
711
742
|
const [, month, day] = iso.split("-");
|
|
712
743
|
return `${Number(month)}/${Number(day)}`;
|
|
713
744
|
}
|
|
745
|
+
/** Compact tick label for the calls axis: `1.2K` / `3.4M`. */
|
|
746
|
+
function shortNumber(value) {
|
|
747
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
748
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
749
|
+
return String(Math.round(value));
|
|
750
|
+
}
|
|
714
751
|
/** Ticks every `step` items for sparse axis labels. */
|
|
715
752
|
function tickIndexes(length, step) {
|
|
716
753
|
const out = [];
|
|
@@ -718,52 +755,56 @@ window.__ModuleLoader__.load({
|
|
|
718
755
|
if (length > 0 && out[out.length - 1] !== length - 1) out.push(length - 1);
|
|
719
756
|
return out;
|
|
720
757
|
}
|
|
758
|
+
/** Single-color fallback identity used when the stats carry no per-model detail. */
|
|
759
|
+
const TOTAL_MODEL = {
|
|
760
|
+
key: "__total__",
|
|
761
|
+
name: "总计",
|
|
762
|
+
color: ""
|
|
763
|
+
};
|
|
721
764
|
/**
|
|
722
|
-
* Render the daily
|
|
765
|
+
* Render the daily grouped cost bars plus the total-calls line.
|
|
723
766
|
* @param props.data - sorted daily rows (ascending date).
|
|
724
|
-
* @param props.models - the model legend, in
|
|
767
|
+
* @param props.models - the model legend, in bar order.
|
|
725
768
|
*/
|
|
726
769
|
function TrendChart({ data, models = [] }) {
|
|
727
770
|
const [hover, setHover] = (0, react.useState)(null);
|
|
728
771
|
const layout = (0, react.useMemo)(() => {
|
|
729
772
|
const n = data.length;
|
|
730
773
|
if (n === 0) return null;
|
|
731
|
-
const maxCost = Math.max(...data.map((d) => d.cost), 1e-4);
|
|
732
774
|
const plotW = W - PAD.left - PAD.right;
|
|
733
775
|
const plotH = H - PAD.top - PAD.bottom;
|
|
734
776
|
const inner = (i) => {
|
|
735
777
|
if (n === 1) return PAD.left + plotW / 2;
|
|
736
778
|
return PAD.left + plotW * i / (n - 1);
|
|
737
779
|
};
|
|
780
|
+
const maxCost = Math.max(...data.flatMap((d) => Object.values(d.byModel ?? {})), ...data.map((d) => d.cost), 1e-4);
|
|
738
781
|
const yCost = (value) => PAD.top + plotH - value / maxCost * plotH;
|
|
782
|
+
const maxCalls = Math.max(...data.map((d) => d.calls), 1);
|
|
783
|
+
const yCalls = (value) => PAD.top + plotH - value / maxCalls * plotH;
|
|
784
|
+
const groupW = plotW / n;
|
|
785
|
+
const barCount = Math.max(models.length, 1);
|
|
786
|
+
const barW = Math.min(14, groupW / barCount * .7);
|
|
739
787
|
return {
|
|
740
788
|
n,
|
|
741
789
|
plotW,
|
|
742
790
|
plotH,
|
|
743
791
|
inner,
|
|
744
792
|
yCost,
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const value = d.byModel?.[model.key] ?? 0;
|
|
750
|
-
const y0 = acc;
|
|
751
|
-
acc += value;
|
|
752
|
-
return {
|
|
753
|
-
model,
|
|
754
|
-
y0,
|
|
755
|
-
y1: acc,
|
|
756
|
-
rounded: false
|
|
757
|
-
};
|
|
758
|
-
});
|
|
759
|
-
const top = segments.at(-1);
|
|
760
|
-
if (top !== void 0) top.rounded = true;
|
|
761
|
-
return {
|
|
793
|
+
yCalls,
|
|
794
|
+
barW,
|
|
795
|
+
bars: data.flatMap((d, i) => {
|
|
796
|
+
if (models.length === 0) return [{
|
|
762
797
|
date: d.date,
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
};
|
|
798
|
+
model: TOTAL_MODEL,
|
|
799
|
+
x: inner(i) - barW / 2,
|
|
800
|
+
value: d.cost
|
|
801
|
+
}];
|
|
802
|
+
return models.map((model, k) => ({
|
|
803
|
+
date: d.date,
|
|
804
|
+
model,
|
|
805
|
+
x: inner(i) - groupW / 2 + groupW * (k + .5) / models.length - barW / 2,
|
|
806
|
+
value: d.byModel?.[model.key] ?? 0
|
|
807
|
+
}));
|
|
767
808
|
}),
|
|
768
809
|
costTicks: [
|
|
769
810
|
0,
|
|
@@ -772,15 +813,24 @@ window.__ModuleLoader__.load({
|
|
|
772
813
|
.75,
|
|
773
814
|
1
|
|
774
815
|
].map((f) => maxCost * f).reverse(),
|
|
775
|
-
|
|
816
|
+
callsTicks: [
|
|
817
|
+
0,
|
|
818
|
+
.25,
|
|
819
|
+
.5,
|
|
820
|
+
.75,
|
|
821
|
+
1
|
|
822
|
+
].map((f) => maxCalls * f).reverse(),
|
|
823
|
+
linePath: data.map((d, i) => {
|
|
824
|
+
const y = yCalls(d.calls);
|
|
825
|
+
return `${i === 0 ? "M" : "L"}${inner(i)} ${y}`;
|
|
826
|
+
}).join(" ")
|
|
776
827
|
};
|
|
777
828
|
}, [data, models]);
|
|
778
829
|
if (layout === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
779
830
|
className: UsageBilling_module_css_default.chartEmpty,
|
|
780
831
|
children: "暂无趋势数据"
|
|
781
832
|
});
|
|
782
|
-
const { n, plotW, inner, yCost, barW,
|
|
783
|
-
const activeColumn = hover === null ? void 0 : columns[hover];
|
|
833
|
+
const { n, plotW, plotH, inner, yCost, yCalls, barW, bars, costTicks, callsTicks, linePath } = layout;
|
|
784
834
|
const activePoint = hover === null ? void 0 : data[hover];
|
|
785
835
|
const indices = tickIndexes(n, Math.max(1, Math.ceil(n / 8)));
|
|
786
836
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -790,7 +840,7 @@ window.__ModuleLoader__.load({
|
|
|
790
840
|
viewBox: `0 0 ${W} ${H}`,
|
|
791
841
|
className: UsageBilling_module_css_default.chartSvg,
|
|
792
842
|
role: "img",
|
|
793
|
-
"aria-label": "Daily cost by model",
|
|
843
|
+
"aria-label": "Daily cost by model and total calls",
|
|
794
844
|
onMouseLeave: () => {
|
|
795
845
|
setHover(null);
|
|
796
846
|
},
|
|
@@ -817,15 +867,30 @@ window.__ModuleLoader__.load({
|
|
|
817
867
|
children: formatMoney(value)
|
|
818
868
|
})] }, `cost-${idx}`);
|
|
819
869
|
}),
|
|
820
|
-
|
|
821
|
-
x:
|
|
822
|
-
y: yCost(
|
|
870
|
+
bars.map((bar) => bar.value > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
871
|
+
x: bar.x,
|
|
872
|
+
y: yCost(bar.value),
|
|
823
873
|
width: barW,
|
|
824
|
-
height:
|
|
825
|
-
rx:
|
|
826
|
-
className: UsageBilling_module_css_default.chartStack,
|
|
827
|
-
style: { fill:
|
|
828
|
-
},
|
|
874
|
+
height: yCost(0) - yCost(bar.value),
|
|
875
|
+
rx: 2,
|
|
876
|
+
className: bar.model.color === "" ? UsageBilling_module_css_default.chartBar : UsageBilling_module_css_default.chartStack,
|
|
877
|
+
style: bar.model.color === "" ? void 0 : { fill: bar.model.color }
|
|
878
|
+
}, `${bar.date}-${bar.model.key}`) : null),
|
|
879
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
880
|
+
d: linePath,
|
|
881
|
+
fill: "none",
|
|
882
|
+
className: UsageBilling_module_css_default.chartLine
|
|
883
|
+
}),
|
|
884
|
+
callsTicks.map((value, idx) => {
|
|
885
|
+
const y = yCalls(value);
|
|
886
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
|
|
887
|
+
x: W - PAD.right + 8,
|
|
888
|
+
y: y + 3,
|
|
889
|
+
textAnchor: "start",
|
|
890
|
+
className: UsageBilling_module_css_default.chartAxisLabel,
|
|
891
|
+
children: shortNumber(value)
|
|
892
|
+
}, `calls-${idx}`);
|
|
893
|
+
}),
|
|
829
894
|
indices.map((i) => {
|
|
830
895
|
const point = data[i];
|
|
831
896
|
if (point === void 0) return null;
|
|
@@ -837,44 +902,44 @@ window.__ModuleLoader__.load({
|
|
|
837
902
|
children: shortDate(point.date)
|
|
838
903
|
}, point.date);
|
|
839
904
|
}),
|
|
840
|
-
|
|
905
|
+
hover !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
841
906
|
x1: inner(hover),
|
|
842
907
|
x2: inner(hover),
|
|
843
908
|
y1: PAD.top,
|
|
844
|
-
y2: PAD.top +
|
|
909
|
+
y2: PAD.top + plotH,
|
|
845
910
|
className: UsageBilling_module_css_default.chartCrosshair
|
|
846
911
|
})
|
|
847
912
|
]
|
|
848
913
|
}),
|
|
849
|
-
|
|
914
|
+
activePoint !== void 0 && hover !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
850
915
|
className: UsageBilling_module_css_default.chartTooltip,
|
|
851
916
|
style: {
|
|
852
917
|
left: `${inner(hover) / W * 100}%`,
|
|
853
|
-
top: `${yCost(
|
|
918
|
+
top: `${yCost(activePoint.cost) / H * 100}%`
|
|
854
919
|
},
|
|
855
920
|
children: [
|
|
856
921
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
857
922
|
className: UsageBilling_module_css_default.chartTooltipDate,
|
|
858
923
|
children: activePoint.date
|
|
859
924
|
}),
|
|
860
|
-
|
|
925
|
+
models.filter((model) => (activePoint.byModel?.[model.key] ?? 0) > 0).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
861
926
|
className: UsageBilling_module_css_default.chartTooltipRow,
|
|
862
927
|
children: [
|
|
863
928
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
864
929
|
className: UsageBilling_module_css_default.chartTooltipSwatch,
|
|
865
|
-
style: { background:
|
|
930
|
+
style: { background: model.color }
|
|
866
931
|
}),
|
|
867
|
-
|
|
932
|
+
model.name,
|
|
868
933
|
" ",
|
|
869
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatMoney(
|
|
934
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatMoney(activePoint.byModel?.[model.key] ?? 0) })
|
|
870
935
|
]
|
|
871
|
-
},
|
|
936
|
+
}, model.key)),
|
|
872
937
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
873
938
|
className: UsageBilling_module_css_default.chartTooltipRow,
|
|
874
939
|
children: [
|
|
875
940
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: UsageBilling_module_css_default.chartLegendBar }),
|
|
876
941
|
"总计 ",
|
|
877
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatMoney(
|
|
942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatMoney(activePoint.cost) })
|
|
878
943
|
]
|
|
879
944
|
}),
|
|
880
945
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -887,12 +952,12 @@ window.__ModuleLoader__.load({
|
|
|
887
952
|
})
|
|
888
953
|
]
|
|
889
954
|
}),
|
|
890
|
-
models.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
955
|
+
models.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
891
956
|
className: UsageBilling_module_css_default.chartLegend,
|
|
892
|
-
children: models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
957
|
+
children: [models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
893
958
|
className: UsageBilling_module_css_default.chartTooltipSwatch,
|
|
894
959
|
style: { background: model.color }
|
|
895
|
-
}), model.name] }, model.key))
|
|
960
|
+
}), model.name] }, model.key)), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: UsageBilling_module_css_default.chartLegendLine }), "调用"] })]
|
|
896
961
|
})
|
|
897
962
|
]
|
|
898
963
|
});
|
|
@@ -1001,6 +1066,43 @@ window.__ModuleLoader__.load({
|
|
|
1001
1066
|
}
|
|
1002
1067
|
/** Path to the usage-stats endpoint served by this plugin's node half. */
|
|
1003
1068
|
const USAGE_STATS_PATH = "/api/billing/usage-stats";
|
|
1069
|
+
/** Path to the live-pricing endpoint served by this plugin's node half. */
|
|
1070
|
+
const PRICING_PATH = "/api/billing/pricing";
|
|
1071
|
+
/** Path to the account-balance endpoint served by this plugin's node half. */
|
|
1072
|
+
const BALANCE_PATH = "/api/billing/balance";
|
|
1073
|
+
/** 弹窗打开期间统计与定价的自动刷新间隔(毫秒)。 */
|
|
1074
|
+
const STATS_REFRESH_INTERVAL_MS = 3e4;
|
|
1075
|
+
/**
|
|
1076
|
+
* 本地时区(北京时间)日期戳:与服务端聚合的 dayStamp 一致。不要用
|
|
1077
|
+
* `toISOString()`——那是 UTC,北京时间的凌晨 0-8 点会取到前一天。
|
|
1078
|
+
*/
|
|
1079
|
+
function localDayStamp(time = Date.now()) {
|
|
1080
|
+
const date = new Date(time);
|
|
1081
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1082
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
1083
|
+
}
|
|
1084
|
+
/** 本地时区时钟:`HH:MM:SS`。 */
|
|
1085
|
+
function formatClock(time) {
|
|
1086
|
+
const date = new Date(time);
|
|
1087
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1088
|
+
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* 高区分度图表色板:趋势图柱、图例与计费表圆点按模型分配。不用模型品牌色
|
|
1092
|
+
* (目录里多为蓝色系,视觉上几乎分不开),保证每个模型一眼可辨。
|
|
1093
|
+
*/
|
|
1094
|
+
const CHART_PALETTE = [
|
|
1095
|
+
"#3b82f6",
|
|
1096
|
+
"#06b6d4",
|
|
1097
|
+
"#8b5cf6",
|
|
1098
|
+
"#f59e0b",
|
|
1099
|
+
"#10b981",
|
|
1100
|
+
"#ef4444",
|
|
1101
|
+
"#ec4899",
|
|
1102
|
+
"#6366f1",
|
|
1103
|
+
"#f97316",
|
|
1104
|
+
"#14b8a6"
|
|
1105
|
+
];
|
|
1004
1106
|
/** Empty snapshot: shown before (or without) real host data — zeros, never fabricated samples. */
|
|
1005
1107
|
const EMPTY_STATS = {
|
|
1006
1108
|
total: {
|
|
@@ -1022,89 +1124,174 @@ window.__ModuleLoader__.load({
|
|
|
1022
1124
|
if (!response.ok) return null;
|
|
1023
1125
|
const text = await response.text();
|
|
1024
1126
|
const parsed = JSON.parse(text);
|
|
1025
|
-
if (parsed
|
|
1026
|
-
|
|
1127
|
+
if (parsed === null || typeof parsed !== "object" || !("total" in parsed)) return null;
|
|
1128
|
+
const candidate = parsed;
|
|
1129
|
+
return {
|
|
1130
|
+
total: candidate.total ?? EMPTY_STATS.total,
|
|
1131
|
+
byModel: candidate.byModel ?? {},
|
|
1132
|
+
byDay: candidate.byDay ?? {},
|
|
1133
|
+
...candidate.byDayModels !== void 0 ? { byDayModels: candidate.byDayModels } : {},
|
|
1134
|
+
...candidate.updatedAt !== void 0 ? { updatedAt: candidate.updatedAt } : {}
|
|
1135
|
+
};
|
|
1027
1136
|
} catch {
|
|
1028
1137
|
return null;
|
|
1029
1138
|
}
|
|
1030
1139
|
}
|
|
1031
1140
|
/**
|
|
1141
|
+
* Apply the node half's live pricing snapshot. The node half refreshes once
|
|
1142
|
+
* at boot, so an early `builtin` answer may just mean the refresh is still
|
|
1143
|
+
* in flight — retry briefly before settling for the built-in values.
|
|
1144
|
+
* Any final failure keeps the built-in catalog and rate — degrade, never
|
|
1145
|
+
* fabricate.
|
|
1146
|
+
* @param attempt - current retry index (0-based).
|
|
1147
|
+
*/
|
|
1148
|
+
async function loadLivePricing(attempt = 0) {
|
|
1149
|
+
const MAX_ATTEMPTS = 4;
|
|
1150
|
+
try {
|
|
1151
|
+
const response = await fetch(PRICING_PATH);
|
|
1152
|
+
if (!response.ok) return;
|
|
1153
|
+
const text = await response.text();
|
|
1154
|
+
const parsed = JSON.parse(text);
|
|
1155
|
+
if (parsed === null || typeof parsed !== "object" || !("source" in parsed)) return;
|
|
1156
|
+
const pricing = parsed;
|
|
1157
|
+
if (pricing.source === "builtin" && attempt < MAX_ATTEMPTS - 1) {
|
|
1158
|
+
setTimeout(() => {
|
|
1159
|
+
loadLivePricing(attempt + 1);
|
|
1160
|
+
}, 2e3);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
applyLivePricing(pricing);
|
|
1164
|
+
} catch {}
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* 拉取各提供方账户余额(供模型计费明细表的余额列);失败返回空列表。
|
|
1168
|
+
* @returns the balance rows, or an empty list on any failure.
|
|
1169
|
+
*/
|
|
1170
|
+
async function fetchBalances() {
|
|
1171
|
+
try {
|
|
1172
|
+
const response = await fetch(BALANCE_PATH);
|
|
1173
|
+
if (!response.ok) return [];
|
|
1174
|
+
const text = await response.text();
|
|
1175
|
+
const parsed = JSON.parse(text);
|
|
1176
|
+
if (parsed !== null && typeof parsed === "object" && "balances" in parsed) return parsed.balances;
|
|
1177
|
+
return [];
|
|
1178
|
+
} catch {
|
|
1179
|
+
return [];
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1032
1183
|
* Sidebar footer trigger: compact pill in wide mode, icon in rail mode.
|
|
1033
1184
|
* @param props - framework props plus `wide` column state.
|
|
1034
1185
|
*/
|
|
1035
1186
|
function UsageBillingTrigger(props) {
|
|
1036
|
-
const { wide, onOpen, monthCost, todayCost } = props;
|
|
1187
|
+
const { wide, t, onOpen, monthCost, todayCost } = props;
|
|
1188
|
+
const cardIcon = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1189
|
+
viewBox: "0 0 24 24",
|
|
1190
|
+
fill: "none",
|
|
1191
|
+
stroke: "currentColor",
|
|
1192
|
+
"aria-hidden": "true",
|
|
1193
|
+
children: [
|
|
1194
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1195
|
+
x: "2.5",
|
|
1196
|
+
y: "5",
|
|
1197
|
+
width: "19",
|
|
1198
|
+
height: "14",
|
|
1199
|
+
rx: "2.5"
|
|
1200
|
+
}),
|
|
1201
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.5 9.5h19" }),
|
|
1202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1203
|
+
x: "6",
|
|
1204
|
+
y: "12",
|
|
1205
|
+
width: "4",
|
|
1206
|
+
height: "3.5",
|
|
1207
|
+
rx: "0.75"
|
|
1208
|
+
})
|
|
1209
|
+
]
|
|
1210
|
+
});
|
|
1037
1211
|
if (!wide) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1038
1212
|
type: "button",
|
|
1039
1213
|
className: UsageBilling_module_css_default.railButton,
|
|
1040
1214
|
onClick: onOpen,
|
|
1041
|
-
title: formatMoney(monthCost)
|
|
1042
|
-
children:
|
|
1043
|
-
viewBox: "0 0 24 24",
|
|
1044
|
-
fill: "none",
|
|
1045
|
-
stroke: "currentColor",
|
|
1046
|
-
"aria-hidden": "true",
|
|
1047
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" })
|
|
1048
|
-
})
|
|
1215
|
+
title: `${t("billing.title")} · ${formatMoney(monthCost)}`,
|
|
1216
|
+
children: cardIcon
|
|
1049
1217
|
});
|
|
1050
1218
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1051
1219
|
type: "button",
|
|
1052
1220
|
className: UsageBilling_module_css_default.trigger,
|
|
1053
1221
|
onClick: onOpen,
|
|
1222
|
+
title: `${t("billing.title")} · 本月 ${formatMoney(monthCost)}`,
|
|
1054
1223
|
children: [
|
|
1055
1224
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1056
1225
|
className: UsageBilling_module_css_default.triggerIcon,
|
|
1057
|
-
children:
|
|
1058
|
-
viewBox: "0 0 24 24",
|
|
1059
|
-
fill: "none",
|
|
1060
|
-
stroke: "currentColor",
|
|
1061
|
-
"aria-hidden": "true",
|
|
1062
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" })
|
|
1063
|
-
})
|
|
1226
|
+
children: cardIcon
|
|
1064
1227
|
}),
|
|
1065
1228
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1066
|
-
className: UsageBilling_module_css_default.
|
|
1229
|
+
className: UsageBilling_module_css_default.triggerToday,
|
|
1067
1230
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1068
|
-
className: UsageBilling_module_css_default.triggerLabel,
|
|
1069
|
-
children: "计费仪表盘"
|
|
1070
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1071
1231
|
className: UsageBilling_module_css_default.triggerMeta,
|
|
1072
|
-
children:
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
]
|
|
1232
|
+
children: "今日"
|
|
1233
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1234
|
+
className: UsageBilling_module_css_default.triggerAmount,
|
|
1235
|
+
children: formatMoney(todayCost)
|
|
1077
1236
|
})]
|
|
1078
1237
|
}),
|
|
1079
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1238
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: UsageBilling_module_css_default.triggerDivider }),
|
|
1239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1240
|
+
className: UsageBilling_module_css_default.triggerMonth,
|
|
1241
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1242
|
+
className: UsageBilling_module_css_default.triggerMeta,
|
|
1243
|
+
children: "当月"
|
|
1244
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1245
|
+
className: UsageBilling_module_css_default.triggerAmountSub,
|
|
1246
|
+
children: formatMoney(monthCost)
|
|
1247
|
+
})]
|
|
1086
1248
|
})
|
|
1087
1249
|
]
|
|
1088
1250
|
});
|
|
1089
1251
|
}
|
|
1090
1252
|
/**
|
|
1091
1253
|
* The centered billing dashboard modal.
|
|
1092
|
-
* @param props - stats, locale function, close handler,
|
|
1254
|
+
* @param props - stats, locale function, close handler, model health, balances.
|
|
1093
1255
|
*/
|
|
1094
|
-
function BillingDashboard({ stats, t, onClose, health }) {
|
|
1256
|
+
function BillingDashboard({ stats, t, onClose, health, balances }) {
|
|
1095
1257
|
const { total, byModel, byDay } = stats;
|
|
1096
1258
|
const [pricingOpen, setPricingOpen] = (0, react.useState)(false);
|
|
1259
|
+
const rateInfo = getRateInfo();
|
|
1260
|
+
const balanceFor = (provider) => balances.find((balance) => normalizeProvider(balance.provider) === normalizeProvider(provider));
|
|
1261
|
+
const renderBalance = (balance) => {
|
|
1262
|
+
if (balance === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1263
|
+
className: UsageBilling_module_css_default.na,
|
|
1264
|
+
children: "—"
|
|
1265
|
+
});
|
|
1266
|
+
if (balance.error === "unconfigured") return t("billing.balanceUnconfigured");
|
|
1267
|
+
if (balance.error === "unauthorized") return t("billing.balanceUnauthorized");
|
|
1268
|
+
if (balance.error === "unreachable") return t("billing.balanceUnreachable");
|
|
1269
|
+
if (balance.totalBalance === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1270
|
+
className: UsageBilling_module_css_default.na,
|
|
1271
|
+
children: "—"
|
|
1272
|
+
});
|
|
1273
|
+
return balance.currency === "USD" ? `$${balance.totalBalance.toFixed(2)}` : formatMoney(balance.totalBalance);
|
|
1274
|
+
};
|
|
1097
1275
|
const cacheHitRate = total.cacheHit + total.cacheMiss > 0 ? total.cacheHit / (total.cacheHit + total.cacheMiss) * 100 : 0;
|
|
1098
1276
|
const dates = Object.keys(byDay).sort();
|
|
1099
|
-
const
|
|
1100
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1277
|
+
const today = localDayStamp();
|
|
1101
1278
|
const todayCost = byDay[today]?.cost ?? 0;
|
|
1102
1279
|
const monthPrefix = today.slice(0, 7);
|
|
1103
1280
|
const yearPrefix = today.slice(0, 4);
|
|
1104
1281
|
const monthCost = dates.reduce((sum, d) => sum + (d.startsWith(monthPrefix) ? byDay[d]?.cost ?? 0 : 0), 0);
|
|
1105
1282
|
const monthCalls = dates.reduce((sum, d) => sum + (d.startsWith(monthPrefix) ? byDay[d]?.calls ?? 0 : 0), 0);
|
|
1106
1283
|
const yearCost = dates.reduce((sum, d) => sum + (d.startsWith(yearPrefix) ? byDay[d]?.cost ?? 0 : 0), 0);
|
|
1107
|
-
const
|
|
1284
|
+
const trendDates = (0, react.useMemo)(() => {
|
|
1285
|
+
const out = [];
|
|
1286
|
+
for (let offset = 6; offset >= 0; offset -= 1) {
|
|
1287
|
+
const day = /* @__PURE__ */ new Date();
|
|
1288
|
+
day.setDate(day.getDate() - offset);
|
|
1289
|
+
out.push(localDayStamp(day.getTime()));
|
|
1290
|
+
}
|
|
1291
|
+
return out;
|
|
1292
|
+
}, []);
|
|
1293
|
+
const latestDate = trendDates.at(-1) ?? today;
|
|
1294
|
+
const trend = (0, react.useMemo)(() => trendDates.map((date) => {
|
|
1108
1295
|
const byModel = {};
|
|
1109
1296
|
const dayModels = stats.byDayModels?.[date];
|
|
1110
1297
|
if (dayModels !== void 0) {
|
|
@@ -1118,7 +1305,7 @@ window.__ModuleLoader__.load({
|
|
|
1118
1305
|
byModel
|
|
1119
1306
|
};
|
|
1120
1307
|
}), [
|
|
1121
|
-
|
|
1308
|
+
trendDates,
|
|
1122
1309
|
byDay,
|
|
1123
1310
|
stats.byDayModels
|
|
1124
1311
|
]);
|
|
@@ -1134,7 +1321,6 @@ window.__ModuleLoader__.load({
|
|
|
1134
1321
|
key,
|
|
1135
1322
|
name: entry.name,
|
|
1136
1323
|
provider: entry.provider,
|
|
1137
|
-
color: resolveToken(entry.colorVar),
|
|
1138
1324
|
calls: data.calls,
|
|
1139
1325
|
input: data.input,
|
|
1140
1326
|
output: data.output,
|
|
@@ -1143,7 +1329,10 @@ window.__ModuleLoader__.load({
|
|
|
1143
1329
|
plan: isSubscriptionPlan(key),
|
|
1144
1330
|
...data.cost > 0 ? { actual: data.cost } : {}
|
|
1145
1331
|
};
|
|
1146
|
-
}).sort((a, b) => (b.actual ?? b.estimated) - (a.actual ?? a.estimated)),
|
|
1332
|
+
}).sort((a, b) => (b.actual ?? b.estimated) - (a.actual ?? a.estimated)).map((row, index) => ({
|
|
1333
|
+
...row,
|
|
1334
|
+
color: CHART_PALETTE[index % CHART_PALETTE.length] ?? "#8b95a3"
|
|
1335
|
+
})), [byModel]);
|
|
1147
1336
|
const estimatedTotal = modelRows.reduce((sum, row) => sum + row.estimated, 0);
|
|
1148
1337
|
const displayTotal = total.cost > 0 ? total.cost : estimatedTotal;
|
|
1149
1338
|
const avgPerCall = total.calls > 0 ? displayTotal / total.calls : 0;
|
|
@@ -1201,64 +1390,56 @@ window.__ModuleLoader__.load({
|
|
|
1201
1390
|
children: [
|
|
1202
1391
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1203
1392
|
className: UsageBilling_module_css_default.hero,
|
|
1204
|
-
children: [
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1393
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1394
|
+
className: UsageBilling_module_css_default.heroMain,
|
|
1395
|
+
children: [
|
|
1396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1397
|
+
className: UsageBilling_module_css_default.heroLabel,
|
|
1398
|
+
children: t("billing.monthCost")
|
|
1399
|
+
}),
|
|
1400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1401
|
+
className: UsageBilling_module_css_default.heroValue,
|
|
1402
|
+
children: formatMoney(monthCost)
|
|
1403
|
+
}),
|
|
1404
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1405
|
+
className: UsageBilling_module_css_default.heroMeta,
|
|
1406
|
+
children: [
|
|
1407
|
+
monthCalls.toLocaleString(),
|
|
1408
|
+
" ",
|
|
1409
|
+
t("billing.calls")
|
|
1410
|
+
]
|
|
1411
|
+
})
|
|
1412
|
+
]
|
|
1413
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1414
|
+
className: UsageBilling_module_css_default.heroSide,
|
|
1415
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1416
|
+
className: UsageBilling_module_css_default.heroSideItem,
|
|
1417
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1418
|
+
className: UsageBilling_module_css_default.heroSideLabel,
|
|
1419
|
+
children: t("billing.yearCost")
|
|
1420
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1421
|
+
className: UsageBilling_module_css_default.heroSideValue,
|
|
1422
|
+
children: formatMoney(yearCost)
|
|
1423
|
+
})]
|
|
1424
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1425
|
+
className: UsageBilling_module_css_default.heroSideItem,
|
|
1426
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1427
|
+
className: UsageBilling_module_css_default.heroSideLabel,
|
|
1428
|
+
children: t("billing.todayCost")
|
|
1429
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1430
|
+
className: UsageBilling_module_css_default.heroSideValue,
|
|
1431
|
+
children: [formatMoney(todayCost), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1432
|
+
className: clsx(UsageBilling_module_css_default.delta, deltaPct >= 0 ? UsageBilling_module_css_default.deltaUp : UsageBilling_module_css_default.deltaDown),
|
|
1218
1433
|
children: [
|
|
1219
|
-
|
|
1434
|
+
deltaPct >= 0 ? "▲" : "▼",
|
|
1220
1435
|
" ",
|
|
1221
|
-
|
|
1436
|
+
Math.abs(deltaPct).toFixed(1),
|
|
1437
|
+
"%"
|
|
1222
1438
|
]
|
|
1223
|
-
})
|
|
1224
|
-
]
|
|
1225
|
-
}),
|
|
1226
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: UsageBilling_module_css_default.heroDivider }),
|
|
1227
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1228
|
-
className: UsageBilling_module_css_default.heroSide,
|
|
1229
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1230
|
-
className: UsageBilling_module_css_default.heroSideItem,
|
|
1231
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1232
|
-
className: UsageBilling_module_css_default.heroSideLabel,
|
|
1233
|
-
children: t("billing.yearCost")
|
|
1234
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1235
|
-
className: UsageBilling_module_css_default.heroSideValue,
|
|
1236
|
-
children: formatMoney(yearCost)
|
|
1237
1439
|
})]
|
|
1238
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1239
|
-
className: UsageBilling_module_css_default.heroSideItem,
|
|
1240
|
-
children: [
|
|
1241
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1242
|
-
className: UsageBilling_module_css_default.heroSideLabel,
|
|
1243
|
-
children: t("billing.todayCost")
|
|
1244
|
-
}),
|
|
1245
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1246
|
-
className: UsageBilling_module_css_default.heroSideValue,
|
|
1247
|
-
children: formatMoney(todayCost)
|
|
1248
|
-
}),
|
|
1249
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1250
|
-
className: clsx(UsageBilling_module_css_default.delta, deltaPct >= 0 ? UsageBilling_module_css_default.deltaUp : UsageBilling_module_css_default.deltaDown),
|
|
1251
|
-
children: [
|
|
1252
|
-
deltaPct >= 0 ? "▲" : "▼",
|
|
1253
|
-
" ",
|
|
1254
|
-
Math.abs(deltaPct).toFixed(1),
|
|
1255
|
-
"%"
|
|
1256
|
-
]
|
|
1257
|
-
})
|
|
1258
|
-
]
|
|
1259
1440
|
})]
|
|
1260
|
-
})
|
|
1261
|
-
]
|
|
1441
|
+
})]
|
|
1442
|
+
})]
|
|
1262
1443
|
}),
|
|
1263
1444
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1264
1445
|
className: UsageBilling_module_css_default.kpiGrid,
|
|
@@ -1376,13 +1557,9 @@ window.__ModuleLoader__.load({
|
|
|
1376
1557
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1377
1558
|
className: UsageBilling_module_css_default.panelTitle,
|
|
1378
1559
|
children: t("billing.models")
|
|
1379
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1560
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1380
1561
|
className: UsageBilling_module_css_default.panelHint,
|
|
1381
|
-
children:
|
|
1382
|
-
t("billing.estimated"),
|
|
1383
|
-
" · ",
|
|
1384
|
-
t("billing.pricePerM")
|
|
1385
|
-
]
|
|
1562
|
+
children: stats.updatedAt !== void 0 ? `${t("billing.lastUpdated")} ${formatClock(stats.updatedAt)}` : ""
|
|
1386
1563
|
})]
|
|
1387
1564
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1388
1565
|
className: UsageBilling_module_css_default.tableScroll,
|
|
@@ -1408,11 +1585,11 @@ window.__ModuleLoader__.load({
|
|
|
1408
1585
|
}),
|
|
1409
1586
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
1410
1587
|
className: UsageBilling_module_css_default.numCol,
|
|
1411
|
-
children: t("billing.
|
|
1588
|
+
children: t("billing.actual")
|
|
1412
1589
|
}),
|
|
1413
1590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
1414
1591
|
className: UsageBilling_module_css_default.numCol,
|
|
1415
|
-
children: t("billing.
|
|
1592
|
+
children: t("billing.balance")
|
|
1416
1593
|
})
|
|
1417
1594
|
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tbody", { children: [modelRows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1418
1595
|
colSpan: 7,
|
|
@@ -1448,13 +1625,6 @@ window.__ModuleLoader__.load({
|
|
|
1448
1625
|
className: UsageBilling_module_css_default.numCol,
|
|
1449
1626
|
children: formatPercent(row.cacheHitRate)
|
|
1450
1627
|
}),
|
|
1451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1452
|
-
className: clsx(UsageBilling_module_css_default.numCol, UsageBilling_module_css_default.costCol),
|
|
1453
|
-
children: row.plan ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1454
|
-
className: UsageBilling_module_css_default.planTag,
|
|
1455
|
-
children: "订阅包含"
|
|
1456
|
-
}) : formatMoney(row.estimated)
|
|
1457
|
-
}),
|
|
1458
1628
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1459
1629
|
className: UsageBilling_module_css_default.numCol,
|
|
1460
1630
|
children: row.plan ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1464,6 +1634,10 @@ window.__ModuleLoader__.load({
|
|
|
1464
1634
|
className: UsageBilling_module_css_default.na,
|
|
1465
1635
|
children: "—"
|
|
1466
1636
|
})
|
|
1637
|
+
}),
|
|
1638
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1639
|
+
className: UsageBilling_module_css_default.numCol,
|
|
1640
|
+
children: renderBalance(balanceFor(row.provider))
|
|
1467
1641
|
})
|
|
1468
1642
|
] }, row.key))] })]
|
|
1469
1643
|
})
|
|
@@ -1483,9 +1657,17 @@ window.__ModuleLoader__.load({
|
|
|
1483
1657
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1484
1658
|
className: UsageBilling_module_css_default.panelTitle,
|
|
1485
1659
|
children: t("billing.pricing")
|
|
1486
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1660
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1487
1661
|
className: UsageBilling_module_css_default.panelHint,
|
|
1488
|
-
children:
|
|
1662
|
+
children: [
|
|
1663
|
+
t("billing.todayRate"),
|
|
1664
|
+
" 1 USD = ",
|
|
1665
|
+
formatMoney(rateInfo.rate),
|
|
1666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1667
|
+
className: clsx(UsageBilling_module_css_default.rateBadge, rateInfo.live ? UsageBilling_module_css_default.rateBadgeLive : UsageBilling_module_css_default.rateBadgeBuiltin),
|
|
1668
|
+
children: rateInfo.live ? t("billing.rateLive") : t("billing.rateBuiltin")
|
|
1669
|
+
})
|
|
1670
|
+
]
|
|
1489
1671
|
})]
|
|
1490
1672
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1491
1673
|
className: clsx(UsageBilling_module_css_default.pricingChevron, pricingOpen && UsageBilling_module_css_default.pricingChevronOpen),
|
|
@@ -1589,22 +1771,37 @@ window.__ModuleLoader__.load({
|
|
|
1589
1771
|
const { t, checkModels } = props;
|
|
1590
1772
|
const [stats, setStats] = (0, react.useState)(EMPTY_STATS);
|
|
1591
1773
|
const [health, setHealth] = (0, react.useState)(IDLE_HEALTH);
|
|
1774
|
+
const [balances, setBalances] = (0, react.useState)([]);
|
|
1592
1775
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1593
1776
|
const close = (0, react.useCallback)(() => {
|
|
1594
1777
|
setOpen(false);
|
|
1595
1778
|
}, []);
|
|
1779
|
+
const reloadStats = (0, react.useCallback)(() => {
|
|
1780
|
+
loadUsageStats().then((data) => {
|
|
1781
|
+
if (data !== null) setStats(data);
|
|
1782
|
+
});
|
|
1783
|
+
fetchBalances().then((list) => {
|
|
1784
|
+
if (list.length > 0) setBalances(list);
|
|
1785
|
+
});
|
|
1786
|
+
}, []);
|
|
1596
1787
|
const openDashboard = (0, react.useCallback)(() => {
|
|
1788
|
+
reloadStats();
|
|
1789
|
+
loadLivePricing();
|
|
1597
1790
|
setOpen(true);
|
|
1598
|
-
}, []);
|
|
1791
|
+
}, [reloadStats]);
|
|
1599
1792
|
(0, react.useEffect)(() => {
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1793
|
+
reloadStats();
|
|
1794
|
+
loadLivePricing();
|
|
1795
|
+
}, [reloadStats]);
|
|
1796
|
+
(0, react.useEffect)(() => {
|
|
1797
|
+
const timer = setInterval(() => {
|
|
1798
|
+
reloadStats();
|
|
1799
|
+
loadLivePricing();
|
|
1800
|
+
}, STATS_REFRESH_INTERVAL_MS);
|
|
1604
1801
|
return () => {
|
|
1605
|
-
|
|
1802
|
+
clearInterval(timer);
|
|
1606
1803
|
};
|
|
1607
|
-
}, []);
|
|
1804
|
+
}, [reloadStats]);
|
|
1608
1805
|
(0, react.useEffect)(() => {
|
|
1609
1806
|
let mounted = true;
|
|
1610
1807
|
checkModels().then((result) => {
|
|
@@ -1614,7 +1811,7 @@ window.__ModuleLoader__.load({
|
|
|
1614
1811
|
mounted = false;
|
|
1615
1812
|
};
|
|
1616
1813
|
}, [checkModels]);
|
|
1617
|
-
const today = (
|
|
1814
|
+
const today = localDayStamp();
|
|
1618
1815
|
const monthCost = Object.entries(stats.byDay).filter(([date]) => date.startsWith(today.slice(0, 7))).reduce((sum, [, day]) => sum + day.cost, 0);
|
|
1619
1816
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBillingTrigger, {
|
|
1620
1817
|
...props,
|
|
@@ -1625,7 +1822,8 @@ window.__ModuleLoader__.load({
|
|
|
1625
1822
|
stats,
|
|
1626
1823
|
t,
|
|
1627
1824
|
onClose: close,
|
|
1628
|
-
health
|
|
1825
|
+
health,
|
|
1826
|
+
balances
|
|
1629
1827
|
})] });
|
|
1630
1828
|
}
|
|
1631
1829
|
//#endregion
|
|
@@ -1665,7 +1863,14 @@ window.__ModuleLoader__.load({
|
|
|
1665
1863
|
"billing.openDashboard": "打开计费仪表盘",
|
|
1666
1864
|
"billing.close": "关闭",
|
|
1667
1865
|
"billing.lastUpdated": "数据更新于",
|
|
1668
|
-
"billing.noData": "暂无计费数据"
|
|
1866
|
+
"billing.noData": "暂无计费数据",
|
|
1867
|
+
"billing.todayRate": "今日汇率",
|
|
1868
|
+
"billing.rateLive": "实时",
|
|
1869
|
+
"billing.rateBuiltin": "内置",
|
|
1870
|
+
"billing.balance": "余额",
|
|
1871
|
+
"billing.balanceUnconfigured": "未配置",
|
|
1872
|
+
"billing.balanceUnauthorized": "密钥无效",
|
|
1873
|
+
"billing.balanceUnreachable": "查询失败"
|
|
1669
1874
|
};
|
|
1670
1875
|
const en = {
|
|
1671
1876
|
"billing.title": "Usage",
|
|
@@ -1701,7 +1906,14 @@ window.__ModuleLoader__.load({
|
|
|
1701
1906
|
"billing.openDashboard": "Open billing dashboard",
|
|
1702
1907
|
"billing.close": "Close",
|
|
1703
1908
|
"billing.lastUpdated": "Updated",
|
|
1704
|
-
"billing.noData": "No billing data yet"
|
|
1909
|
+
"billing.noData": "No billing data yet",
|
|
1910
|
+
"billing.todayRate": "Today rate",
|
|
1911
|
+
"billing.rateLive": "Live",
|
|
1912
|
+
"billing.rateBuiltin": "Built-in",
|
|
1913
|
+
"billing.balance": "Balance",
|
|
1914
|
+
"billing.balanceUnconfigured": "Not set",
|
|
1915
|
+
"billing.balanceUnauthorized": "Bad key",
|
|
1916
|
+
"billing.balanceUnreachable": "Unavailable"
|
|
1705
1917
|
};
|
|
1706
1918
|
//#endregion
|
|
1707
1919
|
//#region src/client/apply.ts
|