[Bug]: Browser preferences reject changes while a project is selected
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 50/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- typescript
- Área
- frontend
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- TypeScript
- Estrellas
- 24.8k
- Forks
- 6.4k
- Merge medio
- 59 min
- PR fusionados (30 d)
- 418
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de pingdotgg/t3code
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 1 día
-
via-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
[Bug]: MCP App inline frames never go below 80px, so compact apps get blank space under themAbierto
Dificultad 2/5 Medio día Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
Question card options show LaTeX as raw source, though the question text renders itPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
[Bug]: Settings inheritance popover shows raw slugs for branch naming, default permissions and response streamingPosiblemente ocupada @juliusmarminge la tomó hace 1 día. Abiertovouch:denounced
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 1 día
Todos los issues de pingdotgg/t3code
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
nikoheikkila/nikoheikkila.fi#1131 ·
Los mantenedores suelen responder en 1 día
-
Resources: Update work of urumqiPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
railmapgen/rmp-gallery#4176 ·
Los mantenedores suelen responder en 2 días
-
GraphQL custom headers duplicate differently cased default headersPosiblemente ocupada @yxr-2025 la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
elie222/rakazo#4625 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
[New Project]: PacaAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
github/forgoodfirstissue#574 ·