Move project save post-commit planning to app core

This commit is contained in:
2026-06-06 12:16:19 +02:00
parent f3834827b1
commit c8b55b36f7
8 changed files with 176 additions and 28 deletions

View File

@@ -307,7 +307,10 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
the app-core commit plan for direct saves, successful temporary swaps, the app-core commit plan for direct saves, successful temporary swaps,
target-remove failures, and rename-after-remove failures. It is covered by target-remove failures, and rename-after-remove failures. It is covered by
forward-slash, Windows backslash, existing-target, remove-failure, forward-slash, Windows backslash, existing-target, remove-failure,
rename-failure, and invalid-path smokes. rename-failure, and invalid-path smokes. The same command now reports the
app-core post-commit side-effect plan for clean/new-document metadata
updates, timelapse sidecar gating, platform flush, progress cleanup, and
title refresh.
- Live equirectangular, layer, animation-frame, and cube-face export adapters - Live equirectangular, layer, animation-frame, and cube-face export adapters
now prepare and log the same payload-bearing canvas document snapshot plus now prepare and log the same payload-bearing canvas document snapshot plus
shared paint-renderer export-readiness report. shared paint-renderer export-readiness report.
@@ -1191,7 +1194,10 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
delegating to retained `Canvas::project_save`. Direct save, successful delegating to retained `Canvas::project_save`. Direct save, successful
temporary swap, remove-failure, and rename-after-remove commit outcomes are temporary swap, remove-failure, and rename-after-remove commit outcomes are
now classified by the same app-core planner before retained success metadata now classified by the same app-core planner before retained success metadata
mutation. mutation. Post-commit clean/new-document metadata, timelapse sidecar,
platform flush, progress cleanup, and title-refresh decisions also come from
`pp_app_core`, while retained code still performs the mutations and sidecar
serialization.
Retained legacy UI/canvas Retained legacy UI/canvas
execution and actual live save serialization remain tracked by `DEBT-0040`, execution and actual live save serialization remain tracked by `DEBT-0040`,
`DEBT-0041`, and `DEBT-0042`; the pure snapshot-to-PPI export handoff is `DEBT-0041`, and `DEBT-0042`; the pure snapshot-to-PPI export handoff is

View File

@@ -559,6 +559,15 @@ agent or engineer to remove them without reconstructing context from chat.
Actual PPI serialization, filesystem remove/rename execution, Actual PPI serialization, filesystem remove/rename execution,
progress/threading, timelapse sidecar serialization, and app metadata progress/threading, timelapse sidecar serialization, and app metadata
mutation remain retained. mutation remain retained.
- 2026-06-06: DEBT-0040/DEBT-0042 were narrowed again. `pp_app_core` now owns
the retained project-save post-commit side-effect policy: successful saves
mark the document clean, commit new-document state, flush platform storage,
optionally write a timelapse sidecar when an encoder exists, and always
dismiss visible progress UI plus refresh the title. Live
`Canvas::project_save_thread` consumes that plan, and
`pano_cli plan-canvas-project-save-target` reports it in JSON. Actual state
mutation, progress UI destruction, platform flush execution, title update,
and timelapse sidecar serialization remain retained.
- 2026-06-05: DEBT-0010/DEBT-0013 were narrowed again. `pp_app_core` now - 2026-06-05: DEBT-0010/DEBT-0013 were narrowed again. `pp_app_core` now
exports payload-complete or metadata-only canvas document snapshots through exports payload-complete or metadata-only canvas document snapshots through
the pure `pp_document` PPI writer and rejects snapshots that still require the pure `pp_document` PPI writer and rejects snapshots that still require

View File

