fix(settings): validate drafts and preserve dialog baselines (#614)

Issue: #614
User-Visible: yes
This commit is contained in:
Claude
2026-09-23 15:27:51 +03:00
parent 5ac596f93c
commit 1a6b12af86
85 changed files with 2167 additions and 1766 deletions
+12 -2
View File
@@ -321,8 +321,14 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000;
* обязана получить правильную геометрию до загрузки ленивого редактора. Новый
* центр оставляет 955 Б сверху и 1 045 Б до нижней границы полосы; общий бюджет
* не меняется.
*
* 2026-09-23, #614: 291 400 → 292 000 (замер 291 041). Перенос baseline
* между warm-диалогами и хостом обязан жить в eager-графе: иначе при
* повторном открытии диалог теряет единую dirty-семантику до загрузки
* редактора. Факт оставляет 959 Б сверху и 1 041 Б до нижней границы;
* общий бюджет не меняется.
*/
export const INITIAL_VIEW_GZIP_CEILING = 291_400;
export const INITIAL_VIEW_GZIP_CEILING = 292_000;
export const INITIAL_VIEW_CEILING_BAND = 2_000;
/**
@@ -450,8 +456,12 @@ export const LAZY_FURNITURE_ART_GZIP_CEILING = 17_900;
* - #608: 243 900 → 244 400 (замер 243 597). Атомарный commit числового поля
* добавил общий normalizer и обработчик change в form-kit; новый потолок
* оставляет 803 Б сверху и 1 197 Б до нижней границы полосы.
* - #614: 244 400 → 245 400 (замер 244 356). Строгие raw-draft поля,
* inline-ошибки маркера и единая повторная валидация живут в lazy
* editor-графе. Новый центр оставляет 1 044 Б сверху и 956 Б до
* нижней границы полосы.
*/
export const LAZY_EDITOR_GZIP_CEILING = 244_400;
export const LAZY_EDITOR_GZIP_CEILING = 245_400;
export const LAZY_GRAPH_CEILING_BAND = 2_000;
/**
+57 -2
View File
@@ -10723,8 +10723,30 @@ const MUTANT_DEFINITIONS = [
+ 'это направление, и лучи через окна с ним считаются, а с null — нет',
patches: [{
file: 'src/editors/general-settings-dialog.ts',
find: "textLink(t('gs.north_clear'), () => set({ northDeg: null }))",
replace: "textLink(t('gs.north_clear'), () => set({ northDeg: 0 }))",
find: "textLink(t('gs.north_clear'), () => set({ northDeg: null, northDegInput: '' }))",
replace: "textLink(t('gs.north_clear'), () => set({ northDeg: 0, northDegInput: '0' }))",
}],
},
{
id: 'general-required-number-falls-back-to-hidden-value',
guard: 'node demo/smoke_general_settings_form.mjs',
because: '#614 AC1: an empty required numeric draft must stay visible and block Save; '
+ 'validating the last typed number silently restores the pre-fix hidden-value fallback',
patches: [{
file: 'src/editors/general-form-state.ts',
find: ' const glow = strictNumber(d.glowRadiusInput);',
replace: ' const glow = d.glowRadius;',
}],
},
{
id: 'space-required-temperature-forgets-raw-draft',
guard: 'node demo/smoke_space_settings_form.mjs',
because: '#614 AC1: each required temperature input owns its raw draft and its own '
+ 'problem count; consulting the last valid number hides an empty lower bound',
patches: [{
file: 'src/editors/space-form-state.ts',
find: ' const min = strictNumber(d.tempMinInput ?? String(d.tempMin));',
replace: ' const min = strictNumber(String(d.tempMin));',
}],
},
{
@@ -10796,6 +10818,39 @@ const MUTANT_DEFINITIONS = [
replace: " && (edit ? true : true);",
}],
},
{
id: 'marker-virtual-name-remains-a-late-toast',
guard: 'node demo/smoke_device_settings_form.mjs',
because: '#614 AC2: a missing virtual name is an inline form problem before Save; '
+ 'dropping this problem restores the late _saveMarker guard with no field guidance',
patches: [{
file: 'src/editors/marker-form-state.ts',
find: " if (d.binding === 'virtual' && !d.name.trim()) {",
replace: " if (d.binding === 'virtual' && !d.name.trim() && false) {",
}],
},
{
id: 'marker-save-click-forgets-baseline-before-result',
guard: 'node demo/smoke_device_settings_form.mjs',
because: '#614 AC3: starting Save cannot erase the comparison baseline; only a '
+ 'successful commit or explicit close may do that',
patches: [{
file: 'src/editors/marker-dialog.ts',
find: '@click=${() => { void this._saveMarker(); }}',
replace: '@click=${() => { forgetMarkerBaseline(this.host); void this._saveMarker(); }}',
}],
},
{
id: 'warm-dialog-drops-transferred-baseline',
guard: 'node demo/smoke_warm_dialogs.mjs',
because: '#614 AC4: a warm replacement must receive the original comparison key; '
+ 'restoring only the visible draft makes every clean dialog spuriously dirty',
patches: [{
file: 'src/houseplan-card.ts',
find: ' restoreWarmDialogBaseline(this, d.kind, d.baseline);',
replace: ' // mutant: the visible draft revives without its dirty baseline',
}],
},
{
id: 'marker-discard-without-asking',
guard: 'node demo/smoke_device_settings_form.mjs',