412 lines
12 KiB
TypeScript
412 lines
12 KiB
TypeScript
/** 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<RawFolder[]>;
|
|
/**
|
|
* 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<RawNote[]>;
|
|
/** Lowercased tag titles for a note. Used to detect recurrence. */
|
|
getNoteTagTitles(noteId: string): Promise<string[]>;
|
|
}
|
|
|
|
export interface RawTag {
|
|
id: string;
|
|
title: string;
|
|
}
|
|
|
|
/** Narrow writable Joplin boundary used only by validated workflow moves. */
|
|
export interface MutationAdapter {
|
|
getNote(noteId: string): Promise<RawNote | null>;
|
|
getNoteTags(noteId: string): Promise<RawTag[]>;
|
|
getAllTags(): Promise<RawTag[]>;
|
|
createTag(title: string): Promise<RawTag>;
|
|
attachTag(tagId: string, noteId: string): Promise<void>;
|
|
detachTag(tagId: string, noteId: string): Promise<void>;
|
|
setTodoCompleted(noteId: string, completedTime: number): Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|