@gamegeek-saikel/dsh-cost-meter 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -400,8 +400,8 @@ window.__ModuleLoader__.load({
400
400
  return `${multiplier.toFixed(1)}×`;
401
401
  }
402
402
  //#endregion
403
- //#region \0dsh-css:D:\Projects\dsh-plugins\dsh-cost-meter\src\client\SessionCostLine.module.css.mjs
404
- const css$4 = ".ScWO-a_root{text-align:center;max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;padding:0 calc(var(--dsh-composer-side-clearance) + 16px) 4px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;margin:0 auto;font-size:12px;line-height:20px;display:block;overflow:hidden}";
403
+ //#region \0dsh-css:/home/runner/work/dsh-cost-meter/dsh-cost-meter/src/client/SessionCostLine.module.css.mjs
404
+ const css$4 = "._09e0Yq_root{text-align:center;max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;padding:0 calc(var(--dsh-composer-side-clearance) + 16px) 4px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;margin:0 auto;font-size:12px;line-height:20px;display:block;overflow:hidden}";
405
405
  const tagId$4 = "@gamegeek-saikel/dsh-cost-meter/SessionCostLine.module.css";
406
406
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
407
407
  const tag = document.createElement("style");
@@ -410,7 +410,7 @@ window.__ModuleLoader__.load({
410
410
  tag.textContent = css$4;
411
411
  document.head.appendChild(tag);
412
412
  }
413
- var SessionCostLine_module_css_default = { "root": "ScWO-a_root" };
413
+ var SessionCostLine_module_css_default = { "root": "_09e0Yq_root" };
414
414
  //#endregion
415
415
  //#region src/client/SessionCostLine.tsx
416
416
  /**
@@ -465,7 +465,9 @@ window.__ModuleLoader__.load({
465
465
  }
466
466
  const SessionCostLine = (0, react.memo)(function SessionCostLine({ useProjection, sessionId, t }) {
467
467
  const currency = displayCurrency(t);
468
- const cost = useProjection(currency === "USD" ? "sessionCostUsd" : "sessionCost");
468
+ const costCny = useProjection("sessionCost");
469
+ const costUsd = useProjection("sessionCostUsd");
470
+ const cost = currency === "USD" ? costUsd : costCny;
469
471
  const [response, setResponse] = (0, react.useState)(null);
470
472
  const [failed, setFailed] = (0, react.useState)(false);
471
473
  (0, react.useEffect)(() => {
@@ -555,8 +557,8 @@ window.__ModuleLoader__.load({
555
557
  });
556
558
  });
557
559
  //#endregion
558
- //#region \0dsh-css:D:\Projects\dsh-plugins\dsh-cost-meter\src\client\CostView.module.css.mjs
559
- const css$3 = ".l7-mcW_root{max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;color:var(--dsw-alias-label-primary);flex-direction:column;gap:16px;margin:0 auto;padding:16px 24px;font-size:13px;line-height:20px;display:flex}.l7-mcW_summary{flex-direction:column;gap:8px;display:flex}.l7-mcW_totalRow{justify-content:space-between;align-items:baseline;display:flex}.l7-mcW_totalLabel{font-size:15px;font-weight:600}.l7-mcW_totalValue{font-variant-numeric:tabular-nums;font-size:18px;font-weight:700}.l7-mcW_bandBadge{opacity:.85;white-space:nowrap;font-size:11px;font-weight:500}.l7-mcW_bandBadge[data-band=peak]{color:var(--dsw-alias-state-error-primary)}.l7-mcW_bandBadge[data-band=offPeak]{color:var(--dsw-alias-state-success-primary)}.l7-mcW_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}.l7-mcW_categories{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 16px;font-size:12px;display:flex}.l7-mcW_unpricedNote{color:var(--dsw-alias-label-secondary);opacity:.75;font-size:12px}.l7-mcW_steps{flex-direction:column;gap:8px;display:flex}.l7-mcW_stepsTitle{margin:0;font-size:13px;font-weight:600}.l7-mcW_stepList{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}.l7-mcW_step{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:2px;padding:8px 10px;display:flex}.l7-mcW_step[data-band=peak]{border-color:var(--dsw-alias-state-error-secondary)}.l7-mcW_step[data-band=offPeak]{border-color:var(--dsw-alias-state-success-secondary)}.l7-mcW_step[data-band=peak] .l7-mcW_stepCost{color:var(--dsw-alias-state-error-primary)}.l7-mcW_step[data-band=offPeak] .l7-mcW_stepCost{color:var(--dsw-alias-state-success-primary)}.l7-mcW_stepHead{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.l7-mcW_stepTurn{font-weight:600}.l7-mcW_stepModel{color:var(--dsw-alias-label-secondary);font-size:12px}.l7-mcW_stepTime{color:var(--dsw-alias-label-secondary);opacity:.75;font-size:12px}.l7-mcW_stepCost{font-variant-numeric:tabular-nums;align-items:baseline;gap:4px;margin-left:auto;font-weight:600;display:inline-flex}.l7-mcW_stepBody{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.l7-mcW_stepMeta{color:var(--dsw-alias-label-secondary);opacity:.75}";
560
+ //#region \0dsh-css:/home/runner/work/dsh-cost-meter/dsh-cost-meter/src/client/CostView.module.css.mjs
561
+ const css$3 = ".fZF-xq_root{max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;color:var(--dsw-alias-label-primary);flex-direction:column;gap:16px;margin:0 auto;padding:16px 24px;font-size:13px;line-height:20px;display:flex}.fZF-xq_summary{flex-direction:column;gap:8px;display:flex}.fZF-xq_totalRow{justify-content:space-between;align-items:baseline;display:flex}.fZF-xq_totalLabel{font-size:15px;font-weight:600}.fZF-xq_totalValue{font-variant-numeric:tabular-nums;font-size:18px;font-weight:700}.fZF-xq_bandBadge{opacity:.85;white-space:nowrap;font-size:11px;font-weight:500}.fZF-xq_bandBadge[data-band=peak]{color:var(--dsw-alias-state-error-primary)}.fZF-xq_bandBadge[data-band=offPeak]{color:var(--dsw-alias-state-success-primary)}.fZF-xq_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}.fZF-xq_categories{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 16px;font-size:12px;display:flex}.fZF-xq_unpricedNote{color:var(--dsw-alias-label-secondary);opacity:.75;font-size:12px}.fZF-xq_steps{flex-direction:column;gap:8px;display:flex}.fZF-xq_stepsTitle{margin:0;font-size:13px;font-weight:600}.fZF-xq_stepList{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}.fZF-xq_step{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:2px;padding:8px 10px;display:flex}.fZF-xq_step[data-band=peak]{border-color:var(--dsw-alias-state-error-secondary)}.fZF-xq_step[data-band=offPeak]{border-color:var(--dsw-alias-state-success-secondary)}.fZF-xq_step[data-band=peak] .fZF-xq_stepCost{color:var(--dsw-alias-state-error-primary)}.fZF-xq_step[data-band=offPeak] .fZF-xq_stepCost{color:var(--dsw-alias-state-success-primary)}.fZF-xq_stepHead{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.fZF-xq_stepTurn{font-weight:600}.fZF-xq_stepModel{color:var(--dsw-alias-label-secondary);font-size:12px}.fZF-xq_stepTime{color:var(--dsw-alias-label-secondary);opacity:.75;font-size:12px}.fZF-xq_stepCost{font-variant-numeric:tabular-nums;align-items:baseline;gap:4px;margin-left:auto;font-weight:600;display:inline-flex}.fZF-xq_stepBody{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.fZF-xq_stepMeta{color:var(--dsw-alias-label-secondary);opacity:.75}";
560
562
  const tagId$3 = "@gamegeek-saikel/dsh-cost-meter/CostView.module.css";
561
563
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
562
564
  const tag = document.createElement("style");
@@ -566,25 +568,25 @@ window.__ModuleLoader__.load({
566
568
  document.head.appendChild(tag);
567
569
  }
568
570
  var CostView_module_css_default = {
569
- "bandBadge": "l7-mcW_bandBadge",
570
- "categories": "l7-mcW_categories",
571
- "root": "l7-mcW_root",
572
- "step": "l7-mcW_step",
573
- "stepBody": "l7-mcW_stepBody",
574
- "stepCost": "l7-mcW_stepCost",
575
- "stepHead": "l7-mcW_stepHead",
576
- "stepList": "l7-mcW_stepList",
577
- "stepMeta": "l7-mcW_stepMeta",
578
- "stepModel": "l7-mcW_stepModel",
579
- "stepTime": "l7-mcW_stepTime",
580
- "stepTurn": "l7-mcW_stepTurn",
581
- "steps": "l7-mcW_steps",
582
- "stepsTitle": "l7-mcW_stepsTitle",
583
- "summary": "l7-mcW_summary",
584
- "totalLabel": "l7-mcW_totalLabel",
585
- "totalRow": "l7-mcW_totalRow",
586
- "totalValue": "l7-mcW_totalValue",
587
- "unpricedNote": "l7-mcW_unpricedNote"
571
+ "bandBadge": "fZF-xq_bandBadge",
572
+ "categories": "fZF-xq_categories",
573
+ "root": "fZF-xq_root",
574
+ "step": "fZF-xq_step",
575
+ "stepBody": "fZF-xq_stepBody",
576
+ "stepCost": "fZF-xq_stepCost",
577
+ "stepHead": "fZF-xq_stepHead",
578
+ "stepList": "fZF-xq_stepList",
579
+ "stepMeta": "fZF-xq_stepMeta",
580
+ "stepModel": "fZF-xq_stepModel",
581
+ "stepTime": "fZF-xq_stepTime",
582
+ "stepTurn": "fZF-xq_stepTurn",
583
+ "steps": "fZF-xq_steps",
584
+ "stepsTitle": "fZF-xq_stepsTitle",
585
+ "summary": "fZF-xq_summary",
586
+ "totalLabel": "fZF-xq_totalLabel",
587
+ "totalRow": "fZF-xq_totalRow",
588
+ "totalValue": "fZF-xq_totalValue",
589
+ "unpricedNote": "fZF-xq_unpricedNote"
588
590
  };
589
591
  //#endregion
590
592
  //#region src/client/CostView.tsx
@@ -603,7 +605,9 @@ window.__ModuleLoader__.load({
603
605
  }
604
606
  const CostView = (0, react.memo)(function CostView({ useProjection, sessionId, t }) {
605
607
  const currency = displayCurrency(t);
606
- const cost = useProjection(currency === "USD" ? "sessionCostUsd" : "sessionCost");
608
+ const costCny = useProjection("sessionCost");
609
+ const costUsd = useProjection("sessionCostUsd");
610
+ const cost = currency === "USD" ? costUsd : costCny;
607
611
  const [response, setResponse] = (0, react.useState)(null);
608
612
  const symbol = currencySymbol(currency);
609
613
  (0, react.useEffect)(() => {
@@ -770,8 +774,8 @@ window.__ModuleLoader__.load({
770
774
  });
771
775
  });
772
776
  //#endregion
773
- //#region \0dsh-css:D:\Projects\dsh-plugins\dsh-cost-meter\src\client\AssistantCostChip.module.css.mjs
774
- const css$2 = ".Sy2Hbq_root{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-variant-numeric:tabular-nums;order:10;align-items:baseline;gap:4px;margin-left:10px;font-size:14px;line-height:24px;display:inline-flex}.Sy2Hbq_root:before{content:\"·\";color:var(--dsw-alias-separator-primary);margin-right:10px}.Sy2Hbq_bandBadge{opacity:.85;white-space:nowrap;font-size:12px;line-height:18px}.Sy2Hbq_bandBadge[data-band=peak]{color:var(--dsw-alias-state-error-primary)}.Sy2Hbq_bandBadge[data-band=offPeak]{color:var(--dsw-alias-state-success-primary)}.Sy2Hbq_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}.Sy2Hbq_unpriced{color:var(--dsw-alias-label-tertiary);white-space:nowrap;opacity:.6;cursor:help;order:10;margin-left:10px;font-size:14px;line-height:24px}.Sy2Hbq_unpriced:before{content:\"·\";color:var(--dsw-alias-separator-primary);margin-right:10px}@media (hover:hover){[data-time-hover-root] [data-cost-chip]{opacity:0;transition:opacity 80ms}[data-time-hover-root]:hover [data-cost-chip],[data-time-hover-root]:focus-within [data-cost-chip]{opacity:1}[data-time-hover-root] [data-cost-chip].Sy2Hbq_unpriced{opacity:0}}";
777
+ //#region \0dsh-css:/home/runner/work/dsh-cost-meter/dsh-cost-meter/src/client/AssistantCostChip.module.css.mjs
778
+ const css$2 = "._9lx30q_root{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-variant-numeric:tabular-nums;order:10;align-items:baseline;gap:4px;margin-left:10px;font-size:14px;line-height:24px;display:inline-flex}._9lx30q_root:before{content:\"·\";color:var(--dsw-alias-separator-primary);margin-right:10px}._9lx30q_bandBadge{opacity:.85;white-space:nowrap;font-size:12px;line-height:18px}._9lx30q_bandBadge[data-band=peak]{color:var(--dsw-alias-state-error-primary)}._9lx30q_bandBadge[data-band=offPeak]{color:var(--dsw-alias-state-success-primary)}._9lx30q_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}._9lx30q_unpriced{color:var(--dsw-alias-label-tertiary);white-space:nowrap;opacity:.6;cursor:help;order:10;margin-left:10px;font-size:14px;line-height:24px}._9lx30q_unpriced:before{content:\"·\";color:var(--dsw-alias-separator-primary);margin-right:10px}@media (hover:hover){[data-time-hover-root] [data-cost-chip]{opacity:0;transition:opacity 80ms}[data-time-hover-root]:hover [data-cost-chip],[data-time-hover-root]:focus-within [data-cost-chip]{opacity:1}[data-time-hover-root] [data-cost-chip]._9lx30q_unpriced{opacity:0}}";
775
779
  const tagId$2 = "@gamegeek-saikel/dsh-cost-meter/AssistantCostChip.module.css";
776
780
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
777
781
  const tag = document.createElement("style");
@@ -781,9 +785,9 @@ window.__ModuleLoader__.load({
781
785
  document.head.appendChild(tag);
782
786
  }
783
787
  var AssistantCostChip_module_css_default = {
784
- "bandBadge": "Sy2Hbq_bandBadge",
785
- "root": "Sy2Hbq_root",
786
- "unpriced": "Sy2Hbq_unpriced"
788
+ "bandBadge": "_9lx30q_bandBadge",
789
+ "root": "_9lx30q_root",
790
+ "unpriced": "_9lx30q_unpriced"
787
791
  };
788
792
  //#endregion
789
793
  //#region src/client/AssistantCostChip.tsx
@@ -822,10 +826,10 @@ window.__ModuleLoader__.load({
822
826
  }
823
827
  const AssistantCostChip = (0, react.memo)(function AssistantCostChip({ messageId, useSession, useProjection, t }) {
824
828
  const currency = displayCurrency(t);
825
- const projectionKey = currency === "USD" ? "sessionCostUsd" : "sessionCost";
826
829
  const step = useSession((snapshot) => stepOfMessage(snapshot, messageId));
827
- const cost = useProjection(projectionKey);
828
- const ledger = step === null ? void 0 : stepOf(cost?.steps, step.turn, step.step);
830
+ const costCny = useProjection("sessionCost");
831
+ const costUsd = useProjection("sessionCostUsd");
832
+ const ledger = step === null ? void 0 : stepOf((currency === "USD" ? costUsd : costCny)?.steps, step.turn, step.step);
829
833
  const [response, setResponse] = (0, react.useState)(null);
830
834
  (0, react.useEffect)(() => {
831
835
  let alive = true;
@@ -866,8 +870,8 @@ window.__ModuleLoader__.load({
866
870
  });
867
871
  });
868
872
  //#endregion
869
- //#region \0dsh-css:D:\Projects\dsh-plugins\dsh-cost-meter\src\client\SessionCostPill.module.css.mjs
870
- const css$1 = "._1P-OIW_wrap{display:inline-flex;position:relative}._1P-OIW_pill{border:1px solid var(--dsw-alias-border-l2);height:32px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer;user-select:none;background:0 0;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:6px 12px;font-size:13px;font-weight:400;line-height:20px;transition:background-color .15s,color .15s,border-color .15s;display:inline-flex}._1P-OIW_pill:hover{background:var(--dsw-alias-interactive-bg-hover)}._1P-OIW_pill:active{transform:translateY(.5px)}._1P-OIW_pill[data-running=true]{color:var(--dsw-alias-brand-primary)}._1P-OIW_pill[data-running=true]:hover{background:var(--dsw-alias-interactive-bg-hover)}._1P-OIW_pill[data-band=peak]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-secondary)}._1P-OIW_pill[data-band=offPeak]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-secondary)}._1P-OIW_bandBadge{opacity:.85;white-space:nowrap;font-size:11px;line-height:16px}._1P-OIW_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}._1P-OIW_estimateNote{opacity:.75;font-size:11px}._1P-OIW_detail{z-index:100;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);min-width:250px;max-width:340px;box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;border-radius:12px;flex-direction:column;gap:5px;padding:12px 14px;font-size:12px;line-height:18px;display:flex;position:absolute;top:calc(100% + 8px);right:0}._1P-OIW_detailRow{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}._1P-OIW_detailRowSecondary{color:var(--dsw-alias-label-secondary)}._1P-OIW_detailRowMuted{color:var(--dsw-alias-label-secondary);opacity:.75}._1P-OIW_detailTotal{font-size:13px;font-weight:600}";
873
+ //#region \0dsh-css:/home/runner/work/dsh-cost-meter/dsh-cost-meter/src/client/SessionCostPill.module.css.mjs
874
+ const css$1 = ".jn2k9a_wrap{display:inline-flex;position:relative}.jn2k9a_pill{border:1px solid var(--dsw-alias-border-l2);height:32px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer;user-select:none;background:0 0;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:6px 12px;font-size:13px;font-weight:400;line-height:20px;transition:background-color .15s,color .15s,border-color .15s;display:inline-flex}.jn2k9a_pill:hover{background:var(--dsw-alias-interactive-bg-hover)}.jn2k9a_pill:active{transform:translateY(.5px)}.jn2k9a_pill[data-running=true]{color:var(--dsw-alias-brand-primary)}.jn2k9a_pill[data-running=true]:hover{background:var(--dsw-alias-interactive-bg-hover)}.jn2k9a_pill[data-band=peak]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-secondary)}.jn2k9a_pill[data-band=offPeak]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-secondary)}.jn2k9a_bandBadge{opacity:.85;white-space:nowrap;font-size:11px;line-height:16px}.jn2k9a_bandBadge:before{content:\"·\";color:currentColor;opacity:.6;margin:0 4px}.jn2k9a_estimateNote{opacity:.75;font-size:11px}.jn2k9a_detail{z-index:100;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);min-width:250px;max-width:340px;box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;border-radius:12px;flex-direction:column;gap:5px;padding:12px 14px;font-size:12px;line-height:18px;display:flex;position:absolute;top:calc(100% + 8px);right:0}.jn2k9a_detailRow{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.jn2k9a_detailRowSecondary{color:var(--dsw-alias-label-secondary)}.jn2k9a_detailRowMuted{color:var(--dsw-alias-label-secondary);opacity:.75}.jn2k9a_detailTotal{font-size:13px;font-weight:600}";
871
875
  const tagId$1 = "@gamegeek-saikel/dsh-cost-meter/SessionCostPill.module.css";
872
876
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
873
877
  const tag = document.createElement("style");
@@ -877,15 +881,15 @@ window.__ModuleLoader__.load({
877
881
  document.head.appendChild(tag);
878
882
  }
879
883
  var SessionCostPill_module_css_default = {
880
- "bandBadge": "_1P-OIW_bandBadge",
881
- "detail": "_1P-OIW_detail",
882
- "detailRow": "_1P-OIW_detailRow",
883
- "detailRowMuted": "_1P-OIW_detailRowMuted",
884
- "detailRowSecondary": "_1P-OIW_detailRowSecondary",
885
- "detailTotal": "_1P-OIW_detailTotal",
886
- "estimateNote": "_1P-OIW_estimateNote",
887
- "pill": "_1P-OIW_pill",
888
- "wrap": "_1P-OIW_wrap"
884
+ "bandBadge": "jn2k9a_bandBadge",
885
+ "detail": "jn2k9a_detail",
886
+ "detailRow": "jn2k9a_detailRow",
887
+ "detailRowMuted": "jn2k9a_detailRowMuted",
888
+ "detailRowSecondary": "jn2k9a_detailRowSecondary",
889
+ "detailTotal": "jn2k9a_detailTotal",
890
+ "estimateNote": "jn2k9a_estimateNote",
891
+ "pill": "jn2k9a_pill",
892
+ "wrap": "jn2k9a_wrap"
889
893
  };
890
894
  //#endregion
891
895
  //#region src/client/SessionCostPill.tsx
@@ -934,7 +938,9 @@ window.__ModuleLoader__.load({
934
938
  }
935
939
  const SessionCostPill = (0, react.memo)(function SessionCostPill({ useSession, useProjection, sessionId, t }) {
936
940
  const currency = displayCurrency(t);
937
- const cost = useProjection(currency === "USD" ? "sessionCostUsd" : "sessionCost");
941
+ const costCny = useProjection("sessionCost");
942
+ const costUsd = useProjection("sessionCostUsd");
943
+ const cost = currency === "USD" ? costUsd : costCny;
938
944
  const pressure = useProjection("contextPressure");
939
945
  const running = useSession(runningOf);
940
946
  const partial = useSession((snapshot) => snapshot.partial);
@@ -1085,8 +1091,8 @@ window.__ModuleLoader__.load({
1085
1091
  });
1086
1092
  });
1087
1093
  //#endregion
1088
- //#region \0dsh-css:D:\Projects\dsh-plugins\dsh-cost-meter\src\client\CostPluginCard.module.css.mjs
1089
- const css = ".XNoGEa_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.XNoGEa_card:hover{border-color:var(--dsw-alias-label-dimmed)}.XNoGEa_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.XNoGEa_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.XNoGEa_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.XNoGEa_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.XNoGEa_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.XNoGEa_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.XNoGEa_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.XNoGEa_chevronOpen{transform:rotate(180deg)}.XNoGEa_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;border-radius:999px;flex:none;max-width:220px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;overflow:hidden}.XNoGEa_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.XNoGEa_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.XNoGEa_field+.XNoGEa_field{border-top:1px solid var(--dsw-alias-border-l2)}.XNoGEa_head{align-items:center;gap:8px;display:flex}.XNoGEa_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.XNoGEa_input,.XNoGEa_select,.XNoGEa_textarea{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;font-size:13px;line-height:1.5}.XNoGEa_input{width:120px;padding:5px 8px}.XNoGEa_select{padding:5px 8px}.XNoGEa_textarea{width:100%;min-height:72px;font-family:var(--dsw-font-mono,monospace);resize:vertical;padding:6px 8px;font-size:12px}.XNoGEa_inline{align-items:center;gap:8px;display:flex}.XNoGEa_checkbox{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary)}.XNoGEa_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.XNoGEa_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.XNoGEa_spacer{flex:1}.XNoGEa_status{color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:1.5}";
1094
+ //#region \0dsh-css:/home/runner/work/dsh-cost-meter/dsh-cost-meter/src/client/CostPluginCard.module.css.mjs
1095
+ const css = ".tDlB8a_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.tDlB8a_card:hover{border-color:var(--dsw-alias-label-dimmed)}.tDlB8a_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.tDlB8a_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.tDlB8a_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.tDlB8a_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.tDlB8a_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.tDlB8a_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.tDlB8a_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.tDlB8a_chevronOpen{transform:rotate(180deg)}.tDlB8a_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;border-radius:999px;flex:none;max-width:220px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;overflow:hidden}.tDlB8a_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.tDlB8a_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.tDlB8a_field+.tDlB8a_field{border-top:1px solid var(--dsw-alias-border-l2)}.tDlB8a_head{align-items:center;gap:8px;display:flex}.tDlB8a_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.tDlB8a_input,.tDlB8a_select,.tDlB8a_textarea{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;font-size:13px;line-height:1.5}.tDlB8a_input{width:120px;padding:5px 8px}.tDlB8a_select{padding:5px 8px}.tDlB8a_textarea{width:100%;min-height:72px;font-family:var(--dsw-font-mono,monospace);resize:vertical;padding:6px 8px;font-size:12px}.tDlB8a_inline{align-items:center;gap:8px;display:flex}.tDlB8a_checkbox{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary)}.tDlB8a_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.tDlB8a_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.tDlB8a_spacer{flex:1}.tDlB8a_status{color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:1.5}";
1090
1096
  const tagId = "@gamegeek-saikel/dsh-cost-meter/CostPluginCard.module.css";
1091
1097
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1092
1098
  const tag = document.createElement("style");
@@ -1096,28 +1102,28 @@ window.__ModuleLoader__.load({
1096
1102
  document.head.appendChild(tag);
1097
1103
  }
1098
1104
  var CostPluginCard_module_css_default = {
1099
- "body": "XNoGEa_body",
1100
- "card": "XNoGEa_card",
1101
- "cardOpen": "XNoGEa_cardOpen",
1102
- "checkbox": "XNoGEa_checkbox",
1103
- "chevron": "XNoGEa_chevron",
1104
- "chevronOpen": "XNoGEa_chevronOpen",
1105
- "description": "XNoGEa_description",
1106
- "field": "XNoGEa_field",
1107
- "footer": "XNoGEa_footer",
1108
- "head": "XNoGEa_head",
1109
- "headText": "XNoGEa_headText",
1110
- "header": "XNoGEa_header",
1111
- "hint": "XNoGEa_hint",
1112
- "inline": "XNoGEa_inline",
1113
- "input": "XNoGEa_input",
1114
- "label": "XNoGEa_label",
1115
- "name": "XNoGEa_name",
1116
- "pending": "XNoGEa_pending",
1117
- "select": "XNoGEa_select",
1118
- "spacer": "XNoGEa_spacer",
1119
- "status": "XNoGEa_status",
1120
- "textarea": "XNoGEa_textarea"
1105
+ "body": "tDlB8a_body",
1106
+ "card": "tDlB8a_card",
1107
+ "cardOpen": "tDlB8a_cardOpen",
1108
+ "checkbox": "tDlB8a_checkbox",
1109
+ "chevron": "tDlB8a_chevron",
1110
+ "chevronOpen": "tDlB8a_chevronOpen",
1111
+ "description": "tDlB8a_description",
1112
+ "field": "tDlB8a_field",
1113
+ "footer": "tDlB8a_footer",
1114
+ "head": "tDlB8a_head",
1115
+ "headText": "tDlB8a_headText",
1116
+ "header": "tDlB8a_header",
1117
+ "hint": "tDlB8a_hint",
1118
+ "inline": "tDlB8a_inline",
1119
+ "input": "tDlB8a_input",
1120
+ "label": "tDlB8a_label",
1121
+ "name": "tDlB8a_name",
1122
+ "pending": "tDlB8a_pending",
1123
+ "select": "tDlB8a_select",
1124
+ "spacer": "tDlB8a_spacer",
1125
+ "status": "tDlB8a_status",
1126
+ "textarea": "tDlB8a_textarea"
1121
1127
  };
1122
1128
  //#endregion
1123
1129
  //#region src/client/CostPluginCard.tsx
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["memo","useState","useMemo","css","Tooltip","memo","useState","css","memo","useState","css","memo","useState","useRef","useMemo","css","memo","useSyncExternalStore","useState","css","IconChevronDownOutline14","Button"],"sources":["../src/client/locales.ts","../src/pricing.ts","../src/client/cost-math.ts","../src/client/format.ts","../src/client/SessionCostLine.tsx","../src/client/CostView.tsx","../src/client/AssistantCostChip.tsx","../src/client/SessionCostPill.tsx","../src/client/CostPluginCard.tsx","../src/client/index.ts"],"sourcesContent":["/** `cost-meter` namespace dictionaries. */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'cost-meter'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n // View tab\n 'view.cost': '花费',\n 'view.empty': '暂无花费数据',\n 'view.total': '总花费',\n 'view.uncached': '输入(缓存未命中)',\n 'view.cacheRead': '输入(缓存命中)',\n 'view.output': '输出',\n 'view.cacheWriteTokens': '缓存写入',\n 'view.unpriced': '{count} 步无价格',\n 'view.steps': '每轮回复花费',\n\n // Step list\n 'step.turn': '第 {turn} 轮',\n 'step.model': '模型 {model}',\n 'step.time': '{time}',\n 'step.tokens': '未命中 {uncached} · 缓存读 {read} · 缓存写 {write} · 输出 {output}',\n 'step.cost': '花费 {amount}',\n 'step.snapshot': '快照 · {time}',\n 'step.unpriced': '—({reason})',\n 'reason.NO_MODEL': '无模型信息',\n 'reason.NO_PRICE': '无价格',\n\n // Shared amounts\n 'cost.label': '本会话 {amount}',\n 'line.uncached': '非缓存输入 {amount}',\n 'line.cacheRead': '缓存输入 {amount}',\n 'line.output': '输出 {amount}',\n 'line.subagent': '子代理 {amount}',\n 'pill.label': '花费 {amount}',\n 'pill.estimate': '预计 {amount}',\n 'pill.estimateNote': '估算',\n 'pill.detail.total': '总花费 {amount}',\n 'pill.detail.snapshot': '价格快照({time})',\n 'pill.detail.model': '模型 {model}',\n 'pill.detail.source': '价格来源 {source}',\n 'source.official': '官方页面',\n 'source.fallback': '内置回退',\n 'source.manual': '手动',\n 'source.openrouter': 'OpenRouter',\n 'source.none': '无',\n 'band.peak': '高峰价',\n 'band.offPeak': '闲时价',\n 'band.single': '单价',\n 'price.peakExtra': '高峰 · 比闲时多 {multiplier} 倍',\n 'price.offPeakSaving': '闲时 · 比高峰省 {percent}',\n 'price.peakRatio': '高峰 {multiplier}',\n 'price.offPeakRatio': '闲时 {multiplier}',\n\n // Chip\n 'chip.title': '本回复花费 {amount}',\n 'chip.unpriced': '—',\n\n // Balance (inherited posture)\n 'balance.label': '余额 {amount}',\n 'balance.failed': '余额不可用',\n 'balance.detail': '余额 {amount}(赠送 {granted} · 充值 {toppedUp})',\n 'balance.suspended': '账户已暂停付费请求',\n 'refreshedAt': '更新于 {time}',\n\n // Plugin configuration card\n 'settings.title': '花费计价',\n 'settings.current': '当前快照 · {source} · {time}',\n 'settings.noSnapshot': '尚无价格快照',\n 'settings.refresh': '立即刷新',\n 'settings.refreshing': '刷新中…',\n 'settings.overrides': '按模型价目表(手动覆盖,元/百万 tokens)',\n 'settings.aliases': 'OpenRouter 别名映射',\n 'settings.discount': '缓存折扣系数(OpenRouter)',\n 'settings.fx': '汇率(USD→CNY)',\n 'settings.fx.auto': '自动',\n 'settings.fx.manual': '手动',\n 'settings.balance': '显示余额',\n 'settings.openrouter': '允许 OpenRouter 回退',\n 'settings.save': '保存',\n 'settings.discard': '丢弃',\n 'settings.saved': '已保存',\n 'settings.error': '操作失败:{error}',\n 'settings.jsonHint': 'JSON 对象,键为模型,值含 single/offPeak/peak 价格桶',\n} as const\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en: Record<ConversationCostKey, string> = {\n 'view.cost': 'Cost',\n 'view.empty': 'No cost data yet',\n 'view.total': 'Total',\n 'view.uncached': 'Input (cache miss)',\n 'view.cacheRead': 'Input (cache hit)',\n 'view.output': 'Output',\n 'view.cacheWriteTokens': 'Cache write',\n 'view.unpriced': '{count} steps unpriced',\n 'view.steps': 'Per-reply cost',\n\n 'step.turn': 'Turn {turn}',\n 'step.model': 'Model {model}',\n 'step.time': '{time}',\n 'step.tokens': 'miss {uncached} · read {read} · write {write} · out {output}',\n 'step.cost': 'Cost {amount}',\n 'step.snapshot': 'snapshot · {time}',\n 'step.unpriced': '—({reason})',\n 'reason.NO_MODEL': 'no model info',\n 'reason.NO_PRICE': 'no price',\n\n 'cost.label': 'Session {amount}',\n 'line.uncached': 'Uncached input {amount}',\n 'line.cacheRead': 'Cached input {amount}',\n 'line.output': 'Output {amount}',\n 'line.subagent': 'Subagents {amount}',\n 'pill.label': 'Cost {amount}',\n 'pill.estimate': '≈ {amount}',\n 'pill.estimateNote': 'estimate',\n 'pill.detail.total': 'Total {amount}',\n 'pill.detail.snapshot': 'Price snapshot ({time})',\n 'pill.detail.model': 'Model {model}',\n 'pill.detail.source': 'Price source {source}',\n 'source.official': 'official page',\n 'source.fallback': 'built-in fallback',\n 'source.manual': 'manual',\n 'source.openrouter': 'OpenRouter',\n 'source.none': 'none',\n 'band.peak': 'peak',\n 'band.offPeak': 'off-peak',\n 'band.single': 'list',\n 'price.peakExtra': 'peak · {multiplier}× more than off-peak',\n 'price.offPeakSaving': 'off-peak · save {percent} vs peak',\n 'price.peakRatio': 'peak {multiplier}',\n 'price.offPeakRatio': 'off-peak {multiplier}',\n\n 'chip.title': 'This reply cost {amount}',\n 'chip.unpriced': '—',\n\n 'balance.label': 'Balance {amount}',\n 'balance.failed': 'Balance unavailable',\n 'balance.detail': 'Balance {amount} (granted {granted} · topped up {toppedUp})',\n 'balance.suspended': 'Account suspended for paid requests',\n 'refreshedAt': 'Updated at {time}',\n\n 'settings.title': 'Cost meter',\n 'settings.current': 'Current snapshot · {source} · {time}',\n 'settings.noSnapshot': 'No price snapshot yet',\n 'settings.refresh': 'Refresh now',\n 'settings.refreshing': 'Refreshing…',\n 'settings.overrides': 'Per-model prices (manual overrides, CNY per 1M tokens)',\n 'settings.aliases': 'OpenRouter alias map',\n 'settings.discount': 'Cache-read discount (OpenRouter)',\n 'settings.fx': 'Exchange rate (USD→CNY)',\n 'settings.fx.auto': 'Auto',\n 'settings.fx.manual': 'Manual',\n 'settings.balance': 'Show balance',\n 'settings.openrouter': 'Allow OpenRouter fallback',\n 'settings.save': 'Save',\n 'settings.discard': 'Discard',\n 'settings.saved': 'Saved',\n 'settings.error': 'Operation failed: {error}',\n 'settings.jsonHint': 'JSON object keyed by model; values carry single/offPeak/peak price buckets',\n}\n\n/** Key domain of the `cost-meter` namespace (zh is the source of truth). */\nexport type ConversationCostKey = keyof typeof zh\n","/**\n * Official DeepSeek pricing: fetches the pricing page\n * (api-docs.deepseek.com/zh-cn/quick_start/pricing/) and parses both the\n * current list prices and the upcoming peak/off-peak table, so price changes\n * and the 2026-08-17 peak-pricing rollout never require a plugin update.\n *\n * The parser is deliberately tolerant: it matches price cells next to the\n * bucket labels and model names anywhere in the HTML, so reordering or\n * rewording still yields values when the numbers are present; failures\n * degrade to the built-in presets rather than throwing.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/pricing\n */\n\nimport type { CurrentPricing, PeakPricing, PriceBucket, PricingSnapshot } from './types.ts'\n\n/** Official pricing page URL (zh-cn). */\nexport const PRICING_URL = 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/'\n\n/** Official pricing page URL (en). */\nexport const PRICING_URL_EN = 'https://api-docs.deepseek.com/quick_start/pricing/'\n\n/** Official peak-pricing rollout: 2026-08-17 00:00 Beijing time (UTC+8). */\nexport const PEAK_PRICING_START_MS = Date.UTC(2026, 7, 16, 16, 0, 0)\n\n/**\n * Built-in fallback prices: the official list for deepseek-v4-flash and\n * deepseek-v4-pro (CNY per 1M tokens). DeepSeek bills cache writes at the\n * uncached input price (its wire usage reports only cache-hit vs cache-miss\n * buckets), so no separate cache-write rate exists.\n */\nexport const FALLBACK_CURRENT: CurrentPricing = {\n flash: { cacheReadPerMillion: 0.02, inputPerMillion: 1, outputPerMillion: 2 },\n pro: { cacheReadPerMillion: 0.025, inputPerMillion: 3, outputPerMillion: 6 },\n}\n\n/**\n * Built-in fallback for the upcoming peak table: the 2026-08-17 schedule\n * (peak hours double the off-peak half).\n */\nexport const FALLBACK_PEAK: PeakPricing = {\n flash: {\n offPeak: { cacheReadPerMillion: 0.05, inputPerMillion: 1.5, outputPerMillion: 4.5 },\n peak: { cacheReadPerMillion: 0.1, inputPerMillion: 3, outputPerMillion: 9 },\n },\n pro: {\n offPeak: { cacheReadPerMillion: 0.15, inputPerMillion: 4.5, outputPerMillion: 13.5 },\n peak: { cacheReadPerMillion: 0.3, inputPerMillion: 9, outputPerMillion: 27 },\n },\n}\n\n/** Built-in fallback USD prices from the English pricing page. */\nexport const FALLBACK_CURRENT_USD: CurrentPricing = {\n flash: { cacheReadPerMillion: 0.007, inputPerMillion: 0.22, outputPerMillion: 0.66 },\n pro: { cacheReadPerMillion: 0.022, inputPerMillion: 0.66, outputPerMillion: 1.98 },\n}\n\n/** Built-in fallback USD peak table from the English pricing page. */\nexport const FALLBACK_PEAK_USD: PeakPricing = {\n flash: {\n offPeak: { ...FALLBACK_CURRENT_USD.flash },\n peak: { cacheReadPerMillion: 0.014, inputPerMillion: 0.44, outputPerMillion: 1.32 },\n },\n pro: {\n offPeak: { ...FALLBACK_CURRENT_USD.pro },\n peak: { cacheReadPerMillion: 0.044, inputPerMillion: 1.32, outputPerMillion: 3.96 },\n },\n}\n\n/** Number regex: `0.02`, `1`, `2`, `3.0` etc. Deliberately non-global: a\n * shared global regex leaks `lastIndex` across `exec` calls and would skip\n * the second price cell (the pro price). */\nconst PRICE_RE = /(\\d+(?:\\.\\d+)?)\\s*元/\n\n/** English price regex: matches `$0.02`, `$0.002`, `0.02` etc. */\nconst PRICE_RE_EN = /\\$?\\s*(\\d+(?:\\.\\d+)?)/\n\n/** Strip HTML tags to plain text (keeps cell order). */\nexport function stripHtml(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/&nbsp;/gi, ' ')\n .replace(/&amp;/gi, '&')\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** Parse one price cell text like `0.02元` into a number; undefined when absent. */\nfunction parsePriceCell(text: string): number | undefined {\n const match = PRICE_RE.exec(text)\n if (match === null) return undefined\n const value = Number(match[1])\n return Number.isFinite(value) ? value : undefined\n}\n\n/** The second price cell of a row (the pro price), stripped of the first. */\n\n/** Parse one English price cell text like `$0.02` into a number. */\nfunction parsePriceCellUsd(text: string): number | undefined {\n const match = PRICE_RE_EN.exec(text)\n if (match === null) return undefined\n const value = Number(match[1])\n return Number.isFinite(value) ? value : undefined\n}\n\n/** The second price cell of an English row, stripped of the first. */\nfunction secondPriceCellUsd(text: string): string {\n return text.replace(/^\\s*\\$?\\s*(\\d+(?:\\.\\d+)?)/, '')\n}\nfunction secondPriceCell(text: string): string {\n return text.replace(/^\\s*(\\d+(?:\\.\\d+)?元)/, '')\n}\n\n/**\n * Parse the current single-price table: three rows labeled with the bucket\n * names, each carrying the flash and pro price cells.\n * @param html - the raw pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\nexport function parseCurrentTable(html: string): CurrentPricing | undefined {\n const hit = /百万tokens输入(缓存命中)([\\s\\S]{0,400}?)百万tokens输入(缓存未命中)([\\s\\S]{0,400}?)百万tokens输出([\\s\\S]{0,400}?)(?:并发限制|<\\/table)/i.exec(stripHtml(html))\n if (hit === null) return undefined\n const cacheReadCell = hit[1] ?? ''\n const inputCell = hit[2] ?? ''\n const outputCell = hit[3] ?? ''\n const cacheReadFlash = parsePriceCell(cacheReadCell)\n const cacheReadPro = parsePriceCell(secondPriceCell(cacheReadCell))\n const inputFlash = parsePriceCell(inputCell)\n const inputPro = parsePriceCell(secondPriceCell(inputCell))\n const outputFlash = parsePriceCell(outputCell)\n const outputPro = parsePriceCell(secondPriceCell(outputCell))\n if (cacheReadFlash === undefined || inputFlash === undefined || outputFlash === undefined) {\n return undefined\n }\n return {\n flash: {\n cacheReadPerMillion: cacheReadFlash,\n inputPerMillion: inputFlash,\n outputPerMillion: outputFlash,\n },\n pro: {\n cacheReadPerMillion: cacheReadPro ?? cacheReadFlash,\n inputPerMillion: inputPro ?? inputFlash,\n outputPerMillion: outputPro ?? outputFlash,\n },\n }\n}\n\n/**\n * Parse the upcoming peak-pricing table: model rows with off-peak and peak\n * cells, e.g. `deepseek-v4-flash 空闲时段 0.05 1.5 4.5 高峰时段 0.10 3.0 9.0`.\n * @param html - the raw pricing page.\n * @returns the parsed table, or undefined when either model row is absent.\n */\nexport function parsePeakTable(html: string): PeakPricing | undefined {\n const text = stripHtml(html)\n const flash = /deepseek-v4-flash\\s+空闲时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+高峰时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元/i.exec(text)\n const pro = /deepseek-v4-pro\\s+空闲时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+高峰时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元/i.exec(text)\n if (flash === null || pro === null) return undefined\n return {\n flash: {\n offPeak: {\n cacheReadPerMillion: Number(flash[1]),\n inputPerMillion: Number(flash[2]),\n outputPerMillion: Number(flash[3]),\n },\n peak: {\n cacheReadPerMillion: Number(flash[4]),\n inputPerMillion: Number(flash[5]),\n outputPerMillion: Number(flash[6]),\n },\n },\n pro: {\n offPeak: {\n cacheReadPerMillion: Number(pro[1]),\n inputPerMillion: Number(pro[2]),\n outputPerMillion: Number(pro[3]),\n },\n peak: {\n cacheReadPerMillion: Number(pro[4]),\n inputPerMillion: Number(pro[5]),\n outputPerMillion: Number(pro[6]),\n },\n },\n }\n}\n\n/**\n * Parse the English current single-price table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\n/**\n * Parse the English combined pricing table. The English page lists all three\n * buckets as rows with OFF-PEAK and PEAK cells, so we extract both the current\n * off-peak list and the peak table from the same markup.\n */\nfunction parseEnglishPricing(html: string): { current: CurrentPricing; peak: PeakPricing } | undefined {\n const text = stripHtml(html)\n const hit = /1M INPUT TOKENS \\(CACHE HIT\\)\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+1M INPUT TOKENS \\(CACHE MISS\\)\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+1M OUTPUT TOKENS\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)/i.exec(text)\n if (hit === null) return undefined\n const n = (index: number): number => Number(hit[index])\n const current: CurrentPricing = {\n flash: {\n cacheReadPerMillion: n(1),\n inputPerMillion: n(5),\n outputPerMillion: n(9),\n },\n pro: {\n cacheReadPerMillion: n(2),\n inputPerMillion: n(6),\n outputPerMillion: n(10),\n },\n }\n const peak: PeakPricing = {\n flash: {\n offPeak: {\n cacheReadPerMillion: n(1),\n inputPerMillion: n(5),\n outputPerMillion: n(9),\n },\n peak: {\n cacheReadPerMillion: n(3),\n inputPerMillion: n(7),\n outputPerMillion: n(11),\n },\n },\n pro: {\n offPeak: {\n cacheReadPerMillion: n(2),\n inputPerMillion: n(6),\n outputPerMillion: n(10),\n },\n peak: {\n cacheReadPerMillion: n(4),\n inputPerMillion: n(8),\n outputPerMillion: n(12),\n },\n },\n }\n return { current, peak }\n}\n\n/**\n * Parse the English current single-price table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\nexport function parseCurrentTableEn(html: string): CurrentPricing | undefined {\n return parseEnglishPricing(html)?.current\n}\n\n/**\n * Parse the English upcoming peak-pricing table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when either model row is absent.\n */\nexport function parsePeakTableEn(html: string): PeakPricing | undefined {\n return parseEnglishPricing(html)?.peak\n}\n\n/**\n * Fetch and parse the official pricing page.\n * @param fetchImpl - fetch-compatible function (injected for testability).\n * @param timeoutMs - abort timeout.\n * @returns the parsed snapshot; `error` is set when fetch/parse failed.\n */\nexport async function fetchPricing(\n fetchImpl: typeof fetch = globalThis.fetch,\n timeoutMs = 15_000,\n locale: 'zh' | 'en' = 'zh',\n): Promise<PricingSnapshot> {\n const fetchedAt = Date.now()\n const currency = locale === 'en' ? 'USD' : 'CNY'\n const url = locale === 'en' ? PRICING_URL_EN : PRICING_URL\n const parseCurrent = locale === 'en' ? parseCurrentTableEn : parseCurrentTable\n const parsePeak = locale === 'en' ? parsePeakTableEn : parsePeakTable\n const fallbackCurrent = locale === 'en' ? FALLBACK_CURRENT_USD : FALLBACK_CURRENT\n const fallbackPeak = locale === 'en' ? FALLBACK_PEAK_USD : FALLBACK_PEAK\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n let response: Response\n try {\n response = await fetchImpl(url, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n if (!response.ok) {\n return { fetchedAt, currency, current: fallbackCurrent, peakActive: false, effective: fallbackCurrent, error: `pricing page HTTP ${response.status}` }\n }\n const html = await response.text()\n const current = parseCurrent(html)\n if (current === undefined) {\n return { fetchedAt, currency, current: fallbackCurrent, peakActive: false, effective: fallbackCurrent, error: 'pricing table not found' }\n }\n const peak = parsePeak(html)\n const now = Date.now()\n const peakActive = now >= PEAK_PRICING_START_MS\n return {\n fetchedAt,\n currency,\n current,\n ...(peak === undefined ? {} : { peak }),\n peakActive,\n effective: effectivePricing(current, peak, peakActive, new Date(now)),\n ...(peakActive ? { band: isPeakHour(new Date(now)) ? 'peak' : 'offPeak' } : {}),\n }\n } catch (error) {\n return {\n fetchedAt,\n currency,\n current: fallbackCurrent,\n peakActive: false,\n effective: fallbackCurrent,\n error: error instanceof Error ? error.message : String(error)\n }\n }\n}\n\n/**\n * Whether the current moment is a peak-pricing hour in Beijing time:\n * 09:00-12:00 and 14:00-18:00 (peak); everything else is off-peak.\n * @param now - the instant to classify.\n * @returns true during peak hours.\n */\nexport function isPeakHour(now: Date = new Date()): boolean {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: 'Asia/Shanghai',\n hour: 'numeric',\n hour12: false,\n }).formatToParts(now)\n const hour = Number(parts.find(part => part.type === 'hour')?.value)\n if (Number.isNaN(hour)) return false\n return (hour >= 9 && hour < 12) || (hour >= 14 && hour < 18)\n}\n\n/**\n * The price bucket in effect for one model at the given moment: the\n * peak/off-peak band once the rollout is live (with the parsed table, or the\n * built-in fallback), otherwise the current official list.\n * @param current - the current list prices.\n * @param peak - the upcoming peak table, when parsed.\n * @param peakActive - whether the rollout has started.\n * @param now - the instant to price for.\n * @param model - `flash` or `pro`.\n * @returns the bucket to apply.\n */\nexport function effectiveBucket(\n current: CurrentPricing,\n peak: PeakPricing | undefined,\n peakActive: boolean,\n now: Date,\n model: keyof CurrentPricing,\n): PriceBucket {\n if (peakActive) {\n const table = peak?.[model] ?? FALLBACK_PEAK[model]\n return isPeakHour(now) ? table.peak : table.offPeak\n }\n return current[model]\n}\n\n/** Per-model effective prices at the given moment. */\nexport function effectivePricing(\n current: CurrentPricing,\n peak: PeakPricing | undefined,\n peakActive: boolean,\n now: Date,\n): CurrentPricing {\n return {\n flash: effectiveBucket(current, peak, peakActive, now, 'flash'),\n pro: effectiveBucket(current, peak, peakActive, now, 'pro'),\n }\n}","/**\n * Client-side price lookup and streaming-estimate helpers.\n *\n * The authoritative anchored ledger arrives through the `sessionCost`\n * projection; this module only mirrors the host pricebook math for display\n * and for the live estimate (which by nature uses CURRENT prices and is\n * labeled 估算/estimate). The lookup logic intentionally mirrors\n * `src/pricebook.ts` — keep the two in sync.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/client/cost-math\n */\n\nimport { isPeakHour, PEAK_PRICING_START_MS } from '../pricing.ts'\nimport type {\n ModelPrice,\n PriceBand,\n PriceBucket,\n PricebookSnapshot,\n SessionCostStep,\n SessionCostTotals,\n SubagentCost,\n} from '../types.ts'\n\n/** Sum totals (this module's mirror of the host `sumTotals`). */\nexport function sumTotals(totals: readonly SessionCostTotals[]): SessionCostTotals {\n const sum: SessionCostTotals = {\n uncachedCost: 0,\n cacheReadCost: 0,\n outputCost: 0,\n cost: 0,\n pricedSteps: 0,\n unpricedSteps: 0,\n steps: 0,\n }\n for (const entry of totals) {\n sum.uncachedCost += entry.uncachedCost\n sum.cacheReadCost += entry.cacheReadCost\n sum.outputCost += entry.outputCost\n sum.cost += entry.cost\n sum.pricedSteps += entry.pricedSteps\n sum.unpricedSteps += entry.unpricedSteps\n sum.steps += entry.steps\n }\n return sum\n}\n\n/** Total spend of every subagent (CNY); 0 without subagents. */\nexport function subagentSpend(subagents: readonly SubagentCost[] | undefined): number {\n if (subagents === undefined || subagents.length === 0) return 0\n return sumTotals(subagents.map(entry => entry.totals)).cost\n}\n\n/**\n * The conversation-wide totals: the main session's ledger plus every\n * subagent's ledger (host-aggregated). Undefined while the main ledger is\n * absent; identical to the main totals without subagents.\n * @param main - the main session's projection totals.\n * @param subagents - the host-aggregated subagent totals.\n * @returns the combined totals, or undefined.\n */\nexport function combineTotals(\n main: SessionCostTotals | undefined,\n subagents: readonly SubagentCost[] | undefined,\n): SessionCostTotals | undefined {\n if (main === undefined) return undefined\n if (subagents === undefined || subagents.length === 0) return main\n return sumTotals([main, ...subagents.map(entry => entry.totals)])\n}\n\n/** The price band in effect at one instant (mirror of the host helper). */\nexport function bandForTime(time: number): PriceBand {\n if (time < PEAK_PRICING_START_MS) return 'single'\n return isPeakHour(new Date(time)) ? 'peak' : 'offPeak'\n}\n\n/** The bucket of one model's entry for one band (mirror of the host helper). */\nexport function bucketOf(modelPrice: ModelPrice, band: PriceBand): PriceBucket | undefined {\n if (band === 'single') return modelPrice.single ?? modelPrice.offPeak ?? modelPrice.peak\n if (band === 'peak') return modelPrice.peak ?? modelPrice.offPeak ?? modelPrice.single\n return modelPrice.offPeak ?? modelPrice.peak ?? modelPrice.single\n}\n\n/** Map a model id to its official pricing key (mirror of the host helper). */\nexport function pricingKeyOfModel(model: string): 'pro' | 'flash' {\n const lower = model.toLowerCase()\n return lower.includes('pro') ? 'pro' : 'flash'\n}\n\n/** Candidate canonical price keys for one model (mirror of the host helper). */\nexport function modelKeys(provider: string | undefined, model: string): string[] {\n const keys: string[] = []\n if (provider !== undefined && provider.length > 0) keys.push(`${provider}/${model}`)\n keys.push(model)\n const lower = model.toLowerCase()\n const deepseekish = lower.includes('deepseek')\n || (provider !== undefined && provider.toLowerCase().includes('deepseek'))\n if (deepseekish) keys.push(pricingKeyOfModel(model))\n return keys\n}\n\n/** Pick the snapshot effective at `time` (newest ≤ time, else earliest). */\nexport function snapshotAt(snapshots: readonly PricebookSnapshot[], time: number): PricebookSnapshot | undefined {\n let best: PricebookSnapshot | undefined\n for (const snapshot of snapshots) {\n // `>=` keeps the NEWER snapshot (later in the array) on an effectiveAt tie.\n if (snapshot.effectiveAt <= time && (best === undefined || snapshot.effectiveAt >= best.effectiveAt)) best = snapshot\n }\n return best ?? (snapshots.length > 0 ? snapshots[0] : undefined)\n}\n\n/** Resolve one model's price entry in a snapshot through the key chain. */\nexport function modelPriceOf(\n snapshot: PricebookSnapshot | null | undefined,\n provider: string | undefined,\n model: string,\n): ModelPrice | undefined {\n if (snapshot === undefined || snapshot === null) return undefined\n for (const key of modelKeys(provider, model)) {\n const entry = snapshot.prices[key]\n if (entry !== undefined) return entry\n }\n return undefined\n}\n\n/** Resolve one model's bucket at one instant from a snapshot. */\nexport function bucketAt(\n snapshot: PricebookSnapshot | undefined,\n provider: string | undefined,\n model: string,\n time: number,\n): PriceBucket | undefined {\n const entry = modelPriceOf(snapshot, provider, model)\n if (entry === undefined) return undefined\n return bucketOf(entry, bandForTime(time))\n}\n\n/**\n * The peak-to-off-peak price multiplier for one model's entry, when the\n * entry carries both bands. The multiplier is the ratio of the summed peak\n * bucket rates to the summed off-peak bucket rates; official DeepSeek prices\n * scale all buckets by the same factor, so this is a stable “peak is N× the\n * off-peak price” display value.\n * @param snapshot - the pricebook snapshot to read.\n * @param provider - the provider route, when known.\n * @param model - the model id.\n * @returns the peak/off-peak multiplier, or null when either band is absent.\n */\nexport function peakOffPeakMultiplier(\n snapshot: PricebookSnapshot | null | undefined,\n provider: string | undefined,\n model: string,\n): number | null {\n const entry = modelPriceOf(snapshot, provider, model)\n if (entry === undefined) return null\n const peak = entry.peak\n const offPeak = entry.offPeak\n if (peak === undefined || offPeak === undefined) return null\n const offPeakTotal = offPeak.cacheReadPerMillion + offPeak.inputPerMillion + offPeak.outputPerMillion\n if (offPeakTotal === 0) return null\n const peakTotal = peak.cacheReadPerMillion + peak.inputPerMillion + peak.outputPerMillion\n return peakTotal / offPeakTotal\n}\n\n/** CJK characters (the /1.5 density class). */\nconst CJK_RE = /[\\u3400-\\u4dbf\\u4e00-\\u9fff]/g\n\n/**\n * Heuristic character→token estimate: CJK ÷ 1.5, everything else ÷ 4\n * (the streaming estimate's output side).\n * @param text - the accumulated text.\n * @returns the estimated token count.\n */\nexport function estimateTokens(text: string): number {\n const cjk = (text.match(CJK_RE) ?? []).length\n return Math.ceil(cjk / 1.5 + (text.length - cjk) / 4)\n}\n\n/**\n * Tokens of the streamed text/reasoning blocks of a partial assistant.\n * @param blocks - the partial's content blocks (text/reasoning carry `text`).\n * @returns the estimated token count.\n */\nexport function blocksOutputTokens(blocks: readonly { kind?: string; text?: string }[] | undefined): number {\n let tokens = 0\n for (const block of blocks ?? []) {\n if ((block.kind === 'text' || block.kind === 'reasoning') && typeof block.text === 'string' && block.text.length > 0) {\n tokens += estimateTokens(block.text)\n }\n }\n return tokens\n}\n\n/**\n * The cache-read ratio of the newest completed step (input side of the\n * estimate). Absent history conservatively prices all input as uncached\n * (upper bound).\n * @param steps - the projection's step ledger.\n * @returns the ratio in [0, 1].\n */\nexport function cacheReadRatioOf(steps: readonly SessionCostStep[]): number {\n for (let i = steps.length - 1; i >= 0; i -= 1) {\n const step = steps[i]\n const total = step.uncachedInputTokens + step.cacheReadTokens + step.cacheWriteTokens\n if (step.priced && total > 0) return step.cacheReadTokens / total\n }\n return 0\n}\n\n/**\n * The live estimate of the running reply: input at the projected context\n * size (weighted by the cache ratio) plus output at the streamed-token\n * heuristic, both at CURRENT prices. Always labeled 估算/estimate — it is\n * replaced by the projection's exact anchored value once the step settles.\n * @param bucket - the current price bucket of the session model.\n * @param inputTokens - the projected prompt size for the next request.\n * @param cachedRatio - the cache-read ratio from recent completed steps.\n * @param outputTokens - the streamed output tokens so far.\n * @returns the estimated CNY cost.\n */\nexport function estimateCost(\n bucket: PriceBucket,\n inputTokens: number,\n cachedRatio: number,\n outputTokens: number,\n): number {\n const inputCost = inputTokens / 1_000_000\n * (bucket.cacheReadPerMillion * cachedRatio + bucket.inputPerMillion * (1 - cachedRatio))\n const outputCost = outputTokens / 1_000_000 * bucket.outputPerMillion\n return inputCost + outputCost\n}\n","/**\n * Shared display formatting for amounts and timestamps.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/client/format\n */\n\n/**\n * Common currency symbols; unknown currencies render as a bare code.\n * @param currency - ISO 4217 code.\n * @returns the display symbol.\n */\nexport function currencySymbol(currency: string): string {\n switch (currency) {\n case 'CNY':\n return '¥'\n case 'USD':\n return '$'\n case 'EUR':\n return '€'\n default:\n return `${currency} `\n }\n}\n\n/** Detect the display currency from the active locale dictionary. */\nexport function displayCurrency(t: (key: string, params?: Record<string, string>) => string): 'CNY' | 'USD' {\n return t('view.cost') === 'Cost' ? 'USD' : 'CNY'\n}\n\n/**\n * Compact money: two decimals above 1, four down to 0.01, six below.\n * Costs are CNY 元 with 4-decimal display precision; zero renders as 0.00.\n * @param value - the amount.\n * @returns the fixed-width display string.\n */\nexport function formatMoney(value: number): string {\n if (value === 0) return '0.00'\n if (value >= 1) return value.toFixed(2)\n if (value >= 0.01) return value.toFixed(4)\n return value.toFixed(6)\n}\n\n/** Time formatting for snapshot stamps and step rows. */\nexport function formatTime(epochMs: number): string {\n return new Date(epochMs).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n}\n\n/**\n * Compact price multiplier, e.g. `2.0×` or `0.5×`.\n * @param multiplier - the ratio to display.\n * @returns the fixed one-decimal multiplier string.\n */\nexport function formatMultiplier(multiplier: number): string {\n return `${multiplier.toFixed(1)}×`\n}\n/**\n * Compact percentage, e.g. `50%`.\n * @param fraction - the ratio in [0, 1].\n * @returns the whole-percent string.\n */\nexport function formatPercent(fraction: number): string {\n return `${Math.round(fraction * 100)}%`\n}\n","/**\n * Composer-dock readout: the current session's anchored spend (the\n * `sessionCost` projection — price-snapshot-anchored, never recomputed at\n * current prices) plus the DeepSeek account balance (host\n * `/cost-meter` route), refreshed every minute. The hover detail\n * breaks the spend into the three billed categories (uncached input incl.\n * cache writes, cache reads, output) and names the anchored price snapshot.\n *\n * The component is deliberately defensive: every derived string is computed\n * inside a guarded block so a single bad field can never take down the whole\n * dock entry (the slot system retires crashed entries).\n */\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { BalanceSnapshot, ConversationCostResponse, SessionCostProjection } from '../types.ts'\nimport { combineTotals, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatTime } from './format.ts'\nimport css from './SessionCostLine.module.css'\n\n/** Host route serving the balance snapshot and the pricebook view. */\nexport const ENDPOINT = '/cost-meter'\n/** Balance refresh interval in milliseconds. */\nexport const REFRESH_MS = 60_000\n\n/** Session-cost namespace key domain for the locale seat. */\nexport type CostLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface SessionCostLineProps {\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n sessionId: string\n t: CostLocale\n}\n\n/** Compose the tooltip's balance detail from an ok snapshot. */\nfunction balanceTooltip(balance: Extract<BalanceSnapshot, { ok: true }>, t: CostLocale): string {\n if (balance.balance === null) return t('balance.failed')\n const symbol = currencySymbol(balance.balance.currency)\n const amount = `${symbol}${formatMoney(balance.balance.total)}`\n const granted = `${symbol}${formatMoney(balance.balance.granted)}`\n const toppedUp = `${symbol}${formatMoney(balance.balance.toppedUp)}`\n const detail = t('balance.detail', { amount, granted, toppedUp })\n return balance.isAvailable ? detail : `${detail} · ${t('balance.suspended')}`\n}\n\n/** The tooltip's cost detail rows from the anchored projection. Never throws. */\nexport function costDetail(cost: SessionCostProjection | undefined, model: string | undefined, t: CostLocale, currency: 'CNY' | 'USD' = 'CNY'): string[] {\n const rows: string[] = []\n try {\n if (cost === undefined) return rows\n const symbol = currencySymbol(currency)\n const totals = cost.totals\n if (totals !== undefined && totals.pricedSteps > 0) {\n rows.push(t('cost.label', { amount: `${symbol}${formatMoney(totals.cost)}` }))\n rows.push(t('view.uncached') + ` ${symbol}${formatMoney(totals.uncachedCost)}`)\n rows.push(t('view.cacheRead') + ` ${symbol}${formatMoney(totals.cacheReadCost)}`)\n rows.push(t('view.output') + ` ${symbol}${formatMoney(totals.outputCost)}`)\n }\n if (model !== undefined) rows.push(t('step.model', { model }))\n const steps = cost.steps\n if (steps !== undefined && steps.length > 0) {\n const last = steps[steps.length - 1]\n if (last !== undefined && last.snapshotVersion !== null) {\n rows.push(t('pill.detail.snapshot', { time: formatTime(last.snapshotEffectiveAt ?? last.time) }))\n }\n }\n if (totals !== undefined && totals.unpricedSteps > 0) {\n rows.push(t('view.unpriced', { count: String(totals.unpricedSteps) }))\n }\n } catch {\n /* a malformed field must never take down the dock entry */\n }\n return rows\n}\n\nexport const SessionCostLine = memo(function SessionCostLine({ useProjection, sessionId, t }: SessionCostLineProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'\n const cost = useProjection(projectionKey)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const [failed, setFailed] = useState(false)\n useEffect(() => {\n let alive = true\n let timer: number | undefined\n const endpoint = `${ENDPOINT}?session=${encodeURIComponent(sessionId)}&currency=${currency}`\n const load = (): void => {\n void fetch(endpoint, { cache: 'no-store' })\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n return res.json() as Promise<ConversationCostResponse>\n })\n .then((data) => {\n if (!alive) return\n setResponse(data)\n setFailed(false)\n })\n .catch(() => {\n if (alive) setFailed(true)\n })\n }\n load()\n timer = window.setInterval(load, REFRESH_MS)\n return () => {\n alive = false\n if (timer !== undefined) window.clearInterval(timer)\n }\n }, [sessionId, currency])\n\n const balance = response?.balance\n const balanceEnabled = response?.pricebook?.balanceEnabled !== false\n const subagents = response?.subagents\n const combined = combineTotals(cost?.totals, subagents)\n const subSpend = subagentSpend(subagents)\n // The dock readout must never vanish on a session switch: switching\n // sessions re-binds the projection hook, and until the new session's\n // baseline has carried the sessionCost key useProjection returns\n // undefined (capability absent - never a usable signal). Treat a\n // missing ledger as a zero ledger so the row renders CNY 0.00\n // instead of disappearing while the baseline is in flight.\n const mainSpend = cost?.totals?.cost ?? 0\n\n const parts: string[] = []\n try {\n const symbol = currencySymbol(currency)\n // Breakdown first (cached input, uncached input, output — combined with\n // subagents), then the main session total, then the subagent total, then\n // the balance.\n if (combined !== undefined && combined.pricedSteps > 0) {\n parts.push(t('line.cacheRead', { amount: `${symbol}${formatMoney(combined.cacheReadCost)}` }))\n parts.push(t('line.uncached', { amount: `${symbol}${formatMoney(combined.uncachedCost)}` }))\n parts.push(t('line.output', { amount: `${symbol}${formatMoney(combined.outputCost)}` }))\n }\n parts.push(t('cost.label', { amount: `${symbol}${formatMoney(mainSpend)}` }))\n if (subSpend > 0) {\n parts.push(t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` }))\n }\n if (balanceEnabled && balance?.ok === true && balance.balance !== null) {\n parts.push(t('balance.label', { amount: `${currencySymbol(balance.balance.currency)}${formatMoney(balance.balance.total)}` }))\n } else if (balanceEnabled && failed) {\n parts.push(t('balance.failed'))\n }\n } catch {\n /* keep the row alive */\n }\n if (parts.length === 0) return null\n\n const model = cost?.model?.model\n const tooltipParts = useMemo(() => {\n const rows: string[] = []\n try {\n rows.push(...costDetail(cost, model, t, currency))\n if (subSpend > 0) {\n rows.push(t('line.subagent', { amount: `${currencySymbol(currency)}${formatMoney(subSpend)}` }))\n }\n const pricebook = response?.pricebook\n if (pricebook !== undefined && pricebook.current !== null) {\n const source = pricebook.current.source\n if (source === 'official' || source === 'fallback' || source === 'manual' || source === 'openrouter') {\n rows.push(t('pill.detail.source', { source: t(`source.${source}` as 'source.manual') }))\n }\n }\n if (balanceEnabled && balance?.ok === true) rows.push(balanceTooltip(balance, t))\n if (response !== null) {\n rows.push(t('refreshedAt', { time: formatTime(balance?.ok === true ? balance.fetchedAt : Date.now()) }))\n }\n } catch {\n /* keep the row alive */\n }\n return rows\n }, [cost, model, t, balance, balanceEnabled, response, subSpend, currency])\n\n const line = parts.join(' · ')\n const tooltip = tooltipParts.filter(Boolean).join(' · ')\n if (tooltip.length === 0) {\n return <div className={css.root} data-testid=\"session-cost-line\">{line}</div>\n }\n return (\n <Tooltip label={tooltip} side=\"top\" delayMs={500}>\n <div className={css.root} data-testid=\"session-cost-line\">{line}</div>\n </Tooltip>\n )\n})\n","/**\n * Cost view tab: the whole conversation's anchored cost ledger — the total\n * (main session + subagent sessions, host-aggregated), the three billed\n * categories (uncached input incl. cache writes, cache reads, output), a\n * per-subagent breakdown, and the per-reply list (turn, time, model, token\n * split, step cost, anchored snapshot version and effective time). Reads the\n * `sessionCost` projection plus the host-aggregated subagent totals; nothing\n * is recomputed at current prices.\n */\nimport { memo, useEffect, useState } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationCostResponse, SessionCostStep } from '../types.ts'\nimport { bandForTime, combineTotals, peakOffPeakMultiplier, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier, formatTime } from './format.ts'\nimport css from './CostView.module.css'\n\nexport type CostViewLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface CostViewProps {\n useProjection: UseProjection\n sessionId: string\n t: CostViewLocale\n}\n\n/** Map an unpriced reason to its localized copy. */\nfunction reasonText(step: SessionCostStep, t: CostViewLocale): string {\n return t(`reason.${step.unpricedReason ?? 'NO_PRICE'}` as 'reason.NO_PRICE')\n}\n\nexport const CostView = memo(function CostView({ useProjection, sessionId, t }: CostViewProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'\n const cost = useProjection(projectionKey)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const symbol = currencySymbol(currency)\n\n useEffect(() => {\n let alive = true\n void fetch(`/cost-meter?session=${encodeURIComponent(sessionId)}&currency=${currency}`, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the tab keeps the last good aggregation */ })\n return () => {\n alive = false\n }\n }, [sessionId, currency])\n\n if (cost === undefined || cost.steps.length === 0) {\n return <div className={css.root} data-testid=\"cost-view-empty\">{t('view.empty')}</div>\n }\n\n const rows = cost.steps\n const combined = combineTotals(cost.totals, response?.subagents)\n const totals = combined ?? cost.totals\n const subSpend = subagentSpend(response?.subagents)\n const money = (value: number | null): string => value === null ? '—' : `${symbol}${formatMoney(value)}`\n\n return (\n <div className={css.root} data-testid=\"cost-view\">\n <section className={css.summary}>\n <div className={css.totalRow}>\n <span className={css.totalLabel}>{t('view.total')}</span>\n <span className={css.totalValue}>{money(totals.cost)}</span>\n </div>\n <div className={css.categories}>\n <span>{t('view.uncached')}: {money(totals.uncachedCost)}</span>\n <span>{t('view.cacheRead')}: {money(totals.cacheReadCost)}</span>\n <span>{t('view.output')}: {money(totals.outputCost)}</span>\n </div>\n {totals.unpricedSteps > 0 && (\n <div className={css.unpricedNote}>{t('view.unpriced', { count: String(totals.unpricedSteps) })}</div>\n )}\n {response !== null && response.subagents.length > 0 && (\n <section className={css.subagents} data-testid=\"cost-view-subagents\">\n <div className={css.unpricedNote}>{t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` })}</div>\n <ol className={css.stepList}>\n {response.subagents.map(sub => (\n <li key={sub.sessionId} className={css.step}>\n <div className={css.stepHead}>\n <span className={css.stepTurn}>{sub.sessionId.slice(0, 8)}</span>\n <span className={css.stepCost}>{money(sub.totals.cost)}</span>\n </div>\n <div className={css.stepBody}>\n <span>{t('view.uncached')}: {money(sub.totals.uncachedCost)}</span>\n <span>{t('view.cacheRead')}: {money(sub.totals.cacheReadCost)}</span>\n <span>{t('view.output')}: {money(sub.totals.outputCost)}</span>\n </div>\n </li>\n ))}\n </ol>\n </section>\n )}\n </section>\n\n <section className={css.steps}>\n <h3 className={css.stepsTitle}>{t('view.steps')}</h3>\n <ol className={css.stepList}>\n {rows.map((step) => {\n const stepBand = bandForTime(Date.now())\n const stepRatio = peakOffPeakMultiplier(response?.pricebook?.current ?? null, step.provider, step.model)\n const stepBandLabel = stepBand === 'peak'\n ? stepRatio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(stepRatio) })\n : stepBand === 'offPeak'\n ? stepRatio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / stepRatio) })\n : null\n return (\n <li key={`${step.turn}:${step.step}`} className={css.step} data-testid=\"cost-step\" data-band={stepBand === 'single' ? undefined : stepBand}>\n <div className={css.stepHead}>\n <span className={css.stepTurn}>{t('step.turn', { turn: String(step.turn) })}</span>\n <span className={css.stepModel}>{t('step.model', { model: step.model })}</span>\n <span className={css.stepTime}>{formatTime(step.time)}</span>\n <span className={css.stepCost}>\n {money(step.cost)}\n {stepBandLabel !== null && <span className={css.bandBadge} data-band={stepBand} data-testid=\"cost-step-band\">{stepBandLabel}</span>}\n </span>\n </div>\n <div className={css.stepBody}>\n <span className={css.stepTokens}>\n {t('step.tokens', {\n uncached: String(step.uncachedInputTokens),\n read: String(step.cacheReadTokens),\n write: String(step.cacheWriteTokens),\n output: String(step.outputTokens),\n })}\n </span>\n <span className={css.stepMeta}>\n {step.priced && step.snapshotVersion !== null\n ? t('step.snapshot', {\n time: formatTime(step.snapshotEffectiveAt ?? step.time),\n })\n : t('step.unpriced', { reason: reasonText(step, t) })}\n </span>\n </div>\n </li>\n );\n })}\n </ol>\n </section>\n </div>\n )\n})\n","/**\n * Per-reply cost chip: the anchored cost of ONE finalized reply. Rendered\n * through the assistant-actions slot but visually joined to the reply's\n * timing row (01:51 · 用时 · 首 token · tok/s): a flex `order` places it\n * after the clock text, and the type matches the clock's (label-tertiary,\n * 14px) with a dot separator, so it reads as part of the same metadata run.\n * The `messageId` owner prop maps to the assistant node's (turn, step),\n * which addresses the projection's step ledger — the cost is the anchored\n * snapshot value, never a current-price recompute. Unpriced replies render\n * `—` (the Cost tab explains why).\n */\nimport { memo, useEffect, useState } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { MessageId } from '@deepseek-ai/dsh-llm/brand'\nimport type { ConversationCostResponse, SessionCostStep } from '../types.ts'\nimport { bandForTime, peakOffPeakMultiplier } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier } from './format.ts'\nimport css from './AssistantCostChip.module.css'\n\nexport type ChipLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface AssistantCostChipProps {\n /** Stable identity of the finalized assistant message the actions address. */\n messageId: MessageId\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n t: ChipLocale\n}\n\n/**\n * Locate a finalized assistant node by its message id and return its\n * (turn, step) coordinates, or null when the node is out of window.\n * @param snapshot - the conversation snapshot.\n * @param messageId - the addressed message identity.\n * @returns the step coordinates, or null.\n */\nexport function stepOfMessage(\n snapshot: ConversationSnapshot,\n messageId: MessageId,\n): { turn: number; step: number } | null {\n const nodes = snapshot.chat.legacy.nodes\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n const node = nodes[i]\n if (node?.kind === 'assistant' && node.messageId === messageId && node.turn !== undefined) {\n return { turn: node.turn, step: node.step }\n }\n }\n return null\n}\n\n/** The projection step for one (turn, step) coordinate, if the ledger has it. */\nexport function stepOf(\n steps: readonly SessionCostStep[] | undefined,\n turn: number,\n step: number,\n): SessionCostStep | undefined {\n return steps?.find(entry => entry.turn === turn && entry.step === step)\n}\n\nexport const AssistantCostChip = memo(function AssistantCostChip({ messageId, useSession, useProjection, t }: AssistantCostChipProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'\n const step = useSession(snapshot => stepOfMessage(snapshot, messageId))\n const cost = useProjection(projectionKey)\n const ledger = step === null ? undefined : stepOf(cost?.steps, step.turn, step.step)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n\n useEffect(() => {\n let alive = true\n void fetch(`/cost-meter?currency=${currency}`, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the chip keeps the last good pricebook */ })\n return () => {\n alive = false\n }\n }, [currency])\n\n if (ledger === undefined || ledger.cost === null) {\n if (ledger === undefined) return null\n // Unpriced reply: show the dash; the native title explains why.\n const reason = ledger.unpricedReason === 'NO_MODEL' ? t('reason.NO_MODEL') : t('reason.NO_PRICE')\n return (\n <span className={css.unpriced} data-cost-chip title={reason} data-testid=\"cost-chip-unpriced\">{t('chip.unpriced')}</span>\n )\n }\n\n const amount = `${currencySymbol(currency)}${formatMoney(ledger.cost)}`\n const band = bandForTime(Date.now())\n const ratio = peakOffPeakMultiplier(response?.pricebook?.current ?? null, ledger.provider, ledger.model)\n const bandLabel = band === 'peak'\n ? ratio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(ratio) })\n : band === 'offPeak'\n ? ratio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / ratio) })\n : null\n\n return (\n <span className={css.root} data-cost-chip title={t('chip.title', { amount })} data-testid=\"cost-chip\">\n {amount}\n {bandLabel !== null && <span className={css.bandBadge} data-band={band} data-testid=\"cost-chip-band\">{bandLabel}</span>}\n </span>\n )\n})\n","/**\n * Session header pill (right-aligned utility): the conversation's anchored\n * total cost, or — while the assistant is replying — a live estimate of the\n * TOTAL once the running reply settles (`预计 ¥x.xx(进行中)`): the already\n * anchored ledger total plus the running reply's estimated input/output.\n * Clicking the pill toggles a detail panel: totals by category, model, and\n * the anchored price snapshot; the panel dismisses on outside clicks or\n * Escape.\n *\n * The estimate is explicitly labeled 估算/estimate: input is priced at the\n * projected context size weighted by the newest completed step's cache\n * ratio (conservative all-uncached without history), output at the\n * character heuristic — both at CURRENT snapshot prices.\n */\nimport { memo, useEffect, useMemo, useRef, useState, type RefObject } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: merges the contextPressure key into SessionProjectionMap.\nimport type {} from '@deepseek-ai/dsh-token-meter/client'\nimport type { ConversationCostResponse, SessionCostProjection } from '../types.ts'\nimport { bandForTime, blocksOutputTokens, bucketAt, cacheReadRatioOf, combineTotals, estimateCost, peakOffPeakMultiplier, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier, formatTime } from './format.ts'\nimport { ENDPOINT, REFRESH_MS } from './SessionCostLine.tsx'\nimport css from './SessionCostPill.module.css'\n\nexport type PillLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface SessionCostPillProps {\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n sessionId: string\n t: PillLocale\n}\n\n/** Read the conversation's running flag (the pill's estimate gate). */\nfunction runningOf(snapshot: ConversationSnapshot): boolean {\n return snapshot.running\n}\n\n/** Close the popover on outside clicks or Escape (product detail-panel posture). */\nfunction useDismissOutside(open: boolean, onDismiss: () => void, anchor: RefObject<HTMLElement | null>): void {\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: MouseEvent | TouchEvent): void => {\n const target = event.target as Node | null\n if (anchor.current !== null && target !== null && !anchor.current.contains(target)) onDismiss()\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') onDismiss()\n }\n document.addEventListener('mousedown', onPointerDown)\n document.addEventListener('touchstart', onPointerDown)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n document.removeEventListener('mousedown', onPointerDown)\n document.removeEventListener('touchstart', onPointerDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open, onDismiss, anchor])\n}\n\nexport const SessionCostPill = memo(function SessionCostPill({ useSession, useProjection, sessionId, t }: SessionCostPillProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'\n const cost = useProjection(projectionKey)\n const pressure = useProjection('contextPressure')\n const running = useSession(runningOf)\n const partial = useSession(snapshot => snapshot.partial)\n const [open, setOpen] = useState(false)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n useDismissOutside(open, () => setOpen(false), wrapRef)\n\n useEffect(() => {\n let alive = true\n let timer: number | undefined\n const endpoint = `${ENDPOINT}?session=${encodeURIComponent(sessionId)}&currency=${currency}`\n const load = (): void => {\n void fetch(endpoint, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the pill keeps the last good pricebook */ })\n }\n load()\n timer = window.setInterval(load, REFRESH_MS)\n return () => {\n alive = false\n if (timer !== undefined) window.clearInterval(timer)\n }\n }, [sessionId, currency])\n\n const projection = cost\n const model = projection?.model ?? null\n const snapshot = response?.pricebook.current ?? null\n const subagents = response?.subagents\n const combined = combineTotals(projection?.totals, subagents)\n const subSpend = subagentSpend(subagents)\n const symbol = currencySymbol(currency)\n\n // The estimate projects the TOTAL once the running reply settles: the\n // already-anchored ledger total (main + subagents) plus this reply's\n // estimated input/output (subagent replies cannot be projected live).\n const estimate = useMemo(() => {\n if (!running || projection === undefined) return null\n const bucket = bucketAt(snapshot ?? undefined, model?.provider, model?.model ?? 'unknown', Date.now())\n if (bucket === undefined) return null\n const inputTokens = pressure?.projectedTokens ?? pressure?.pressureTokens ?? 0\n const cachedRatio = cacheReadRatioOf(projection.steps)\n const outputTokens = blocksOutputTokens(partial?.blocks)\n const stepEstimate = estimateCost(bucket, inputTokens, cachedRatio, outputTokens)\n return (combined?.cost ?? 0) + stepEstimate\n }, [running, projection, combined, snapshot, model, pressure, partial, currency])\n\n if (projection === undefined && estimate === null) return null\n\n const total = combined?.cost ?? 0\n const label = estimate !== null\n ? t('pill.estimate', { amount: `${symbol}${formatMoney(estimate)}` })\n : t('pill.label', { amount: `${symbol}${formatMoney(total)}` })\n\n const last = projection?.steps.length !== undefined && projection.steps.length > 0\n ? projection.steps[projection.steps.length - 1]\n : undefined\n\n const sourceKey = snapshot?.source ?? null\n const source = sourceKey === null ? t('source.none') : t(`source.${sourceKey}` as 'source.manual')\n\n const band = bandForTime(Date.now())\n const peakRatio = peakOffPeakMultiplier(snapshot ?? undefined, model?.provider, model?.model ?? 'unknown')\n const bandLabel = band === 'peak'\n ? peakRatio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(peakRatio) })\n : band === 'offPeak'\n ? peakRatio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / peakRatio) })\n : null\n\n return (\n <div className={css.wrap} ref={wrapRef}>\n <button\n type=\"button\"\n className={css.pill}\n data-testid=\"cost-pill\"\n data-running={estimate !== null}\n data-band={band === 'single' ? undefined : band}\n onClick={() => setOpen(prev => !prev)}\n aria-expanded={open}\n >\n {label}\n {bandLabel !== null && <span className={css.bandBadge} data-testid=\"cost-pill-band\">{bandLabel}</span>}\n {estimate !== null && <span className={css.estimateNote}>· {t('pill.estimateNote')}</span>}\n </button>\n {open && (\n <div className={css.detail} data-testid=\"cost-pill-detail\">\n <div className={css.detailRow + ' ' + css.detailTotal}>{t('pill.detail.total', { amount: `${symbol}${formatMoney(total)}` })}</div>\n {bandLabel !== null && (\n <div className={css.detailRow + ' ' + css.detailRowSecondary} data-testid=\"cost-pill-detail-band\">{bandLabel}</div>\n )}\n {combined !== undefined && combined.pricedSteps > 0 && (\n <>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.uncached')}: {symbol}{formatMoney(combined.uncachedCost)}</div>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.cacheRead')}: {symbol}{formatMoney(combined.cacheReadCost)}</div>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.output')}: {symbol}{formatMoney(combined.outputCost)}</div>\n </>\n )}\n {subSpend > 0 && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` })}</div>\n )}\n {model !== null && (\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('pill.detail.model', { model: model.model })}</div>\n )}\n {last !== undefined && last.snapshotVersion !== null && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>\n {t('pill.detail.snapshot', { time: formatTime(last.snapshotEffectiveAt ?? last.time) })}\n {last.band !== null && ` · ${t(`band.${last.band}` as 'band.single')}`}\n </div>\n )}\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('pill.detail.source', { source })}</div>\n {projection !== undefined && projection.totals.unpricedSteps > 0 && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('view.unpriced', { count: String(projection.totals.unpricedSteps) })}</div>\n )}\n {estimate !== null && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('pill.estimateNote')}</div>\n )}\n </div>\n )}\n </div>\n )\n})\n","/**\n * Plugin configuration card (设置 → 插件 → cost-meter): the standard\n * DSH plugin-card shape (collapsible header, disclosure body, save/discard\n * footer) over the `cost-meter` settings namespace. Every editable\n * field binds the settings scope (`ctx.settingsScope.bind`) — the same\n * transport the shipped llm-deepseek / bash / web-search cards use — so the\n * card looks, stages, and persists like every other plugin configuration.\n * The JSON textareas carry the two structured fields (per-model price\n * overrides, OpenRouter aliases); the refresh button is a plain action.\n */\nimport { memo, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { Button, IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConversationCostResponse } from '../types.ts'\nimport { formatTime } from './format.ts'\nimport css from './CostPluginCard.module.css'\n\nexport type PluginCardLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface CostPluginCardProps {\n /** The bound settings scope for the `cost-meter` namespace. */\n scope: SettingsScope<unknown>\n t: PluginCardLocale\n}\n\n/** One staged field group the card writes through the scope. */\ninterface Staged {\n overridesText: string\n aliasesText: string\n cacheReadDiscount: string\n fxMode: 'auto' | 'manual'\n manualRate: string\n balanceEnabled: boolean\n openRouterEnabled: boolean\n}\n\ninterface SectionShape {\n overrides?: Record<string, unknown>\n aliases?: Record<string, string>\n cacheReadDiscount?: number\n fxMode?: 'auto' | 'manual'\n manualRate?: number\n balanceEnabled?: boolean\n openRouterEnabled?: boolean\n}\n\nconst json = (value: unknown): string => JSON.stringify(value ?? {}, null, 2)\n\nexport const CostPluginCard = memo(function CostPluginCard({ scope, t }: CostPluginCardProps) {\n const snapshot = useSyncExternalStore(\n (listener) => scope.subscribe(listener),\n () => scope.getSnapshot(),\n ) as SettingsScopeSnapshot<SectionShape>\n\n const [open, setOpen] = useState(false)\n const [busy, setBusy] = useState(false)\n const [status, setStatus] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [staged, setStaged] = useState<Staged | null>(null)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n\n // Seed the staged form from the latest accepted section.\n const value = snapshot.value\n useEffect(() => {\n setStaged({\n overridesText: json(value?.overrides),\n aliasesText: json(value?.aliases),\n cacheReadDiscount: String(value?.cacheReadDiscount ?? 0.25),\n fxMode: value?.fxMode ?? 'auto',\n manualRate: String(value?.manualRate ?? 7.2),\n balanceEnabled: value?.balanceEnabled ?? true,\n openRouterEnabled: value?.openRouterEnabled ?? true,\n })\n }, [value])\n\n // Snapshot info for the header hint (source/time, no version numbers).\n useEffect(() => {\n let alive = true\n const load = (): void => {\n void fetch('/cost-meter', { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* keep the last snapshot info */ })\n }\n load()\n return () => {\n alive = false\n }\n }, [])\n\n const current = response?.pricebook.current ?? null\n const source = current === null\n ? t('source.none')\n : t(`source.${current.source}` as 'source.manual')\n\n const write = async (field: string, fieldValue: unknown): Promise<void> => {\n setBusy(true)\n setStatus(null)\n setError(null)\n try {\n await scope.set(field, fieldValue)\n setStatus(t('settings.saved'))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setBusy(false)\n }\n }\n\n const save = (): void => {\n if (staged === null) return\n setError(null)\n let overrides: Record<string, unknown>\n let aliases: Record<string, string>\n try {\n overrides = JSON.parse(staged.overridesText) as Record<string, unknown>\n aliases = JSON.parse(staged.aliasesText) as Record<string, string>\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n return\n }\n void write('overrides', overrides)\n void write('aliases', aliases)\n const discount = Number(staged.cacheReadDiscount)\n if (Number.isFinite(discount) && discount >= 0 && discount <= 1) void write('cacheReadDiscount', discount)\n const rate = Number(staged.manualRate)\n if (Number.isFinite(rate) && rate > 0) void write('manualRate', rate)\n if (staged.fxMode === 'auto' || staged.fxMode === 'manual') void write('fxMode', staged.fxMode)\n void write('balanceEnabled', staged.balanceEnabled)\n void write('openRouterEnabled', staged.openRouterEnabled)\n }\n\n const refresh = (): void => {\n setBusy(true)\n setStatus(null)\n setError(null)\n void fetch('/cost-meter', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action: 'refresh' }),\n })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (data !== null) setResponse(data)\n setStatus(t('settings.saved'))\n })\n .catch((cause: unknown) => setError(cause instanceof Error ? cause.message : String(cause)))\n .finally(() => setBusy(false))\n }\n\n const hint = current === null\n ? t('settings.noSnapshot')\n : `${t('settings.current', { source, time: formatTime(current.effectiveAt) })}`\n\n return (\n <li className={css.card + (open ? ` ${css.cardOpen}` : '')} data-testid=\"cost-plugin-card\">\n <button type=\"button\" className={css.header} onClick={() => setOpen(prev => !prev)} aria-expanded={open}>\n <span className={css.headText}>\n <span className={css.name}>{t('settings.title')}</span>\n <span className={css.description}>{hint}</span>\n </span>\n {error !== null && <span className={css.pending}>{t('settings.error', { error })}</span>}\n <IconChevronDownOutline14 className={css.chevron + (open ? ` ${css.chevronOpen}` : '')} />\n </button>\n {open && (\n <div className={css.body}>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.discount')}</span>\n </div>\n <input\n className={css.input}\n type=\"number\"\n min={0}\n max={1}\n step={0.01}\n value={staged?.cacheReadDiscount ?? ''}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, cacheReadDiscount: event.target.value })}\n />\n </div>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.fx')}</span>\n </div>\n <div className={css.inline}>\n <select\n className={css.select}\n value={staged?.fxMode ?? 'auto'}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, fxMode: event.target.value as 'auto' | 'manual' })}\n >\n <option value=\"auto\">{t('settings.fx.auto')}</option>\n <option value=\"manual\">{t('settings.fx.manual')}</option>\n </select>\n <input\n className={css.input}\n type=\"number\"\n min={0.001}\n step={0.01}\n value={staged?.manualRate ?? ''}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, manualRate: event.target.value })}\n />\n </div>\n </div>\n <label className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.balance')}</span>\n <input\n className={css.checkbox}\n type=\"checkbox\"\n checked={staged?.balanceEnabled ?? true}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, balanceEnabled: event.target.checked })}\n />\n </div>\n </label>\n <label className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.openrouter')}</span>\n <input\n className={css.checkbox}\n type=\"checkbox\"\n checked={staged?.openRouterEnabled ?? true}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, openRouterEnabled: event.target.checked })}\n />\n </div>\n </label>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.overrides')}</span>\n </div>\n <textarea\n className={css.textarea}\n value={staged?.overridesText ?? ''}\n spellCheck={false}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, overridesText: event.target.value })}\n />\n <div className={css.hint}>{t('settings.jsonHint')}</div>\n </div>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.aliases')}</span>\n </div>\n <textarea\n className={css.textarea}\n value={staged?.aliasesText ?? ''}\n spellCheck={false}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, aliasesText: event.target.value })}\n />\n </div>\n <div className={css.footer}>\n {status !== null && <span className={css.status}>{status}</span>}\n <span className={css.spacer} />\n <Button variant=\"outline\" onClick={() => setStaged(null)}>{t('settings.discard')}</Button>\n <Button variant=\"primary\" onClick={save} disabled={busy || snapshot.status !== 'ready'}>{t('settings.save')}</Button>\n <Button variant=\"outline\" onClick={refresh} disabled={busy}>{busy ? t('settings.refreshing') : t('settings.refresh')}</Button>\n </div>\n </div>\n )}\n </li>\n )\n})\n","/**\n * Cost-meter plugin, browser half: contributes the cost surfaces —\n * the composer-dock readout (anchored session spend + account balance), the\n * Cost view tab, the per-reply cost chip, the header pill with the live\n * streaming estimate, and the plugin configuration card (设置 → 插件). The\n * anchored ledger arrives through the `sessionCost` projection; the balance\n * and the pricebook arrive over the trust-fenced host `/cost-meter`\n * route; the editable configuration binds the `cost-meter` settings\n * namespace through the standard settings scope.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the ui-conversation SlotMap merge (composer.dock etc.).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the settings-domain SlotMap merges (settings.plugin.item)\n// and the settingsScope service.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: pulls the plugin-card slot merge (settings.plugin.item).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport { en, NS, zh, type ConversationCostKey } from './locales.ts'\nimport { SessionCostLine } from './SessionCostLine.tsx'\nimport { CostView } from './CostView.tsx'\nimport { AssistantCostChip } from './AssistantCostChip.tsx'\nimport { SessionCostPill } from './SessionCostPill.tsx'\nimport { CostPluginCard } from './CostPluginCard.tsx'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Cost meter and account balance copy. */\n 'cost-meter': ConversationCostKey\n }\n}\n\n/** The settings namespace this plugin owns (mirrors the host registration). */\nexport const SETTINGS_NAMESPACE = 'cost-meter'\n\n/** Required services for locale registration, slot contributions, and the settings scope. */\nexport const inject = ['sessions', 'slots', 'locale', 'settingsScope']\n\n/**\n * Client plugin body: register the dictionaries and the five slot entries.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-cost-meter: dictionaries')\n const t = ctx.locale.bind(NS)\n const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE })\n\n // Composer-dock readout: anchored session spend + balance (after the\n // shipped stats line, order 0).\n ctx.slots.inject(\n 'conversation.composer.dock',\n () => ctx.slots.register({\n name: 'conversation.composer.dock',\n id: 'cost-meter',\n order: 1,\n locale: NS,\n }, SessionCostLine),\n )\n\n // Cost view tab (after trajectory at order 10).\n ctx.slots.inject(\n 'conversation.view',\n () => ctx.slots.register({\n name: 'conversation.view',\n id: 'cost',\n order: 20,\n locale: NS,\n label: () => t('view.cost'),\n }, CostView),\n )\n\n // Per-reply cost chip (after the feedback action at order 10).\n ctx.slots.inject(\n 'conversation.chat.assistant-actions',\n () => ctx.slots.register({\n name: 'conversation.chat.assistant-actions',\n id: 'cost',\n order: 20,\n locale: NS,\n }, AssistantCostChip),\n )\n\n // Header pill: total / live estimate with the expandable detail.\n // Negative order keeps it left of the built-in Session log utility.\n ctx.slots.inject(\n 'conversation.session.header.utilities',\n () => ctx.slots.register({\n name: 'conversation.session.header.utilities',\n id: 'cost-meter',\n order: -10,\n locale: NS,\n }, SessionCostPill),\n )\n\n // Plugin configuration card (设置 → 插件), standard plugin-card shape over\n // the `cost-meter` settings namespace.\n ctx.slots.inject(\n 'settings.plugin.item',\n () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'cost-meter',\n order: 30,\n locale: NS,\n inject: () => ({ scope }),\n }, CostPluginCard),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["memo","useState","useMemo","css","Tooltip","memo","useState","css","memo","useState","css","memo","useState","useRef","useMemo","css","memo","useSyncExternalStore","useState","css","IconChevronDownOutline14","Button"],"sources":["../src/client/locales.ts","../src/pricing.ts","../src/client/cost-math.ts","../src/client/format.ts","../src/client/SessionCostLine.tsx","../src/client/CostView.tsx","../src/client/AssistantCostChip.tsx","../src/client/SessionCostPill.tsx","../src/client/CostPluginCard.tsx","../src/client/index.ts"],"sourcesContent":["/** `cost-meter` namespace dictionaries. */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'cost-meter'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n // View tab\n 'view.cost': '花费',\n 'view.empty': '暂无花费数据',\n 'view.total': '总花费',\n 'view.uncached': '输入(缓存未命中)',\n 'view.cacheRead': '输入(缓存命中)',\n 'view.output': '输出',\n 'view.cacheWriteTokens': '缓存写入',\n 'view.unpriced': '{count} 步无价格',\n 'view.steps': '每轮回复花费',\n\n // Step list\n 'step.turn': '第 {turn} 轮',\n 'step.model': '模型 {model}',\n 'step.time': '{time}',\n 'step.tokens': '未命中 {uncached} · 缓存读 {read} · 缓存写 {write} · 输出 {output}',\n 'step.cost': '花费 {amount}',\n 'step.snapshot': '快照 · {time}',\n 'step.unpriced': '—({reason})',\n 'reason.NO_MODEL': '无模型信息',\n 'reason.NO_PRICE': '无价格',\n\n // Shared amounts\n 'cost.label': '本会话 {amount}',\n 'line.uncached': '非缓存输入 {amount}',\n 'line.cacheRead': '缓存输入 {amount}',\n 'line.output': '输出 {amount}',\n 'line.subagent': '子代理 {amount}',\n 'pill.label': '花费 {amount}',\n 'pill.estimate': '预计 {amount}',\n 'pill.estimateNote': '估算',\n 'pill.detail.total': '总花费 {amount}',\n 'pill.detail.snapshot': '价格快照({time})',\n 'pill.detail.model': '模型 {model}',\n 'pill.detail.source': '价格来源 {source}',\n 'source.official': '官方页面',\n 'source.fallback': '内置回退',\n 'source.manual': '手动',\n 'source.openrouter': 'OpenRouter',\n 'source.none': '无',\n 'band.peak': '高峰价',\n 'band.offPeak': '闲时价',\n 'band.single': '单价',\n 'price.peakExtra': '高峰 · 比闲时多 {multiplier} 倍',\n 'price.offPeakSaving': '闲时 · 比高峰省 {percent}',\n 'price.peakRatio': '高峰 {multiplier}',\n 'price.offPeakRatio': '闲时 {multiplier}',\n\n // Chip\n 'chip.title': '本回复花费 {amount}',\n 'chip.unpriced': '—',\n\n // Balance (inherited posture)\n 'balance.label': '余额 {amount}',\n 'balance.failed': '余额不可用',\n 'balance.detail': '余额 {amount}(赠送 {granted} · 充值 {toppedUp})',\n 'balance.suspended': '账户已暂停付费请求',\n 'refreshedAt': '更新于 {time}',\n\n // Plugin configuration card\n 'settings.title': '花费计价',\n 'settings.current': '当前快照 · {source} · {time}',\n 'settings.noSnapshot': '尚无价格快照',\n 'settings.refresh': '立即刷新',\n 'settings.refreshing': '刷新中…',\n 'settings.overrides': '按模型价目表(手动覆盖,元/百万 tokens)',\n 'settings.aliases': 'OpenRouter 别名映射',\n 'settings.discount': '缓存折扣系数(OpenRouter)',\n 'settings.fx': '汇率(USD→CNY)',\n 'settings.fx.auto': '自动',\n 'settings.fx.manual': '手动',\n 'settings.balance': '显示余额',\n 'settings.openrouter': '允许 OpenRouter 回退',\n 'settings.save': '保存',\n 'settings.discard': '丢弃',\n 'settings.saved': '已保存',\n 'settings.error': '操作失败:{error}',\n 'settings.jsonHint': 'JSON 对象,键为模型,值含 single/offPeak/peak 价格桶',\n} as const\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en: Record<ConversationCostKey, string> = {\n 'view.cost': 'Cost',\n 'view.empty': 'No cost data yet',\n 'view.total': 'Total',\n 'view.uncached': 'Input (cache miss)',\n 'view.cacheRead': 'Input (cache hit)',\n 'view.output': 'Output',\n 'view.cacheWriteTokens': 'Cache write',\n 'view.unpriced': '{count} steps unpriced',\n 'view.steps': 'Per-reply cost',\n\n 'step.turn': 'Turn {turn}',\n 'step.model': 'Model {model}',\n 'step.time': '{time}',\n 'step.tokens': 'miss {uncached} · read {read} · write {write} · out {output}',\n 'step.cost': 'Cost {amount}',\n 'step.snapshot': 'snapshot · {time}',\n 'step.unpriced': '—({reason})',\n 'reason.NO_MODEL': 'no model info',\n 'reason.NO_PRICE': 'no price',\n\n 'cost.label': 'Session {amount}',\n 'line.uncached': 'Uncached input {amount}',\n 'line.cacheRead': 'Cached input {amount}',\n 'line.output': 'Output {amount}',\n 'line.subagent': 'Subagents {amount}',\n 'pill.label': 'Cost {amount}',\n 'pill.estimate': '≈ {amount}',\n 'pill.estimateNote': 'estimate',\n 'pill.detail.total': 'Total {amount}',\n 'pill.detail.snapshot': 'Price snapshot ({time})',\n 'pill.detail.model': 'Model {model}',\n 'pill.detail.source': 'Price source {source}',\n 'source.official': 'official page',\n 'source.fallback': 'built-in fallback',\n 'source.manual': 'manual',\n 'source.openrouter': 'OpenRouter',\n 'source.none': 'none',\n 'band.peak': 'peak',\n 'band.offPeak': 'off-peak',\n 'band.single': 'list',\n 'price.peakExtra': 'peak · {multiplier}× more than off-peak',\n 'price.offPeakSaving': 'off-peak · save {percent} vs peak',\n 'price.peakRatio': 'peak {multiplier}',\n 'price.offPeakRatio': 'off-peak {multiplier}',\n\n 'chip.title': 'This reply cost {amount}',\n 'chip.unpriced': '—',\n\n 'balance.label': 'Balance {amount}',\n 'balance.failed': 'Balance unavailable',\n 'balance.detail': 'Balance {amount} (granted {granted} · topped up {toppedUp})',\n 'balance.suspended': 'Account suspended for paid requests',\n 'refreshedAt': 'Updated at {time}',\n\n 'settings.title': 'Cost meter',\n 'settings.current': 'Current snapshot · {source} · {time}',\n 'settings.noSnapshot': 'No price snapshot yet',\n 'settings.refresh': 'Refresh now',\n 'settings.refreshing': 'Refreshing…',\n 'settings.overrides': 'Per-model prices (manual overrides, CNY per 1M tokens)',\n 'settings.aliases': 'OpenRouter alias map',\n 'settings.discount': 'Cache-read discount (OpenRouter)',\n 'settings.fx': 'Exchange rate (USD→CNY)',\n 'settings.fx.auto': 'Auto',\n 'settings.fx.manual': 'Manual',\n 'settings.balance': 'Show balance',\n 'settings.openrouter': 'Allow OpenRouter fallback',\n 'settings.save': 'Save',\n 'settings.discard': 'Discard',\n 'settings.saved': 'Saved',\n 'settings.error': 'Operation failed: {error}',\n 'settings.jsonHint': 'JSON object keyed by model; values carry single/offPeak/peak price buckets',\n}\n\n/** Key domain of the `cost-meter` namespace (zh is the source of truth). */\nexport type ConversationCostKey = keyof typeof zh\n","/**\n * Official DeepSeek pricing: fetches the pricing page\n * (api-docs.deepseek.com/zh-cn/quick_start/pricing/) and parses both the\n * current list prices and the upcoming peak/off-peak table, so price changes\n * and the 2026-08-17 peak-pricing rollout never require a plugin update.\n *\n * The parser is deliberately tolerant: it matches price cells next to the\n * bucket labels and model names anywhere in the HTML, so reordering or\n * rewording still yields values when the numbers are present; failures\n * degrade to the built-in presets rather than throwing.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/pricing\n */\n\nimport type { CurrentPricing, PeakPricing, PriceBucket, PricingSnapshot } from './types.ts'\n\n/** Official pricing page URL (zh-cn). */\nexport const PRICING_URL = 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/'\n\n/** Official pricing page URL (en). */\nexport const PRICING_URL_EN = 'https://api-docs.deepseek.com/quick_start/pricing/'\n\n/** Official peak-pricing rollout: 2026-08-17 00:00 Beijing time (UTC+8). */\nexport const PEAK_PRICING_START_MS = Date.UTC(2026, 7, 16, 16, 0, 0)\n\n/**\n * Built-in fallback prices: the official list for deepseek-v4-flash and\n * deepseek-v4-pro (CNY per 1M tokens). DeepSeek bills cache writes at the\n * uncached input price (its wire usage reports only cache-hit vs cache-miss\n * buckets), so no separate cache-write rate exists.\n */\nexport const FALLBACK_CURRENT: CurrentPricing = {\n flash: { cacheReadPerMillion: 0.02, inputPerMillion: 1, outputPerMillion: 2 },\n pro: { cacheReadPerMillion: 0.025, inputPerMillion: 3, outputPerMillion: 6 },\n}\n\n/**\n * Built-in fallback for the upcoming peak table: the 2026-08-17 schedule\n * (peak hours double the off-peak half).\n */\nexport const FALLBACK_PEAK: PeakPricing = {\n flash: {\n offPeak: { cacheReadPerMillion: 0.05, inputPerMillion: 1.5, outputPerMillion: 4.5 },\n peak: { cacheReadPerMillion: 0.1, inputPerMillion: 3, outputPerMillion: 9 },\n },\n pro: {\n offPeak: { cacheReadPerMillion: 0.15, inputPerMillion: 4.5, outputPerMillion: 13.5 },\n peak: { cacheReadPerMillion: 0.3, inputPerMillion: 9, outputPerMillion: 27 },\n },\n}\n\n/** Built-in fallback USD prices from the English pricing page. */\nexport const FALLBACK_CURRENT_USD: CurrentPricing = {\n flash: { cacheReadPerMillion: 0.007, inputPerMillion: 0.22, outputPerMillion: 0.66 },\n pro: { cacheReadPerMillion: 0.022, inputPerMillion: 0.66, outputPerMillion: 1.98 },\n}\n\n/** Built-in fallback USD peak table from the English pricing page. */\nexport const FALLBACK_PEAK_USD: PeakPricing = {\n flash: {\n offPeak: { ...FALLBACK_CURRENT_USD.flash },\n peak: { cacheReadPerMillion: 0.014, inputPerMillion: 0.44, outputPerMillion: 1.32 },\n },\n pro: {\n offPeak: { ...FALLBACK_CURRENT_USD.pro },\n peak: { cacheReadPerMillion: 0.044, inputPerMillion: 1.32, outputPerMillion: 3.96 },\n },\n}\n\n/** Number regex: `0.02`, `1`, `2`, `3.0` etc. Deliberately non-global: a\n * shared global regex leaks `lastIndex` across `exec` calls and would skip\n * the second price cell (the pro price). */\nconst PRICE_RE = /(\\d+(?:\\.\\d+)?)\\s*元/\n\n/** English price regex: matches `$0.02`, `$0.002`, `0.02` etc. */\nconst PRICE_RE_EN = /\\$?\\s*(\\d+(?:\\.\\d+)?)/\n\n/** Strip HTML tags to plain text (keeps cell order). */\nexport function stripHtml(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/&nbsp;/gi, ' ')\n .replace(/&amp;/gi, '&')\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** Parse one price cell text like `0.02元` into a number; undefined when absent. */\nfunction parsePriceCell(text: string): number | undefined {\n const match = PRICE_RE.exec(text)\n if (match === null) return undefined\n const value = Number(match[1])\n return Number.isFinite(value) ? value : undefined\n}\n\n/** The second price cell of a row (the pro price), stripped of the first. */\n\n/** Parse one English price cell text like `$0.02` into a number. */\nfunction parsePriceCellUsd(text: string): number | undefined {\n const match = PRICE_RE_EN.exec(text)\n if (match === null) return undefined\n const value = Number(match[1])\n return Number.isFinite(value) ? value : undefined\n}\n\n/** The second price cell of an English row, stripped of the first. */\nfunction secondPriceCellUsd(text: string): string {\n return text.replace(/^\\s*\\$?\\s*(\\d+(?:\\.\\d+)?)/, '')\n}\nfunction secondPriceCell(text: string): string {\n return text.replace(/^\\s*(\\d+(?:\\.\\d+)?元)/, '')\n}\n\n/**\n * Parse the current single-price table: three rows labeled with the bucket\n * names, each carrying the flash and pro price cells.\n * @param html - the raw pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\nexport function parseCurrentTable(html: string): CurrentPricing | undefined {\n const hit = /百万tokens输入(缓存命中)([\\s\\S]{0,400}?)百万tokens输入(缓存未命中)([\\s\\S]{0,400}?)百万tokens输出([\\s\\S]{0,400}?)(?:并发限制|<\\/table)/i.exec(stripHtml(html))\n if (hit === null) return undefined\n const cacheReadCell = hit[1] ?? ''\n const inputCell = hit[2] ?? ''\n const outputCell = hit[3] ?? ''\n const cacheReadFlash = parsePriceCell(cacheReadCell)\n const cacheReadPro = parsePriceCell(secondPriceCell(cacheReadCell))\n const inputFlash = parsePriceCell(inputCell)\n const inputPro = parsePriceCell(secondPriceCell(inputCell))\n const outputFlash = parsePriceCell(outputCell)\n const outputPro = parsePriceCell(secondPriceCell(outputCell))\n if (cacheReadFlash === undefined || inputFlash === undefined || outputFlash === undefined) {\n return undefined\n }\n return {\n flash: {\n cacheReadPerMillion: cacheReadFlash,\n inputPerMillion: inputFlash,\n outputPerMillion: outputFlash,\n },\n pro: {\n cacheReadPerMillion: cacheReadPro ?? cacheReadFlash,\n inputPerMillion: inputPro ?? inputFlash,\n outputPerMillion: outputPro ?? outputFlash,\n },\n }\n}\n\n/**\n * Parse the upcoming peak-pricing table: model rows with off-peak and peak\n * cells, e.g. `deepseek-v4-flash 空闲时段 0.05 1.5 4.5 高峰时段 0.10 3.0 9.0`.\n * @param html - the raw pricing page.\n * @returns the parsed table, or undefined when either model row is absent.\n */\nexport function parsePeakTable(html: string): PeakPricing | undefined {\n const text = stripHtml(html)\n const flash = /deepseek-v4-flash\\s+空闲时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+高峰时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元/i.exec(text)\n const pro = /deepseek-v4-pro\\s+空闲时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+高峰时段\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元\\s+(\\d+(?:\\.\\d+)?)元/i.exec(text)\n if (flash === null || pro === null) return undefined\n return {\n flash: {\n offPeak: {\n cacheReadPerMillion: Number(flash[1]),\n inputPerMillion: Number(flash[2]),\n outputPerMillion: Number(flash[3]),\n },\n peak: {\n cacheReadPerMillion: Number(flash[4]),\n inputPerMillion: Number(flash[5]),\n outputPerMillion: Number(flash[6]),\n },\n },\n pro: {\n offPeak: {\n cacheReadPerMillion: Number(pro[1]),\n inputPerMillion: Number(pro[2]),\n outputPerMillion: Number(pro[3]),\n },\n peak: {\n cacheReadPerMillion: Number(pro[4]),\n inputPerMillion: Number(pro[5]),\n outputPerMillion: Number(pro[6]),\n },\n },\n }\n}\n\n/**\n * Parse the English current single-price table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\n/**\n * Parse the English combined pricing table. The English page lists all three\n * buckets as rows with OFF-PEAK and PEAK cells, so we extract both the current\n * off-peak list and the peak table from the same markup.\n */\nfunction parseEnglishPricing(html: string): { current: CurrentPricing; peak: PeakPricing } | undefined {\n const text = stripHtml(html)\n const hit = /1M INPUT TOKENS \\(CACHE HIT\\)\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+1M INPUT TOKENS \\(CACHE MISS\\)\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+1M OUTPUT TOKENS\\s+OFF-PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+PEAK\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)\\s+\\$?\\s*(\\d+(?:\\.\\d+)?)/i.exec(text)\n if (hit === null) return undefined\n const n = (index: number): number => Number(hit[index])\n const current: CurrentPricing = {\n flash: {\n cacheReadPerMillion: n(1),\n inputPerMillion: n(5),\n outputPerMillion: n(9),\n },\n pro: {\n cacheReadPerMillion: n(2),\n inputPerMillion: n(6),\n outputPerMillion: n(10),\n },\n }\n const peak: PeakPricing = {\n flash: {\n offPeak: {\n cacheReadPerMillion: n(1),\n inputPerMillion: n(5),\n outputPerMillion: n(9),\n },\n peak: {\n cacheReadPerMillion: n(3),\n inputPerMillion: n(7),\n outputPerMillion: n(11),\n },\n },\n pro: {\n offPeak: {\n cacheReadPerMillion: n(2),\n inputPerMillion: n(6),\n outputPerMillion: n(10),\n },\n peak: {\n cacheReadPerMillion: n(4),\n inputPerMillion: n(8),\n outputPerMillion: n(12),\n },\n },\n }\n return { current, peak }\n}\n\n/**\n * Parse the English current single-price table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when the labels are absent.\n */\nexport function parseCurrentTableEn(html: string): CurrentPricing | undefined {\n return parseEnglishPricing(html)?.current\n}\n\n/**\n * Parse the English upcoming peak-pricing table.\n * @param html - the raw English pricing page.\n * @returns the parsed table, or undefined when either model row is absent.\n */\nexport function parsePeakTableEn(html: string): PeakPricing | undefined {\n return parseEnglishPricing(html)?.peak\n}\n\n/**\n * Fetch and parse the official pricing page.\n * @param fetchImpl - fetch-compatible function (injected for testability).\n * @param timeoutMs - abort timeout.\n * @returns the parsed snapshot; `error` is set when fetch/parse failed.\n */\nexport async function fetchPricing(\n fetchImpl: typeof fetch = globalThis.fetch,\n timeoutMs = 15_000,\n locale: 'zh' | 'en' = 'zh',\n): Promise<PricingSnapshot> {\n const fetchedAt = Date.now()\n const currency = locale === 'en' ? 'USD' : 'CNY'\n const url = locale === 'en' ? PRICING_URL_EN : PRICING_URL\n const parseCurrent = locale === 'en' ? parseCurrentTableEn : parseCurrentTable\n const parsePeak = locale === 'en' ? parsePeakTableEn : parsePeakTable\n const fallbackCurrent = locale === 'en' ? FALLBACK_CURRENT_USD : FALLBACK_CURRENT\n const fallbackPeak = locale === 'en' ? FALLBACK_PEAK_USD : FALLBACK_PEAK\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n let response: Response\n try {\n response = await fetchImpl(url, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n if (!response.ok) {\n return { fetchedAt, currency, current: fallbackCurrent, peakActive: false, effective: fallbackCurrent, error: `pricing page HTTP ${response.status}` }\n }\n const html = await response.text()\n const current = parseCurrent(html)\n if (current === undefined) {\n return { fetchedAt, currency, current: fallbackCurrent, peakActive: false, effective: fallbackCurrent, error: 'pricing table not found' }\n }\n const peak = parsePeak(html)\n const now = Date.now()\n const peakActive = now >= PEAK_PRICING_START_MS\n return {\n fetchedAt,\n currency,\n current,\n ...(peak === undefined ? {} : { peak }),\n peakActive,\n effective: effectivePricing(current, peak, peakActive, new Date(now)),\n ...(peakActive ? { band: isPeakHour(new Date(now)) ? 'peak' : 'offPeak' } : {}),\n }\n } catch (error) {\n return {\n fetchedAt,\n currency,\n current: fallbackCurrent,\n peakActive: false,\n effective: fallbackCurrent,\n error: error instanceof Error ? error.message : String(error)\n }\n }\n}\n\n/**\n * Whether the current moment is a peak-pricing hour in Beijing time:\n * 09:00-12:00 and 14:00-18:00 (peak); everything else is off-peak.\n * @param now - the instant to classify.\n * @returns true during peak hours.\n */\nexport function isPeakHour(now: Date = new Date()): boolean {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: 'Asia/Shanghai',\n hour: 'numeric',\n hour12: false,\n }).formatToParts(now)\n const hour = Number(parts.find(part => part.type === 'hour')?.value)\n if (Number.isNaN(hour)) return false\n return (hour >= 9 && hour < 12) || (hour >= 14 && hour < 18)\n}\n\n/**\n * The price bucket in effect for one model at the given moment: the\n * peak/off-peak band once the rollout is live (with the parsed table, or the\n * built-in fallback), otherwise the current official list.\n * @param current - the current list prices.\n * @param peak - the upcoming peak table, when parsed.\n * @param peakActive - whether the rollout has started.\n * @param now - the instant to price for.\n * @param model - `flash` or `pro`.\n * @returns the bucket to apply.\n */\nexport function effectiveBucket(\n current: CurrentPricing,\n peak: PeakPricing | undefined,\n peakActive: boolean,\n now: Date,\n model: keyof CurrentPricing,\n): PriceBucket {\n if (peakActive) {\n const table = peak?.[model] ?? FALLBACK_PEAK[model]\n return isPeakHour(now) ? table.peak : table.offPeak\n }\n return current[model]\n}\n\n/** Per-model effective prices at the given moment. */\nexport function effectivePricing(\n current: CurrentPricing,\n peak: PeakPricing | undefined,\n peakActive: boolean,\n now: Date,\n): CurrentPricing {\n return {\n flash: effectiveBucket(current, peak, peakActive, now, 'flash'),\n pro: effectiveBucket(current, peak, peakActive, now, 'pro'),\n }\n}","/**\n * Client-side price lookup and streaming-estimate helpers.\n *\n * The authoritative anchored ledger arrives through the `sessionCost`\n * projection; this module only mirrors the host pricebook math for display\n * and for the live estimate (which by nature uses CURRENT prices and is\n * labeled 估算/estimate). The lookup logic intentionally mirrors\n * `src/pricebook.ts` — keep the two in sync.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/client/cost-math\n */\n\nimport { isPeakHour, PEAK_PRICING_START_MS } from '../pricing.ts'\nimport type {\n ModelPrice,\n PriceBand,\n PriceBucket,\n PricebookSnapshot,\n SessionCostStep,\n SessionCostTotals,\n SubagentCost,\n} from '../types.ts'\n\n/** Sum totals (this module's mirror of the host `sumTotals`). */\nexport function sumTotals(totals: readonly SessionCostTotals[]): SessionCostTotals {\n const sum: SessionCostTotals = {\n uncachedCost: 0,\n cacheReadCost: 0,\n outputCost: 0,\n cost: 0,\n pricedSteps: 0,\n unpricedSteps: 0,\n steps: 0,\n }\n for (const entry of totals) {\n sum.uncachedCost += entry.uncachedCost\n sum.cacheReadCost += entry.cacheReadCost\n sum.outputCost += entry.outputCost\n sum.cost += entry.cost\n sum.pricedSteps += entry.pricedSteps\n sum.unpricedSteps += entry.unpricedSteps\n sum.steps += entry.steps\n }\n return sum\n}\n\n/** Total spend of every subagent (CNY); 0 without subagents. */\nexport function subagentSpend(subagents: readonly SubagentCost[] | undefined): number {\n if (subagents === undefined || subagents.length === 0) return 0\n return sumTotals(subagents.map(entry => entry.totals)).cost\n}\n\n/**\n * The conversation-wide totals: the main session's ledger plus every\n * subagent's ledger (host-aggregated). Undefined while the main ledger is\n * absent; identical to the main totals without subagents.\n * @param main - the main session's projection totals.\n * @param subagents - the host-aggregated subagent totals.\n * @returns the combined totals, or undefined.\n */\nexport function combineTotals(\n main: SessionCostTotals | undefined,\n subagents: readonly SubagentCost[] | undefined,\n): SessionCostTotals | undefined {\n if (main === undefined) return undefined\n if (subagents === undefined || subagents.length === 0) return main\n return sumTotals([main, ...subagents.map(entry => entry.totals)])\n}\n\n/** The price band in effect at one instant (mirror of the host helper). */\nexport function bandForTime(time: number): PriceBand {\n if (time < PEAK_PRICING_START_MS) return 'single'\n return isPeakHour(new Date(time)) ? 'peak' : 'offPeak'\n}\n\n/** The bucket of one model's entry for one band (mirror of the host helper). */\nexport function bucketOf(modelPrice: ModelPrice, band: PriceBand): PriceBucket | undefined {\n if (band === 'single') return modelPrice.single ?? modelPrice.offPeak ?? modelPrice.peak\n if (band === 'peak') return modelPrice.peak ?? modelPrice.offPeak ?? modelPrice.single\n return modelPrice.offPeak ?? modelPrice.peak ?? modelPrice.single\n}\n\n/** Map a model id to its official pricing key (mirror of the host helper). */\nexport function pricingKeyOfModel(model: string): 'pro' | 'flash' {\n const lower = model.toLowerCase()\n return lower.includes('pro') ? 'pro' : 'flash'\n}\n\n/** Candidate canonical price keys for one model (mirror of the host helper). */\nexport function modelKeys(provider: string | undefined, model: string): string[] {\n const keys: string[] = []\n if (provider !== undefined && provider.length > 0) keys.push(`${provider}/${model}`)\n keys.push(model)\n const lower = model.toLowerCase()\n const deepseekish = lower.includes('deepseek')\n || (provider !== undefined && provider.toLowerCase().includes('deepseek'))\n if (deepseekish) keys.push(pricingKeyOfModel(model))\n return keys\n}\n\n/** Pick the snapshot effective at `time` (newest ≤ time, else earliest). */\nexport function snapshotAt(snapshots: readonly PricebookSnapshot[], time: number): PricebookSnapshot | undefined {\n let best: PricebookSnapshot | undefined\n for (const snapshot of snapshots) {\n // `>=` keeps the NEWER snapshot (later in the array) on an effectiveAt tie.\n if (snapshot.effectiveAt <= time && (best === undefined || snapshot.effectiveAt >= best.effectiveAt)) best = snapshot\n }\n return best ?? (snapshots.length > 0 ? snapshots[0] : undefined)\n}\n\n/** Resolve one model's price entry in a snapshot through the key chain. */\nexport function modelPriceOf(\n snapshot: PricebookSnapshot | null | undefined,\n provider: string | undefined,\n model: string,\n): ModelPrice | undefined {\n if (snapshot === undefined || snapshot === null) return undefined\n for (const key of modelKeys(provider, model)) {\n const entry = snapshot.prices[key]\n if (entry !== undefined) return entry\n }\n return undefined\n}\n\n/** Resolve one model's bucket at one instant from a snapshot. */\nexport function bucketAt(\n snapshot: PricebookSnapshot | undefined,\n provider: string | undefined,\n model: string,\n time: number,\n): PriceBucket | undefined {\n const entry = modelPriceOf(snapshot, provider, model)\n if (entry === undefined) return undefined\n return bucketOf(entry, bandForTime(time))\n}\n\n/**\n * The peak-to-off-peak price multiplier for one model's entry, when the\n * entry carries both bands. The multiplier is the ratio of the summed peak\n * bucket rates to the summed off-peak bucket rates; official DeepSeek prices\n * scale all buckets by the same factor, so this is a stable “peak is N× the\n * off-peak price” display value.\n * @param snapshot - the pricebook snapshot to read.\n * @param provider - the provider route, when known.\n * @param model - the model id.\n * @returns the peak/off-peak multiplier, or null when either band is absent.\n */\nexport function peakOffPeakMultiplier(\n snapshot: PricebookSnapshot | null | undefined,\n provider: string | undefined,\n model: string,\n): number | null {\n const entry = modelPriceOf(snapshot, provider, model)\n if (entry === undefined) return null\n const peak = entry.peak\n const offPeak = entry.offPeak\n if (peak === undefined || offPeak === undefined) return null\n const offPeakTotal = offPeak.cacheReadPerMillion + offPeak.inputPerMillion + offPeak.outputPerMillion\n if (offPeakTotal === 0) return null\n const peakTotal = peak.cacheReadPerMillion + peak.inputPerMillion + peak.outputPerMillion\n return peakTotal / offPeakTotal\n}\n\n/** CJK characters (the /1.5 density class). */\nconst CJK_RE = /[\\u3400-\\u4dbf\\u4e00-\\u9fff]/g\n\n/**\n * Heuristic character→token estimate: CJK ÷ 1.5, everything else ÷ 4\n * (the streaming estimate's output side).\n * @param text - the accumulated text.\n * @returns the estimated token count.\n */\nexport function estimateTokens(text: string): number {\n const cjk = (text.match(CJK_RE) ?? []).length\n return Math.ceil(cjk / 1.5 + (text.length - cjk) / 4)\n}\n\n/**\n * Tokens of the streamed text/reasoning blocks of a partial assistant.\n * @param blocks - the partial's content blocks (text/reasoning carry `text`).\n * @returns the estimated token count.\n */\nexport function blocksOutputTokens(blocks: readonly { kind?: string; text?: string }[] | undefined): number {\n let tokens = 0\n for (const block of blocks ?? []) {\n if ((block.kind === 'text' || block.kind === 'reasoning') && typeof block.text === 'string' && block.text.length > 0) {\n tokens += estimateTokens(block.text)\n }\n }\n return tokens\n}\n\n/**\n * The cache-read ratio of the newest completed step (input side of the\n * estimate). Absent history conservatively prices all input as uncached\n * (upper bound).\n * @param steps - the projection's step ledger.\n * @returns the ratio in [0, 1].\n */\nexport function cacheReadRatioOf(steps: readonly SessionCostStep[]): number {\n for (let i = steps.length - 1; i >= 0; i -= 1) {\n const step = steps[i]\n const total = step.uncachedInputTokens + step.cacheReadTokens + step.cacheWriteTokens\n if (step.priced && total > 0) return step.cacheReadTokens / total\n }\n return 0\n}\n\n/**\n * The live estimate of the running reply: input at the projected context\n * size (weighted by the cache ratio) plus output at the streamed-token\n * heuristic, both at CURRENT prices. Always labeled 估算/estimate — it is\n * replaced by the projection's exact anchored value once the step settles.\n * @param bucket - the current price bucket of the session model.\n * @param inputTokens - the projected prompt size for the next request.\n * @param cachedRatio - the cache-read ratio from recent completed steps.\n * @param outputTokens - the streamed output tokens so far.\n * @returns the estimated CNY cost.\n */\nexport function estimateCost(\n bucket: PriceBucket,\n inputTokens: number,\n cachedRatio: number,\n outputTokens: number,\n): number {\n const inputCost = inputTokens / 1_000_000\n * (bucket.cacheReadPerMillion * cachedRatio + bucket.inputPerMillion * (1 - cachedRatio))\n const outputCost = outputTokens / 1_000_000 * bucket.outputPerMillion\n return inputCost + outputCost\n}\n","/**\n * Shared display formatting for amounts and timestamps.\n *\n * @module @gamegeek-saikel/dsh-cost-meter/client/format\n */\n\n/**\n * Common currency symbols; unknown currencies render as a bare code.\n * @param currency - ISO 4217 code.\n * @returns the display symbol.\n */\nexport function currencySymbol(currency: string): string {\n switch (currency) {\n case 'CNY':\n return '¥'\n case 'USD':\n return '$'\n case 'EUR':\n return '€'\n default:\n return `${currency} `\n }\n}\n\n/** Detect the display currency from the active locale dictionary. */\nexport function displayCurrency(t: (key: string, params?: Record<string, string>) => string): 'CNY' | 'USD' {\n return t('view.cost') === 'Cost' ? 'USD' : 'CNY'\n}\n\n/**\n * Compact money: two decimals above 1, four down to 0.01, six below.\n * Costs are CNY 元 with 4-decimal display precision; zero renders as 0.00.\n * @param value - the amount.\n * @returns the fixed-width display string.\n */\nexport function formatMoney(value: number): string {\n if (value === 0) return '0.00'\n if (value >= 1) return value.toFixed(2)\n if (value >= 0.01) return value.toFixed(4)\n return value.toFixed(6)\n}\n\n/** Time formatting for snapshot stamps and step rows. */\nexport function formatTime(epochMs: number): string {\n return new Date(epochMs).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n}\n\n/**\n * Compact price multiplier, e.g. `2.0×` or `0.5×`.\n * @param multiplier - the ratio to display.\n * @returns the fixed one-decimal multiplier string.\n */\nexport function formatMultiplier(multiplier: number): string {\n return `${multiplier.toFixed(1)}×`\n}\n/**\n * Compact percentage, e.g. `50%`.\n * @param fraction - the ratio in [0, 1].\n * @returns the whole-percent string.\n */\nexport function formatPercent(fraction: number): string {\n return `${Math.round(fraction * 100)}%`\n}\n","/**\n * Composer-dock readout: the current session's anchored spend (the\n * `sessionCost` projection — price-snapshot-anchored, never recomputed at\n * current prices) plus the DeepSeek account balance (host\n * `/cost-meter` route), refreshed every minute. The hover detail\n * breaks the spend into the three billed categories (uncached input incl.\n * cache writes, cache reads, output) and names the anchored price snapshot.\n *\n * The component is deliberately defensive: every derived string is computed\n * inside a guarded block so a single bad field can never take down the whole\n * dock entry (the slot system retires crashed entries).\n */\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { BalanceSnapshot, ConversationCostResponse, SessionCostProjection } from '../types.ts'\nimport { combineTotals, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatTime } from './format.ts'\nimport css from './SessionCostLine.module.css'\n\n/** Host route serving the balance snapshot and the pricebook view. */\nexport const ENDPOINT = '/cost-meter'\n/** Balance refresh interval in milliseconds. */\nexport const REFRESH_MS = 60_000\n\n/** Session-cost namespace key domain for the locale seat. */\nexport type CostLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface SessionCostLineProps {\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n sessionId: string\n t: CostLocale\n}\n\n/** Compose the tooltip's balance detail from an ok snapshot. */\nfunction balanceTooltip(balance: Extract<BalanceSnapshot, { ok: true }>, t: CostLocale): string {\n if (balance.balance === null) return t('balance.failed')\n const symbol = currencySymbol(balance.balance.currency)\n const amount = `${symbol}${formatMoney(balance.balance.total)}`\n const granted = `${symbol}${formatMoney(balance.balance.granted)}`\n const toppedUp = `${symbol}${formatMoney(balance.balance.toppedUp)}`\n const detail = t('balance.detail', { amount, granted, toppedUp })\n return balance.isAvailable ? detail : `${detail} · ${t('balance.suspended')}`\n}\n\n/** The tooltip's cost detail rows from the anchored projection. Never throws. */\nexport function costDetail(cost: SessionCostProjection | undefined, model: string | undefined, t: CostLocale, currency: 'CNY' | 'USD' = 'CNY'): string[] {\n const rows: string[] = []\n try {\n if (cost === undefined) return rows\n const symbol = currencySymbol(currency)\n const totals = cost.totals\n if (totals !== undefined && totals.pricedSteps > 0) {\n rows.push(t('cost.label', { amount: `${symbol}${formatMoney(totals.cost)}` }))\n rows.push(t('view.uncached') + ` ${symbol}${formatMoney(totals.uncachedCost)}`)\n rows.push(t('view.cacheRead') + ` ${symbol}${formatMoney(totals.cacheReadCost)}`)\n rows.push(t('view.output') + ` ${symbol}${formatMoney(totals.outputCost)}`)\n }\n if (model !== undefined) rows.push(t('step.model', { model }))\n const steps = cost.steps\n if (steps !== undefined && steps.length > 0) {\n const last = steps[steps.length - 1]\n if (last !== undefined && last.snapshotVersion !== null) {\n rows.push(t('pill.detail.snapshot', { time: formatTime(last.snapshotEffectiveAt ?? last.time) }))\n }\n }\n if (totals !== undefined && totals.unpricedSteps > 0) {\n rows.push(t('view.unpriced', { count: String(totals.unpricedSteps) }))\n }\n } catch {\n /* a malformed field must never take down the dock entry */\n }\n return rows\n}\n\nexport const SessionCostLine = memo(function SessionCostLine({ useProjection, sessionId, t }: SessionCostLineProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const costCny = useProjection('sessionCost')\n const costUsd = useProjection('sessionCostUsd')\n const cost = currency === 'USD' ? costUsd : costCny\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const [failed, setFailed] = useState(false)\n useEffect(() => {\n let alive = true\n let timer: number | undefined\n const endpoint = `${ENDPOINT}?session=${encodeURIComponent(sessionId)}&currency=${currency}`\n const load = (): void => {\n void fetch(endpoint, { cache: 'no-store' })\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n return res.json() as Promise<ConversationCostResponse>\n })\n .then((data) => {\n if (!alive) return\n setResponse(data)\n setFailed(false)\n })\n .catch(() => {\n if (alive) setFailed(true)\n })\n }\n load()\n timer = window.setInterval(load, REFRESH_MS)\n return () => {\n alive = false\n if (timer !== undefined) window.clearInterval(timer)\n }\n }, [sessionId, currency])\n\n const balance = response?.balance\n const balanceEnabled = response?.pricebook?.balanceEnabled !== false\n const subagents = response?.subagents\n const combined = combineTotals(cost?.totals, subagents)\n const subSpend = subagentSpend(subagents)\n // The dock readout must never vanish on a session switch: switching\n // sessions re-binds the projection hook, and until the new session's\n // baseline has carried the sessionCost key useProjection returns\n // undefined (capability absent - never a usable signal). Treat a\n // missing ledger as a zero ledger so the row renders CNY 0.00\n // instead of disappearing while the baseline is in flight.\n const mainSpend = cost?.totals?.cost ?? 0\n\n const parts: string[] = []\n try {\n const symbol = currencySymbol(currency)\n // Breakdown first (cached input, uncached input, output — combined with\n // subagents), then the main session total, then the subagent total, then\n // the balance.\n if (combined !== undefined && combined.pricedSteps > 0) {\n parts.push(t('line.cacheRead', { amount: `${symbol}${formatMoney(combined.cacheReadCost)}` }))\n parts.push(t('line.uncached', { amount: `${symbol}${formatMoney(combined.uncachedCost)}` }))\n parts.push(t('line.output', { amount: `${symbol}${formatMoney(combined.outputCost)}` }))\n }\n parts.push(t('cost.label', { amount: `${symbol}${formatMoney(mainSpend)}` }))\n if (subSpend > 0) {\n parts.push(t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` }))\n }\n if (balanceEnabled && balance?.ok === true && balance.balance !== null) {\n parts.push(t('balance.label', { amount: `${currencySymbol(balance.balance.currency)}${formatMoney(balance.balance.total)}` }))\n } else if (balanceEnabled && failed) {\n parts.push(t('balance.failed'))\n }\n } catch {\n /* keep the row alive */\n }\n if (parts.length === 0) return null\n\n const model = cost?.model?.model\n const tooltipParts = useMemo(() => {\n const rows: string[] = []\n try {\n rows.push(...costDetail(cost, model, t, currency))\n if (subSpend > 0) {\n rows.push(t('line.subagent', { amount: `${currencySymbol(currency)}${formatMoney(subSpend)}` }))\n }\n const pricebook = response?.pricebook\n if (pricebook !== undefined && pricebook.current !== null) {\n const source = pricebook.current.source\n if (source === 'official' || source === 'fallback' || source === 'manual' || source === 'openrouter') {\n rows.push(t('pill.detail.source', { source: t(`source.${source}` as 'source.manual') }))\n }\n }\n if (balanceEnabled && balance?.ok === true) rows.push(balanceTooltip(balance, t))\n if (response !== null) {\n rows.push(t('refreshedAt', { time: formatTime(balance?.ok === true ? balance.fetchedAt : Date.now()) }))\n }\n } catch {\n /* keep the row alive */\n }\n return rows\n }, [cost, model, t, balance, balanceEnabled, response, subSpend, currency])\n\n const line = parts.join(' · ')\n const tooltip = tooltipParts.filter(Boolean).join(' · ')\n if (tooltip.length === 0) {\n return <div className={css.root} data-testid=\"session-cost-line\">{line}</div>\n }\n return (\n <Tooltip label={tooltip} side=\"top\" delayMs={500}>\n <div className={css.root} data-testid=\"session-cost-line\">{line}</div>\n </Tooltip>\n )\n})\n","/**\n * Cost view tab: the whole conversation's anchored cost ledger — the total\n * (main session + subagent sessions, host-aggregated), the three billed\n * categories (uncached input incl. cache writes, cache reads, output), a\n * per-subagent breakdown, and the per-reply list (turn, time, model, token\n * split, step cost, anchored snapshot version and effective time). Reads the\n * `sessionCost` projection plus the host-aggregated subagent totals; nothing\n * is recomputed at current prices.\n */\nimport { memo, useEffect, useState } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationCostResponse, SessionCostStep } from '../types.ts'\nimport { bandForTime, combineTotals, peakOffPeakMultiplier, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier, formatTime } from './format.ts'\nimport css from './CostView.module.css'\n\nexport type CostViewLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface CostViewProps {\n useProjection: UseProjection\n sessionId: string\n t: CostViewLocale\n}\n\n/** Map an unpriced reason to its localized copy. */\nfunction reasonText(step: SessionCostStep, t: CostViewLocale): string {\n return t(`reason.${step.unpricedReason ?? 'NO_PRICE'}` as 'reason.NO_PRICE')\n}\n\nexport const CostView = memo(function CostView({ useProjection, sessionId, t }: CostViewProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const costCny = useProjection('sessionCost')\n const costUsd = useProjection('sessionCostUsd')\n const cost = currency === 'USD' ? costUsd : costCny\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const symbol = currencySymbol(currency)\n\n useEffect(() => {\n let alive = true\n void fetch(`/cost-meter?session=${encodeURIComponent(sessionId)}&currency=${currency}`, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the tab keeps the last good aggregation */ })\n return () => {\n alive = false\n }\n }, [sessionId, currency])\n\n if (cost === undefined || cost.steps.length === 0) {\n return <div className={css.root} data-testid=\"cost-view-empty\">{t('view.empty')}</div>\n }\n\n const rows = cost.steps\n const combined = combineTotals(cost.totals, response?.subagents)\n const totals = combined ?? cost.totals\n const subSpend = subagentSpend(response?.subagents)\n const money = (value: number | null): string => value === null ? '—' : `${symbol}${formatMoney(value)}`\n\n return (\n <div className={css.root} data-testid=\"cost-view\">\n <section className={css.summary}>\n <div className={css.totalRow}>\n <span className={css.totalLabel}>{t('view.total')}</span>\n <span className={css.totalValue}>{money(totals.cost)}</span>\n </div>\n <div className={css.categories}>\n <span>{t('view.uncached')}: {money(totals.uncachedCost)}</span>\n <span>{t('view.cacheRead')}: {money(totals.cacheReadCost)}</span>\n <span>{t('view.output')}: {money(totals.outputCost)}</span>\n </div>\n {totals.unpricedSteps > 0 && (\n <div className={css.unpricedNote}>{t('view.unpriced', { count: String(totals.unpricedSteps) })}</div>\n )}\n {response !== null && response.subagents.length > 0 && (\n <section className={css.subagents} data-testid=\"cost-view-subagents\">\n <div className={css.unpricedNote}>{t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` })}</div>\n <ol className={css.stepList}>\n {response.subagents.map(sub => (\n <li key={sub.sessionId} className={css.step}>\n <div className={css.stepHead}>\n <span className={css.stepTurn}>{sub.sessionId.slice(0, 8)}</span>\n <span className={css.stepCost}>{money(sub.totals.cost)}</span>\n </div>\n <div className={css.stepBody}>\n <span>{t('view.uncached')}: {money(sub.totals.uncachedCost)}</span>\n <span>{t('view.cacheRead')}: {money(sub.totals.cacheReadCost)}</span>\n <span>{t('view.output')}: {money(sub.totals.outputCost)}</span>\n </div>\n </li>\n ))}\n </ol>\n </section>\n )}\n </section>\n\n <section className={css.steps}>\n <h3 className={css.stepsTitle}>{t('view.steps')}</h3>\n <ol className={css.stepList}>\n {rows.map((step) => {\n const stepBand = bandForTime(Date.now())\n const stepRatio = peakOffPeakMultiplier(response?.pricebook?.current ?? null, step.provider, step.model)\n const stepBandLabel = stepBand === 'peak'\n ? stepRatio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(stepRatio) })\n : stepBand === 'offPeak'\n ? stepRatio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / stepRatio) })\n : null\n return (\n <li key={`${step.turn}:${step.step}`} className={css.step} data-testid=\"cost-step\" data-band={stepBand === 'single' ? undefined : stepBand}>\n <div className={css.stepHead}>\n <span className={css.stepTurn}>{t('step.turn', { turn: String(step.turn) })}</span>\n <span className={css.stepModel}>{t('step.model', { model: step.model })}</span>\n <span className={css.stepTime}>{formatTime(step.time)}</span>\n <span className={css.stepCost}>\n {money(step.cost)}\n {stepBandLabel !== null && <span className={css.bandBadge} data-band={stepBand} data-testid=\"cost-step-band\">{stepBandLabel}</span>}\n </span>\n </div>\n <div className={css.stepBody}>\n <span className={css.stepTokens}>\n {t('step.tokens', {\n uncached: String(step.uncachedInputTokens),\n read: String(step.cacheReadTokens),\n write: String(step.cacheWriteTokens),\n output: String(step.outputTokens),\n })}\n </span>\n <span className={css.stepMeta}>\n {step.priced && step.snapshotVersion !== null\n ? t('step.snapshot', {\n time: formatTime(step.snapshotEffectiveAt ?? step.time),\n })\n : t('step.unpriced', { reason: reasonText(step, t) })}\n </span>\n </div>\n </li>\n );\n })}\n </ol>\n </section>\n </div>\n )\n})\n","/**\n * Per-reply cost chip: the anchored cost of ONE finalized reply. Rendered\n * through the assistant-actions slot but visually joined to the reply's\n * timing row (01:51 · 用时 · 首 token · tok/s): a flex `order` places it\n * after the clock text, and the type matches the clock's (label-tertiary,\n * 14px) with a dot separator, so it reads as part of the same metadata run.\n * The `messageId` owner prop maps to the assistant node's (turn, step),\n * which addresses the projection's step ledger — the cost is the anchored\n * snapshot value, never a current-price recompute. Unpriced replies render\n * `—` (the Cost tab explains why).\n */\nimport { memo, useEffect, useState } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { MessageId } from '@deepseek-ai/dsh-llm/brand'\nimport type { ConversationCostResponse, SessionCostStep } from '../types.ts'\nimport { bandForTime, peakOffPeakMultiplier } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier } from './format.ts'\nimport css from './AssistantCostChip.module.css'\n\nexport type ChipLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface AssistantCostChipProps {\n /** Stable identity of the finalized assistant message the actions address. */\n messageId: MessageId\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n t: ChipLocale\n}\n\n/**\n * Locate a finalized assistant node by its message id and return its\n * (turn, step) coordinates, or null when the node is out of window.\n * @param snapshot - the conversation snapshot.\n * @param messageId - the addressed message identity.\n * @returns the step coordinates, or null.\n */\nexport function stepOfMessage(\n snapshot: ConversationSnapshot,\n messageId: MessageId,\n): { turn: number; step: number } | null {\n const nodes = snapshot.chat.legacy.nodes\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n const node = nodes[i]\n if (node?.kind === 'assistant' && node.messageId === messageId && node.turn !== undefined) {\n return { turn: node.turn, step: node.step }\n }\n }\n return null\n}\n\n/** The projection step for one (turn, step) coordinate, if the ledger has it. */\nexport function stepOf(\n steps: readonly SessionCostStep[] | undefined,\n turn: number,\n step: number,\n): SessionCostStep | undefined {\n return steps?.find(entry => entry.turn === turn && entry.step === step)\n}\n\nexport const AssistantCostChip = memo(function AssistantCostChip({ messageId, useSession, useProjection, t }: AssistantCostChipProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const step = useSession(snapshot => stepOfMessage(snapshot, messageId))\n const costCny = useProjection('sessionCost')\n const costUsd = useProjection('sessionCostUsd')\n const cost = currency === 'USD' ? costUsd : costCny\n const ledger = step === null ? undefined : stepOf(cost?.steps, step.turn, step.step)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n\n useEffect(() => {\n let alive = true\n void fetch(`/cost-meter?currency=${currency}`, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the chip keeps the last good pricebook */ })\n return () => {\n alive = false\n }\n }, [currency])\n\n if (ledger === undefined || ledger.cost === null) {\n if (ledger === undefined) return null\n // Unpriced reply: show the dash; the native title explains why.\n const reason = ledger.unpricedReason === 'NO_MODEL' ? t('reason.NO_MODEL') : t('reason.NO_PRICE')\n return (\n <span className={css.unpriced} data-cost-chip title={reason} data-testid=\"cost-chip-unpriced\">{t('chip.unpriced')}</span>\n )\n }\n\n const amount = `${currencySymbol(currency)}${formatMoney(ledger.cost)}`\n const band = bandForTime(Date.now())\n const ratio = peakOffPeakMultiplier(response?.pricebook?.current ?? null, ledger.provider, ledger.model)\n const bandLabel = band === 'peak'\n ? ratio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(ratio) })\n : band === 'offPeak'\n ? ratio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / ratio) })\n : null\n\n return (\n <span className={css.root} data-cost-chip title={t('chip.title', { amount })} data-testid=\"cost-chip\">\n {amount}\n {bandLabel !== null && <span className={css.bandBadge} data-band={band} data-testid=\"cost-chip-band\">{bandLabel}</span>}\n </span>\n )\n})\n","/**\n * Session header pill (right-aligned utility): the conversation's anchored\n * total cost, or — while the assistant is replying — a live estimate of the\n * TOTAL once the running reply settles (`预计 ¥x.xx(进行中)`): the already\n * anchored ledger total plus the running reply's estimated input/output.\n * Clicking the pill toggles a detail panel: totals by category, model, and\n * the anchored price snapshot; the panel dismisses on outside clicks or\n * Escape.\n *\n * The estimate is explicitly labeled 估算/estimate: input is priced at the\n * projected context size weighted by the newest completed step's cache\n * ratio (conservative all-uncached without history), output at the\n * character heuristic — both at CURRENT snapshot prices.\n */\nimport { memo, useEffect, useMemo, useRef, useState, type RefObject } from 'react'\nimport type { UseProjection } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: merges the contextPressure key into SessionProjectionMap.\nimport type {} from '@deepseek-ai/dsh-token-meter/client'\nimport type { ConversationCostResponse, SessionCostProjection } from '../types.ts'\nimport { bandForTime, blocksOutputTokens, bucketAt, cacheReadRatioOf, combineTotals, estimateCost, peakOffPeakMultiplier, subagentSpend } from './cost-math.ts'\nimport { currencySymbol, displayCurrency, formatMoney, formatMultiplier, formatTime } from './format.ts'\nimport { ENDPOINT, REFRESH_MS } from './SessionCostLine.tsx'\nimport css from './SessionCostPill.module.css'\n\nexport type PillLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface SessionCostPillProps {\n useSession: SnapshotSelectorHook<ConversationSnapshot>\n useProjection: UseProjection\n sessionId: string\n t: PillLocale\n}\n\n/** Read the conversation's running flag (the pill's estimate gate). */\nfunction runningOf(snapshot: ConversationSnapshot): boolean {\n return snapshot.running\n}\n\n/** Close the popover on outside clicks or Escape (product detail-panel posture). */\nfunction useDismissOutside(open: boolean, onDismiss: () => void, anchor: RefObject<HTMLElement | null>): void {\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: MouseEvent | TouchEvent): void => {\n const target = event.target as Node | null\n if (anchor.current !== null && target !== null && !anchor.current.contains(target)) onDismiss()\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') onDismiss()\n }\n document.addEventListener('mousedown', onPointerDown)\n document.addEventListener('touchstart', onPointerDown)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n document.removeEventListener('mousedown', onPointerDown)\n document.removeEventListener('touchstart', onPointerDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open, onDismiss, anchor])\n}\n\nexport const SessionCostPill = memo(function SessionCostPill({ useSession, useProjection, sessionId, t }: SessionCostPillProps) {\n const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)\n const costCny = useProjection('sessionCost')\n const costUsd = useProjection('sessionCostUsd')\n const cost = currency === 'USD' ? costUsd : costCny\n const pressure = useProjection('contextPressure')\n const running = useSession(runningOf)\n const partial = useSession(snapshot => snapshot.partial)\n const [open, setOpen] = useState(false)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n useDismissOutside(open, () => setOpen(false), wrapRef)\n\n useEffect(() => {\n let alive = true\n let timer: number | undefined\n const endpoint = `${ENDPOINT}?session=${encodeURIComponent(sessionId)}&currency=${currency}`\n const load = (): void => {\n void fetch(endpoint, { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* the pill keeps the last good pricebook */ })\n }\n load()\n timer = window.setInterval(load, REFRESH_MS)\n return () => {\n alive = false\n if (timer !== undefined) window.clearInterval(timer)\n }\n }, [sessionId, currency])\n\n const projection = cost\n const model = projection?.model ?? null\n const snapshot = response?.pricebook.current ?? null\n const subagents = response?.subagents\n const combined = combineTotals(projection?.totals, subagents)\n const subSpend = subagentSpend(subagents)\n const symbol = currencySymbol(currency)\n\n // The estimate projects the TOTAL once the running reply settles: the\n // already-anchored ledger total (main + subagents) plus this reply's\n // estimated input/output (subagent replies cannot be projected live).\n const estimate = useMemo(() => {\n if (!running || projection === undefined) return null\n const bucket = bucketAt(snapshot ?? undefined, model?.provider, model?.model ?? 'unknown', Date.now())\n if (bucket === undefined) return null\n const inputTokens = pressure?.projectedTokens ?? pressure?.pressureTokens ?? 0\n const cachedRatio = cacheReadRatioOf(projection.steps)\n const outputTokens = blocksOutputTokens(partial?.blocks)\n const stepEstimate = estimateCost(bucket, inputTokens, cachedRatio, outputTokens)\n return (combined?.cost ?? 0) + stepEstimate\n }, [running, projection, combined, snapshot, model, pressure, partial, currency])\n\n if (projection === undefined && estimate === null) return null\n\n const total = combined?.cost ?? 0\n const label = estimate !== null\n ? t('pill.estimate', { amount: `${symbol}${formatMoney(estimate)}` })\n : t('pill.label', { amount: `${symbol}${formatMoney(total)}` })\n\n const last = projection?.steps.length !== undefined && projection.steps.length > 0\n ? projection.steps[projection.steps.length - 1]\n : undefined\n\n const sourceKey = snapshot?.source ?? null\n const source = sourceKey === null ? t('source.none') : t(`source.${sourceKey}` as 'source.manual')\n\n const band = bandForTime(Date.now())\n const peakRatio = peakOffPeakMultiplier(snapshot ?? undefined, model?.provider, model?.model ?? 'unknown')\n const bandLabel = band === 'peak'\n ? peakRatio === null ? t('band.peak') : t('price.peakRatio', { multiplier: formatMultiplier(peakRatio) })\n : band === 'offPeak'\n ? peakRatio === null ? t('band.offPeak') : t('price.offPeakRatio', { multiplier: formatMultiplier(1 / peakRatio) })\n : null\n\n return (\n <div className={css.wrap} ref={wrapRef}>\n <button\n type=\"button\"\n className={css.pill}\n data-testid=\"cost-pill\"\n data-running={estimate !== null}\n data-band={band === 'single' ? undefined : band}\n onClick={() => setOpen(prev => !prev)}\n aria-expanded={open}\n >\n {label}\n {bandLabel !== null && <span className={css.bandBadge} data-testid=\"cost-pill-band\">{bandLabel}</span>}\n {estimate !== null && <span className={css.estimateNote}>· {t('pill.estimateNote')}</span>}\n </button>\n {open && (\n <div className={css.detail} data-testid=\"cost-pill-detail\">\n <div className={css.detailRow + ' ' + css.detailTotal}>{t('pill.detail.total', { amount: `${symbol}${formatMoney(total)}` })}</div>\n {bandLabel !== null && (\n <div className={css.detailRow + ' ' + css.detailRowSecondary} data-testid=\"cost-pill-detail-band\">{bandLabel}</div>\n )}\n {combined !== undefined && combined.pricedSteps > 0 && (\n <>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.uncached')}: {symbol}{formatMoney(combined.uncachedCost)}</div>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.cacheRead')}: {symbol}{formatMoney(combined.cacheReadCost)}</div>\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('view.output')}: {symbol}{formatMoney(combined.outputCost)}</div>\n </>\n )}\n {subSpend > 0 && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('line.subagent', { amount: `${symbol}${formatMoney(subSpend)}` })}</div>\n )}\n {model !== null && (\n <div className={css.detailRow + ' ' + css.detailRowSecondary}>{t('pill.detail.model', { model: model.model })}</div>\n )}\n {last !== undefined && last.snapshotVersion !== null && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>\n {t('pill.detail.snapshot', { time: formatTime(last.snapshotEffectiveAt ?? last.time) })}\n {last.band !== null && ` · ${t(`band.${last.band}` as 'band.single')}`}\n </div>\n )}\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('pill.detail.source', { source })}</div>\n {projection !== undefined && projection.totals.unpricedSteps > 0 && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('view.unpriced', { count: String(projection.totals.unpricedSteps) })}</div>\n )}\n {estimate !== null && (\n <div className={css.detailRow + ' ' + css.detailRowMuted}>{t('pill.estimateNote')}</div>\n )}\n </div>\n )}\n </div>\n )\n})\n","/**\n * Plugin configuration card (设置 → 插件 → cost-meter): the standard\n * DSH plugin-card shape (collapsible header, disclosure body, save/discard\n * footer) over the `cost-meter` settings namespace. Every editable\n * field binds the settings scope (`ctx.settingsScope.bind`) — the same\n * transport the shipped llm-deepseek / bash / web-search cards use — so the\n * card looks, stages, and persists like every other plugin configuration.\n * The JSON textareas carry the two structured fields (per-model price\n * overrides, OpenRouter aliases); the refresh button is a plain action.\n */\nimport { memo, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { Button, IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConversationCostResponse } from '../types.ts'\nimport { formatTime } from './format.ts'\nimport css from './CostPluginCard.module.css'\n\nexport type PluginCardLocale = PropsLocale<'cost-meter'>['t']\n\nexport interface CostPluginCardProps {\n /** The bound settings scope for the `cost-meter` namespace. */\n scope: SettingsScope<unknown>\n t: PluginCardLocale\n}\n\n/** One staged field group the card writes through the scope. */\ninterface Staged {\n overridesText: string\n aliasesText: string\n cacheReadDiscount: string\n fxMode: 'auto' | 'manual'\n manualRate: string\n balanceEnabled: boolean\n openRouterEnabled: boolean\n}\n\ninterface SectionShape {\n overrides?: Record<string, unknown>\n aliases?: Record<string, string>\n cacheReadDiscount?: number\n fxMode?: 'auto' | 'manual'\n manualRate?: number\n balanceEnabled?: boolean\n openRouterEnabled?: boolean\n}\n\nconst json = (value: unknown): string => JSON.stringify(value ?? {}, null, 2)\n\nexport const CostPluginCard = memo(function CostPluginCard({ scope, t }: CostPluginCardProps) {\n const snapshot = useSyncExternalStore(\n (listener) => scope.subscribe(listener),\n () => scope.getSnapshot(),\n ) as SettingsScopeSnapshot<SectionShape>\n\n const [open, setOpen] = useState(false)\n const [busy, setBusy] = useState(false)\n const [status, setStatus] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [staged, setStaged] = useState<Staged | null>(null)\n const [response, setResponse] = useState<ConversationCostResponse | null>(null)\n\n // Seed the staged form from the latest accepted section.\n const value = snapshot.value\n useEffect(() => {\n setStaged({\n overridesText: json(value?.overrides),\n aliasesText: json(value?.aliases),\n cacheReadDiscount: String(value?.cacheReadDiscount ?? 0.25),\n fxMode: value?.fxMode ?? 'auto',\n manualRate: String(value?.manualRate ?? 7.2),\n balanceEnabled: value?.balanceEnabled ?? true,\n openRouterEnabled: value?.openRouterEnabled ?? true,\n })\n }, [value])\n\n // Snapshot info for the header hint (source/time, no version numbers).\n useEffect(() => {\n let alive = true\n const load = (): void => {\n void fetch('/cost-meter', { cache: 'no-store' })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (!alive || data === null) return\n setResponse(data)\n })\n .catch(() => { /* keep the last snapshot info */ })\n }\n load()\n return () => {\n alive = false\n }\n }, [])\n\n const current = response?.pricebook.current ?? null\n const source = current === null\n ? t('source.none')\n : t(`source.${current.source}` as 'source.manual')\n\n const write = async (field: string, fieldValue: unknown): Promise<void> => {\n setBusy(true)\n setStatus(null)\n setError(null)\n try {\n await scope.set(field, fieldValue)\n setStatus(t('settings.saved'))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setBusy(false)\n }\n }\n\n const save = (): void => {\n if (staged === null) return\n setError(null)\n let overrides: Record<string, unknown>\n let aliases: Record<string, string>\n try {\n overrides = JSON.parse(staged.overridesText) as Record<string, unknown>\n aliases = JSON.parse(staged.aliasesText) as Record<string, string>\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n return\n }\n void write('overrides', overrides)\n void write('aliases', aliases)\n const discount = Number(staged.cacheReadDiscount)\n if (Number.isFinite(discount) && discount >= 0 && discount <= 1) void write('cacheReadDiscount', discount)\n const rate = Number(staged.manualRate)\n if (Number.isFinite(rate) && rate > 0) void write('manualRate', rate)\n if (staged.fxMode === 'auto' || staged.fxMode === 'manual') void write('fxMode', staged.fxMode)\n void write('balanceEnabled', staged.balanceEnabled)\n void write('openRouterEnabled', staged.openRouterEnabled)\n }\n\n const refresh = (): void => {\n setBusy(true)\n setStatus(null)\n setError(null)\n void fetch('/cost-meter', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action: 'refresh' }),\n })\n .then(res => (res.ok ? res.json() as Promise<ConversationCostResponse> : null))\n .then((data) => {\n if (data !== null) setResponse(data)\n setStatus(t('settings.saved'))\n })\n .catch((cause: unknown) => setError(cause instanceof Error ? cause.message : String(cause)))\n .finally(() => setBusy(false))\n }\n\n const hint = current === null\n ? t('settings.noSnapshot')\n : `${t('settings.current', { source, time: formatTime(current.effectiveAt) })}`\n\n return (\n <li className={css.card + (open ? ` ${css.cardOpen}` : '')} data-testid=\"cost-plugin-card\">\n <button type=\"button\" className={css.header} onClick={() => setOpen(prev => !prev)} aria-expanded={open}>\n <span className={css.headText}>\n <span className={css.name}>{t('settings.title')}</span>\n <span className={css.description}>{hint}</span>\n </span>\n {error !== null && <span className={css.pending}>{t('settings.error', { error })}</span>}\n <IconChevronDownOutline14 className={css.chevron + (open ? ` ${css.chevronOpen}` : '')} />\n </button>\n {open && (\n <div className={css.body}>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.discount')}</span>\n </div>\n <input\n className={css.input}\n type=\"number\"\n min={0}\n max={1}\n step={0.01}\n value={staged?.cacheReadDiscount ?? ''}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, cacheReadDiscount: event.target.value })}\n />\n </div>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.fx')}</span>\n </div>\n <div className={css.inline}>\n <select\n className={css.select}\n value={staged?.fxMode ?? 'auto'}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, fxMode: event.target.value as 'auto' | 'manual' })}\n >\n <option value=\"auto\">{t('settings.fx.auto')}</option>\n <option value=\"manual\">{t('settings.fx.manual')}</option>\n </select>\n <input\n className={css.input}\n type=\"number\"\n min={0.001}\n step={0.01}\n value={staged?.manualRate ?? ''}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, manualRate: event.target.value })}\n />\n </div>\n </div>\n <label className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.balance')}</span>\n <input\n className={css.checkbox}\n type=\"checkbox\"\n checked={staged?.balanceEnabled ?? true}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, balanceEnabled: event.target.checked })}\n />\n </div>\n </label>\n <label className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.openrouter')}</span>\n <input\n className={css.checkbox}\n type=\"checkbox\"\n checked={staged?.openRouterEnabled ?? true}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, openRouterEnabled: event.target.checked })}\n />\n </div>\n </label>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.overrides')}</span>\n </div>\n <textarea\n className={css.textarea}\n value={staged?.overridesText ?? ''}\n spellCheck={false}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, overridesText: event.target.value })}\n />\n <div className={css.hint}>{t('settings.jsonHint')}</div>\n </div>\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{t('settings.aliases')}</span>\n </div>\n <textarea\n className={css.textarea}\n value={staged?.aliasesText ?? ''}\n spellCheck={false}\n onChange={event => setStaged(prev => prev === null ? prev : { ...prev, aliasesText: event.target.value })}\n />\n </div>\n <div className={css.footer}>\n {status !== null && <span className={css.status}>{status}</span>}\n <span className={css.spacer} />\n <Button variant=\"outline\" onClick={() => setStaged(null)}>{t('settings.discard')}</Button>\n <Button variant=\"primary\" onClick={save} disabled={busy || snapshot.status !== 'ready'}>{t('settings.save')}</Button>\n <Button variant=\"outline\" onClick={refresh} disabled={busy}>{busy ? t('settings.refreshing') : t('settings.refresh')}</Button>\n </div>\n </div>\n )}\n </li>\n )\n})\n","/**\n * Cost-meter plugin, browser half: contributes the cost surfaces —\n * the composer-dock readout (anchored session spend + account balance), the\n * Cost view tab, the per-reply cost chip, the header pill with the live\n * streaming estimate, and the plugin configuration card (设置 → 插件). The\n * anchored ledger arrives through the `sessionCost` projection; the balance\n * and the pricebook arrive over the trust-fenced host `/cost-meter`\n * route; the editable configuration binds the `cost-meter` settings\n * namespace through the standard settings scope.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the ui-conversation SlotMap merge (composer.dock etc.).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the settings-domain SlotMap merges (settings.plugin.item)\n// and the settingsScope service.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: pulls the plugin-card slot merge (settings.plugin.item).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport { en, NS, zh, type ConversationCostKey } from './locales.ts'\nimport { SessionCostLine } from './SessionCostLine.tsx'\nimport { CostView } from './CostView.tsx'\nimport { AssistantCostChip } from './AssistantCostChip.tsx'\nimport { SessionCostPill } from './SessionCostPill.tsx'\nimport { CostPluginCard } from './CostPluginCard.tsx'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Cost meter and account balance copy. */\n 'cost-meter': ConversationCostKey\n }\n}\n\n/** The settings namespace this plugin owns (mirrors the host registration). */\nexport const SETTINGS_NAMESPACE = 'cost-meter'\n\n/** Required services for locale registration, slot contributions, and the settings scope. */\nexport const inject = ['sessions', 'slots', 'locale', 'settingsScope']\n\n/**\n * Client plugin body: register the dictionaries and the five slot entries.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-cost-meter: dictionaries')\n const t = ctx.locale.bind(NS)\n const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE })\n\n // Composer-dock readout: anchored session spend + balance (after the\n // shipped stats line, order 0).\n ctx.slots.inject(\n 'conversation.composer.dock',\n () => ctx.slots.register({\n name: 'conversation.composer.dock',\n id: 'cost-meter',\n order: 1,\n locale: NS,\n }, SessionCostLine),\n )\n\n // Cost view tab (after trajectory at order 10).\n ctx.slots.inject(\n 'conversation.view',\n () => ctx.slots.register({\n name: 'conversation.view',\n id: 'cost',\n order: 20,\n locale: NS,\n label: () => t('view.cost'),\n }, CostView),\n )\n\n // Per-reply cost chip (after the feedback action at order 10).\n ctx.slots.inject(\n 'conversation.chat.assistant-actions',\n () => ctx.slots.register({\n name: 'conversation.chat.assistant-actions',\n id: 'cost',\n order: 20,\n locale: NS,\n }, AssistantCostChip),\n )\n\n // Header pill: total / live estimate with the expandable detail.\n // Negative order keeps it left of the built-in Session log utility.\n ctx.slots.inject(\n 'conversation.session.header.utilities',\n () => ctx.slots.register({\n name: 'conversation.session.header.utilities',\n id: 'cost-meter',\n order: -10,\n locale: NS,\n }, SessionCostPill),\n )\n\n // Plugin configuration card (设置 → 插件), standard plugin-card shape over\n // the `cost-meter` settings namespace.\n ctx.slots.inject(\n 'settings.plugin.item',\n () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'cost-meter',\n order: 30,\n locale: NS,\n inject: () => ({ scope }),\n }, CostPluginCard),\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantCostChip.d.ts","sourceRoot":"","sources":["../../../src/client/AssistantCostChip.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,KAAK,EAA4B,eAAe,EAAE,MAAM,aAAa,CAAA;AAK5E,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,CAAC,EAAE,UAAU,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,oBAAoB,EAC9B,SAAS,EAAE,SAAS,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASvC;AAED,iFAAiF;AACjF,wBAAgB,MAAM,CACpB,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,EAC7C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,eAAe,GAAG,SAAS,CAE7B;AAED,eAAO,MAAM,iBAAiB,oFAAgF,sBAAsB,wCA8ClI,CAAA"}
1
+ {"version":3,"file":"AssistantCostChip.d.ts","sourceRoot":"","sources":["../../../src/client/AssistantCostChip.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,KAAK,EAA4B,eAAe,EAAE,MAAM,aAAa,CAAA;AAK5E,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,CAAC,EAAE,UAAU,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,oBAAoB,EAC9B,SAAS,EAAE,SAAS,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASvC;AAED,iFAAiF;AACjF,wBAAgB,MAAM,CACpB,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,EAC7C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,eAAe,GAAG,SAAS,CAE7B;AAED,eAAO,MAAM,iBAAiB,oFAAgF,sBAAsB,wCA+ClI,CAAA"}
@@ -37,9 +37,10 @@ export function stepOf(steps, turn, step) {
37
37
  }
38
38
  export const AssistantCostChip = memo(function AssistantCostChip({ messageId, useSession, useProjection, t }) {
39
39
  const currency = displayCurrency(t);
40
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost';
41
40
  const step = useSession(snapshot => stepOfMessage(snapshot, messageId));
42
- const cost = useProjection(projectionKey);
41
+ const costCny = useProjection('sessionCost');
42
+ const costUsd = useProjection('sessionCostUsd');
43
+ const cost = currency === 'USD' ? costUsd : costCny;
43
44
  const ledger = step === null ? undefined : stepOf(cost?.steps, step.turn, step.step);
44
45
  const [response, setResponse] = useState(null);
45
46
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"CostView.d.ts","sourceRoot":"","sources":["../../../src/client/CostView.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAMnE,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,cAAc,CAAA;CAClB;AAOD,eAAO,MAAM,QAAQ,wEAA2D,aAAa,iCAkH3F,CAAA"}
1
+ {"version":3,"file":"CostView.d.ts","sourceRoot":"","sources":["../../../src/client/CostView.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAMnE,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,cAAc,CAAA;CAClB;AAOD,eAAO,MAAM,QAAQ,wEAA2D,aAAa,iCAmH3F,CAAA"}
@@ -18,8 +18,9 @@ function reasonText(step, t) {
18
18
  }
19
19
  export const CostView = memo(function CostView({ useProjection, sessionId, t }) {
20
20
  const currency = displayCurrency(t);
21
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost';
22
- const cost = useProjection(projectionKey);
21
+ const costCny = useProjection('sessionCost');
22
+ const costUsd = useProjection('sessionCostUsd');
23
+ const cost = currency === 'USD' ? costUsd : costCny;
23
24
  const [response, setResponse] = useState(null);
24
25
  const symbol = currencySymbol(currency);
25
26
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"SessionCostLine.d.ts","sourceRoot":"","sources":["../../../src/client/SessionCostLine.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAA6C,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAKnG,sEAAsE;AACtE,eAAO,MAAM,QAAQ,gBAAgB,CAAA;AACrC,gDAAgD;AAChD,eAAO,MAAM,UAAU,QAAS,CAAA;AAEhC,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,UAAU,CAAA;CACd;AAaD,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,qBAAqB,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,GAAE,KAAK,GAAG,KAAa,GAAG,MAAM,EAAE,CA2BvJ;AAED,eAAO,MAAM,eAAe,wEAAkE,oBAAoB,wCA0GhH,CAAA"}
1
+ {"version":3,"file":"SessionCostLine.d.ts","sourceRoot":"","sources":["../../../src/client/SessionCostLine.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAA6C,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAKnG,sEAAsE;AACtE,eAAO,MAAM,QAAQ,gBAAgB,CAAA;AACrC,gDAAgD;AAChD,eAAO,MAAM,UAAU,QAAS,CAAA;AAEhC,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,UAAU,CAAA;CACd;AAaD,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,qBAAqB,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,GAAE,KAAK,GAAG,KAAa,GAAG,MAAM,EAAE,CA2BvJ;AAED,eAAO,MAAM,eAAe,wEAAkE,oBAAoB,wCA2GhH,CAAA"}
@@ -65,8 +65,9 @@ export function costDetail(cost, model, t, currency = 'CNY') {
65
65
  }
66
66
  export const SessionCostLine = memo(function SessionCostLine({ useProjection, sessionId, t }) {
67
67
  const currency = displayCurrency(t);
68
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost';
69
- const cost = useProjection(projectionKey);
68
+ const costCny = useProjection('sessionCost');
69
+ const costUsd = useProjection('sessionCostUsd');
70
+ const cost = currency === 'USD' ? costUsd : costCny;
70
71
  const [response, setResponse] = useState(null);
71
72
  const [failed, setFailed] = useState(false);
72
73
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"SessionCostPill.d.ts","sourceRoot":"","sources":["../../../src/client/SessionCostPill.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AASnE,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,UAAU,CAAA;CACd;AA6BD,eAAO,MAAM,eAAe,oFAA8E,oBAAoB,wCAgI5H,CAAA"}
1
+ {"version":3,"file":"SessionCostPill.d.ts","sourceRoot":"","sources":["../../../src/client/SessionCostPill.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AASnE,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IACtD,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,CAAC,EAAE,UAAU,CAAA;CACd;AA6BD,eAAO,MAAM,eAAe,oFAA8E,oBAAoB,wCAiI5H,CAAA"}
@@ -48,8 +48,9 @@ function useDismissOutside(open, onDismiss, anchor) {
48
48
  }
49
49
  export const SessionCostPill = memo(function SessionCostPill({ useSession, useProjection, sessionId, t }) {
50
50
  const currency = displayCurrency(t);
51
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost';
52
- const cost = useProjection(projectionKey);
51
+ const costCny = useProjection('sessionCost');
52
+ const costUsd = useProjection('sessionCostUsd');
53
+ const cost = currency === 'USD' ? costUsd : costCny;
53
54
  const pressure = useProjection('contextPressure');
54
55
  const running = useSession(runningOf);
55
56
  const partial = useSession(snapshot => snapshot.partial);
@@ -123,7 +123,7 @@ export declare function pickBalanceInfo(wire: WireBalanceResponse): BalanceInfo
123
123
  */
124
124
  export declare function fetchBalance(ctx: Context, config: ResolvedConfig): Promise<BalanceSnapshot>;
125
125
  /** The `cost-meter` settings namespace (plugin configuration page). */
126
- export declare const SETTINGS_NAMESPACE: Branded<"SettingsNamespace">;
126
+ export declare const SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
127
127
  /**
128
128
  * Mount the plugin: open the pricebook domain, refresh every remote source,
129
129
  * register the `sessionCost` projection, register the /cost-meter
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gamegeek-saikel/dsh-cost-meter",
3
3
  "description": "Cost tracking plugin for the DeepSeek Harness Web GUI — snapshot-anchored per-turn pricing, account balance, and live cost estimates.",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.7.0",
7
7
  "engines": {
@@ -62,9 +62,10 @@ export function stepOf(
62
62
 
63
63
  export const AssistantCostChip = memo(function AssistantCostChip({ messageId, useSession, useProjection, t }: AssistantCostChipProps) {
64
64
  const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)
65
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'
66
65
  const step = useSession(snapshot => stepOfMessage(snapshot, messageId))
67
- const cost = useProjection(projectionKey)
66
+ const costCny = useProjection('sessionCost')
67
+ const costUsd = useProjection('sessionCostUsd')
68
+ const cost = currency === 'USD' ? costUsd : costCny
68
69
  const ledger = step === null ? undefined : stepOf(cost?.steps, step.turn, step.step)
69
70
  const [response, setResponse] = useState<ConversationCostResponse | null>(null)
70
71
 
@@ -30,8 +30,9 @@ function reasonText(step: SessionCostStep, t: CostViewLocale): string {
30
30
 
31
31
  export const CostView = memo(function CostView({ useProjection, sessionId, t }: CostViewProps) {
32
32
  const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)
33
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'
34
- const cost = useProjection(projectionKey)
33
+ const costCny = useProjection('sessionCost')
34
+ const costUsd = useProjection('sessionCostUsd')
35
+ const cost = currency === 'USD' ? costUsd : costCny
35
36
  const [response, setResponse] = useState<ConversationCostResponse | null>(null)
36
37
  const symbol = currencySymbol(currency)
37
38
 
@@ -79,8 +79,9 @@ export function costDetail(cost: SessionCostProjection | undefined, model: strin
79
79
 
80
80
  export const SessionCostLine = memo(function SessionCostLine({ useProjection, sessionId, t }: SessionCostLineProps) {
81
81
  const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)
82
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'
83
- const cost = useProjection(projectionKey)
82
+ const costCny = useProjection('sessionCost')
83
+ const costUsd = useProjection('sessionCostUsd')
84
+ const cost = currency === 'USD' ? costUsd : costCny
84
85
  const [response, setResponse] = useState<ConversationCostResponse | null>(null)
85
86
  const [failed, setFailed] = useState(false)
86
87
  useEffect(() => {
@@ -63,8 +63,9 @@ function useDismissOutside(open: boolean, onDismiss: () => void, anchor: RefObje
63
63
 
64
64
  export const SessionCostPill = memo(function SessionCostPill({ useSession, useProjection, sessionId, t }: SessionCostPillProps) {
65
65
  const currency = displayCurrency(t as (key: string, params?: Record<string, string>) => string)
66
- const projectionKey = currency === 'USD' ? 'sessionCostUsd' : 'sessionCost'
67
- const cost = useProjection(projectionKey)
66
+ const costCny = useProjection('sessionCost')
67
+ const costUsd = useProjection('sessionCostUsd')
68
+ const cost = currency === 'USD' ? costUsd : costCny
68
69
  const pressure = useProjection('contextPressure')
69
70
  const running = useSession(runningOf)
70
71
  const partial = useSession(snapshot => snapshot.partial)
@@ -1,4 +1,4 @@
1
- declare module '*.module.css' {
2
- const classes: Record<string, string>
3
- export default classes
4
- }
1
+ declare module '*.module.css' {
2
+ const classes: Record<string, string>
3
+ export default classes
4
+ }
package/src/invariant.ts CHANGED
@@ -1,47 +1,47 @@
1
- /**
2
- * Package-owned invariant companion for `@gamegeek-saikel/dsh-cost-meter`.
3
- * @module @gamegeek-saikel/dsh-cost-meter/invariant
4
- */
5
-
6
- /* jscpd:ignore-start */
7
- import type { Context } from '@deepseek-ai/cordis'
8
- import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
9
-
10
- const PACKAGE_NAME = '@gamegeek-saikel/dsh-cost-meter'
11
-
12
- /** Cordis companion plugin name. */
13
- export const name = 'cost-meter-invariant'
14
- /** Service required before the companion can register. */
15
- export const inject = ['invariants']
16
-
17
- /**
18
- * Route-disposer symmetry: after the owning fiber of the /cost-meter
19
- * route unloads, the webserver must no longer answer that path. The probe
20
- * cycle registers and disposes twice on a reserved path — a leftover from
21
- * the first cycle makes the second register throw the duplicate error,
22
- * exactly the asymmetry this package would otherwise share with its host.
23
- */
24
- const install: InvariantInstaller = (ctx, fail) => {
25
- ctx.on('internal/plugin', () => {
26
- const server = ctx.get('webServer') as
27
- | { register(route: { kind: 'exact'; path: string; handler: () => void }): () => void }
28
- | undefined
29
- if (server === undefined) return // no webserver row in this composition
30
- const probe = { kind: 'exact' as const, path: '/__dsh_fare_meter_invariant_probe__', handler: () => {} }
31
- try {
32
- server.register(probe)()
33
- server.register(probe)()
34
- } catch {
35
- fail('cost-meter webserver route disposer left a route registered — route tables and fiber lifecycles diverged')
36
- }
37
- }, { global: true })
38
- }
39
-
40
- /**
41
- * Register this package's invariant companion.
42
- * @param ctx - Cordis context carrying the invariant service.
43
- * @returns the installed registration's disposer after setup succeeds.
44
- */
45
- export const apply = (ctx: Context): Promise<() => void> =>
46
- Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
1
+ /**
2
+ * Package-owned invariant companion for `@gamegeek-saikel/dsh-cost-meter`.
3
+ * @module @gamegeek-saikel/dsh-cost-meter/invariant
4
+ */
5
+
6
+ /* jscpd:ignore-start */
7
+ import type { Context } from '@deepseek-ai/cordis'
8
+ import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
9
+
10
+ const PACKAGE_NAME = '@gamegeek-saikel/dsh-cost-meter'
11
+
12
+ /** Cordis companion plugin name. */
13
+ export const name = 'cost-meter-invariant'
14
+ /** Service required before the companion can register. */
15
+ export const inject = ['invariants']
16
+
17
+ /**
18
+ * Route-disposer symmetry: after the owning fiber of the /cost-meter
19
+ * route unloads, the webserver must no longer answer that path. The probe
20
+ * cycle registers and disposes twice on a reserved path — a leftover from
21
+ * the first cycle makes the second register throw the duplicate error,
22
+ * exactly the asymmetry this package would otherwise share with its host.
23
+ */
24
+ const install: InvariantInstaller = (ctx, fail) => {
25
+ ctx.on('internal/plugin', () => {
26
+ const server = ctx.get('webServer') as
27
+ | { register(route: { kind: 'exact'; path: string; handler: () => void }): () => void }
28
+ | undefined
29
+ if (server === undefined) return // no webserver row in this composition
30
+ const probe = { kind: 'exact' as const, path: '/__dsh_fare_meter_invariant_probe__', handler: () => {} }
31
+ try {
32
+ server.register(probe)()
33
+ server.register(probe)()
34
+ } catch {
35
+ fail('cost-meter webserver route disposer left a route registered — route tables and fiber lifecycles diverged')
36
+ }
37
+ }, { global: true })
38
+ }
39
+
40
+ /**
41
+ * Register this package's invariant companion.
42
+ * @param ctx - Cordis context carrying the invariant service.
43
+ * @returns the installed registration's disposer after setup succeeds.
44
+ */
45
+ export const apply = (ctx: Context): Promise<() => void> =>
46
+ Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
47
47
  /* jscpd:ignore-end */