Skip to content

Workspace Parsing

Relevant source files

The following files were used as context for generating this wiki page:

The workspace parsing system transforms raw markdown file content from a WorkspaceSnapshot into strongly-typed TypeScript objects (KanbanParseResult). This parsing layer interprets markdown conventions (headings, wikilinks, frontmatter) and produces structured board and card documents that the application can work with. For serialization (converting structured data back to markdown), see Board and Card Serialization. For path and slug utilities used during parsing, see Path and Slug Management.

Sources: src/utils/parseWorkspace.ts:1-463


Overview and Entry Point

The primary entry point is the parseWorkspace function, which accepts a WorkspaceSnapshot (raw file paths and contents from the Rust backend) and returns a KanbanParseResult containing parsed boards, cards, and diagnostics.

Parsing Flow Diagram

Key Function:

FunctionInputOutputPurpose
parseWorkspaceWorkspaceSnapshotKanbanParseResultMain entry point that delegates to board and card parsers
parseBoardFilepath: string, rawContent: stringKanbanBoardDocumentParses a single board markdown file
parseCardFilepath: string, rawContent: stringKanbanCardDocumentParses a single card markdown file

The function implementation src/utils/parseWorkspace.ts:30-40 maps over all boards and cards in the snapshot, collects their diagnostics, and returns a structured result with version identifier "kanban-parser/v1".

Sources: src/utils/parseWorkspace.ts:30-40, docs/schemas/kanban-parser-schema.ts:12-17


Board File Parsing State Machine

Board file parsing (parseBoardFile) is a stateful line-by-line parser that tracks the current column, section, and special regions (settings blocks, sub-boards section).

Board Parsing State Machine Diagram

State Tracking Variables:

VariableTypePurpose
currentColumnKanbanColumn | nullTracks the active column being parsed
currentSectionIndexnumber | nullIndex of the active section within current column
inSubBoardsbooleanWhether parser is inside the "Sub Boards" section
inSettingsbooleanWhether parser is inside the settings block
settingsLinesstring[]Accumulates lines within the settings block

The parser src/utils/parseWorkspace.ts:58-133 iterates through each line and:

  1. Settings Block Detection: Lines between %% kanban:settings and %% are collected for JSON parsing
  2. Column Detection: ## headings create new columns with slugified names
  3. Sub-Boards Section: ## Sub Boards heading switches to sub-board collection mode
  4. Section Detection: ### headings within columns create sections
  5. Card Links: Bullet-point wikilinks are added to the active section or column

Sources: src/utils/parseWorkspace.ts:42-157, docs/schemas/kanban-parser-schema.ts:28-53


Board Structure Elements

Columns and Sections

Columns are top-level organizational units marked by ## headings. Each column contains sections (marked by ### headings or an implicit unnamed section).

Column Structure Mapping

The getActiveSection helper src/utils/parseWorkspace.ts:251-260 returns the current section if one exists, otherwise it calls ensureUnnamedSection src/utils/parseWorkspace.ts:262-277 to create an implicit section with name: null and slug: null for cards that appear outside explicit ### sections.

Sources: src/utils/parseWorkspace.ts:76-108, src/utils/parseWorkspace.ts:251-277

Settings Block Parsing

Board settings are stored in a special markdown comment block:

%% kanban:settings
```json
{
  "show-archive": true,
  "group-by": "section"
}

%%


The parser [src/utils/parseWorkspace.ts:61-74](../src/utils/parseWorkspace.ts) collects lines between the delimiters and passes them to `parseBoardSettings` [src/utils/parseWorkspace.ts:221-249](../src/utils/parseWorkspace.ts), which:

1. Attempts to extract JSON from a code fence (`\`\`\`json ... \`\`\``)
2. Falls back to parsing the entire block as JSON
3. Returns `null` if empty or invalid, emitting a diagnostic on parse failure

