backlog¶
Index¶
- Constants
- Variables
- func AddBlockRefToFocusPage(transaction *logseq.Transaction, focusPageTitle, uuid string) error
- func BlockRefExistsUnder(parent *content.Block, uuid logseqapi.TaskUUID) bool
- func FindFirstSectionDivider(page logseq.Page) *content.Block
- func FormatCount(count int, singular, plural string) string
- func MoveBlockRefToTriagedSection(transaction *logseq.Transaction, backlogPage string, uuid logseqapi.TaskUUID, triagedText, scheduledText string) error
- func NormalizeHeaderText(page logseq.Page) bool
- func RemoveBlockRefFromRegularArea(page logseq.Page, uuid logseqapi.TaskUUID)
- func addTasksToCategories(jsonTasks []logseqapi.TaskJSON, tasks *logseqapi.CategorizedTasks, currentTime func() time.Time)
- func blockRefsFromPages(page logseq.Page) *set.Set[string]
- func collectTriagedRefs(page logseq.Page, state *pageState)
- func createTriagedSectionWithRef(page logseq.Page, uuid logseqapi.TaskUUID, triagedText, scheduledText string) error
- func defaultQuery(pageTitle string) string
- func handleDefaultBlockRef(node content.Node, blockRef *content.BlockRef, state *pageState, underScheduled bool, futureScheduledBlockRefs *set.Set[string]) bool
- func insertNewTasks(page logseq.Page, state *pageState, newBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string]) bool
- func insertOverdueTasks(page logseq.Page, state *pageState, overdueBlockRefs *set.Set[string])
- func insertScheduledTasks(page logseq.Page, state *pageState, futureScheduledBlockRefs *set.Set[string])
- func nextChildHasPin(node content.Node) bool
- func printQuickCaptureURL(graph *logseq.Graph)
- func processAllBlocks(page logseq.Page, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
- func processBlockRef(node content.Node, blockRef *content.BlockRef, block *content.Block, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
- func queryTasksFromPages(graph *logseq.Graph, logseqAPI logseqapi.LogseqAPI, pageTitles []string, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
- func queryTasksFromPagesConcurrent(logseqAPI logseqapi.LogseqAPI, pageTitles []string, tasks *logseqapi.CategorizedTasks, finder logseqext.LogseqFinder, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
- func queryTasksFromPagesSequential(logseqAPI logseqapi.LogseqAPI, pageTitles []string, tasks *logseqapi.CategorizedTasks, finder logseqext.LogseqFinder, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
- func queryTasksFromSinglePage(logseqAPI logseqapi.LogseqAPI, pageTitle string, finder logseqext.LogseqFinder) ([]logseqapi.TaskJSON, error)
- func recordSectionDivider(block *content.Block, textValue string, state *pageState)
- func reportCounts(state *pageState, save bool) bool
- func scanPageBlocks(page logseq.Page, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
- func sortTriagedSection(state *pageState, taskLookup map[logseqapi.TaskUUID]logseqapi.TaskJSON)
- func taskSortKeyLess(left, right taskSortKey) bool
- type Backlog
- func NewBacklog(graph *logseq.Graph, logseqAPI logseqapi.LogseqAPI, reader ConfigReader, currentTime func() time.Time) Backlog
- type Config
- func (c *Config) FindBacklogPageTitle(backlogName string) string
- type ConfigReader
- func NewPageConfigReader(graph *logseq.Graph, configPage string) ConfigReader
- type Header
- func (h Header) Matches(blockText string) bool
- func (h Header) NewHeading() *content.Heading
- func (h Header) String() string
- type Result
- func insertAndRemoveRefs(graph *logseq.Graph, pageTitle string, newBlockRefs, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string], taskLookup map[logseqapi.TaskUUID]logseqapi.TaskJSON) (*Result, error)
- type SingleBacklogConfig
- type backlogImpl
- func (b *backlogImpl) Graph() *logseq.Graph
- func (b *backlogImpl) ProcessAll(partialNames []string) error
- func (b *backlogImpl) ProcessOne(pageTitle string, funcQueryRefs func() (*logseqapi.CategorizedTasks, error)) (*Result, error)
- type pageConfigReader
- func (p *pageConfigReader) ReadConfig() (*Config, error)
- type pageState
- func newPageState() *pageState
- type taskSortKey
- func newSortKeys(children content.BlockList, taskLookup map[logseqapi.TaskUUID]logseqapi.TaskJSON) ([]taskSortKey, int)
Constants¶
Section values for the PocketBase `section` field. Ranked=1, Unranked=2, Orphan=3 so that (backlog_index, section, rank) sorts ranked tasks before unranked tasks before orphans within any backlog.
const (
SectionRanked = 1 // manually ordered, above the ⤵️ Unranked tasks divider
SectionUnranked = 2 // under ⤵️ Unranked tasks, 📅 Overdue tasks, ⏰ Scheduled tasks, ✨ New tasks, 🏷️ Triaged tasks
SectionOrphan = 3 // not referenced in any backlog page
)
quickCapturePageName is the Logseq page linked in newly created section dividers so the user can identify dividers inserted by lqd backlog.
Variables¶
Backlog section header definitions. Detection uses Header.Matches (case-insensitive label search). Creation uses Header.String() so the canonical emoji+label+tasks is always written.
var (
HeaderFocus = Header{"🎯", "Focus"}
HeaderOverdue = Header{"📅", "Overdue"}
HeaderNewTasks = Header{"✨", "New"}
HeaderTriaged = Header{"🏷️", "Triaged"}
HeaderScheduled = Header{"⏰", "Scheduled"}
HeaderUnranked = Header{"⤵️", "Unranked"}
)
allHeaders is the full list used to normalize section dividers on write-back.
var allHeaders = []Header{
HeaderFocus, HeaderOverdue, HeaderNewTasks,
HeaderTriaged, HeaderScheduled, HeaderUnranked,
}
focusSectionHeaders are the section dividers on the Focus page. Used to find the insertion point for new block refs.
regularAreaSectionHeaders are section headers that mark the boundary of the regular area. A top-level block matching any of these is a section divider, not part of the regular area.
var regularAreaSectionHeaders = []Header{
HeaderFocus, HeaderOverdue, HeaderNewTasks, HeaderTriaged, HeaderScheduled, HeaderUnranked,
}
func AddBlockRefToFocusPage¶
AddBlockRefToFocusPage adds a block ref ((uuid)) to the Focus page.
func BlockRefExistsUnder¶
BlockRefExistsUnder returns true if a block ref with the given UUID exists anywhere in the descendant tree of parent.
func FindFirstSectionDivider¶
FindFirstSectionDivider finds the first block whose text matches a known section header.
func FormatCount¶
FormatCount returns a string with the count and the singular or plural form of a word.
func MoveBlockRefToTriagedSection¶
func MoveBlockRefToTriagedSection(transaction *logseq.Transaction, backlogPage string, uuid logseqapi.TaskUUID, triagedText, scheduledText string) error
MoveBlockRefToTriagedSection moves a block ref to the Triaged section of a backlog page. If the ref exists in the regular area (not under Focus, New tasks, Overdue, or Scheduled), it is removed from there. If it's already in Triaged, no duplicate is added. Creates the Triaged section if it doesn't exist.
func NormalizeHeaderText¶
NormalizeHeaderText scans all top-level blocks on the page and normalizes any block whose text node contains a known header keyword:
- fixes the text to the canonical "emoji Label tasks" form
- upgrades a plain Paragraph container to a Heading level 1
Returns true if any block was changed.
func RemoveBlockRefFromRegularArea¶
RemoveBlockRefFromRegularArea removes the block ref with the given UUID from the regular area of the page. The regular area is any top-level block ref that is not itself a section divider. Section dividers (Focus, Overdue, New tasks, Triaged, Scheduled, Unranked) and their children are not part of the regular area. Since we walk only top-level blocks, child refs are never seen.
func addTasksToCategories¶
func addTasksToCategories(jsonTasks []logseqapi.TaskJSON, tasks *logseqapi.CategorizedTasks, currentTime func() time.Time)
addTasksToCategories adds tasks to the appropriate categories in CategorizedTasks.
func blockRefsFromPages¶
func collectTriagedRefs¶
collectTriagedRefs performs a first pass over the page to find the Triaged section divider and record all block-ref UUIDs that are descendants of it.
func createTriagedSectionWithRef¶
func createTriagedSectionWithRef(page logseq.Page, uuid logseqapi.TaskUUID, triagedText, scheduledText string) error
createTriagedSectionWithRef creates a new Triaged section with a block reference. It inserts the section before the Scheduled section if found, or appends to the end.
func defaultQuery¶
func handleDefaultBlockRef¶
func handleDefaultBlockRef(node content.Node, blockRef *content.BlockRef, state *pageState, underScheduled bool, futureScheduledBlockRefs *set.Set[string]) bool
handleDefaultBlockRef handles the default case in processBlockRef: moves stale Scheduled tasks back to new, and unpins regular tasks. Returns true if the block should be deleted.
func insertNewTasks¶
func insertNewTasks(page logseq.Page, state *pageState, newBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string]) bool
insertNewTasks inserts new task refs under the new-tasks divider (creating it if needed). Returns updated save flag.
func insertOverdueTasks¶
insertOverdueTasks inserts overdue task refs under the overdue divider (creating it if needed).
Overdue tasks go after the focus section and before new ones so the user can manually decide which overdue tasks deserve focus.
func insertScheduledTasks¶
func insertScheduledTasks(page logseq.Page, state *pageState, futureScheduledBlockRefs *set.Set[string])
insertScheduledTasks moves future-scheduled tasks to the bottom of the page.
func nextChildHasPin¶
func printQuickCaptureURL¶
func processAllBlocks¶
func processAllBlocks(page logseq.Page, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
processAllBlocks iterates all blocks on the page, records section dividers, and removes or unpins block refs that are obsolete, overdue, or scheduled.
func processBlockRef¶
func processBlockRef(node content.Node, blockRef *content.BlockRef, block *content.Block, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
processBlockRef decides whether to delete, pin, unpin, or keep a block ref.
func queryTasksFromPages¶
func queryTasksFromPages(graph *logseq.Graph, logseqAPI logseqapi.LogseqAPI, pageTitles []string, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
queryTasksFromPages queries Logseq API for tasks from specified pages. It uses concurrent processing for multiple pages and sequential processing for a single page.
func queryTasksFromPagesConcurrent¶
func queryTasksFromPagesConcurrent(logseqAPI logseqapi.LogseqAPI, pageTitles []string, tasks *logseqapi.CategorizedTasks, finder logseqext.LogseqFinder, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
queryTasksFromPagesConcurrent processes pages concurrently using goroutines.
func queryTasksFromPagesSequential¶
func queryTasksFromPagesSequential(logseqAPI logseqapi.LogseqAPI, pageTitles []string, tasks *logseqapi.CategorizedTasks, finder logseqext.LogseqFinder, currentTime func() time.Time) (*logseqapi.CategorizedTasks, error)
queryTasksFromPagesSequential processes pages sequentially (original implementation).
func queryTasksFromSinglePage¶
func queryTasksFromSinglePage(logseqAPI logseqapi.LogseqAPI, pageTitle string, finder logseqext.LogseqFinder) ([]logseqapi.TaskJSON, error)
queryTasksFromSinglePage queries tasks from a single page and returns the JSON tasks.
func recordSectionDivider¶
recordSectionDivider updates state with a block if its text matches a known section header.
func reportCounts¶
reportCounts prints colored summaries and returns updated save flag.
func scanPageBlocks¶
func scanPageBlocks(page logseq.Page, state *pageState, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string])
scanPageBlocks is a two-pass coordinator: first it normalizes headers, then collects UUIDs already in the Triaged section, then processes all blocks (which uses those UUIDs for deduplication in the regular area).
func sortTriagedSection¶
sortTriagedSection sorts children of the Triaged divider by priority, date, name, and ID.
func taskSortKeyLess¶
taskSortKeyLess compares two sort keys for ordering in the Triaged section.
type Backlog¶
type Backlog interface {
Graph() *logseq.Graph
ProcessAll(partialNames []string) error
ProcessOne(pageTitle string, funcQueryRefs func() (*logseqapi.CategorizedTasks, error)) (*Result, error)
}
func NewBacklog¶
func NewBacklog(graph *logseq.Graph, logseqAPI logseqapi.LogseqAPI, reader ConfigReader, currentTime func() time.Time) Backlog
type Config¶
func (*Config) FindBacklogPageTitle¶
FindBacklogPageTitle looks up the full backlog page path from config by backlog name. Returns empty string if no matching backlog is found.
type ConfigReader¶
func NewPageConfigReader¶
NewPageConfigReader creates a new ConfigReader that reads the backlog configuration from a Logseq page.
type Header¶
Header represents a backlog section divider. Label is the display word(s) without the "tasks" suffix (e.g. "Focus"). String() always returns "Emoji Label tasks".
func (Header) Matches¶
Matches reports whether blockText contains the label, case-insensitively.
func (Header) NewHeading¶
NewHeading returns a level-1 heading node with the canonical header text followed by a [[quick capture]] page link. Use this when creating a new section divider so the user can identify blocks inserted by lqd backlog.
func (Header) String¶
String returns the canonical display form: "emoji label tasks".
type Result¶
func insertAndRemoveRefs¶
func insertAndRemoveRefs(graph *logseq.Graph, pageTitle string, newBlockRefs, obsoleteBlockRefs, overdueBlockRefs, futureScheduledBlockRefs *set.Set[string], taskLookup map[logseqapi.TaskUUID]logseqapi.TaskJSON) (*Result, error)
type SingleBacklogConfig¶
type backlogImpl¶
type backlogImpl struct {
graph *logseq.Graph
logseqAPI logseqapi.LogseqAPI
configReader ConfigReader
currentTime func() time.Time
}
func (*backlogImpl) Graph¶
func (*backlogImpl) ProcessAll¶
func (*backlogImpl) ProcessOne¶
func (b *backlogImpl) ProcessOne(pageTitle string, funcQueryRefs func() (*logseqapi.CategorizedTasks, error)) (*Result, error)
type pageConfigReader¶
func (*pageConfigReader) ReadConfig¶
ReadConfig reads the backlog configuration from a Logseq page.
type pageState¶
pageState holds mutable state accumulated while scanning a backlog page.
type pageState struct {
firstBlock *content.Block
dividerNewTasks *content.Block
dividerOverdue *content.Block
dividerFocus *content.Block
dividerScheduled *content.Block
dividerTriaged *content.Block
dividerUnranked *content.Block
deletedCount int
movedCount int
movedScheduledCount int
movedFromScheduledCount int
unpinnedCount int
result *Result
pinnedBlockRefs *set.Set[string]
triagedBlockRefs *set.Set[string] // UUIDs already in the Triaged section
unscheduledRefs *set.Set[string] // UUIDs removed from Scheduled because they lost their scheduled date
}
func newPageState¶
type taskSortKey¶
taskSortKey holds the fields used to sort tasks in the Triaged section.
type taskSortKey struct {
priority content.PriorityValue // PriorityNone=0 sorts FIRST (unprioritized at top)
createdDate time.Time // oldest first
firstLine string // alphabetical tiebreaker
id logseqapi.TaskUUID // UUID, guaranteed unique final tiebreaker
block *content.Block // reference to the block for reordering
}
func newSortKeys¶
func newSortKeys(children content.BlockList, taskLookup map[logseqapi.TaskUUID]logseqapi.TaskJSON) ([]taskSortKey, int)
newSortKeys creates sort keys for all children of the Triaged section.
Generated by gomarkdoc