Plan cloud browse decisions in app core

This commit is contained in:
2026-06-02 23:39:03 +02:00
parent 3a78361aea
commit 8a7db3bca8
9 changed files with 174 additions and 4 deletions

View File

@@ -422,6 +422,10 @@ Known local toolchain state:
new-document warning, publish prompt, and save-before-upload planning as JSON; new-document warning, publish prompt, and save-before-upload planning as JSON;
the live cloud upload command consumes the same start contract before the live cloud upload command consumes the same start contract before
reaching legacy UI, canvas save, and network upload execution. reaching legacy UI, canvas save, and network upload execution.
- `pano_cli plan-cloud-browse` exposes `pp_app_core` cloud browse availability
and selected-file download planning as JSON; the live cloud browse command
consumes those contracts before reaching legacy dialog, network download,
canvas project-open, layer UI, and action-history execution.
- `pano_cli simulate-app-session` exposes `pp_app_core` project-open, - `pano_cli simulate-app-session` exposes `pp_app_core` project-open,
app-close, save, save-as, save-version, and save-before-workflow decisions app-close, save, save-as, save-version, and save-before-workflow decisions
as JSON and is covered for clean, dirty, already-prompting, missing-canvas, as JSON and is covered for clean, dirty, already-prompting, missing-canvas,
@@ -436,6 +440,7 @@ Known local toolchain state:
decisions. decisions.
- `pp_app_core_document_cloud_tests` covers cloud upload no-canvas, - `pp_app_core_document_cloud_tests` covers cloud upload no-canvas,
new-document warning, clean publish prompt, and dirty save-before-upload new-document warning, clean publish prompt, and dirty save-before-upload
decisions, plus cloud browse no-canvas/show-browser and selected-download
decisions. decisions.
- `pp_app_core_document_session_tests` covers clean and dirty app session, - `pp_app_core_document_session_tests` covers clean and dirty app session,
document-open action planning, save-request, save-before-workflow, document-open action planning, save-request, save-before-workflow,

View File

@@ -77,7 +77,7 @@ and validation command.
| Capability | Current Area | Target Owner | Required Tests | | Capability | Current Area | Target Owner | Required Tests |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Upload/download/browse | `app_cloud`, CURL helpers | `pp_app_core`, app service, `pp_platform_*` | Upload prompt/new-doc/no-canvas decision tests, mocked HTTP and timeout tests | | Upload/download/browse | `app_cloud`, CURL helpers | `pp_app_core`, app service, `pp_platform_*` | Upload prompt/new-doc/no-canvas decision tests, browse/selection decision tests, mocked HTTP and timeout tests |
| License/check flows | app/cloud code | app service | Mocked response tests | | License/check flows | app/cloud code | app service | Mocked response tests |
| Logging/crash reporting | `log`, BugTrap/AppCenter | `pp_foundation`, platform wrappers | Log formatting and platform compile | | Logging/crash reporting | `log`, BugTrap/AppCenter | `pp_foundation`, platform wrappers | Log formatting and platform compile |
| Headless automation | none yet | `tools/pano_cli` | JSON command fixtures | | Headless automation | none yet | `tools/pano_cli` | JSON command fixtures |

View File

