Skip to content

Feature/core/1 handling file management - #9

Merged
sromic1990 merged 42 commits into
mainfrom
feature/core/1-handling-file-management
Mar 21, 2026
Merged

sromic1990 merged 42 commits into
mainfrom
feature/core/1-handling-file-management

Conversation

@sromic1990

@sromic1990 sromic1990 commented Mar 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

Implements the file selection layer for Doculith. Users can now pick .docx files through the OS's native file picker, queue them for conversion, choose an output PDF path via a native save dialog, and manage the queue (remove individual files, clear all). This is the first chapter where the app becomes genuinely interactive.


What's Changed

New: DocumentQueue

Core domain type that owns the list of files queued for conversion. Enforces a strict invariant: all entries must be unique, canonical .docx paths sharing a common parent directory. addFiles() validates every candidate against this invariant in a documented 7-step pipeline and returns an AddResult with human-readable feedback. Also exposes removeAt(), move() (for drag-and-drop, Chapter 9), clear(), and status/thumbnail mutators.

New: Document

Lightweight value type holding sourcePath, pdfPath, displayName, ConversionStatus, and thumbnailTextureId. Serves as the single source of truth for per-file state throughout the pipeline.

Modified: AppModel

Extended with DocumentQueue queue, outputPathBuf[512], addFeedback, and refreshOutputPath(). The output path buffer is the one place in the codebase where ImGui writes directly into the model — intentional, bounded, and documented.

Modified: UiEvents

Added removeAtIndex, moveFromIndex, and moveToIndex (all std::optional<size_t>). The move fields are declared as a placeholder for Chapter 9; remove is live.

Modified: Ui

render() is now AppModel& (non-const, for InputText). Added renderFileList() (status dots, per-row remove with deferred mutation) and renderControlsPanel() (live output path display, feedback label, Clear All).

Modified: Application

dispatch() now routes all four live events to private handlers. onAddFilesClicked() calls NFD::OpenDialogMultiple with a .docx filter. onBrowseOutputClicked() calls NFD::SaveDialog with a .pdf filter and a merged.pdf default name. NFD is never called from Ui — platform-level calls belong in Application.

Modified: ImGuiContextGuard

NFD::Init() added to constructor; NFD::Quit() added at the top of the destructor (before ImGui shutdown — correct teardown order).


Architecture Notes

  • Ui never touches the domain model directly. It observes AppModel (read) and emits UiEvents (write-intent). All mutations flow through dispatch().
  • Deferred mutation in render loops. renderFileList() records the index to remove after EndChild() — not mid-loop — to avoid iterator invalidation.
  • NFD lifecycle is tied to the window/context lifetime. Init/Quit live in ImGuiContextGuard so the dialog is always called with a live GL context beneath it.

Tests

17 unit tests in tests/document_queue_tests.cpp covering:

  • Happy-path multi-file add
  • Duplicate rejection
  • Mixed-directory rejection
  • Non-.docx extension rejection
  • Empty input
  • removeAt() bounds and mid-list removal
  • move() reorder correctness
  • clear() resets to empty
  • allConverted() with std::all_of

CI runs on push and pull request via GitHub Actions (Windows + macOS).


Files Changed

File Status
src/Document.hpp ✨ New
src/DocumentQueue.hpp ✨ New
src/DocumentQueue.cpp ✨ New
src/AppModel.hpp 🔧 Modified
src/UiEvents.hpp 🔧 Modified
src/Ui.hpp 🔧 Modified
src/Ui.cpp 🔧 Modified
src/Application.hpp 🔧 Modified
src/Application.cpp 🔧 Modified
src/platform/ImGuiContextGuard.cpp 🔧 Modified
CMakeLists.txt 🔧 Modified
tests/document_queue_test.cpp ✨ New
tests/CMakeLists.txt 🔧 Modified

