fix: recompile after saving direct edits

Saving wrote the v2 document but nothing else, and `/api/state` serves the
outputs of the last compile — so a refresh showed the pre-edit geometry and a
successful save was indistinguishable from a failed one. Reported as "saved,
refreshed, edit gone"; the document on disk was in fact correct, resolving as
`exact` with the constraint reaching the profile.

Save now recompiles and clears the preview overlay, since the baseline carries
the edit afterwards and would otherwise draw it twice. The recompile is reported
separately from the save: if it fails the message says the edit was saved and
names the compile error, rather than claiming the save failed.

The existing identity test could not have caught this. An ignored `editsFile`
and an empty document produce the same output, so it proved nothing about a
non-empty one. The API test now compiles with and without a saved constraint and
asserts the outputs differ.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 09:25:09 +08:00
parent 38593f6f67
commit d94c546450
2 changed files with 46 additions and 2 deletions

View File

@@ -277,15 +277,16 @@ function App() {
const saveDirectEdits = async () => {
if (!session) return;
const fragment = session.fragment();
let version: number;
try {
const result = await api.saveEdits({
expectedDocumentVersion: session.documentVersion,
constraints: fragment.constraints,
operations: fragment.operations,
});
session.markSaved(result.documentVersion);
version = result.documentVersion;
session.markSaved(version);
touch();
setStatus(`直接编辑已保存,文档版本 ${result.documentVersion}`);
} catch (error) {
if (!isVersionConflict(error)) {
setStatus(`保存失败:${(error as Error).message}`);
@@ -298,6 +299,22 @@ function App() {
if (Number.isInteger(current)) session.setDocumentVersion(current as number);
touch();
setStatus(`保存冲突:文档已被改到版本 ${current ?? '未知'},请刷新后重做本次编辑。`);
return;
}
// The document is written, but `/api/state` serves the outputs of the last
// compile — without recompiling, a refresh shows the pre-edit geometry and a
// successful save is indistinguishable from a failed one. Reported separately
// because at this point the save has already succeeded.
try {
setStatus('直接编辑已保存,正在重新生成几何...');
const next = await api.compile();
setState(next);
setSelected((current) => next.compiled.model.roads.find((road) => road.id === current?.id) || null);
// The baseline now carries the edit, so the preview overlay would draw it twice.
setPreview(null);
setStatus(`直接编辑已保存并重新生成,文档版本 ${version}`);
} catch (error) {
setStatus(`直接编辑已保存(版本 ${version}),但重新生成失败:${(error as Error).message}`);
}
};
const stage = (change: Override) =>