[Bug]: Browser preferences reject changes while a project is selected
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 新手友好度
- 50/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- typescript
- 领域
- frontend
调研方向
Start with SETTINGS_DEVICE_ONLY_PATHS in apps/web/src/components/settings/SettingsScopeSentence.tsx and the deviceOnly prop passed from apps/web/src/routes/_chat.settings.tsx for /settings/browser. Then read BrowserSearchEngineSetting in IntegrationsSettings.tsx and the serverScoped handling in settingsLayout.tsx. Run the focused scope test in apps/web/src/components/settings/ before and after the change. Done when the Browser page's project scope either saves device preferences or shows the rows read-only, with no warning toast.
由索引模型根据 Issue 内容生成。
描述
Summary
On Settings → Browser, selecting a project leaves device preference controls interactive, but changing one produces a “Setting not saved” warning and leaves the preference unchanged. Device preferences are documented to ignore the project selection, and settings that cannot be overridden should be shown read-only. An isolated check reproduced the writer's rejection for Search engine and confirmed the active row and scope wiring in source; the full UI interaction was not run.
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/web
Steps to reproduce
- Run the script below against a clean checkout of the specified commit with installed dependencies; it creates and removes one uniquely named focused test file in that checkout.
- The focused test compares a valid browser search-engine patch in project and all-projects scopes. The script then checks that the Browser route remains scope-aware and that the Search engine row still offers the scoped writer through an active select.
One run reproduced the failure path. The direct pointer interaction in a running web or desktop client was not exercised; the row state and hook connection were confirmed from the pinned source. The script prints tool versions and returns 1 with RESULT: defect reproduced when both the rejection and active UI wiring are present, 0 with RESULT: expected behavior when that wiring changes, and 2 with RESULT: inconclusive if the focused test or setup fails. Save it as repro.sh, then run sh repro.sh "$PWD" from the checkout; Node, Vite+, and installed workspace dependencies are required.
Reproduction script: repro.sh
#!/bin/sh
set -eu
checkout=${1:?pass the T3 Code checkout path}
case "$checkout" in /*) ;; *) checkout=$(cd "$checkout" && pwd) ;; esac
scratch=$(mktemp -d)
test_file="$checkout/apps/web/src/components/settings/browser-scope.y02-01.test.ts"
if [ -e "$test_file" ]; then
echo 'RESULT: inconclusive'
exit 2
fi
cleanup() { rm -f "$test_file"; rm -rf "$scratch"; }
trap cleanup EXIT HUP INT TERM
export HOME="$scratch/home" TMPDIR="$scratch/tmp" T3CODE_HOME="$scratch/t3"
mkdir -p "$HOME" "$TMPDIR" "$T3CODE_HOME"
printf 'Node: %s\n' "$(node --version)"
vp --version | sed -n '1,3p'
cat > "$test_file" <<'TS'
import { DEFAULT_SERVER_SETTINGS, EnvironmentId } from "@t3tools/contracts";
import { expect, it } from "vite-plus/test";
import { planScopedSettingsPatch } from "./scopedSettings";
import type { ResolvedSettingsScope } from "./settingsScope";
it("compares Browser client-setting writes in project and all scopes", () => {
const id = EnvironmentId.make("example");
const environments = [{
environmentId: id,
label: "Example",
connection: { phase: "connected" as const },
serverConfig: { settings: DEFAULT_SERVER_SETTINGS },
}];
const project = {
kind: "project", environmentIds: [id], members: [],
} as unknown as ResolvedSettingsScope;
const all = {
kind: "all", environmentIds: [id], members: [],
} as unknown as ResolvedSettingsScope;
const patch = { browserSearchEngine: "google" as const };
const rejected = planScopedSettingsPatch(project, environments, patch);
const accepted = planScopedSettingsPatch(all, environments, patch);
expect(rejected).toMatchObject({
clientPatch: {}, hasClientWrite: false, serverWrites: [],
unavailableReason: "This setting is environment-wide and cannot be overridden by a project.",
});
expect(accepted).toMatchObject({
clientPatch: patch, hasClientWrite: true, unavailableReason: null,
});
});
TS
if ! (cd "$checkout" && vp test run apps/web/src/components/settings/browser-scope.y02-01.test.ts); then
echo 'RESULT: inconclusive'
exit 2
fi
node - "$checkout" <<'NODE'
const fs = require('node:fs');
const path = require('node:path');
const root = process.argv[2];
const read = (name) => fs.readFileSync(path.join(root, name), 'utf8');
const paths = read('apps/web/src/components/settings/SettingsScopeSentence.tsx');
const pathList = paths.match(/SETTINGS_DEVICE_ONLY_PATHS:[\s\S]*?new Set\(\[([\s\S]*?)\]\)/)?.[1];
const panel = read('apps/web/src/components/settings/IntegrationsSettings.tsx');
const search = panel.match(/function BrowserSearchEngineSetting\(\) \{([\s\S]*?)\nexport function PullRequestLinksOpenInAppSetting/)?.[1];
const route = read('apps/web/src/routes/_chat.settings.tsx');
const scopeAware = pathList !== undefined && !pathList.includes('"/settings/browser"') && route.includes('deviceOnly={SETTINGS_DEVICE_ONLY_PATHS.has(pathname)}');
const activeWrite = search !== undefined && search.includes('useUpdateScopedSettings()') && search.includes('updateSettings({ browserSearchEngine: next })') && !search.includes('serverScoped') && !search.includes('settingKeys=') && !search.includes('disabled=');
console.log(`Browser page keeps project scope: ${scopeAware}`);
console.log(`Search-engine control remains active and uses scoped writer: ${activeWrite}`);
if (!scopeAware || !activeWrite) {
console.log('RESULT: expected behavior');
process.exit(0);
}
console.log('RESULT: defect reproduced');
process.exit(1);
NODE
Expected behavior
Device preferences should ignore the selected project, as the user guide says, or the UI should present a read-only control for a value it cannot change at project scope: docs/user/project-settings.md#L3-L17. The Browser page's profile create, rename, and remove actions use the device writer in every scope, which demonstrates the existing device-preference pattern on this page: apps/web/src/components/settings/IntegrationsSettings.tsx#L1110-L1116.
Actual behavior
The Browser page exposes the project scope picker because /settings/browser is absent from SETTINGS_DEVICE_ONLY_PATHS, and the route passes deviceOnly={false} for that path: apps/web/src/components/settings/SettingsScopeSentence.tsx#L50-L60, apps/web/src/routes/_chat.settings.tsx#L146-L155. The Search engine row uses useUpdateScopedSettings, offers an enabled select, and submits browserSearchEngine when changed: apps/web/src/components/settings/IntegrationsSettings.tsx#L706-L751. The same panel renders this row outside the desktop-only group: apps/web/src/components/settings/IntegrationsSettings.tsx#L1581-L1608.
The isolated run found that a project-scoped browserSearchEngine patch has no client write or server write and returns “This setting is environment-wide and cannot be overridden by a project”; the same patch in all-projects scope has a client write. This follows the planner's client-only key rule: apps/web/src/components/settings/scopedSettings.ts#L220-L245, apps/web/src/components/settings/scopedSettings.ts#L331-L343. The update hook passes the page's deviceOnly value to that planner and shows a warning toast instead of persisting an unavailable patch: apps/web/src/components/settings/useScopedSettings.ts#L50-L62, apps/web/src/components/settings/useScopedSettings.ts#L100-L105.
Impact and scope
Impact: P2 - a project or checkout selection blocks changes to otherwise interactive browser preferences, but selecting All projects or an environment lets the same preference save.
The verified search-engine path is available in both web and desktop clients. Source inspection finds the same scoped writer on desktop browser viewport, zoom, appearance, recording, link-target, and floating-preview rows, and on the “Set as default” profile action: apps/web/src/components/settings/IntegrationsSettings.tsx#L323-L325, apps/web/src/components/settings/IntegrationsSettings.tsx#L473-L475, apps/web/src/components/settings/IntegrationsSettings.tsx#L562-L565, apps/web/src/components/settings/IntegrationsSettings.tsx#L654-L656, apps/web/src/components/settings/IntegrationsSettings.tsx#L1076-L1078, apps/web/src/components/settings/IntegrationsSettings.tsx#L1423-L1432. Those controls were not exercised at runtime. The failure does not corrupt the saved value; it prevents the requested change.
Earliest failure stage
Core processing
Analysis or suspected cause
SettingsRow only makes an environment-wide row inert at project scope when serverScoped is true; the Search engine row supplies neither that flag nor another disabled condition: apps/web/src/components/settings/settingsLayout.tsx#L305-L328, apps/web/src/components/settings/settingsLayout.tsx#L368-L379, apps/web/src/components/settings/settingsLayout.tsx#L427-L449.
Additional context
The six governing files checked have identical blobs on current main and the tested commit, so the checked path remains present.
Impact
Minor bug or occasional failure
Version or commit
main @ c00a373
Environment
Node.js v26.12.1, Vite+ v1.5.1 with local vite-plus v1.4.0 and Vitest v5.0.5, macOS arm64. The focused test ran on this platform.
Logs or stack traces
The script's output below is from the run against the stated commit. Vite+'s checkout path, run timestamp, duration, and advisory line were omitted from this excerpt; the result and diagnostic lines are unchanged.
Node: v26.12.1
vp v1.5.1
Local vite-plus:
vite-plus v1.4.0
Test Files 1 passed (1)
Tests 1 passed (1)
Browser page keeps project scope: true
Search-engine control remains active and uses scoped writer: true
RESULT: defect reproduced
Screenshots, recordings, or supporting files
No response
Workaround
Select All projects or an environment to save the same preference.
References
- https://github.com/pingdotgg/t3code/issues/35093 — closed report of the earlier project-scope overwrite; this report concerns the current rejection with an enabled control.
- https://github.com/pingdotgg/t3code/pull/35143 — merged fix for that overwrite; its description explicitly leaves the interactive project-scoped Browser rows unchanged.
- https://github.com/pingdotgg/t3code/issues/35094 — closed request for true per-project browser defaults; this report concerns editing existing device preferences.
- 主要语言
- TypeScript
- 星标
- 24.8k
- 派生
- 6.4k
- 平均合并
- 59 分钟
- 30 天内合并 PR
- 418
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
pingdotgg/t3code 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 66/100
维护者通常 1 天内回复
-
via-triage
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
难度 2/5 半天 新手友好度 68/100
维护者通常 1 天内回复
-
Question card options show LaTeX as raw source, though the question text renders it可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 2/5 1-3 小时 新手友好度 76/100
维护者通常 1 天内回复
-
[Bug]: Settings inheritance popover shows raw slugs for branch naming, default permissions and response streaming可能已有人在做 @juliusmarminge 于 1 天前认领。 未关闭vouch:denounced
难度 2/5 1-3 小时 新手友好度 84/100
维护者通常 1 天内回复
相似的 Issue
-
bug platform:mobile root-cause:found
难度 2/5 1-3 小时 新手友好度 72/100
openchamber/openchamber#6926 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 66/100
push-based/user-flow#356 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 70/100
Cygnux-Labs/Tracekit#827 ·
维护者通常 1 天内回复
-
www build: a refused import shows only "ENOENT scandir <outDir>", never the build-audience error未关闭kind:defect
难度 1/5 1 小时以内 新手友好度 91/100
维护者通常 1 天内回复
-
type:bug
难度 2/5 1-3 小时 新手友好度 72/100
nightgauge/nightgauge#3266 ·
维护者通常 1 天内回复