… involves doc queue mutation and queries. Design the DocumentQueue here.
…ck string. Point to note: Ui takes in the AppModel by ref and writes into the outputpathBuffer text field. This is the only instance where the UI mutates the model. All other mutations flow through UiEvents and dispatch.
…lication. The plan is simple: UI never mutates domain model directly, only reports user intents.
…y DOCX to PDFs, this could change in the future, so the Output text is left broad.
…rper. Fix compile error with invalid file comparison for std::ranges::transform.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Doculith’s first interactive “file management” layer by introducing a domain-level document queue, wiring native file dialogs through Application, and extending the UI to display/manage queued files and output path selection.

Changes:

  • Introduces Document and DocumentQueue core types (add/remove/move/clear, status + thumbnail metadata, suggested output path).
  • Wires native file open/save dialogs (NFD) into Application::dispatch() and updates UI to render/manage the queue and output destination.
  • Adds Catch2 unit tests for DocumentQueue and updates CMake to build/link core logic for tests.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
CMakeLists.txt Adds DocumentQueue.cpp to app and introduces doculith_core library target
README.md Updates project structure heading text
src/AppModel.h Adds queue + output path buffer + feedback + refreshOutputPath()
src/AppStrings.h Renames “Output PDF” label to “Output Destination”
src/Application.h Adds event handler declarations and a filesystem alias
src/Application.cpp Implements dispatch handlers for add/browse/clear/remove and queue move placeholder
src/Document.h Adds Document value type and ConversionStatus enum
src/DocumentQueue.h Declares DocumentQueue API and AddResult
src/DocumentQueue.cpp Implements queue invariant enforcement, mutation, and status helpers
src/platform/ImGuiContextGuard.cpp Initializes/quits NFD alongside ImGui context lifetime
src/Ui.h Makes render() take AppModel& and splits main panel rendering into helper functions
src/Ui.cpp Renders file list (status + remove) and controls panel (output path, feedback, merge enable)
src/UiEvents.h Adds optional remove/move indices to UI event payload
tests/CMakeLists.txt Adds document_queue_test.cpp and switches test linking to doculith_core
tests/document_queue_test.cpp Adds unit tests for DocumentQueue behaviors

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CMakeLists.txt Outdated
Comment on lines +29 to +43
@@ -30,13 +30,22 @@ include(cmake/Dependencies.cmake)
add_executable(doculith
src/main.cpp
src/Application.cpp
src/DocumentQueue.cpp
src/Ui.cpp
src/platform/GlfwWindow.cpp
src/platform/ImGuiContextGuard.cpp
src/platform/PlatformContext.cpp

)

add_library(doculith_core
src/DocumentQueue.cpp
)

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This CMake change builds DocumentQueue.cpp into both the doculith executable and the new doculith_core library. That duplicates compilation and can drift over time. Consider linking doculith against doculith_core and removing DocumentQueue.cpp from the executable sources so the core logic is built once.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CMakeLists.txt Resolved
Comment thread src/Ui.cpp Resolved
Comment thread src/platform/ImGuiContextGuard.cpp Resolved
Comment thread src/UiEvents.h
Comment on lines 5 to +16
@@ -11,7 +12,13 @@ namespace Doculith
bool addFilesClicked = false;
bool browseOutputClicked = false;
bool clearAllClicked = false;

std::optional<std::size_t> removeAtIndex; // Set when user clicks remove

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UiEvents uses std::size_t but this header only includes . This can fail to compile depending on include order (size_t is not guaranteed to be provided by ). Add an explicit #include (or <stddef.h>) here to keep the header self-contained.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread src/Document.h Resolved
Comment thread src/Document.h Resolved
Comment thread src/DocumentQueue.cpp
Comment on lines +3 to +36
#include "DocumentQueue.h"
#include <algorithm>