@@ -715,8 +715,11 @@ and preserving the legacy direct-write fallback when the temporary file cannot
be opened. The same app-core boundary now also classifies the post-write be opened. The same app-core boundary now also classifies the post-write
commit result for direct writes, successful temporary swaps, remove failures, commit result for direct writes, successful temporary swaps, remove failures,
and rename-after-remove failures before retained save metadata mutation and rename-after-remove failures before retained save metadata mutation
continues. The same automation now feeds payload-complete snapshots through the continues. Post-save side effects are now planned there too: mark-clean,
shared new-document commitment, timelapse sidecar gating, platform flush, progress
cleanup, and title refresh are reported before retained execution performs the
actual mutations and sidecar serialization. The same automation now feeds
payload-complete snapshots through the shared
`pp_paint_renderer::prepare_document_frame_export_readiness` report, which `pp_paint_renderer::prepare_document_frame_export_readiness` report, which
records renderer-neutral six-face texture upload commands and encodes the records renderer-neutral six-face texture upload commands and encodes the
active document frame's six composited faces to PNG bytes. This gives CLI active document frame's six composited faces to PNG bytes. This gives CLI
@@ -2326,6 +2329,9 @@ Results:
The command now reports the app-core commit plan for direct saves, successful The command now reports the app-core commit plan for direct saves, successful
temporary swaps, target-remove failures, and rename-after-remove failures, temporary swaps, target-remove failures, and rename-after-remove failures,
including whether the target may be missing after a failed swap. including whether the target may be missing after a failed swap.
It also reports the app-core post-commit side-effect plan, including
clean/new-document metadata updates, timelapse sidecar gating, platform
flush, progress cleanup, and title refresh.
- The same payload-complete snapshot automation now uploads the active document - The same payload-complete snapshot automation now uploads the active document
frame through `pp_paint_renderer::upload_document_frame_faces` and the frame through `pp_paint_renderer::upload_document_frame_faces` and the
`RecordingRenderDevice`, emitting `rendererUpload` JSON with texture, `RecordingRenderDevice`, emitting `rendererUpload` JSON with texture,

View File

@@ -135,6 +135,21 @@ struct DocumentCanvasProjectSaveCommitPlan {
std::string_view log_message; std::string_view log_message;
}; };
struct DocumentCanvasProjectSavePostCommitInput {
bool save_succeeded = false;
bool timelapse_encoder_available = false;
bool progress_ui_visible = false;
};
struct DocumentCanvasProjectSavePostCommitPlan {
bool marks_document_clean = false;
bool marks_new_document_committed = false;
bool saves_timelapse_sidecar = false;
bool flushes_platform_storage = false;
bool dismisses_progress_ui = false;
bool updates_title = true;
};
class DocumentCanvasClearServices { class DocumentCanvasClearServices {
public: public:
virtual ~DocumentCanvasClearServices() = default; virtual ~DocumentCanvasClearServices() = default;
@@ -444,6 +459,23 @@ plan_document_canvas_project_save_write(
return plan; return plan;
} }
[[nodiscard]] constexpr DocumentCanvasProjectSavePostCommitPlan plan_document_canvas_project_save_post_commit(
DocumentCanvasProjectSavePostCommitInput input) noexcept
{
DocumentCanvasProjectSavePostCommitPlan plan;
plan.dismisses_progress_ui = input.progress_ui_visible;
if (!input.save_succeeded) {
return plan;
}
plan.marks_document_clean = true;
plan.marks_new_document_committed = true;
plan.saves_timelapse_sidecar = input.timelapse_encoder_available;
plan.flushes_platform_storage = true;
return plan;
}
[[nodiscard]] inline pp::foundation::Result<DocumentCanvasClearPlan> plan_document_canvas_clear( [[nodiscard]] inline pp::foundation::Result<DocumentCanvasClearPlan> plan_document_canvas_clear(
bool has_canvas, bool has_canvas,
float r = 0.0F, float r = 0.0F,

View File

@@ -2577,13 +2577,23 @@ bool Canvas::project_save_thread(std::string file_path, bool show_progress)
LOG("project saved to %s", file_path.c_str()); LOG("project saved to %s", file_path.c_str());
} }
if (success) const auto post_commit_plan = pp::app::plan_document_canvas_project_save_post_commit(
pp::app::DocumentCanvasProjectSavePostCommitInput {
.save_succeeded = success,
.timelapse_encoder_available = Canvas::I->m_encoder != nullptr,
.progress_ui_visible = show_progress,
});
if (post_commit_plan.marks_document_clean)
{ {
m_unsaved = false; m_unsaved = false;
}
if (post_commit_plan.marks_new_document_committed)
{
m_newdoc = false; m_newdoc = false;
}
// save timelapse if (post_commit_plan.saves_timelapse_sidecar)
if (Canvas::I->m_encoder)
{ {
BinaryStreamWriter sw; BinaryStreamWriter sw;
sw.init(BinaryStream::ByteOrder::LittleEndian); sw.init(BinaryStream::ByteOrder::LittleEndian);
@@ -2597,12 +2607,20 @@ bool Canvas::project_save_thread(std::string file_path, bool show_progress)
if (!sw.save(lapse_path)) if (!sw.save(lapse_path))
LOG("cannot save timelase to %s", lapse_path.c_str()); LOG("cannot save timelase to %s", lapse_path.c_str());
} }
if (post_commit_plan.flushes_platform_storage)
{
App::I->flush_platform_storage(); App::I->flush_platform_storage();
} }
if (show_progress) if (post_commit_plan.dismisses_progress_ui)
{
pb->destroy(); pb->destroy();
}
if (post_commit_plan.updates_title)
{
App::I->title_update(); App::I->title_update();
}
return success; return success;
} }

