Plan recording export progress dialog

This commit is contained in:
2026-06-05 10:15:33 +02:00
parent 407297dc2e
commit b534c4a4da
8 changed files with 63 additions and 16 deletions

View File

@@ -665,13 +665,13 @@ Known local toolchain state:
contract before reaching legacy canvas/recording export execution. contract before reaching legacy canvas/recording export execution.
- `pano_cli plan-recording-session` exposes `pp_app_core` recording start, - `pano_cli plan-recording-session` exposes `pp_app_core` recording start,
stop, clear, platform cleanup, frame-count reset, and export progress-total stop, clear, platform cleanup, frame-count reset, and export progress-total
planning as JSON; the live recording controls consume those contracts before plus export progress-dialog planning as JSON; the live recording controls
reaching legacy recording threads, retained PBO readback call sites, and MP4 consume those contracts before reaching legacy recording threads, retained
encoder execution. PBO readback call sites, and MP4 encoder execution.
- `src/legacy_recording_services.*` is the current app-shell bridge for - `src/legacy_recording_services.*` is the current app-shell bridge for
recording start/stop/clear and MP4 export execution. It keeps those live paths recording start/stop/clear and MP4 export execution. It keeps those live paths
on the `pp_app_core` contracts while legacy recording thread ownership, on the `pp_app_core` contracts while legacy recording thread ownership,
retained PBO readback call sites, progress UI, platform cleanup, and retained PBO readback call sites, progress UI lifetime, platform cleanup, and
`MP4Encoder` execution remain `MP4Encoder` execution remain
tracked by `DEBT-0037`. tracked by `DEBT-0037`.
- `pano_cli plan-share-file` exposes `pp_app_core` share availability planning - `pano_cli plan-share-file` exposes `pp_app_core` share availability planning

View File

