opencode-go-usage-tui 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README_EN.md +1 -1
- package/dist/tui.js +43 -11
- package/package.json +1 -1
- package/src/index.tsx +51 -11
package/README.md
CHANGED
package/README_EN.md
CHANGED
|
@@ -122,7 +122,7 @@ The cookie is valid for about 1 year; get a new one after it expires.
|
|
|
122
122
|
- **`/go-lang`**: switch between 中文 / English
|
|
123
123
|
- Usage above the threshold (default 80%, adjustable via `OPENCODE_GO_WARN_THRESHOLD`) is shown in red
|
|
124
124
|
- Click the panel title to collapse/expand
|
|
125
|
-
- The panel title shows the current plugin version (e.g. `v1.3.
|
|
125
|
+
- The panel title shows the current plugin version (e.g. `v1.3.3`)
|
|
126
126
|
|
|
127
127
|
## Model request limits panel
|
|
128
128
|
|
package/dist/tui.js
CHANGED
|
@@ -488,7 +488,7 @@ function updatePricingStore(newData) {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// src/index.tsx
|
|
491
|
-
var VERSION = "1.3.
|
|
491
|
+
var VERSION = "1.3.3";
|
|
492
492
|
var CHECK_INTERVAL = Number(process?.env?.OPENCODE_GO_CHECK_INTERVAL ?? 6e4);
|
|
493
493
|
var WARN_THRESHOLD = Number(process?.env?.OPENCODE_GO_WARN_THRESHOLD ?? 0.8);
|
|
494
494
|
var CONFIG_DIR = process?.env?.OPENCODE_CONFIG_DIR || (process?.env?.USERPROFILE ? `${process.env.USERPROFILE}\\.config\\opencode` : "") || process?.env?.HOME + "/.config/opencode";
|
|
@@ -1105,9 +1105,30 @@ function PricePanel(props) {
|
|
|
1105
1105
|
const [priceError, setPriceError] = createSignal("");
|
|
1106
1106
|
const [priceLoading, setPriceLoading] = createSignal(false);
|
|
1107
1107
|
const [panelWidth, setPanelWidth] = createSignal(24);
|
|
1108
|
+
const [renderTick, setRenderTick] = createSignal(0);
|
|
1109
|
+
const [listReady, setListReady] = createSignal(false);
|
|
1110
|
+
createEffect(() => {
|
|
1111
|
+
const p = sharedPricing();
|
|
1112
|
+
if (p && p.models.length > 0) {
|
|
1113
|
+
const id = setTimeout(() => setListReady(true), 150);
|
|
1114
|
+
onCleanup(() => clearTimeout(id));
|
|
1115
|
+
} else {
|
|
1116
|
+
setListReady(false);
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
createEffect(() => {
|
|
1120
|
+
if (!(listReady() && sharedPricing())) return;
|
|
1121
|
+
const t1 = setTimeout(() => setRenderTick((v) => v + 1), 250);
|
|
1122
|
+
const t2 = setTimeout(() => setRenderTick((v) => v + 1), 600);
|
|
1123
|
+
const t3 = setTimeout(() => setRenderTick((v) => v + 1), 1e3);
|
|
1124
|
+
onCleanup(() => {
|
|
1125
|
+
clearTimeout(t1);
|
|
1126
|
+
clearTimeout(t2);
|
|
1127
|
+
clearTimeout(t3);
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1108
1130
|
const [changedModels, setChangedModels] = createSignal(/* @__PURE__ */ new Map());
|
|
1109
1131
|
const [expandedId, setExpandedId] = createSignal(null);
|
|
1110
|
-
const [mounted, setMounted] = createSignal(false);
|
|
1111
1132
|
const [cfg, setCfg] = createSignal(loadConfig());
|
|
1112
1133
|
createEffect(() => {
|
|
1113
1134
|
void configTick();
|
|
@@ -1115,14 +1136,23 @@ function PricePanel(props) {
|
|
|
1115
1136
|
});
|
|
1116
1137
|
let boxEl;
|
|
1117
1138
|
let priceController = null;
|
|
1139
|
+
let widthSyncTimer = null;
|
|
1118
1140
|
onMount(() => {
|
|
1119
|
-
|
|
1141
|
+
let tries = 0;
|
|
1142
|
+
const syncWidth = () => {
|
|
1143
|
+
tries++;
|
|
1144
|
+
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
1145
|
+
setPanelWidth(Math.max(20, boxEl.width));
|
|
1146
|
+
setRenderTick((v) => v + 1);
|
|
1147
|
+
widthSyncTimer = setTimeout(() => setRenderTick((v) => v + 1), 150);
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
if (tries < 50) widthSyncTimer = setTimeout(syncWidth, 80);
|
|
1151
|
+
};
|
|
1152
|
+
widthSyncTimer = setTimeout(syncWidth, 30);
|
|
1120
1153
|
});
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
1124
|
-
setPanelWidth(Math.max(20, boxEl.width));
|
|
1125
|
-
}
|
|
1154
|
+
onCleanup(() => {
|
|
1155
|
+
if (widthSyncTimer) clearTimeout(widthSyncTimer);
|
|
1126
1156
|
});
|
|
1127
1157
|
async function refreshPricing() {
|
|
1128
1158
|
if (priceLoading()) return;
|
|
@@ -1188,6 +1218,7 @@ function PricePanel(props) {
|
|
|
1188
1218
|
return out + "\u2026";
|
|
1189
1219
|
}
|
|
1190
1220
|
function justify(label, value) {
|
|
1221
|
+
void renderTick();
|
|
1191
1222
|
const outer = boxEl && typeof boxEl.width === "number" && boxEl.width > 0 ? boxEl.width : panelWidth();
|
|
1192
1223
|
const gauge = Math.max(10, outer - 4);
|
|
1193
1224
|
const used = visualWidth(label) + visualWidth(value);
|
|
@@ -1204,7 +1235,6 @@ function PricePanel(props) {
|
|
|
1204
1235
|
const row = (() => {
|
|
1205
1236
|
var _el$30 = _$createElement("text");
|
|
1206
1237
|
_$setProp(_el$30, "fg", fg);
|
|
1207
|
-
_$setProp(_el$30, "selectable", false);
|
|
1208
1238
|
_$setProp(_el$30, "onMouseUp", (e) => {
|
|
1209
1239
|
e.preventDefault();
|
|
1210
1240
|
setExpandedId(expanded ? null : m.id);
|
|
@@ -1298,6 +1328,7 @@ function PricePanel(props) {
|
|
|
1298
1328
|
});
|
|
1299
1329
|
const colors = () => pal();
|
|
1300
1330
|
const sep = () => {
|
|
1331
|
+
void renderTick();
|
|
1301
1332
|
const outer = boxEl && typeof boxEl.width === "number" && boxEl.width > 0 ? boxEl.width : panelWidth();
|
|
1302
1333
|
return "\u2500".repeat(Math.max(1, outer - 4));
|
|
1303
1334
|
};
|
|
@@ -1325,6 +1356,7 @@ function PricePanel(props) {
|
|
|
1325
1356
|
resizeTimeout = setTimeout(() => {
|
|
1326
1357
|
const w = boxEl ? Math.max(20, boxEl.width ?? 24) : 24;
|
|
1327
1358
|
setPanelWidth(w);
|
|
1359
|
+
setRenderTick((v) => v + 1);
|
|
1328
1360
|
}, 100);
|
|
1329
1361
|
});
|
|
1330
1362
|
_$insertNode(_el$37, _el$38);
|
|
@@ -1383,9 +1415,9 @@ function PricePanel(props) {
|
|
|
1383
1415
|
})();
|
|
1384
1416
|
},
|
|
1385
1417
|
get children() {
|
|
1386
|
-
return [_$memo(() => sharedPricing().models.filter((m) => {
|
|
1418
|
+
return [_$memo(() => _$memo(() => !!listReady())() ? sharedPricing().models.filter((m) => {
|
|
1387
1419
|
return cfg().focusModels.length === 0 || cfg().focusModels.includes(m.id);
|
|
1388
|
-
}).map((m) => renderLimitRow(m))), (() => {
|
|
1420
|
+
}).map((m) => renderLimitRow(m)) : null), (() => {
|
|
1389
1421
|
var _el$48 = _$createElement("text");
|
|
1390
1422
|
_$insert(_el$48, () => justify(t("priceUpdated"), new Date(sharedPricing().fetchTime).toLocaleTimeString()));
|
|
1391
1423
|
_$effect((_$p) => _$setProp(_el$48, "fg", colors().muted, _$p));
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -445,25 +445,62 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
445
445
|
const [priceError, setPriceError] = createSignal("")
|
|
446
446
|
const [priceLoading, setPriceLoading] = createSignal(false)
|
|
447
447
|
const [panelWidth, setPanelWidth] = createSignal(24)
|
|
448
|
+
// 渲染强制刷新计数:列表首帧常在数据到达使面板变高的瞬时布局中测量,留下
|
|
449
|
+
// "模型间空行";该残留不会随盒子变宽自愈,但会在一次额外重渲染(重新测量文本
|
|
450
|
+
// 高度)后消失。故列表就绪后级联触发若干次 renderTick,强制重新测量每行高度。
|
|
451
|
+
const [renderTick, setRenderTick] = createSignal(0)
|
|
452
|
+
// 列表就绪标记:sharedPricing 数据到达那帧盒子常在重布局(数据使面板内容变高),
|
|
453
|
+
// 直接渲染列表会命中"未稳定测量"。故数据到达后延迟一帧再创建列表文本
|
|
454
|
+
const [listReady, setListReady] = createSignal(false)
|
|
455
|
+
createEffect(() => {
|
|
456
|
+
const p = sharedPricing()
|
|
457
|
+
if (p && p.models.length > 0) {
|
|
458
|
+
const id = setTimeout(() => setListReady(true), 150)
|
|
459
|
+
onCleanup(() => clearTimeout(id))
|
|
460
|
+
} else {
|
|
461
|
+
setListReady(false)
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
// 列表首帧常在"数据到达使面板变高"的瞬时布局中测量,留下"模型间空行";
|
|
465
|
+
// 该残留不会随盒子变宽自愈,但会在一次额外重渲染(重新测量文本高度)后消失
|
|
466
|
+
// —— 即用户观察到的"展开任意行后空行消失"现象。故在列表就绪后再强制若干次
|
|
467
|
+
// 重渲染,让 @opentui 在稳定布局下重新测量每行高度,自愈空行。
|
|
468
|
+
createEffect(() => {
|
|
469
|
+
if (!(listReady() && sharedPricing())) return
|
|
470
|
+
const t1 = setTimeout(() => setRenderTick((v) => v + 1), 250)
|
|
471
|
+
const t2 = setTimeout(() => setRenderTick((v) => v + 1), 600)
|
|
472
|
+
const t3 = setTimeout(() => setRenderTick((v) => v + 1), 1000)
|
|
473
|
+
onCleanup(() => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3) })
|
|
474
|
+
})
|
|
448
475
|
// 记录变化的模型:modelId -> 变化类别集合(added/pricing/limits),用于行高亮与颜色区分
|
|
449
476
|
const [changedModels, setChangedModels] = createSignal<Map<string, Set<string>>>(new Map<string, Set<string>>())
|
|
450
477
|
// 行内展开:当前展开的模型 id(单选 accordion)
|
|
451
478
|
const [expandedId, setExpandedId] = createSignal<string | null>(null)
|
|
452
|
-
// 挂载后延迟校正:首帧 boxEl.width 尚未测量,布局完成后再触发一次重渲染以拿到真实宽度
|
|
453
|
-
const [mounted, setMounted] = createSignal(false)
|
|
454
479
|
const [cfg, setCfg] = createSignal<LoadedConfig>(loadConfig())
|
|
455
480
|
createEffect(() => { void configTick(); setCfg(loadConfig()) })
|
|
456
481
|
let boxEl: any
|
|
457
482
|
let priceController: AbortController | null = null
|
|
458
483
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
484
|
+
// 轮询等待布局就绪:首帧 boxEl.width 尚未测量(为 0)时文本会按临时窄宽度折行,
|
|
485
|
+
// 产生"模型间空行"。单次延时可能早于布局完成而浪费唯一机会,故循环重试,
|
|
486
|
+
// 宽度就绪那一刻强制重渲染自愈(上限 50 次 × 80ms 防泄漏)
|
|
487
|
+
let widthSyncTimer: ReturnType<typeof setTimeout> | null = null
|
|
488
|
+
onMount(() => {
|
|
489
|
+
let tries = 0
|
|
490
|
+
const syncWidth = () => {
|
|
491
|
+
tries++
|
|
492
|
+
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
493
|
+
setPanelWidth(Math.max(20, boxEl.width))
|
|
494
|
+
setRenderTick((v) => v + 1)
|
|
495
|
+
// 补一次延迟 tick:覆盖 onSizeChange 防抖窗口结束后的稳定态
|
|
496
|
+
widthSyncTimer = setTimeout(() => setRenderTick((v) => v + 1), 150)
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
if (tries < 50) widthSyncTimer = setTimeout(syncWidth, 80)
|
|
465
500
|
}
|
|
501
|
+
widthSyncTimer = setTimeout(syncWidth, 30)
|
|
466
502
|
})
|
|
503
|
+
onCleanup(() => { if (widthSyncTimer) clearTimeout(widthSyncTimer) })
|
|
467
504
|
|
|
468
505
|
async function refreshPricing() {
|
|
469
506
|
if (priceLoading()) return
|
|
@@ -534,6 +571,7 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
534
571
|
// visual-cache 风格:左标签 + 自动填充 + 右值
|
|
535
572
|
// 直接读取 boxEl 实时宽度(避免 panelWidth 信号滞后)
|
|
536
573
|
function justify(label: string, value: string): string {
|
|
574
|
+
void renderTick()
|
|
537
575
|
const outer = boxEl && typeof boxEl.width === "number" && boxEl.width > 0 ? boxEl.width : panelWidth()
|
|
538
576
|
const gauge = Math.max(10, outer - 4)
|
|
539
577
|
const used = visualWidth(label) + visualWidth(value)
|
|
@@ -555,7 +593,7 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
555
593
|
// 行尾展开指示符(右对齐,不挤占名称列)
|
|
556
594
|
const ind = expanded ? " \u25be" : " \u25b8"
|
|
557
595
|
const row = (
|
|
558
|
-
<text fg={fg}
|
|
596
|
+
<text fg={fg} onMouseUp={(e) => { e.preventDefault(); setExpandedId(expanded ? null : m.id) }}>
|
|
559
597
|
{justify(name, req + ind)}
|
|
560
598
|
</text>
|
|
561
599
|
)
|
|
@@ -643,6 +681,7 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
643
681
|
const colors = () => pal()
|
|
644
682
|
|
|
645
683
|
const sep = () => {
|
|
684
|
+
void renderTick()
|
|
646
685
|
const outer = boxEl && typeof boxEl.width === "number" && boxEl.width > 0 ? boxEl.width : panelWidth()
|
|
647
686
|
return "\u2500".repeat(Math.max(1, outer - 4))
|
|
648
687
|
}
|
|
@@ -671,6 +710,7 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
671
710
|
resizeTimeout = setTimeout(() => {
|
|
672
711
|
const w = boxEl ? Math.max(20, boxEl.width ?? 24) : 24
|
|
673
712
|
setPanelWidth(w)
|
|
713
|
+
setRenderTick((v) => v + 1)
|
|
674
714
|
}, 100)
|
|
675
715
|
}}
|
|
676
716
|
>
|
|
@@ -693,9 +733,9 @@ function PricePanel(props: { theme: TuiThemeCurrent; api: TuiPluginApi }): JSX.E
|
|
|
693
733
|
<Show when={sharedPricing()} fallback={
|
|
694
734
|
<text fg={colors().muted}>{t("priceLoading")}</text>
|
|
695
735
|
}>
|
|
696
|
-
{sharedPricing()!.models.filter((m) => {
|
|
736
|
+
{listReady() ? sharedPricing()!.models.filter((m) => {
|
|
697
737
|
return cfg().focusModels.length === 0 || cfg().focusModels.includes(m.id)
|
|
698
|
-
}).map((m) => renderLimitRow(m))}
|
|
738
|
+
}).map((m) => renderLimitRow(m)) : null}
|
|
699
739
|
<text fg={colors().muted}>
|
|
700
740
|
{justify(t("priceUpdated"), new Date(sharedPricing()!.fetchTime).toLocaleTimeString())}
|
|
701
741
|
</text>
|