/** Parsed and normalised ```gtd-calendar config. */ export interface CalendarConfig { view: "day" | "week" | "month" | "month-grid"; title: string | null; /** Folder recursion depth: 0 = this folder only, Infinity = all children. */ scopeDepth: number; /** * `scope: all` — scan every notebook in the profile, ignoring the root * folder entirely. When true, `scopeDepth` and `notebook` are irrelevant. */ scopeAll: boolean; /** * Notebook to root the folder scan at (id, title, or Parent/Child path). * null = the host note's own folder. `scopeDepth` applies relative to it. * Ignored when `scopeAll` is true. */ notebook: string | null; /** Notebook roots whose complete descendant trees are removed from scope. */ excludeNotebooks: string[]; notes: InclusionMode; todos: InclusionMode; sort: "asc" | "desc"; sortType: "title" | "modified_date"; /** Whether to show the Unscheduled to-dos / notes sub-sections. */ unscheduledTodos: boolean; unscheduledNotes: boolean; /** Non-fatal problems found while parsing, surfaced in the UI. */ warnings: string[]; } export type InclusionMode = "gtd-only" | "all" | "none"; /** A note/todo as fetched from the Joplin data API. */ export interface RawNote { id: string; title: string; parent_id: string; is_todo: number; todo_due: number; todo_completed: number; updated_time: number; /** * Note body. Only fetched when the caller asks for it via * `getNotesInFolder(folderId, true)`; undefined otherwise. Collectors only * omit the body when their inclusion mode means the gtd block is never * scanned, so callers must never read this in a body-less path. */ body?: string; } /** Properties parsed from a ```gtd block. */ export interface GtdBlock { date: string | null; bgColour: string | null; fgColour: string | null; icon: string | null; title: string | null; text: string | null; } export interface GtdBlockResult { found: boolean; block: GtdBlock | null; error: string | null; } /** Event payload sent to the webview. */ export interface GtdEvent { id: string; title: string; /** ISO yyyy-mm-dd, or null for unscheduled. */ date: string | null; isTodo: boolean; completed: boolean; bgColour: string | null; fgColour: string | null; icon: string | null; text: string | null; updatedTime: number; /** True when the to-do carries the recurrence index tag (see RECURRING_TAG). */ isRecurring: boolean; } export interface RawFolder { id: string; parent_id: string; /** * Notebook title. Needed only to resolve the `notebook:` option by * name/path (folder scoping uses id/parent_id alone), so it is optional for * the benefit of scope-only test fixtures; the real adapter always sets it. */ title?: string; } /** Thin abstraction over the Joplin data API so collectEvents is testable. */ export interface DataAdapter { getFolders(): Promise; /** * Notes in a folder. `includeBody` is a fetch hint: when false, the adapter * may omit the (potentially large) `body` field. Collectors pass false only * when their inclusion mode guarantees the gtd block is never scanned. */ getNotesInFolder(folderId: string, includeBody: boolean): Promise; /** Lowercased tag titles for a note. Used to detect recurrence. */ getNoteTagTitles(noteId: string): Promise; } export interface RawTag { id: string; title: string; } /** Narrow writable Joplin boundary used only by validated workflow moves. */ export interface MutationAdapter { getNote(noteId: string): Promise; getNoteTags(noteId: string): Promise; getAllTags(): Promise; createTag(title: string): Promise; attachTag(tagId: string, noteId: string): Promise; detachTag(tagId: string, noteId: string): Promise; setTodoCompleted(noteId: string, completedTime: number): Promise; } /** * Tag maintained by the "Repeating To-Dos" plugin family as a query index * for recurring to-dos. We treat its presence as the recurrence signal. * Kept as a constant so a future `recurring-tag:` config option (or a * switch to a different recurrence plugin) is a one-line change. */ export const RECURRING_TAG = "recurring"; /** * Default tag that places a to-do in the kanban "In Progress" column. * Overridable via the gtd-kanban `in-progress-tag:` option. Constant * mirrors RECURRING_TAG. */ export const IN_PROGRESS_TAG = "in-progress"; /** Default tag that marks an opted-in ordinary note complete. */ export const DONE_TAG = "done"; export type KanbanSortType = "due-date" | "title" | "modified-date"; /** Ordinary-note inclusion for kanban/matrix; notes still require a gtd block. */ export type NoteInclusionMode = "all" | "none"; /** Parsed and normalised ```gtd-kanban config. */ export interface KanbanConfig { title: string | null; scopeDepth: number; /** `scope: all` — scan every notebook; ignores scopeDepth and notebook. */ scopeAll: boolean; /** Separate exact-owner notebook views; requires children + notebook grouping. */ groupByNotebook: boolean; /** Notebook to root the scan at (id/title/path); null = host folder. */ notebook: string | null; /** Notebook roots whose complete descendant trees are removed from scope. */ excludeNotebooks: string[]; notes: NoteInclusionMode; todos: InclusionMode; sortType: KanbanSortType; sort: "asc" | "desc"; inProgressTag: string; /** Tag that marks an opted-in ordinary note complete. */ doneTag: string; cardDetail: "hover" | "always" | "none"; /** Cards initially shown, and revealed per "List more" action. */ pageSize: number; /** Whether this board explicitly opts in to persisted workflow moves. */ editable: boolean; /** Days; Infinity means "all". */ doneWindow: number; warnings: string[]; } /** Fixed semantic destinations accepted from an editable kanban webview. */ export type KanbanDestination = "backlog" | "inProgress" | "done"; /** Constrained intent sent by the webview; the main process derives all writes. */ export interface MoveKanbanCardIntent { type: "moveKanbanCard"; view: "kanban"; hostNoteId: string; cardId: string; destination: KanbanDestination; rawConfig: string; viewInstanceId: string; } export type MoveCardResult = | { status: "success"; viewInstanceId: string; changed: boolean } | { status: "stale"; viewInstanceId: string; message: string } | { status: "error"; viewInstanceId: string; message: string }; /** A note or to-do rendered as a kanban/matrix card. */ export interface KanbanCard { id: string; title: string; date: string | null; isTodo: boolean; completed: boolean; completedTime: number; isRecurring: boolean; bgColour: string | null; fgColour: string | null; icon: string | null; text: string | null; updatedTime: number; } export interface KanbanBoard { backlog: KanbanCard[]; inProgress: KanbanCard[]; done: KanbanCard[]; } /** Identity shared by every exact-owner notebook view. */ export interface NotebookViewMetadata { folderId: string; notebookPath: string; cardCount: number; warnings: string[]; } export interface NotebookKanbanGroup extends NotebookViewMetadata { board: KanbanBoard; } export type KanbanLayout = | { kind: "single"; board: KanbanBoard } | { kind: "notebooks"; groups: NotebookKanbanGroup[] }; /** * Default tags for the Eisenhower matrix axes. Overridable via the * gtd-matrix `urgent-tag:` / `important-tag:` options. */ export const URGENT_TAG = "urgent"; export const IMPORTANT_TAG = "important"; export type MatrixMode = "skeleton" | "eisenhower"; /** Parsed and normalised ```gtd-matrix config. */ export interface MatrixConfig { mode: MatrixMode; title: string | null; scopeDepth: number; /** `scope: all` — scan every notebook; ignores scopeDepth and notebook. */ scopeAll: boolean; /** Separate exact-owner notebook views; requires children + notebook grouping. */ groupByNotebook: boolean; /** Notebook to root the scan at (id/title/path); null = host folder. */ notebook: string | null; /** Notebook roots whose complete descendant trees are removed from scope. */ excludeNotebooks: string[]; notes: NoteInclusionMode; todos: InclusionMode; sortType: KanbanSortType; sort: "asc" | "desc"; urgentTag: string; importantTag: string; /** Skeleton mode: tag marking active work (shared with the kanban). */ inProgressTag: string; /** Tag that marks an opted-in ordinary note complete. */ doneTag: string; /** Skeleton mode: days ahead within which a due date counts as "due soon". */ urgentWindow: number; cardDetail: "hover" | "always" | "none"; /** Cards initially shown, and revealed per "List more" action. */ pageSize: number; /** Effective editable state; only valid for distinct-axis Eisenhower mode. */ editable: boolean; warnings: string[]; } export type MatrixDestination = | "topLeft" | "topRight" | "bottomLeft" | "bottomRight"; export interface MoveMatrixCardIntent { type: "moveMatrixCard"; view: "matrix"; hostNoteId: string; cardId: string; destination: MatrixDestination; rawConfig: string; viewInstanceId: string; } /** * The four matrix quadrants, positional (row x column): * topLeft topRight * bottomLeft bottomRight * * Both modes display Do Next / Scheduled / On Deck / Backlog; their axes and * bucketing semantics remain mode-specific. */ export interface MatrixBoard { topLeft: KanbanCard[]; topRight: KanbanCard[]; bottomLeft: KanbanCard[]; bottomRight: KanbanCard[]; } export interface NotebookMatrixGroup extends NotebookViewMetadata { board: MatrixBoard; } export type MatrixLayout = | { kind: "single"; board: MatrixBoard } | { kind: "notebooks"; groups: NotebookMatrixGroup[] }; /** Display labels for a matrix mode, sent to the webview. */ export interface MatrixLabels { columns: [string, string]; rows: [string, string]; quadrants: { topLeft: string; topRight: string; bottomLeft: string; bottomRight: string; }; } // --------------------------------------------------------------------------- // Gantt (SLICE4 data layer) // --------------------------------------------------------------------------- export type GanttSortType = "begin-date" | "title" | "modified-date"; /** * Parsed properties of an item `gtd-gantt` block (one carrying a `project:` * key). Notes become bars (begin/end), to-dos become milestones (the to-do's * native due date; begin/end on a to-do are ignored with a warning). */ export interface GanttItemBlock { /** Non-null marks this as an ITEM block; null/absent means a CHART block. */ project: string | null; beginDate: string | null; endDate: string | null; bgColour: string | null; fgColour: string | null; title: string | null; text: string | null; } export interface GanttBlockResult { found: boolean; block: GanttItemBlock | null; error: string | null; } /** Parsed and normalised CHART `gtd-gantt` config (no `project:` key). */ export interface GanttConfig { title: string | null; scopeDepth: number; scopeAll: boolean; notebook: string | null; /** Limit the chart to a single project (case-insensitive); null = all. */ filterProject: string | null; sortType: GanttSortType; sort: "asc" | "desc"; cardDetail: "hover" | "always" | "none"; warnings: string[]; } /** A note rendered as a gantt bar (a dated span). */ export interface GanttBar { id: string; title: string; /** yyyy-mm-dd, inclusive. */ beginDate: string; endDate: string; bgColour: string | null; fgColour: string | null; text: string | null; updatedTime: number; } /** A to-do rendered as a gantt milestone (a single dated point). */ export interface GanttMilestone { id: string; title: string; /** yyyy-mm-dd (the to-do's due date). */ date: string; completed: boolean; isRecurring: boolean; bgColour: string | null; fgColour: string | null; text: string | null; updatedTime: number; } /** One swimlane: all bars + milestones sharing a `project:`. */ export interface GanttProject { name: string; bars: GanttBar[]; milestones: GanttMilestone[]; } /** Full gantt payload: projects plus the overall date envelope. */ export interface GanttChart { projects: GanttProject[]; /** Earliest/latest dates across all items; null when the chart is empty. */ rangeStart: string | null; rangeEnd: string | null; warnings: string[]; scannedFolders: number; scannedNotes: number; itemCount: number; }