@@ -22,7 +22,7 @@ agent or engineer to remove them without reconstructing context from chat.
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets | | DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation | | DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, `App::cloud_upload`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new-document target/resolution/overwrite decisions, save-as document file naming and overwrite decisions, save-version target decisions, export start/target naming/path decisions, cloud-upload prompt/save-before-upload decisions, `pano_cli classify-open`, `pano_cli plan-open-route`, `pano_cli plan-new-document`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-start`, `pano_cli plan-export-target`, `pano_cli plan-cloud-upload`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export/cloud contracts, but document creation/loading, brush import execution, saving, export execution, cloud upload execution, and cloud browse/download still reach legacy `Canvas::I`/UI/network singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_cloud_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries | | DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, `App::cloud_upload`, `App::cloud_browse`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new-document target/resolution/overwrite decisions, save-as document file naming and overwrite decisions, save-version target decisions, export start/target naming/path decisions, cloud-upload prompt/save-before-upload decisions, cloud-browse availability and selected-download decisions, `pano_cli classify-open`, `pano_cli plan-open-route`, `pano_cli plan-new-document`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-start`, `pano_cli plan-export-target`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-browse`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export/cloud contracts, but document creation/loading, brush import execution, saving, export execution, cloud upload execution, and cloud browse/download execution still reach legacy `Canvas::I`/UI/network singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_cloud_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path | | DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated | | DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions | | DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |

View File

@@ -448,6 +448,10 @@ canvas/recording export execution.
the live cloud upload command for missing-canvas, new-document warning, publish the live cloud upload command for missing-canvas, new-document warning, publish
prompt, and dirty-document save-before-upload states before legacy UI, canvas, prompt, and dirty-document save-before-upload states before legacy UI, canvas,
and network execution continue. and network execution continue.
`pano_cli plan-cloud-browse` exposes the app-core cloud browse and selected
download decisions used by the live cloud browse command before legacy dialog,
network download, canvas project-open, layer UI, and action-history execution
continue.
`pano_cli parse-layout` exercises the XML layout path. Continue expanding `pano_cli parse-layout` exercises the XML layout path. Continue expanding
document behavior toward legacy Canvas parity and then port OpenGL classes document behavior toward legacy Canvas parity and then port OpenGL classes
behind the renderer boundary. behind the renderer boundary.
@@ -895,12 +899,17 @@ Results:
aggregate stroke-script counts/distances. aggregate stroke-script counts/distances.
- `pp_app_core_document_cloud_tests` passed, covering cloud upload no-canvas, - `pp_app_core_document_cloud_tests` passed, covering cloud upload no-canvas,
new-document warning, clean publish prompt, and dirty save-before-upload new-document warning, clean publish prompt, and dirty save-before-upload
decisions, plus cloud browse no-canvas/show-browser and selected-download
decisions. decisions.
- `pano_cli_plan_cloud_upload_clean_smoke`, - `pano_cli_plan_cloud_upload_clean_smoke`,
`pano_cli_plan_cloud_upload_unsaved_smoke`, `pano_cli_plan_cloud_upload_unsaved_smoke`,
`pano_cli_plan_cloud_upload_new_document_smoke`, and `pano_cli_plan_cloud_upload_new_document_smoke`, and
`pano_cli_plan_cloud_upload_no_canvas_smoke` passed and expose those app-core `pano_cli_plan_cloud_upload_no_canvas_smoke` passed and expose those app-core
cloud upload decisions as JSON. cloud upload decisions as JSON.
- `pano_cli_plan_cloud_browse_waiting_smoke`,
`pano_cli_plan_cloud_browse_selected_smoke`, and
`pano_cli_plan_cloud_browse_no_canvas_smoke` passed and expose app-core cloud
browse/download-selection decisions as JSON.
- `panopainter_validate_shaders` passed, validating 25 shader programs and 7 - `panopainter_validate_shaders` passed, validating 25 shader programs and 7
shader includes for stage markers and include graph integrity. shader includes for stage markers and include graph integrity.
- `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless, - `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless,

View File

@@ -83,8 +83,14 @@ void App::cloud_upload_all()
void App::cloud_browse() void App::cloud_browse()
{ {
if (!canvas) const auto browse_plan = pp::app::plan_cloud_browse(canvas != nullptr);
switch (browse_plan)
{
case pp::app::CloudBrowseAction::unavailable_no_canvas:
return; return;
case pp::app::CloudBrowseAction::show_browser:
break;
}
// load thumbnail test // load thumbnail test
auto dialog = std::make_shared<NodeDialogCloud>(); auto dialog = std::make_shared<NodeDialogCloud>();
@@ -97,7 +103,8 @@ void App::cloud_browse()
dialog->btn_ok->on_click = [this, dialog](Node*) dialog->btn_ok->on_click = [this, dialog](Node*)
{ {
if (dialog->selected_file.empty()) const auto selection_plan = pp::app::plan_cloud_download_selection(dialog->selected_file);
if (selection_plan == pp::app::CloudDownloadSelectionAction::wait_for_selection)
return; return;
dialog->destroy(); dialog->destroy();
std::thread([this, dialog] { std::thread([this, dialog] {

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <string_view>
namespace pp::app { namespace pp::app {
enum class CloudUploadAction { enum class CloudUploadAction {
@@ -8,6 +10,16 @@ enum class CloudUploadAction {
prompt_publish, prompt_publish,
}; };
enum class CloudBrowseAction {
unavailable_no_canvas,
show_browser,
};
enum class CloudDownloadSelectionAction {
wait_for_selection,
start_download,
};
struct CloudUploadPlan { struct CloudUploadPlan {
CloudUploadAction action = CloudUploadAction::unavailable_no_canvas; CloudUploadAction action = CloudUploadAction::unavailable_no_canvas;
bool save_before_upload = false; bool save_before_upload = false;
@@ -29,4 +41,19 @@ struct CloudUploadPlan {
return { CloudUploadAction::prompt_publish, has_unsaved_changes }; return { CloudUploadAction::prompt_publish, has_unsaved_changes };
} }
[[nodiscard]] constexpr CloudBrowseAction plan_cloud_browse(bool has_canvas) noexcept
{
return has_canvas
? CloudBrowseAction::show_browser
: CloudBrowseAction::unavailable_no_canvas;
}
[[nodiscard]] constexpr CloudDownloadSelectionAction plan_cloud_download_selection(
std::string_view selected_file) noexcept
{
return selected_file.empty()
? CloudDownloadSelectionAction::wait_for_selection
: CloudDownloadSelectionAction::start_download;
}
} }

View File

@@ -513,6 +513,24 @@ if(TARGET pano_cli)
LABELS "app;integration;desktop-fast;fuzz" LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":false.*\"decision\":\"unavailable-no-canvas\".*\"saveBeforeUpload\":false") PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":false.*\"decision\":\"unavailable-no-canvas\".*\"saveBeforeUpload\":false")
add_test(NAME pano_cli_plan_cloud_browse_waiting_smoke
COMMAND pano_cli plan-cloud-browse)
set_tests_properties(pano_cli_plan_cloud_browse_waiting_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-browse\".*\"hasCanvas\":true.*\"selectedFile\":\"\".*\"browseDecision\":\"show-browser\".*\"selectionDecision\":\"wait-for-selection\"")
add_test(NAME pano_cli_plan_cloud_browse_selected_smoke
COMMAND pano_cli plan-cloud-browse --selected-file demo.ppi)
set_tests_properties(pano_cli_plan_cloud_browse_selected_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-browse\".*\"hasCanvas\":true.*\"selectedFile\":\"demo.ppi\".*\"browseDecision\":\"show-browser\".*\"selectionDecision\":\"start-download\"")
add_test(NAME pano_cli_plan_cloud_browse_no_canvas_smoke
COMMAND pano_cli plan-cloud-browse --no-canvas --selected-file demo.ppi)
set_tests_properties(pano_cli_plan_cloud_browse_no_canvas_smoke PROPERTIES
LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-browse\".*\"hasCanvas\":false.*\"browseDecision\":\"unavailable-no-canvas\".*\"selectionDecision\":\"start-download\"")
add_test(NAME pano_cli_simulate_app_session_clean_smoke add_test(NAME pano_cli_simulate_app_session_clean_smoke
COMMAND pano_cli simulate-app-session) COMMAND pano_cli simulate-app-session)
set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES

View File

@@ -31,6 +31,30 @@ void cloud_upload_records_save_before_upload_for_dirty_existing_documents(pp::te
PP_EXPECT(harness, plan.save_before_upload); PP_EXPECT(harness, plan.save_before_upload);
} }
void cloud_browse_is_unavailable_without_canvas(pp::tests::Harness& harness)
{
PP_EXPECT(harness, pp::app::plan_cloud_browse(false) == pp::app::CloudBrowseAction::unavailable_no_canvas);
}
void cloud_browse_shows_browser_with_canvas(pp::tests::Harness& harness)
{
PP_EXPECT(harness, pp::app::plan_cloud_browse(true) == pp::app::CloudBrowseAction::show_browser);
}
void cloud_download_selection_waits_for_empty_file(pp::tests::Harness& harness)
{
PP_EXPECT(
harness,
pp::app::plan_cloud_download_selection("") == pp::app::CloudDownloadSelectionAction::wait_for_selection);
}
void cloud_download_selection_starts_for_selected_file(pp::tests::Harness& harness)
{
PP_EXPECT(
harness,
pp::app::plan_cloud_download_selection("demo.ppi") == pp::app::CloudDownloadSelectionAction::start_download);
}
} }
int main() int main()
@@ -40,5 +64,9 @@ int main()
harness.run("cloud upload warns for new documents", cloud_upload_warns_for_new_documents); harness.run("cloud upload warns for new documents", cloud_upload_warns_for_new_documents);
harness.run("cloud upload prompts for clean existing documents", cloud_upload_prompts_for_clean_existing_documents); harness.run("cloud upload prompts for clean existing documents", cloud_upload_prompts_for_clean_existing_documents);
harness.run("cloud upload records save before upload for dirty existing documents", cloud_upload_records_save_before_upload_for_dirty_existing_documents); harness.run("cloud upload records save before upload for dirty existing documents", cloud_upload_records_save_before_upload_for_dirty_existing_documents);
harness.run("cloud browse is unavailable without canvas", cloud_browse_is_unavailable_without_canvas);
harness.run("cloud browse shows browser with canvas", cloud_browse_shows_browser_with_canvas);
harness.run("cloud download selection waits for empty file", cloud_download_selection_waits_for_empty_file);
harness.run("cloud download selection starts for selected file", cloud_download_selection_starts_for_selected_file);
return harness.finish(); return harness.finish();
} }

View File

@@ -143,6 +143,11 @@ struct PlanCloudUploadArgs {
bool unsaved = false; bool unsaved = false;
}; };
struct PlanCloudBrowseArgs {
bool has_canvas = true;
std::string selected_file;
};
struct SimulateAppSessionArgs { struct SimulateAppSessionArgs {
bool has_canvas = true; bool has_canvas = true;
bool new_document = false; bool new_document = false;
@@ -416,6 +421,30 @@ const char* cloud_upload_action_name(pp::app::CloudUploadAction action) noexcept
return "unavailable-no-canvas"; return "unavailable-no-canvas";
} }
const char* cloud_browse_action_name(pp::app::CloudBrowseAction action) noexcept
{
switch (action) {
case pp::app::CloudBrowseAction::unavailable_no_canvas:
return "unavailable-no-canvas";
case pp::app::CloudBrowseAction::show_browser:
return "show-browser";
}
return "unavailable-no-canvas";
}
const char* cloud_download_selection_action_name(pp::app::CloudDownloadSelectionAction action) noexcept
{
switch (action) {
case pp::app::CloudDownloadSelectionAction::wait_for_selection:
return "wait-for-selection";
case pp::app::CloudDownloadSelectionAction::start_download:
return "start-download";
}
return "wait-for-selection";
}
pp::foundation::Result<float> parse_float_arg(std::string_view text) pp::foundation::Result<float> parse_float_arg(std::string_view text)
{ {
float value = 0.0F; float value = 0.0F;
@@ -453,6 +482,7 @@ void print_help()
<< " plan-export-start [--requires-license] [--demo] [--no-canvas]\n" << " plan-export-start [--requires-license] [--demo] [--no-canvas]\n"
<< " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n" << " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n"
<< " plan-cloud-upload [--no-canvas] [--new-document] [--unsaved]\n" << " plan-cloud-upload [--no-canvas] [--new-document] [--unsaved]\n"
<< " plan-cloud-browse [--no-canvas] [--selected-file FILE]\n"
<< " load-project --path FILE\n" << " load-project --path FILE\n"
<< " parse-layout --path FILE\n" << " parse-layout --path FILE\n"
<< " record-render [--width N] [--height N] [--exercise-clear]\n" << " record-render [--width N] [--height N] [--exercise-clear]\n"
@@ -1640,6 +1670,48 @@ int plan_cloud_upload(int argc, char** argv)
return 0; return 0;
} }
pp::foundation::Status parse_plan_cloud_browse_args(
int argc,
char** argv,
PlanCloudBrowseArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--no-canvas") {
args.has_canvas = false;
} else if (key == "--selected-file") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
args.selected_file = argv[++i];
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
int plan_cloud_browse(int argc, char** argv)
{
PlanCloudBrowseArgs args;
const auto status = parse_plan_cloud_browse_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-cloud-browse", status.message);
return 2;
}
const auto browse = pp::app::plan_cloud_browse(args.has_canvas);
const auto selection = pp::app::plan_cloud_download_selection(args.selected_file);
std::cout << "{\"ok\":true,\"command\":\"plan-cloud-browse\""
<< ",\"state\":{\"hasCanvas\":" << json_bool(args.has_canvas)
<< ",\"selectedFile\":\"" << json_escape(args.selected_file)
<< "\"},\"browseDecision\":\"" << cloud_browse_action_name(browse)
<< "\",\"selectionDecision\":\"" << cloud_download_selection_action_name(selection)
<< "\"}\n";
return 0;
}
pp::foundation::Status parse_plan_export_target_args( pp::foundation::Status parse_plan_export_target_args(
int argc, int argc,
char** argv, char** argv,
@@ -3795,6 +3867,10 @@ int main(int argc, char** argv)
return plan_cloud_upload(argc, argv); return plan_cloud_upload(argc, argv);
} }
if (command == "plan-cloud-browse") {
return plan_cloud_browse(argc, argv);
}
if (command == "load-project") { if (command == "load-project") {
return load_project(argc, argv); return load_project(argc, argv);
} }