Skip to content

Commit a780281

Browse files
authored
Merge pull request lidge-jun#739 from lidge-jun/stack/gui-codex-pool-hydration
fix(gui): gate Codex pool writes until hydrated and ignore stale strategy polls
2 parents 9d94c92 + 297ceef commit a780281

30 files changed

Lines changed: 1516 additions & 407 deletions

gui/src/clamp-draft.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** Clamp a numeric draft string into [min, max] after applying delta (for NumberStepper). */
2+
export function clampNumberDraft(
3+
raw: string,
4+
delta: number,
5+
min: number,
6+
max: number,
7+
step = 1,
8+
): string {
9+
const trimmed = raw.trim();
10+
const parsed = trimmed === "" ? NaN : Number(trimmed);
11+
const base = Number.isFinite(parsed) ? parsed : min;
12+
const next = Math.min(max, Math.max(min, base + delta));
13+
// Keep one decimal for GiB-style steps; integers otherwise.
14+
return step < 1 ? String(Math.round(next * 10) / 10) : String(Math.round(next));
15+
}

gui/src/codex-auto-switch.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ export function autoSwitchThresholdReadDisposition(
3939
return editing || saving ? "defer" : "apply";
4040
}
4141

42+
/** Accept bare threshold numbers or a full /active payload. */
43+
export function extractAutoSwitchThresholdPayload(value: unknown): unknown {
44+
if (value && typeof value === "object" && value !== null && "autoSwitchThreshold" in value) {
45+
return (value as { autoSwitchThreshold: unknown }).autoSwitchThreshold;
46+
}
47+
return value;
48+
}
49+
4250
export interface AutoSwitchTogglePlan {
4351
threshold: number;
4452
lastEnabled: number;

gui/src/components/AccountPoolStrategyControls.tsx

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import {
33
ACCOUNT_POOL_STRATEGIES,
44
type AccountPoolStrategy,
55
} from "../account-pool-strategy";
6+
import { clampNumberDraft } from "../clamp-draft";
7+
import { NumberStepper } from "./NumberStepper";
8+
import { Select } from "../ui";
69

710
const STRATEGY_LABEL_KEYS = {
811
quota: "accountPool.strategyQuota",
@@ -24,7 +27,8 @@ export interface AccountPoolStrategyControlsProps {
2427
strategyLabelHidden?: boolean;
2528
onStrategyChange(strategy: AccountPoolStrategy): void;
2629
onStickyDraftChange(value: string): void;
27-
onStickyCommit(): void;
30+
/** Optional draft overrides React state when steppers commit in the same tick as a draft change. */
31+
onStickyCommit(nextDraft?: string): void;
2832
}
2933

3034
/**
@@ -42,57 +46,73 @@ export default function AccountPoolStrategyControls({
4246
onStickyCommit,
4347
}: AccountPoolStrategyControlsProps) {
4448
const t = useT();
49+
const strategyOptions = ACCOUNT_POOL_STRATEGIES.map((value) => ({
50+
value,
51+
label: t(STRATEGY_LABEL_KEYS[value]),
52+
}));
53+
4554
return (
46-
<div style={{ marginTop: 12 }}>
47-
<label className="field" style={{ display: "block" }} htmlFor={strategySelectId}>
48-
<span className={strategyLabelHidden ? "sr-only" : "field-label"}>
55+
<div className="account-pool-strategy-controls">
56+
<div className="field">
57+
<span
58+
className={strategyLabelHidden ? "sr-only" : "field-label"}
59+
id={`${strategySelectId}-label`}
60+
>
4961
{t("accountPool.strategy")}
5062
</span>
51-
<select
63+
<Select
5264
id={strategySelectId}
53-
className="input"
5465
value={strategy}
66+
options={strategyOptions}
5567
disabled={disabled}
56-
aria-label={t("accountPool.strategy")}
57-
onChange={(event) => {
58-
onStrategyChange(event.target.value as AccountPoolStrategy);
59-
}}
60-
>
61-
{ACCOUNT_POOL_STRATEGIES.map((value) => (
62-
<option key={value} value={value}>
63-
{t(STRATEGY_LABEL_KEYS[value])}
64-
</option>
65-
))}
66-
</select>
67-
</label>
68-
<div className="card-sub" style={{ marginTop: 4 }}>
69-
{t("accountPool.strategyHint")}
68+
label={t("accountPool.strategy")}
69+
style={{ width: "100%", display: "block" }}
70+
onChange={(next) => onStrategyChange(next as AccountPoolStrategy)}
71+
/>
7072
</div>
73+
<div className="card-sub">{t("accountPool.strategyHint")}</div>
7174
{strategy === "round-robin" && (
72-
<label className="field" style={{ display: "block", marginTop: 12 }} htmlFor={stickyInputId}>
75+
<label className="field" htmlFor={stickyInputId}>
7376
<span className="field-label">{t("accountPool.stickyLimit")}</span>
74-
<input
75-
id={stickyInputId}
76-
className="input mono"
77-
type="number"
78-
min={1}
79-
max={100}
80-
step={1}
81-
inputMode="numeric"
82-
value={stickyDraft}
83-
disabled={disabled}
84-
aria-label={t("accountPool.stickyLimitAria")}
85-
onChange={(event) => onStickyDraftChange(event.target.value)}
86-
onBlur={() => onStickyCommit()}
87-
onKeyDown={(event) => {
88-
if (event.nativeEvent.isComposing || disabled) return;
89-
if (event.key === "Enter") {
90-
event.preventDefault();
91-
onStickyCommit();
92-
}
93-
}}
94-
/>
95-
<div className="card-sub" style={{ marginTop: 4 }}>{t("accountPool.stickyLimitHelp")}</div>
77+
<span className="codex-auto-switch-input-wrap">
78+
<input
79+
id={stickyInputId}
80+
className="input mono codex-auto-switch-input"
81+
type="number"
82+
min={1}
83+
max={100}
84+
step={1}
85+
inputMode="numeric"
86+
value={stickyDraft}
87+
disabled={disabled}
88+
aria-label={t("accountPool.stickyLimitAria")}
89+
onChange={(event) => onStickyDraftChange(event.target.value)}
90+
onBlur={() => onStickyCommit()}
91+
onKeyDown={(event) => {
92+
if (event.nativeEvent.isComposing || disabled) return;
93+
if (event.key === "Enter") {
94+
event.preventDefault();
95+
onStickyCommit();
96+
}
97+
}}
98+
/>
99+
<NumberStepper
100+
disabled={disabled}
101+
incrementLabel={t("accountPool.stickyLimitInc")}
102+
decrementLabel={t("accountPool.stickyLimitDec")}
103+
onIncrement={() => {
104+
const next = clampNumberDraft(stickyDraft, 1, 1, 100);
105+
onStickyDraftChange(next);
106+
onStickyCommit(next);
107+
}}
108+
onDecrement={() => {
109+
const next = clampNumberDraft(stickyDraft, -1, 1, 100);
110+
onStickyDraftChange(next);
111+
onStickyCommit(next);
112+
}}
113+
/>
114+
</span>
115+
<div className="card-sub">{t("accountPool.stickyLimitHelp")}</div>
96116
</label>
97117
)}
98118
</div>

0 commit comments

Comments
 (0)