View File

@@ -1530,7 +1530,7 @@ if(TARGET pano_cli)
COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi) COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi)
set_tests_properties(pano_cli_plan_canvas_project_save_target_smoke PROPERTIES set_tests_properties(pano_cli_plan_canvas_project_save_target_smoke PROPERTIES
LABELS "app;document;integration;desktop-fast" LABELS "app;document;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"dataDirectory\":\"D:/Paint/data\".*\"targetPath\":\"D:/Paint/projects/demo.ppi\".*\"fileName\":\"demo\".*\"temporaryPath\":\"D:/Paint/data/demo.tmp.ppi\".*\"timelapsePath\":\"D:/Paint/data/demo.pptl\".*\"writePlan\":\\{\"action\":\"write-direct-to-target\",\"targetExists\":false,\"usesTemporary\":false,\"writePath\":\"D:/Paint/projects/demo.ppi\",\"fallbackDirectOnTemporaryOpenFailure\":false\\}.*\"commitPlan\":\\{\"saved\":true,\"usedTemporary\":false,\"targetRemoved\":false,\"temporaryRenamed\":false,\"targetMayBeMissing\":false,\"message\":\"project saved to target\"\\}") PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"dataDirectory\":\"D:/Paint/data\".*\"targetPath\":\"D:/Paint/projects/demo.ppi\".*\"fileName\":\"demo\".*\"temporaryPath\":\"D:/Paint/data/demo.tmp.ppi\".*\"timelapsePath\":\"D:/Paint/data/demo.pptl\".*\"writePlan\":\\{\"action\":\"write-direct-to-target\",\"targetExists\":false,\"usesTemporary\":false,\"writePath\":\"D:/Paint/projects/demo.ppi\",\"fallbackDirectOnTemporaryOpenFailure\":false\\}.*\"commitPlan\":\\{\"saved\":true,\"usedTemporary\":false,\"targetRemoved\":false,\"temporaryRenamed\":false,\"targetMayBeMissing\":false,\"message\":\"project saved to target\"\\}.*\"postCommitPlan\":\\{\"marksDocumentClean\":true,\"marksNewDocumentCommitted\":true,\"savesTimelapseSidecar\":false,\"flushesPlatformStorage\":true,\"dismissesProgressUi\":false,\"updatesTitle\":true\\}")
add_test(NAME pano_cli_plan_canvas_project_save_target_backslash_smoke add_test(NAME pano_cli_plan_canvas_project_save_target_backslash_smoke
COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path "D:\\Paint\\projects\\demo.ppi") COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path "D:\\Paint\\projects\\demo.ppi")
@@ -1539,10 +1539,10 @@ if(TARGET pano_cli)
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"fileName\":\"demo\".*\"temporaryPath\":\"D:/Paint/data/demo.tmp.ppi\".*\"timelapsePath\":\"D:/Paint/data/demo.pptl\"") PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"fileName\":\"demo\".*\"temporaryPath\":\"D:/Paint/data/demo.tmp.ppi\".*\"timelapsePath\":\"D:/Paint/data/demo.pptl\"")
add_test(NAME pano_cli_plan_canvas_project_save_target_existing_smoke add_test(NAME pano_cli_plan_canvas_project_save_target_existing_smoke
COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists) COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists --encoder --progress)
set_tests_properties(pano_cli_plan_canvas_project_save_target_existing_smoke PROPERTIES set_tests_properties(pano_cli_plan_canvas_project_save_target_existing_smoke PROPERTIES
LABELS "app;document;integration;desktop-fast" LABELS "app;document;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"targetPath\":\"D:/Paint/projects/demo.ppi\".*\"writePlan\":\\{\"action\":\"write-temporary-then-swap\",\"targetExists\":true,\"usesTemporary\":true,\"writePath\":\"D:/Paint/data/demo.tmp.ppi\",\"fallbackDirectOnTemporaryOpenFailure\":true\\}.*\"commitPlan\":\\{\"saved\":true,\"usedTemporary\":true,\"targetRemoved\":true,\"temporaryRenamed\":true,\"targetMayBeMissing\":false,\"message\":\"temporary project swapped successfully\"\\}") PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"targetPath\":\"D:/Paint/projects/demo.ppi\".*\"writePlan\":\\{\"action\":\"write-temporary-then-swap\",\"targetExists\":true,\"usesTemporary\":true,\"writePath\":\"D:/Paint/data/demo.tmp.ppi\",\"fallbackDirectOnTemporaryOpenFailure\":true\\}.*\"commitPlan\":\\{\"saved\":true,\"usedTemporary\":true,\"targetRemoved\":true,\"temporaryRenamed\":true,\"targetMayBeMissing\":false,\"message\":\"temporary project swapped successfully\"\\}.*\"postCommitPlan\":\\{\"marksDocumentClean\":true,\"marksNewDocumentCommitted\":true,\"savesTimelapseSidecar\":true,\"flushesPlatformStorage\":true,\"dismissesProgressUi\":true,\"updatesTitle\":true\\}")
add_test(NAME pano_cli_plan_canvas_project_save_target_remove_failure_smoke add_test(NAME pano_cli_plan_canvas_project_save_target_remove_failure_smoke
COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists --remove-fails) COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists --remove-fails)
@@ -1554,7 +1554,7 @@ if(TARGET pano_cli)
COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists --rename-fails) COMMAND pano_cli plan-canvas-project-save-target --data-dir D:/Paint/data --path D:/Paint/projects/demo.ppi --target-exists --rename-fails)
set_tests_properties(pano_cli_plan_canvas_project_save_target_rename_failure_smoke PROPERTIES set_tests_properties(pano_cli_plan_canvas_project_save_target_rename_failure_smoke PROPERTIES
LABELS "app;document;integration;desktop-fast;fuzz" LABELS "app;document;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"commitPlan\":\\{\"saved\":false,\"usedTemporary\":true,\"targetRemoved\":true,\"temporaryRenamed\":false,\"targetMayBeMissing\":true,\"message\":\"temporary project not swapped after original removal\"\\}") PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-project-save-target\".*\"commitPlan\":\\{\"saved\":false,\"usedTemporary\":true,\"targetRemoved\":true,\"temporaryRenamed\":false,\"targetMayBeMissing\":true,\"message\":\"temporary project not swapped after original removal\"\\}.*\"postCommitPlan\":\\{\"marksDocumentClean\":false,\"marksNewDocumentCommitted\":false,\"savesTimelapseSidecar\":false,\"flushesPlatformStorage\":false,\"dismissesProgressUi\":false,\"updatesTitle\":true\\}")
add_test(NAME pano_cli_plan_canvas_project_save_target_rejects_empty_path add_test(NAME pano_cli_plan_canvas_project_save_target_rejects_empty_path
COMMAND pano_cli plan-canvas-project-save-target --path "") COMMAND pano_cli plan-canvas-project-save-target --path "")