@@ -166,6 +166,12 @@ agent or engineer to remove them without reconstructing context from chat.
`App::rec_loop` and `pano_cli plan-recording-session`; retained PBO `App::rec_loop` and `pano_cli plan-recording-session`; retained PBO
equirect generation, dirty-stroke mutation, MP4 encoder calls, and frame equirect generation, dirty-stroke mutation, MP4 encoder calls, and frame
label rendering remain in the legacy app/canvas/video path. label rendering remain in the legacy app/canvas/video path.
- 2026-06-05: DEBT-0037 was narrowed. MP4 recording export progress-dialog
metadata now lives in tested `pp_app_core` planning, is exposed by
`pano_cli plan-recording-session`, and retained progress bar creation now
routes through `src/legacy_app_dialog_services.*`. Recording thread
lifecycle, frame readback scheduling, progress lifetime, and MP4 writing
remain open.
- 2026-06-05: DEBT-0038 was narrowed. Cloud transfer request/progress policy - 2026-06-05: DEBT-0038 was narrowed. Cloud transfer request/progress policy
now lives in tested `pp_app_core` planning, live `App::download` and now lives in tested `pp_app_core` planning, live `App::download` and
`App::upload` consume those plans before retained CURL setup, and `App::upload` consume those plans before retained CURL setup, and
@@ -261,7 +267,7 @@ agent or engineer to remove them without reconstructing context from chat.
| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter | | DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter |
| DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary; toolbar test-message dialog metadata now lives in `pp_app_core` through `plan_main_toolbar_message_dialog`, retained message-box creation routes through `src/legacy_app_dialog_services.*`, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy open/save/settings dialogs, stores raw compatibility pointers, and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command message-box`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter | | DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary; toolbar test-message dialog metadata now lives in `pp_app_core` through `plan_main_toolbar_message_dialog`, retained message-box creation routes through `src/legacy_app_dialog_services.*`, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy open/save/settings dialogs, stores raw compatibility pointers, and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command message-box`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter |
| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read `ShaderManager::ext_*` flags directly. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. The retained `copy_framebuffer_to_texture_2d` utility bridge now routes 2D framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch, retained `RTT::create`/`RTT::destroy` render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested `pp_renderer_gl` dispatch, retained RTT clear, masked clear with color-write-mask restore, texture bind/unbind, and RGBA8 dirty-region texture writes now route through tested `pp_renderer_gl` dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, retained NodeCanvas, CanvasMode, and NodePanelGrid capability-state snapshots now route through tested `pp_renderer_gl` query dispatch, CanvasLayer cube/equirect generation plus frame clears now route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch adapters, `NodePanelGrid` live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, bake viewport execution, sun-overlay viewport query, and desktop texture-resize readback through tested `pp_renderer_gl` dispatch adapters, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, transform/cut viewport execution, paint-mode blend/depth state snapshots, and canvas-tip pick framebuffer readback through tested `pp_renderer_gl` dispatch adapters, retained simple UI draw paths now share `legacy_ui_gl_dispatch` for blend-state execution, fallback 2D texture unbinds, `NodeViewport` viewport query/restore, color-buffer clears, and clear-color restore, retained `NodeCanvas` plus `NodeStrokePreview` draw-state paths now route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested `pp_renderer_gl` dispatch helpers, and retained `Canvas` plus `CanvasLayer` stroke/object/thumbnail/frame-clear draw-state paths now route saved viewport or clear-color query and restore through the same tested helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and the retained `ShaderManager::ext_*` compatibility fields still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior | | DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read `ShaderManager::ext_*` flags directly. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. The retained `copy_framebuffer_to_texture_2d` utility bridge now routes 2D framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch, retained `RTT::create`/`RTT::destroy` render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested `pp_renderer_gl` dispatch, retained RTT clear, masked clear with color-write-mask restore, texture bind/unbind, and RGBA8 dirty-region texture writes now route through tested `pp_renderer_gl` dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, retained NodeCanvas, CanvasMode, and NodePanelGrid capability-state snapshots now route through tested `pp_renderer_gl` query dispatch, CanvasLayer cube/equirect generation plus frame clears now route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch adapters, `NodePanelGrid` live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, bake viewport execution, sun-overlay viewport query, and desktop texture-resize readback through tested `pp_renderer_gl` dispatch adapters, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, transform/cut viewport execution, paint-mode blend/depth state snapshots, and canvas-tip pick framebuffer readback through tested `pp_renderer_gl` dispatch adapters, retained simple UI draw paths now share `legacy_ui_gl_dispatch` for blend-state execution, fallback 2D texture unbinds, `NodeViewport` viewport query/restore, color-buffer clears, and clear-color restore, retained `NodeCanvas` plus `NodeStrokePreview` draw-state paths now route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested `pp_renderer_gl` dispatch helpers, and retained `Canvas` plus `CanvasLayer` stroke/object/thumbnail/frame-clear draw-state paths now route saved viewport or clear-color query and restore through the same tested helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and the retained `ShaderManager::ext_*` compatibility fields still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior |
| DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, and retained `PBO` allocation/readback/map/unmap/delete operations now route through tested `pp_renderer_gl` dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, retained `App::rec_loop` readback call sites, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pp_renderer_gl_capabilities_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback scheduling, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters | | DEBT-0037 | Open | Modernization | Recording lifecycle/export planning, export progress-dialog metadata, and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, retained MP4 export progress bar creation routes through `src/legacy_app_dialog_services.*`, and retained `PBO` allocation/readback/map/unmap/delete operations now route through tested `pp_renderer_gl` dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress lifetime, retained `App::rec_loop` readback call sites, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pp_renderer_gl_capabilities_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-deletes-recorded-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback scheduling, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters |
| DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning, cloud prompt/progress metadata, and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, `pano_cli plan-cloud-transfer`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download` CURL helpers now consume `pp_app_core` cloud transfer request/progress planning and the platform TLS-verification bypass policy before retained CURL setup, upload warning/publish/success prompts, upload/bulk progress dialog titles, download-progress prompt metadata, and formatted download progress text come from tested app-core plans, retained cloud prompt/progress creation routes through `src/legacy_app_dialog_services.*` via `App::message_box`/`App::show_progress` or direct bridge calls, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, upload form construction, response/error handling, retained prompt/progress lifetime, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters | | DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning, cloud prompt/progress metadata, and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, `pano_cli plan-cloud-transfer`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download` CURL helpers now consume `pp_app_core` cloud transfer request/progress planning and the platform TLS-verification bypass policy before retained CURL setup, upload warning/publish/success prompts, upload/bulk progress dialog titles, download-progress prompt metadata, and formatted download progress text come from tested app-core plans, retained cloud prompt/progress creation routes through `src/legacy_app_dialog_services.*` via `App::message_box`/`App::show_progress` or direct bridge calls, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, upload form construction, response/error handling, retained prompt/progress lifetime, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters |
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter | | DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |
| DEBT-0040 | Open | Modernization | Close request, document save, save-before-workflow planning/execution dispatch, and close/save-before/save-error prompt metadata now consume pure `pp_app_core` through `App::request_close`, `App::save_document`, `App::continue_document_workflow_after_optional_save`, `pano_cli simulate-app-session`, `pano_cli plan-document-session-prompt`, `DocumentSaveServices`, `CloseRequestServices`, `DocumentWorkflowServices`, and `src/legacy_document_session_services.*`; close/save-before prompt creation now uses `src/legacy_app_dialog_services.*`, and Save dialog working-directory picker visibility/path formatting now dispatches through `PlatformServices`, but the bridge still opens retained save dialogs, wires prompt callbacks directly, calls `Canvas::I->project_save`, mutates the unsaved flag on close confirmation, invokes native app close, and routes save-version through the retained legacy dialog | Preserve current close/save/dirty-workflow behavior while document session execution moves toward app/document/UI/platform services | `pp_app_core_document_session_tests`; `pp_platform_api_tests`; `pano_cli plan-document-session-prompt --kind close-unsaved`; `pano_cli plan-document-session-prompt --kind save-before-workflow`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `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`; `ctest --preset desktop-fast --build-config Debug` | Close prompt execution, native close requests, dirty-workflow save prompts, existing-project saves, save dialogs, save-version execution, and unsaved-flag mutation are owned by injected app/document/UI/platform services with `App` methods acting only as adapters | | DEBT-0040 | Open | Modernization | Close request, document save, save-before-workflow planning/execution dispatch, and close/save-before/save-error prompt metadata now consume pure `pp_app_core` through `App::request_close`, `App::save_document`, `App::continue_document_workflow_after_optional_save`, `pano_cli simulate-app-session`, `pano_cli plan-document-session-prompt`, `DocumentSaveServices`, `CloseRequestServices`, `DocumentWorkflowServices`, and `src/legacy_document_session_services.*`; close/save-before prompt creation now uses `src/legacy_app_dialog_services.*`, and Save dialog working-directory picker visibility/path formatting now dispatches through `PlatformServices`, but the bridge still opens retained save dialogs, wires prompt callbacks directly, calls `Canvas::I->project_save`, mutates the unsaved flag on close confirmation, invokes native app close, and routes save-version through the retained legacy dialog | Preserve current close/save/dirty-workflow behavior while document session execution moves toward app/document/UI/platform services | `pp_app_core_document_session_tests`; `pp_platform_api_tests`; `pano_cli plan-document-session-prompt --kind close-unsaved`; `pano_cli plan-document-session-prompt --kind save-before-workflow`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `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`; `ctest --preset desktop-fast --build-config Debug` | Close prompt execution, native close requests, dirty-workflow save prompts, existing-project saves, save dialogs, save-version execution, and unsaved-flag mutation are owned by injected app/document/UI/platform services with `App` methods acting only as adapters |

View File

@@ -527,11 +527,13 @@ including platform-style destinations and no-message/suppressed branches.
dialog metadata now consumed by live export dialogs before retained legacy dialog metadata now consumed by live export dialogs before retained legacy
export execution/logging continues. export execution/logging continues.
`pano_cli plan-recording-session` exposes the app-core recording start, stop, `pano_cli plan-recording-session` exposes the app-core recording start, stop,
clear, platform recorded-file cleanup, frame reset, and export progress-total clear, platform recorded-file cleanup, frame reset, export progress-total, and
decisions used by the live recording controls. Recording lifecycle and MP4 export progress-dialog decisions used by the live recording controls. Recording
export execution now dispatch through `RecordingServices` in lifecycle and MP4 export execution now dispatch through `RecordingServices` in
`src/legacy_recording_services.*` before legacy recording threads, PBO `src/legacy_recording_services.*` before legacy recording threads, PBO readback,
readback, and MP4 encoder execution continue. and MP4 encoder execution continue. The retained MP4 export progress bar now
uses `src/legacy_app_dialog_services.*` for creation while progress lifetime
and MP4 writing remain legacy-owned.
`pano_cli plan-share-file` exposes the app-core saved-path decision used by the `pano_cli plan-share-file` exposes the app-core saved-path decision used by the
live platform share command before iOS/macOS sharing bridges or retained no-op live platform share command before iOS/macOS sharing bridges or retained no-op
platform branches execute. platform branches execute.
@@ -1891,6 +1893,17 @@ Results:
app-core recording lifecycle/export decisions as JSON. On 2026-06-05, app-core recording lifecycle/export decisions as JSON. On 2026-06-05,
`pano_cli_plan_recording_session_missing_encoder_smoke` was added for the `pano_cli_plan_recording_session_missing_encoder_smoke` was added for the
worker no-encode path. worker no-encode path.
- `PanoPainter`, `pp_app_core_document_recording_tests`,
`pp_app_core_app_dialog_tests`, and `pano_cli` built after MP4 recording
export progress-dialog metadata moved into `pp_app_core` and retained
progress bar creation routed through `src/legacy_app_dialog_services.*`.
- Focused recording/app-dialog CTest coverage passed for
`pp_app_core_document_recording_tests`, `pp_app_core_app_dialog_tests`,
`pano_cli_plan_recording_session_*`, and `pano_cli_plan_app_dialog_*`,
including the MP4 export progress-dialog metadata smoke.
- Android arm64 headless `pp_app_core`, `pano_cli`,
`pp_app_core_document_recording_tests`, and `pp_app_core_app_dialog_tests`
built after the recording progress bridge split.
- `pp_app_core_document_resize_tests` passed, covering resize dialog state, - `pp_app_core_document_resize_tests` passed, covering resize dialog state,
unknown current-resolution labeling, selected-resolution mapping, square unknown current-resolution labeling, selected-resolution mapping, square
canvas sizing, history-clearing intent, invalid selection rejection, service canvas sizing, history-clearing intent, invalid selection rejection, service

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "app_core/app_dialog.h"
#include "foundation/result.h" #include "foundation/result.h"
#include <cstddef> #include <cstddef>
@@ -84,6 +85,12 @@ public:
}; };
} }
[[nodiscard]] inline AppProgressDialogPlan plan_recording_export_progress_dialog(
const RecordingExportPlan& plan)
{
return plan_app_progress_dialog("Exporting MP4 movie", plan.progress_total);
}
[[nodiscard]] constexpr RecordingWorkerIterationPlan plan_recording_worker_iteration( [[nodiscard]] constexpr RecordingWorkerIterationPlan plan_recording_worker_iteration(
bool is_running_after_wake, bool is_running_after_wake,
bool has_encoder, bool has_encoder,

View File

@@ -4,6 +4,7 @@
#include "app.h" #include "app.h"
#include "canvas.h" #include "canvas.h"
#include "legacy_app_dialog_services.h"
#include "node_progress_bar.h" #include "node_progress_bar.h"
namespace pp::panopainter { namespace pp::panopainter {
@@ -48,11 +49,11 @@ public:
void begin_export(int progress_total) override void begin_export(int progress_total) override
{ {
progress_ = app_.layout[app_.main_id]->add_child<NodeProgressBar>(); pp::app::RecordingExportPlan plan;
progress_->m_progress->SetWidthP(0); plan.progress_total = progress_total;
progress_->m_title->set_text("Exporting MP4 movie"); progress_ = pp::panopainter::create_legacy_app_progress_dialog(
progress_->m_total = progress_total; app_,
progress_->m_count = 0; pp::app::plan_recording_export_progress_dialog(plan)).get();
} }
void write_mp4(std::string_view path) override void write_mp4(std::string_view path) override

View File

@@ -1056,7 +1056,7 @@ if(TARGET pano_cli)
COMMAND pano_cli plan-recording-session --frame-count 12) COMMAND pano_cli plan-recording-session --frame-count 12)
set_tests_properties(pano_cli_plan_recording_session_stopped_smoke PROPERTIES set_tests_properties(pano_cli_plan_recording_session_stopped_smoke PROPERTIES
LABELS "app;integration;desktop-fast" LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-recording-session\".*\"running\":false.*\"frameCount\":12.*\"startDecision\":\"start-thread\".*\"stopDecision\":\"no-op-not-running\".*\"stopRunningRecording\":false.*\"deleteRecordedFiles\":false.*\"progressTotal\":12") PASS_REGULAR_EXPRESSION "\"command\":\"plan-recording-session\".*\"running\":false.*\"frameCount\":12.*\"startDecision\":\"start-thread\".*\"stopDecision\":\"no-op-not-running\".*\"stopRunningRecording\":false.*\"deleteRecordedFiles\":false.*\"progressTotal\":12.*\"progressDialog\":\\{\"title\":\"Exporting MP4 movie\",\"total\":12,\"count\":0,\"progressFraction\":0")
add_test(NAME pano_cli_plan_recording_session_running_smoke add_test(NAME pano_cli_plan_recording_session_running_smoke
COMMAND pano_cli plan-recording-session --running --frame-count 12) COMMAND pano_cli plan-recording-session --running --frame-count 12)

View File

@@ -111,6 +111,17 @@ void recording_export_tracks_frame_count(pp::tests::Harness& harness)
PP_EXPECT(harness, plan.progress_total == 120); PP_EXPECT(harness, plan.progress_total == 120);
} }
void recording_export_progress_dialog_preserves_legacy_title(pp::tests::Harness& harness)
{
const auto export_plan = pp::app::plan_recording_export(7);
const auto progress = pp::app::plan_recording_export_progress_dialog(export_plan);
PP_EXPECT(harness, progress.title == "Exporting MP4 movie");
PP_EXPECT(harness, progress.total == 7);
PP_EXPECT(harness, progress.count == 0);
PP_EXPECT(harness, progress.progress_fraction == 0.0F);
}
void recording_export_clamps_progress_total(pp::tests::Harness& harness) void recording_export_clamps_progress_total(pp::tests::Harness& harness)
{ {
const auto too_many_frames = static_cast<std::size_t>(std::numeric_limits<int>::max()) + 1U; const auto too_many_frames = static_cast<std::size_t>(std::numeric_limits<int>::max()) + 1U;
@@ -198,6 +209,9 @@ int main()
"recording clear resets frames and preserves platform delete flag", "recording clear resets frames and preserves platform delete flag",
recording_clear_resets_frames_and_preserves_platform_delete_flag); recording_clear_resets_frames_and_preserves_platform_delete_flag);
harness.run("recording export tracks frame count", recording_export_tracks_frame_count); harness.run("recording export tracks frame count", recording_export_tracks_frame_count);
harness.run(
"recording export progress dialog preserves legacy title",
recording_export_progress_dialog_preserves_legacy_title);
harness.run("recording export clamps progress total", recording_export_clamps_progress_total); harness.run("recording export clamps progress total", recording_export_clamps_progress_total);
harness.run("recording worker iteration encodes only when ready", recording_worker_iteration_encodes_only_when_ready); harness.run("recording worker iteration encodes only when ready", recording_worker_iteration_encodes_only_when_ready);
harness.run("executor dispatches recording lifecycle", executor_dispatches_recording_lifecycle); harness.run("executor dispatches recording lifecycle", executor_dispatches_recording_lifecycle);

View File

@@ -4268,6 +4268,7 @@ int plan_recording_session(int argc, char** argv)
args.running, args.running,
args.platform_deletes_recorded_files); args.platform_deletes_recorded_files);
const auto export_plan = pp::app::plan_recording_export(args.frame_count); const auto export_plan = pp::app::plan_recording_export(args.frame_count);
const auto progress_dialog = pp::app::plan_recording_export_progress_dialog(export_plan);
const auto worker = pp::app::plan_recording_worker_iteration( const auto worker = pp::app::plan_recording_worker_iteration(
args.running, args.running,
args.encoder_available, args.encoder_available,
@@ -4285,6 +4286,11 @@ int plan_recording_session(int argc, char** argv)
<< ",\"frameCountAfterClear\":" << clear.frame_count_after_clear << ",\"frameCountAfterClear\":" << clear.frame_count_after_clear
<< "},\"export\":{\"frameCount\":" << export_plan.frame_count << "},\"export\":{\"frameCount\":" << export_plan.frame_count
<< ",\"progressTotal\":" << export_plan.progress_total << ",\"progressTotal\":" << export_plan.progress_total
<< ",\"progressDialog\":{\"title\":\"" << json_escape(progress_dialog.title)
<< "\",\"total\":" << progress_dialog.total
<< ",\"count\":" << progress_dialog.count
<< ",\"progressFraction\":" << progress_dialog.progress_fraction
<< "}"
<< "},\"worker\":{\"continueRunning\":" << json_bool(worker.continue_running) << "},\"worker\":{\"continueRunning\":" << json_bool(worker.continue_running)
<< ",\"encodeFrame\":" << json_bool(worker.encode_frame) << ",\"encodeFrame\":" << json_bool(worker.encode_frame)
<< ",\"clearDirtyStroke\":" << json_bool(worker.clear_dirty_stroke) << ",\"clearDirtyStroke\":" << json_bool(worker.clear_dirty_stroke)