**Sources:** [src/utils/parseWorkspace.ts:61-74](../src/utils/parseWorkspace.ts), [src/utils/parseWorkspace.ts:221-249](../src/utils/parseWorkspace.ts), [docs/schemas/kanban-parser-schema.ts:66-74](../docs/schemas/kanban-parser-schema.ts)

### Sub-Boards Section

The special `## Sub Boards` heading marks a section for hierarchical board references. When detected [src/utils/parseWorkspace.ts:78-83](../src/utils/parseWorkspace.ts), the parser sets `inSubBoards = true` and subsequent bullet wikilinks are parsed as board links (not card links) and added to the `subBoards` array.

**Sources:** [src/utils/parseWorkspace.ts:78-83](../src/utils/parseWorkspace.ts), [src/utils/parseWorkspace.ts:115-118](../src/utils/parseWorkspace.ts)

---

## Card File Parsing

Card parsing is simpler than board parsing, focusing on extracting metadata, title, body content, and structured sections.

**Card Parsing Pipeline**

```mermaid
graph TB
    Input["Raw Card Content"]
    PFM["parseFrontmatter()"]
    FM["frontmatter extracted"]
    Title["Extract title"]
    Body["Strip leading title"]
    Sections["parseCardSections()"]
    Check["extractChecklist()"]
    Wiki["extractWikilinks()"]
    
    Input --> PFM
    PFM --> FM
    PFM --> Content["content body"]
    
    FM --> Title
    Content --> Title
    Content --> Body
    
    Body --> Sections
    Body --> Check
    Body --> Wiki
    
    Title --> CD["KanbanCardDocument"]
    FM --> CD
    Body --> CD
    Sections --> CD
    Check --> CD
    Wiki --> CD

The parseCardFile function src/utils/parseWorkspace.ts:159-180 performs these steps:

  1. Parse Frontmatter: Extract YAML metadata from the card
  2. Determine Title: Priority is metadata.title, then first # heading, then slugified filename
  3. Extract Body: Strip the leading # title heading if present src/utils/parseWorkspace.ts:435-454
  4. Parse Sections: Extract ## sections from the body
  5. Extract Checklist: Find all - [ ] and - [x] items
  6. Extract Wikilinks: Find all [[...]] references

Sources: src/utils/parseWorkspace.ts:159-180, docs/schemas/kanban-parser-schema.ts:83-94

Card Sections

Card sections are extracted by parseCardSections src/utils/parseWorkspace.ts:182-219, which scans for ## headings and accumulates lines until the next section. Each section includes:

  • Name and slugified slug
  • Full markdown content
  • Extracted checklist items within that section
  • Extracted wikilinks within that section

The parser uses a flush() pattern to finalize the current section when a new one begins or the file ends.

Sources: src/utils/parseWorkspace.ts:182-219, docs/schemas/kanban-parser-schema.ts:114-121


The parseBulletWikiLink function src/utils/parseWorkspace.ts:279-305 handles both card and board links. It:

  1. Matches lines starting with - [[...]] or * [[...]]
  2. Normalizes the wikilink target using normalizeWikiTarget from path utilities
  3. Resolves the target to a slug using either resolveBoardTargetPath or resolveCardTargetId
  4. Returns a link object with slug, target, and optional title

Wikilink Resolution Flow

The resolution functions are wrapped in try-catch blocks src/utils/parseWorkspace.ts:307-321 to handle invalid paths gracefully, falling back to the normalized target as the slug.

Sources: src/utils/parseWorkspace.ts:279-321

The extractWikilinks function src/utils/parseWorkspace.ts:334-336 extracts all [[...]] patterns from markdown content (not just bulleted ones). It returns an array of slugs after normalizing each target. This is used to build the wikilinks array on card documents and sections.

Sources: src/utils/parseWorkspace.ts:334-336

Checklist Extraction

The extractChecklist function src/utils/parseWorkspace.ts:323-332 uses a regex to find markdown checklist items:

  • Pattern: /^[-*]\s+\[( |x|X)\]\s+(.+)$/
  • Maps to KanbanChecklistItem objects with checked (boolean) and text (string)

This extraction is performed on both the card body and individual sections.

Sources: src/utils/parseWorkspace.ts:323-332, docs/schemas/kanban-parser-schema.ts:123-126


Frontmatter Parsing

Frontmatter is YAML data enclosed in --- delimiters at the start of a file. The parseFrontmatter function src/utils/parseWorkspace.ts:338-388:

Frontmatter Parsing Logic

The parser emits diagnostics for two error cases:

  • frontmatter.unclosed: Starts with --- but no closing delimiter found
  • frontmatter.invalid: Closing delimiter found but YAML parsing failed

Parsed YAML is normalized using toMarkdownRecord src/utils/parseWorkspace.ts:390-398, which ensures the result is a flat object (not array, not primitive) and recursively normalizes nested values via normalizeMarkdownValue src/utils/parseWorkspace.ts:400-424.

Sources: src/utils/parseWorkspace.ts:338-424


Title Extraction Strategy

Both board and card files use a three-tier fallback strategy for determining the title:

Title Resolution Priority

PrioritySourceBoard ImplementationCard Implementation
1Frontmatter title fieldreadString(frontmatter.title)readString(metadata.title)
2First # heading in bodyreadHeading(parsed.content, '# ')readHeading(parsed.content, '# ')
3Derived from pathlocalBoardNameFromBoardPath(path)titleFromSlug(slugFromMarkdownPath(path))

The readHeading helper src/utils/parseWorkspace.ts:426-429 finds the first line starting with the given prefix and extracts the text after it.

The titleFromSlug helper src/utils/parseWorkspace.ts:456-462 converts a kebab-case slug to Title Case by splitting on hyphens and capitalizing each part.

Sources: src/utils/parseWorkspace.ts:47, src/utils/parseWorkspace.ts:164, src/utils/parseWorkspace.ts:426-462


Diagnostic Collection

Diagnostics are warnings or errors collected during parsing. Each document (board or card) maintains its own diagnostics array, and the final KanbanParseResult aggregates all diagnostics src/utils/parseWorkspace.ts:38.

Diagnostic Types

CodeLevelConditionLocation
frontmatter.unclosedwarningFrontmatter starts but never closessrc/utils/parseWorkspace.ts:354-361
frontmatter.invalidwarningFrontmatter YAML parse failedsrc/utils/parseWorkspace.ts:377-384
board.card-outside-columnwarningCard link appears before any ## columnsrc/utils/parseWorkspace.ts:121-129
board.no-columnswarningBoard file contains no ## columnssrc/utils/parseWorkspace.ts:135-142
board.invalid-settingswarningSettings block JSON parse failedsrc/utils/parseWorkspace.ts:241-246

Each diagnostic includes:

  • level: "error" or "warning"
  • code: Machine-readable identifier
  • message: Human-readable description
  • path: File path where issue occurred
  • line and column: Optional position information

Sources: src/utils/parseWorkspace.ts:43, src/utils/parseWorkspace.ts:121-142, src/utils/parseWorkspace.ts:241-246, src/utils/parseWorkspace.ts:354-384, docs/schemas/kanban-parser-schema.ts:19-26


Integration with LoadedWorkspace

The parsed result is immediately consumed by buildLoadedWorkspace in the workspace snapshot module src/utils/workspaceSnapshot.ts:5-27. This function:

  1. Calls parseWorkspace(snapshot) to get structured documents
  2. Builds indexed maps: boardsBySlug, cardsBySlug, boardFilesBySlug
  3. Determines boardOrder from parse result
  4. Resolves rootBoardSlug from the snapshot's rootBoardPath

This creates a LoadedWorkspace object optimized for efficient lookups during UI rendering and operations.

Complete Parsing to Loading Pipeline

Sources: src/utils/workspaceSnapshot.ts:5-27


Type System Summary

Core Parsing Types Hierarchy

All types are defined in docs/schemas/kanban-parser-schema.ts:1-127.

Sources: docs/schemas/kanban-parser-schema.ts:1-127, src/utils/parseWorkspace.ts:3-17