View File

@@ -425,6 +425,57 @@ void project_save_commit_plan_flags_missing_target_after_rename_failure(pp::test
PP_EXPECT(harness, plan.log_message == "temporary project not swapped after original removal"); PP_EXPECT(harness, plan.log_message == "temporary project not swapped after original removal");
} }
void project_save_post_commit_plan_records_success_side_effects(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_document_canvas_project_save_post_commit(
pp::app::DocumentCanvasProjectSavePostCommitInput {
.save_succeeded = true,
.timelapse_encoder_available = true,
.progress_ui_visible = true,
});
PP_EXPECT(harness, plan.marks_document_clean);
PP_EXPECT(harness, plan.marks_new_document_committed);
PP_EXPECT(harness, plan.saves_timelapse_sidecar);
PP_EXPECT(harness, plan.flushes_platform_storage);
PP_EXPECT(harness, plan.dismisses_progress_ui);
PP_EXPECT(harness, plan.updates_title);
}
void project_save_post_commit_plan_skips_timelapse_without_encoder(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_document_canvas_project_save_post_commit(
pp::app::DocumentCanvasProjectSavePostCommitInput {
.save_succeeded = true,
.timelapse_encoder_available = false,
.progress_ui_visible = false,
});
PP_EXPECT(harness, plan.marks_document_clean);
PP_EXPECT(harness, plan.marks_new_document_committed);
PP_EXPECT(harness, !plan.saves_timelapse_sidecar);
PP_EXPECT(harness, plan.flushes_platform_storage);
PP_EXPECT(harness, !plan.dismisses_progress_ui);
PP_EXPECT(harness, plan.updates_title);
}
void project_save_post_commit_plan_preserves_state_after_failure(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_document_canvas_project_save_post_commit(
pp::app::DocumentCanvasProjectSavePostCommitInput {
.save_succeeded = false,
.timelapse_encoder_available = true,
.progress_ui_visible = true,
});
PP_EXPECT(harness, !plan.marks_document_clean);
PP_EXPECT(harness, !plan.marks_new_document_committed);
PP_EXPECT(harness, !plan.saves_timelapse_sidecar);
PP_EXPECT(harness, !plan.flushes_platform_storage);
PP_EXPECT(harness, plan.dismisses_progress_ui);
PP_EXPECT(harness, plan.updates_title);
}
void snapshot_plan_rejects_invalid_canvas_state(pp::tests::Harness& harness) void snapshot_plan_rejects_invalid_canvas_state(pp::tests::Harness& harness)
{ {
const std::uint32_t frames[] { 100U }; const std::uint32_t frames[] { 100U };
@@ -616,6 +667,9 @@ int main()
harness.run("project save commit plan succeeds for temporary swap", project_save_commit_plan_succeeds_for_temporary_swap); harness.run("project save commit plan succeeds for temporary swap", project_save_commit_plan_succeeds_for_temporary_swap);
harness.run("project save commit plan fails when target remove fails", project_save_commit_plan_fails_when_target_remove_fails); harness.run("project save commit plan fails when target remove fails", project_save_commit_plan_fails_when_target_remove_fails);
harness.run("project save commit plan flags missing target after rename failure", project_save_commit_plan_flags_missing_target_after_rename_failure); harness.run("project save commit plan flags missing target after rename failure", project_save_commit_plan_flags_missing_target_after_rename_failure);
harness.run("project save post commit plan records success side effects", project_save_post_commit_plan_records_success_side_effects);
harness.run("project save post commit plan skips timelapse without encoder", project_save_post_commit_plan_skips_timelapse_without_encoder);
harness.run("project save post commit plan preserves state after failure", project_save_post_commit_plan_preserves_state_after_failure);
harness.run("snapshot plan rejects invalid canvas state", snapshot_plan_rejects_invalid_canvas_state); harness.run("snapshot plan rejects invalid canvas state", snapshot_plan_rejects_invalid_canvas_state);
harness.run("clear plan records legacy canvas effects", clear_plan_records_legacy_canvas_effects); harness.run("clear plan records legacy canvas effects", clear_plan_records_legacy_canvas_effects);
harness.run("clear plan noops without canvas", clear_plan_noops_without_canvas); harness.run("clear plan noops without canvas", clear_plan_noops_without_canvas);

View File

@@ -416,6 +416,8 @@ struct PlanCanvasProjectSaveTargetArgs {
bool target_exists = false; bool target_exists = false;
bool remove_fails = false; bool remove_fails = false;
bool rename_fails = false; bool rename_fails = false;
bool encoder_available = false;
bool progress_visible = false;
}; };
struct PlanCanvasDocumentSnapshotArgs { struct PlanCanvasDocumentSnapshotArgs {
@@ -2562,7 +2564,7 @@ void print_help()
<< " plan-canvas-view-density [--density N] [--bad-float]\n" << " plan-canvas-view-density [--density N] [--bad-float]\n"
<< " plan-canvas-view-cursor-mode [--mode N]\n" << " plan-canvas-view-cursor-mode [--mode N]\n"
<< " plan-canvas-cursor [--mode draw|erase|line|camera|grid|copy|cut|fill|mask-free|mask-line|bucket] [--visibility never|small-brush|not-painting|always] [--brush-size N] [--no-brush] [--drawing] [--alt] [--resizing] [--picking] [--bad-size]\n" << " plan-canvas-cursor [--mode draw|erase|line|camera|grid|copy|cut|fill|mask-free|mask-line|bucket] [--visibility never|small-brush|not-painting|always] [--brush-size N] [--no-brush] [--drawing] [--alt] [--resizing] [--picking] [--bad-size]\n"
<< " plan-canvas-project-save-target [--data-dir DIR] [--path FILE] [--target-exists] [--remove-fails] [--rename-fails]\n" << " plan-canvas-project-save-target [--data-dir DIR] [--path FILE] [--target-exists] [--remove-fails] [--rename-fails] [--encoder] [--progress]\n"
<< " plan-grid-operation --kind pick|load|reload|clear|render|commit [--path FILE] [--no-heightmap] [--no-canvas] [--float32] [--float16] [--texture-resolution N] [--samples N]\n" << " plan-grid-operation --kind pick|load|reload|clear|render|commit [--path FILE] [--no-heightmap] [--no-canvas] [--float32] [--float16] [--texture-resolution N] [--samples N]\n"
<< " plan-history-operation --kind undo|redo|clear [--undo-count N] [--redo-count N] [--memory-bytes N]\n" << " plan-history-operation --kind undo|redo|clear [--undo-count N] [--redo-count N] [--memory-bytes N]\n"
<< " plan-main-toolbar --command open|save|undo|redo|clear-history|clear-canvas|message-box|settings [--undo-count N] [--redo-count N] [--memory-bytes N] [--no-canvas]\n" << " plan-main-toolbar --command open|save|undo|redo|clear-history|clear-canvas|message-box|settings [--undo-count N] [--redo-count N] [--memory-bytes N] [--no-canvas]\n"
@@ -6097,6 +6099,10 @@ pp::foundation::Status parse_plan_canvas_project_save_target_args(
args.remove_fails = true; args.remove_fails = true;
} else if (key == "--rename-fails") { } else if (key == "--rename-fails") {
args.rename_fails = true; args.rename_fails = true;
} else if (key == "--encoder") {
args.encoder_available = true;
} else if (key == "--progress") {
args.progress_visible = true;
} else { } else {
return pp::foundation::Status::invalid_argument("unknown option"); return pp::foundation::Status::invalid_argument("unknown option");
} }
@@ -6144,6 +6150,12 @@ int plan_canvas_project_save_target(int argc, char** argv)
.temporary_rename_attempted = temporary_rename_attempted, .temporary_rename_attempted = temporary_rename_attempted,
.temporary_rename_succeeded = temporary_rename_succeeded, .temporary_rename_succeeded = temporary_rename_succeeded,
}); });
const auto post_commit_plan = pp::app::plan_document_canvas_project_save_post_commit(
pp::app::DocumentCanvasProjectSavePostCommitInput {
.save_succeeded = commit_plan.saved,
.timelapse_encoder_available = args.encoder_available,
.progress_ui_visible = args.progress_visible,
});
std::cout << "{\"ok\":true,\"command\":\"plan-canvas-project-save-target\"" std::cout << "{\"ok\":true,\"command\":\"plan-canvas-project-save-target\""
<< ",\"dataDirectory\":\"" << json_escape(args.data_directory) << ",\"dataDirectory\":\"" << json_escape(args.data_directory)
<< "\",\"targetPath\":\"" << json_escape(value.target_path) << "\",\"targetPath\":\"" << json_escape(value.target_path)
@@ -6163,7 +6175,18 @@ int plan_canvas_project_save_target(int argc, char** argv)
<< ",\"temporaryRenamed\":" << json_bool(commit_plan.temporary_renamed) << ",\"temporaryRenamed\":" << json_bool(commit_plan.temporary_renamed)
<< ",\"targetMayBeMissing\":" << json_bool(commit_plan.target_may_be_missing) << ",\"targetMayBeMissing\":" << json_bool(commit_plan.target_may_be_missing)
<< ",\"message\":\"" << json_escape(std::string(commit_plan.log_message)) << ",\"message\":\"" << json_escape(std::string(commit_plan.log_message))
<< "\"}" << "\"},\"postCommitPlan\":{\"marksDocumentClean\":"
<< json_bool(post_commit_plan.marks_document_clean)
<< ",\"marksNewDocumentCommitted\":"
<< json_bool(post_commit_plan.marks_new_document_committed)
<< ",\"savesTimelapseSidecar\":"
<< json_bool(post_commit_plan.saves_timelapse_sidecar)
<< ",\"flushesPlatformStorage\":"
<< json_bool(post_commit_plan.flushes_platform_storage)
<< ",\"dismissesProgressUi\":"
<< json_bool(post_commit_plan.dismisses_progress_ui)
<< ",\"updatesTitle\":" << json_bool(post_commit_plan.updates_title)
<< "}"
<< "}\n"; << "}\n";
return 0; return 0;
} }