fix(web): use wrapper+icon pattern for agent model select to fix duplicate chevrons (#1831)
Some checks failed
ci / Packaged mac smoke (push) Blocked by required conditions
ci / Packaged windows smoke (push) Blocked by required conditions
ci / Detect PR change scopes (push) Failing after 3s
ci / Validate workspace (push) Has been skipped
Docker image / build-and-push (push) Failing after 12s
nix-check / build (push) Failing after 2s
ci / Packaged linux headless smoke (push) Has been skipped

Native macOS browser sometimes renders its own disclosure indicator even
with appearance:none, stacking it on top of the CSS background-image SVG
chevron. Apply the same orbit-template-select-wrap pattern: clear the
background-image via explicit wrapper class, use an absolutely-positioned
Icon component for a single, controlled chevron. Affects both the model
picker and reasoning picker selects in the agent-model-row.
This commit is contained in:
epic 2026-05-16 01:29:53 +09:00 committed by GitHub
parent 9a64fccdc0
commit 1896c699a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 52 deletions

View file

@ -2185,47 +2185,40 @@ export function SettingsDialog({
<span className="field-label">
{t('settings.modelPicker')}
</span>
<select
value={selectValue}
onChange={(e) => {
if (e.target.value === CUSTOM_MODEL_SENTINEL) {
// Switching to "Custom…" should clear the
// value so the input below opens empty for
// typing. Keep an explicit edit-mode flag so
// intermediate values like `gpt-5` do not
// collapse the custom input while typing
// `gpt-5.5`.
setAgentCustomModelIds((prev) => {
const next = new Set(prev);
next.add(selected.id);
return next;
});
setChoice({ model: '' });
} else {
setAgentCustomModelIds((prev) => {
if (!prev.has(selected.id)) return prev;
const next = new Set(prev);
next.delete(selected.id);
return next;
});
setChoice({ model: e.target.value });
}
}}
>
{renderModelOptions(selected.models!)}
<option value={CUSTOM_MODEL_SENTINEL}>
{t('settings.modelCustom')}
</option>
</select>
<div className="agent-model-select-wrap">
<select
value={selectValue}
onChange={(e) => {
if (e.target.value === CUSTOM_MODEL_SENTINEL) {
setAgentCustomModelIds((prev) => {
const next = new Set(prev);
next.add(selected.id);
return next;
});
setChoice({ model: '' });
} else {
setAgentCustomModelIds((prev) => {
if (!prev.has(selected.id)) return prev;
const next = new Set(prev);
next.delete(selected.id);
return next;
});
setChoice({ model: e.target.value });
}
}}
>
{renderModelOptions(selected.models!)}
<option value={CUSTOM_MODEL_SENTINEL}>
{t('settings.modelCustom')}
</option>
</select>
<Icon
name="chevron-down"
size={12}
className="agent-model-select-chevron"
/>
</div>
</label>
{/*
Hint sits with its own field so the user reads
"Default vs Custom…" right next to the dropdown
that exposes those options. Older layouts parked
this paragraph at the bottom of the section, past
the Memory picker, where it got mistaken for
memory documentation.
*/}
<p className="hint agent-model-row-hint">
{t('settings.modelPickerHint')}
</p>
@ -2251,18 +2244,25 @@ export function SettingsDialog({
<span className="field-label">
{t('settings.reasoningPicker')}
</span>
<select
value={reasoningValue}
onChange={(e) =>
setChoice({ reasoning: e.target.value })
}
>
{selected.reasoningOptions!.map((r) => (
<option key={r.id} value={r.id}>
{r.label}
</option>
))}
</select>
<div className="agent-model-select-wrap">
<select
value={reasoningValue}
onChange={(e) =>
setChoice({ reasoning: e.target.value })
}
>
{selected.reasoningOptions!.map((r) => (
<option key={r.id} value={r.id}>
{r.label}
</option>
))}
</select>
<Icon
name="chevron-down"
size={12}
className="agent-model-select-chevron"
/>
</div>
</label>
) : null}
<MemoryModelInline

View file

@ -5007,6 +5007,24 @@ a.avatar-item:visited {
color: var(--text-muted);
}
.agent-model-row .hint { margin: 0; font-size: 11.5px; }
.agent-model-select-wrap {
position: relative;
}
.agent-model-select-wrap select {
appearance: none;
-webkit-appearance: none;
background-image: none;
padding-right: 28px;
width: 100%;
}
.agent-model-select-chevron {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--text-soft);
pointer-events: none;
}
.agent-cli-env {
padding: 10px 12px;
border: 1px solid var(--border-soft);