feat(FN-1375): add null-as-delete pattern for settings with Reset button
- Send null instead of undefined when clearing token cap to explicitly delete setting - Handle null values as delete operations in updateSettings (for clearing keys) - Add Reset button in Settings modal to clear token cap with one click - Update placeholder and help text for token cap input - Fix TypeScript cast error for config.settings
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
- Browser directory pickers (`webkitdirectory`) cannot provide a server filesystem path; for dashboard import flows, parse selected `AGENTS.md` files client-side and send `{ agents }` payloads instead of trying to submit a directory `source` path.
|
||||
- For conditionally rendered mobile inputs in dashboard components, prefer React `autoFocus` on the input over effect+`setTimeout` focus logic keyed to open-state booleans; mount timing is more reliable and simpler.
|
||||
- Checkout leasing is explicit: use `checkoutTask`/`releaseTask` (or `/api/tasks/:id/checkout` + `/release`) for ownership, treat 409 conflicts as non-retryable contention, and let `HeartbeatMonitor.executeHeartbeat()` only validate `checkedOutBy` (never auto-acquire leases).
|
||||
- The null-as-delete pattern for settings: In `TaskStore.updateSettings()`, `null` values in the settings patch are treated as "delete this key from settings" (since `JSON.stringify` drops `undefined` keys). This allows the frontend to explicitly clear a setting by sending `null`. The key is deleted from both `config.settings` and `projectPatch` before merging, so cleared settings fall back to `DEFAULT_SETTINGS`.
|
||||
|
||||
## Color Theme System
|
||||
|
||||
|
||||
@@ -595,6 +595,17 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
|
||||
return this.withConfigLock(async () => {
|
||||
const config = await this.readConfig();
|
||||
|
||||
// Handle null values as "delete this key from settings"
|
||||
// This allows the frontend to explicitly clear a setting by sending null
|
||||
// (since JSON.stringify drops undefined keys, we use null as a sentinel)
|
||||
for (const key of Object.keys(projectPatch)) {
|
||||
if ((projectPatch as Record<string, unknown>)[key] === null) {
|
||||
delete (config.settings as unknown as Record<string, unknown>)[key];
|
||||
delete (projectPatch as Record<string, unknown>)[key];
|
||||
}
|
||||
}
|
||||
|
||||
const globalSettings = await this.globalSettingsStore.getSettings();
|
||||
const previousMerged: Settings = { ...DEFAULT_SETTINGS, ...globalSettings, ...config.settings } as Settings;
|
||||
const updatedProjectSettings = { ...config.settings, ...projectPatch };
|
||||
|
||||
@@ -746,17 +746,30 @@ export function SettingsModal({
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="tokenCap">Token Cap</label>
|
||||
<input
|
||||
id="tokenCap"
|
||||
type="number"
|
||||
placeholder="100000"
|
||||
value={(form as any).tokenCap ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, tokenCap: val ? parseInt(val, 10) : undefined } as any));
|
||||
}}
|
||||
/>
|
||||
<small>Automatically compact context when approaching this token count. Leave empty to use default behavior (compact only on overflow errors).</small>
|
||||
<div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
|
||||
<input
|
||||
id="tokenCap"
|
||||
type="number"
|
||||
placeholder="No cap"
|
||||
value={(form as any).tokenCap ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, tokenCap: val ? parseInt(val, 10) : null } as any));
|
||||
}}
|
||||
/>
|
||||
{(form as any).tokenCap != null && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-sm"
|
||||
title="Reset to default (no cap)"
|
||||
onClick={() => setForm((f) => ({ ...f, tokenCap: null } as any))}
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<small>Automatically compact context when approaching this token count. Leave empty for no cap (compact only on overflow errors). Set a number to proactively compact when reaching this token count.</small>
|
||||
</div>
|
||||
|
||||
{/* --- Planning & Validation --- */}
|
||||
|
||||
Reference in New Issue
Block a user