kanban-todo.txt/SPEC.md
2025-12-30 17:14:34 +08:00

8.8 KiB

KBTD - Kanban TODO

A minimal Kanban board web app that uses a Markdown file as its data store.

Overview

KBTD presents a Kanban board interface for managing tasks stored in a standard Markdown TODO file. The Markdown file is the canonical data store—the app provides a visual interface for viewing and manipulating it.

Core Concepts

Term Description
Project A working directory containing a TODO.md file (identified by absolute file path)
Section An H1 (# Name) in the TODO file — rendered as a selectable Kanban board
Column An H2 (## Name) under a section — a column within that board
Card A - [ ] or - [x] item — draggable between columns

Markdown Format

# Project Alpha

## Backlog

- [ ] Research competitors
- [ ] Write RFC

## In Progress

- [x] Design mockups
- [ ] Build prototype

## Done

- [x] Initial brainstorm

# Project Beta

## TODO

- [ ] Something else

Parsing Rules

  • H1 headers (#) define sections (boards)
  • H2 headers (##) define columns within the preceding section
  • - [ ] is an incomplete task
  • - [x] is a complete task
  • Task text is everything after the ] until end of line
  • Content before the first H1 is ignored
  • Content between H1 and first H2 is ignored
  • Nested content under tasks (indented lines) is preserved but not displayed

Architecture

Components

  1. Shell script (kbtd) - Cross-platform launcher
  2. Single HTML file (index.html) - The entire web application

Data Flow

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  TODO.md    │◄───►│  Web App    │◄───►│ localStorage│
│  (file)     │     │  (browser)  │     │ (UI state)  │
└─────────────┘     └─────────────┘     └─────────────┘
      │                    │
      │   File System      │   IndexedDB
      │   Access API       │   (FileHandle)
      │                    │
      └────────────────────┘

State Management

  • File state: The TODO.md file is the source of truth for task data
  • UI state: localStorage stores current section selection, keyed by file path
  • File handle: IndexedDB stores the FileSystemFileHandle for persistence across sessions

localStorage Schema

Key: kbtd:${filePath}

{
  "currentSection": "Project Alpha"
}

IndexedDB Schema

Database: kbtd Object Store: fileHandles Key: file path from URL query string Value: FileSystemFileHandle

User Interface

States

  1. No file associated: Shows file picker button and instructions
  2. Permission needed: Shows button to re-grant permission (after page reload)
  3. File not found: Shows error message if TODO.md doesn't exist
  4. Section picker: Shows list of sections (H1s) to choose from
  5. Kanban board: Shows columns and cards for selected section

Kanban Board Features

  • Columns displayed horizontally
  • Cards displayed vertically within columns
  • Drag and drop cards between columns
  • Click card to toggle complete/incomplete
  • Visual distinction for completed tasks (strikethrough or muted)

Navigation

  • Back button to return to section picker
  • Current section name displayed in header

File Synchronization

Polling

  • Poll file every 250ms using file.lastModified
  • On change detected: reload file, rebuild state, re-render UI
  • If currently selected section no longer exists: return to section picker

Write Strategy

  • On any card move or status toggle: serialize full state to Markdown and write
  • Use FileSystemFileHandle.createWritable() for atomic writes

Conflict Resolution

None. Last write wins. External changes blow away app state.

Shell Script (kbtd)

Usage

kbtd [OPTIONS] <path-to-markdown-file>

Options:
  --browser    Open in existing browser instead of app mode
  --help       Show help message

Behavior

  1. Resolve the markdown file path to absolute path
  2. Locate a Chromium-based browser (Chrome, Chromium, Edge)
  3. Determine mode:
    • Default: --app mode with isolated --user-data-dir in current directory
    • With --browser: open in existing browser instance with --new-window
  4. Launch browser with URL: {base_url}?file={absolute_path}

Browser Detection

Check in order:

  1. $BROWSER environment variable (if Chromium-based)
  2. google-chrome / chromium / chromium-browser / microsoft-edge in PATH
  3. macOS: /Applications/Google Chrome.app/..., /Applications/Chromium.app/..., /Applications/Microsoft Edge.app/...
  4. WSL: /mnt/c/Program Files/Google/Chrome/Application/chrome.exe, etc.

If no Chromium browser found: exit with error message explaining requirement.

App Mode Launch

# Create isolated user data directory
USER_DATA_DIR="./.kbtd-chrome-data"

# Launch in app mode
google-chrome \
  --user-data-dir="$USER_DATA_DIR" \
  --app="http://localhost:8000/?file=/absolute/path/to/TODO.md"

Browser Mode Launch

google-chrome --new-window "http://localhost:8000/?file=/absolute/path/to/TODO.md"

URL Configuration

  • Development: http://localhost:8000
  • Production: Configurable via $KBTD_URL environment variable or hardcoded default

Web App Behavior

Startup Flow

1. Parse ?file= from URL query string
2. If no file param:
   → Show landing page with instructions and script download link
3. Check IndexedDB for stored FileHandle for this file path
4. If handle exists:
   → Check permission with queryPermission()
   → If "granted": proceed to load file
   → If "prompt": show "Grant Access" button
   → If "denied": show file picker
5. If no handle:
   → Show file picker button
6. On file picker success:
   → Store handle in IndexedDB
   → Load and parse file
   → Show section picker or board

File Picker

const [fileHandle] = await window.showOpenFilePicker({
  id: 'kbtd-todo-file',
  mode: 'readwrite',
  types: [{
    description: 'Markdown files',
    accept: { 'text/markdown': ['.md'] }
  }]
});

Permission Re-grant

After page reload, stored handles return "prompt" for permission state. User must click a button to trigger:

const permission = await fileHandle.requestPermission({ mode: 'readwrite' });

This requires a user gesture (button click).

Browser Compatibility

Required: Chromium-based browser (Chrome 86+, Edge 86+, Opera 72+)

Not supported: Firefox, Safari (File System Access API not implemented)

The shell script should detect and warn if no compatible browser is found.

File Structure

kbtd/
├── index.html      # Complete web application (single file)
├── kbtd            # Shell script launcher
└── SPEC.md         # This specification

Landing Page

When loaded without a ?file= parameter, the app displays:

  1. App name and brief description
  2. Instructions for use
  3. Download link/instructions for the shell script
  4. Note about Chromium browser requirement

Architecture Principles

Source of Truth

The Markdown file is the only source of truth. The Kanban UI is purely a renderer and editor for the file. There is no separate application state that needs to be "synced" with the file.

Functional & Idempotent Design

  • Pure parsing: parseMarkdown(text) → {sections, columns, cards} — same input always produces same output
  • Pure serialization: serializeToMarkdown(data) → text — same input always produces same output
  • Idempotent renders: render(data) can be called any number of times with the same data and produce the same DOM
  • No hidden state: UI state (current section) is minimal and stored separately from task data
  • Stateless transformations: operations like moveCard(data, cardIndex, fromColumn, toColumn) → newData return new data structures rather than mutating

Data Flow

File Change → Read File → Parse → Render
User Action → Transform Data → Serialize → Write File → Read File → Parse → Render

Note: After every write, the app re-reads and re-parses the file. This ensures the file remains the source of truth and the UI always reflects exactly what's on disk.

Non-Goals (v1)

  • No task metadata (due dates, tags, assignees)
  • No task IDs or UUIDs
  • No multi-file support
  • No task details/descriptions (subtasks, notes)
  • No offline support beyond what the browser provides
  • No collaborative editing
  • No mobile support
  • No server component (pure client-side)

Future Considerations

  • Task detail files in .issues/ subdirectory
  • UUID-based task tracking for stable identity
  • Folder access instead of single file
  • Multiple TODO files in project