Files
nocal/include/nocal/tui/Screen.hpp
Bernardo Magri 8b6cf2e877 feat: add bounded agenda view
Add a read-only 42-day occurrence agenda with keyboard navigation, six-week paging, exact return-to-grid focus, search and calendar overlays, and bounded recurrence expansion.

Keep agenda state outside persistence, document month-only mutations, and cover empty windows, filtering, overlays, navigation, rendering, and terminal restoration.
2026-07-18 09:19:01 +01:00

145 lines
4.5 KiB
C++

#pragma once
#include <chrono>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include "nocal/domain/event.hpp"
#include "nocal/domain/event_query.hpp"
namespace nocal::tui {
enum class ItemSegment {
single,
start,
continuation,
end,
};
// A deliberately small view-model keeps the renderer independent of storage and
// makes it straightforward to unit test. TuiApp adapts domain Events to this.
struct CalendarItem {
std::string id;
std::string title;
std::chrono::year_month_day day;
std::optional<std::chrono::minutes> time_of_day;
bool all_day{false};
std::optional<std::chrono::minutes> end_time_of_day;
std::chrono::year_month_day start_day{};
std::chrono::year_month_day end_day{};
std::string location;
std::string description;
std::string detail_title;
bool detail_all_day{false};
std::optional<std::chrono::minutes> detail_start_time_of_day;
ItemSegment segment{ItemSegment::single};
bool recurring{false};
std::string recurrence_summary;
std::string source_time_zone;
};
struct SearchResultItem {
std::string focus_id;
std::string title;
std::chrono::year_month_day day;
std::optional<std::chrono::minutes> time_of_day;
bool all_day{false};
std::string location;
bool recurring{false};
};
struct AgendaItem {
std::string focus_id;
std::string title;
std::chrono::year_month_day day;
std::optional<std::chrono::minutes> time_of_day;
bool all_day{false};
std::string location;
std::string calendar_id;
bool recurring{false};
};
struct ScreenState {
std::chrono::year_month visible_month{std::chrono::year{1970}, std::chrono::month{1}};
std::chrono::year_month_day selected_day{std::chrono::year{1970}, std::chrono::month{1},
std::chrono::day{1}};
std::chrono::year_month_day today{std::chrono::year{1970}, std::chrono::month{1},
std::chrono::day{1}};
int width{80};
int height{24};
bool show_help{false};
bool ansi{true};
std::optional<std::string> focused_event_id;
bool show_event_details{false};
bool confirm_delete{false};
bool search_prompt{false};
bool show_search_results{false};
std::string search_query;
std::vector<SearchResultItem> search_results;
std::size_t search_result_index{0};
std::string notification;
bool notification_is_error{false};
bool show_calendar_picker{false};
std::size_t calendar_index{0};
bool show_agenda{false};
std::chrono::year_month_day agenda_start_day{
std::chrono::year{1970}, std::chrono::month{1}, std::chrono::day{1}};
std::vector<AgendaItem> agenda_items;
std::size_t agenda_index{0};
};
// Render a complete frame. The result contains no cursor positioning, which
// makes it useful both for snapshots and for non-interactive output.
[[nodiscard]] std::string render_month(std::span<const CalendarItem> items,
const ScreenState& state);
// Convenience adapter for the domain model. Occurrence instants are projected
// into the user's process-local zone; the reader retains an explicit source TZID.
[[nodiscard]] std::string render_month(std::span<const Event> events,
const ScreenState& state);
// Calendar visibility filters presentation only. Focus identities are still
// derived from the complete event collection.
[[nodiscard]] std::string render_month(std::span<const Event> events,
std::span<const Calendar> calendars,
const ScreenState& state);
// Stable focus identity shared by the renderer and controller. Non-recurring
// events retain their historical UID/synthetic identity; recurring instances
// additionally include their concrete start instant.
[[nodiscard]] std::string occurrence_focus_id(std::span<const Event> events,
const EventOccurrence& occurrence);
enum class Action {
none,
left,
right,
up,
down,
previous_month,
next_month,
today,
toggle_help,
previous_event,
next_event,
select,
back,
add_event,
edit_event,
delete_event,
undo,
redo,
search,
agenda,
calendars,
quit,
};
// Decodes a complete key sequence, including common terminal arrow/Page keys.
[[nodiscard]] Action decode_key(std::string_view sequence) noexcept;
} // namespace nocal::tui