namespace Doculith
{
namespace fs = std::filesystem;

AddResult DocumentQueue::addFiles(const std::vector<std::filesystem::path>& paths)
{
AddResult result;

for (const auto& rawPath : paths)
{
// Must be a regular file that exists
std::error_code ec;
if (!fs::exists(rawPath, ec) || !fs::is_regular_file(rawPath, ec))
{
++result.rejected;
continue;
}

// Canonicalise
const fs::path canonical = fs::canonical(rawPath, ec);
if (ec) { ++result.rejected; continue; }

// Must have DOCX extension (case-insensitive)
auto ext = canonical.extension().string();
std::ranges::transform(ext, ext.begin(),
[](unsigned char c) -> char
{
return static_cast<char>(std::tolower(c));
}
);
if (ext != ".docx") { ++result.rejected; continue; }

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DocumentQueue.cpp uses std::tolower but doesn't include . This is undefined/fragile depending on transitive includes. Add the missing standard include(s) (at least ; also consider <system_error> if relying on std::error_code definitions).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread src/DocumentQueue.cpp Resolved
Comment thread src/AppModel.h
Comment on lines 4 to +31
#pragma once
#include "DocumentQueue.h"

namespace Doculith
{
struct AppModel
{

DocumentQueue queue;

// Output path buffer, owned by AppModel, written to by ImGui::InputText.
// Ui::render() takes AppModel& (non-const) specifically to allow this.
char outputPathBuf[512] = {};

// Human readable status message shown below the control panel.
std::string addFeedback;

// Called by Application::dispatch() after files are successfully added.
// Populate outputPathBuf from DocumentQueue::suggestedOutputPath()
void refreshOutputPath()
{
const auto suggested = queue.suggestedOutputPath();
if (suggested.has_value() && outputPathBuf[0] == '\0')
{
const auto str = suggested->string();
const auto len = std::min(str.size(), sizeof(outputPathBuf) - 1u);
std::memcpy(outputPathBuf, str.data(), len);
outputPathBuf[len] = '\0';
}

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppModel now uses std::string, std::min, and std::memcpy, but this header doesn't include the corresponding standard headers. With -Werror builds, relying on transitive includes will break on some compilers. Please add the needed includes (e.g., , , ) so AppModel.h is self-contained.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #10, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #11, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #12, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 5 commits March 21, 2026 23:02
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #15, to work on those changes. Once the pull request is ready, I'll request review from you.

sromic1990 and others added 2 commits March 22, 2026 01:05
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #16, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #17, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 3 commits March 21, 2026 23:06
… link doculith against it

Co-authored-by: sromic1990 <13477194+sromic1990@users.noreply.github.com>
Agent-Logs-Url: https://github.com/sromic1990/Doculith/sessions/d7f5b5c9-65ce-4df8-b81a-4a32c71ebd5d
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Copilot AI commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

@sromic1990 I've opened a new pull request, #18, to work on those changes. Once the pull request is ready, I'll request review from you.

sromic1990 and others added 12 commits March 22, 2026 01:06
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ncurrent test collisions

Co-authored-by: sromic1990 <13477194+sromic1990@users.noreply.github.com>
Agent-Logs-Url: https://github.com/sromic1990/Doculith/sessions/ac8a38bd-ec1c-4ed3-bfa1-f88aa3624ed0
…-451f-bd80-be1efbbf3081

[WIP] [WIP] Address feedback on file management handling in Core feature PR
…-47b7-9ef8-364edc2dde7a

[WIP] [WIP] Address feedback on file management handling in core feature PR
…-48dc-b35d-e06993d4278f

[WIP] [WIP] Address feedback on file management implementation in core feature PR
[WIP] [WIP] Address feedback on file management handling in core feature PR
Fix missing `#include <cstddef>` in UiEvents.h
Eliminate duplicate compilation of DocumentQueue.cpp
Make AppModel.h self-contained by adding missing standard headers
Fix missing `#include <cctype>` in DocumentQueue.cpp
@sromic1990
sromic1990 merged commit 1e9cc03 into main Mar 21, 2026
2 checks passed
@sromic1990
sromic1990 deleted the feature/core/1-handling-file-management branch March 21, 2026 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants