svm0n_datadeck/docs/dev-workflow.md
SVM0N 4faff4588e docs/config: restore current docs and package metadata, history stripped
README.md, docs/{architecture,css-classes,dev-workflow}.md, manifest.json,
package.json, package-lock.json, versions.json, .gitignore, LICENSE, and
tsconfig.json are re-added here with their current content only. Their
prior revision history is gone by design: an audit found 22 of README.md's
24 historical revisions still linked to handoff.md (a gitignored, never-
published file) — a dead reference that only ever disclosed the private
doc's existence, not its content, but was live across nearly the entire
project history until a recent cleanup. Since these are docs/metadata
rather than code, commit-by-commit revision history has no review value
here; only the current, already-audited content is kept. Code history
(everything else) is untouched.
2026-07-16 14:03:23 +00:00

9.8 KiB

Dev workflow

Reference doc — load when running the dev loop, adding tests, or debugging build/deploy. For what the code does, see architecture.md; for the CSS surface, see css-classes.md.

Project structure

datadeck/
├── main.ts                       # CardView, Settings, Plugin
├── main.js                       # Compiled output — do not edit directly (minified,
│                                 # post-CSV migration; was much larger with SheetJS)
├── bench-load.mjs                # Measures bundle parse/eval cost; run after big refactors
├── src/                          # types, utils, modals, and one file per view mode
│                                 # under src/view/ (table, kanban, library, tasks, chart,
│                                 # dashboard, focus, stats, toolbar, anki)
├── styles.css                    # All plugin CSS
├── manifest.json                 # Obsidian plugin manifest (id: datadeck)
├── package.json                  # deps: chart.js, papaparse, esbuild, obsidian types
│                                 # (xlsx/SheetJS removed in "SWITCH TO CSV AS MAIN")
├── esbuild.config.mjs            # Build config
├── tsconfig.json                 # TypeScript config
├── test-csv-parser.mjs           # CSV parsing tests
├── test-plugin-logic.mjs         # Plugin logic tests — sanitization, parsing, column
│                                 # resolution, round-trip, edge cases, resolvePath,
│                                 # migrateFileConfigKey, and more
├── test-view-smoke.mjs           # DOM regression harness for view renderers
├── test-support/                 # jsdom + Obsidian/Chart.js stubs for the smoke harness
├── migrate-xlsx-to-csv.mjs       # One-shot migration script for anyone still on an xlsx-era
│                                 # install: writes canonical csv, archives xlsx to Archive/,
│                                 # rewrites fileConfigs keys
├── create-chart-test-data.mjs    # Generates a synthetic Chart Test.csv fixture
├── create-world-map.mjs          # Regenerates world-map.svg from source map data
├── world-map.svg                 # Static country-outline asset for the travel view
├── docs/                         # Reference docs (architecture, css-classes, dev-workflow)
└── (local/, handoff.md)          # gitignored personal dev tooling + session notes —
                                  # not part of the published repo

Live data location

Live data is any folder of CSVs in the vault. Mobile/ and Archive/ subfolders are created as needed by the plugin on first click of the respective buttons; Archive/ also holds *_pre-csv-migration.xlsx originals preserved by the migration script and * sync-conflict *.csv stashes from the sync guard.

Build & Deploy

npm install              # Install dependencies
npm run build            # Build main.js (minified, ~720 KB)
npm run build:deploy     # Build and copy to Obsidian plugin folder
npm run dev              # Watch mode (rebuild on changes, unminified, inline sourcemaps)
npm run deploy           # Copy already-built files to Obsidian plugin folder

Watch mode is unminified with inline sourcemaps so DevTools in Obsidian stays readable during dev. Production builds (npm run build, build:deploy) minify.

Install in Obsidian: copy main.js, manifest.json, styles.css into <vault>/.obsidian/plugins/datadeck/ and enable in Community plugins. The datadeck/ folder in this repo is a symlink to that path, so npm run deploy (or build:deploy) updates the plugin in place. Reload with npm run reload (Obsidian CLI — see below) or Cmd+R in Obsidian.

Testing

npm run test             # Plugin logic tests (88)
npm run test:csv         # CSV parser tests (6)
npm run test:all         # All of the above (115 total)
npm run typecheck        # TypeScript type checking
npm run check            # Full check: typecheck + tests + build + deploy

Test surface

  • CSV parsing — CRLF, quotes, escaping, long fields, embedded newlines, Unicode, empty input.
  • Column resolution — case-insensitive matching, fallback chains.
  • Round-trip serialization — Papa unparse → parseCSV round-trip stability.
  • Title case, binary col detection, date col detection, search filtering.
  • findExistingRowByDate / mergeHabitEntry / sortByDate — habit-tracker write paths.
  • formatRatingForDisplay (9) — empty / unrated / glyph-pass-through / numeric / text-mapped / out-of-range / unknown-on-rating-col / unknown-on-other-col.
  • resolvePath (9) — sibling / vault-relative / ../ / ../../ / ./ / mixed / root-clamp.
  • migrateFileConfigKey (4) — rename moves config, delete drops it, no-op for missing keys, caller-set new entry wins.
  • Mobile dashboard simulator (21) — per-file no-throw + render assertions, movie-specific compact-grid/year/rating/theme checks, generic-table wrap, watched-dot count.

The mobile simulator runs the dataviewjs body against real CSVs via Papa Parse with dynamicTyping: true, so any drift between template and rendered output fails the test the same way it would fail in Obsidian.

Active dev loop

npm run build:deploy && npm run test:all
npm run reload           # hot-reload the plugin in the running Obsidian (no Cmd+R)
npm run errors           # surface any runtime errors the reload produced

Mobile-dashboard regeneration/verification lives in the gitignored local/ tooling (OBSIDIAN_VAULT env var required).

npm run check:live chains the whole thing: typecheck + tests + build + deploy + reload + error check.

Obsidian CLI (driving the running app)

The obsidian CLI (ships with Obsidian ≥1.12, desktop must be running) can drive the live app — this replaces the manual "Cmd+R and click around" step for most verification. Full reference: obsidian help or https://help.obsidian.md/cli.

obsidian plugin:reload id=datadeck      # reload after build:deploy (= npm run reload)
obsidian dev:errors                          # captured plugin errors (= npm run errors)
obsidian dev:console level=error             # console output
obsidian dev:screenshot path=shot.png        # screenshot the app for visual verification
obsidian dev:dom selector=".csv-card" text   # inspect rendered DOM (CSS classes: css-classes.md)
obsidian dev:css selector=".csv-chip-value" prop=max-width
obsidian dev:mobile on                       # toggle mobile emulation (kanban scroll-snap etc.)
obsidian eval code="app.vault.getFiles().length"

Typical verify pass after a change: npm run check:live, then dev:screenshot / dev:dom against an open CSV view instead of eyeballing, and dev:mobile on to check the phone layout without a device. Commands target the most recently focused vault; pass vault="<name>" first if several are open.

Bench

node bench-load.mjs main.js measures V8 parse cost on the bundle text + parse+top-level eval. 5-run averages. Current baseline:

Stage Size Parse only Parse+eval
Eager + unminified (historical) 1338 KB 2.6 ms 3.4 ms
Lazy + unminified 1533 KB 2.0 ms 0.8 ms
Lazy + minified (with SheetJS) 720 KB 1.5 ms 0.9 ms
CSV-only (current, post-migration) 297 KB 0.7 ms 0.5 ms

Chart.js is dynamic-imported (only initialises when dashboard renders). SheetJS is gone entirely — the CSV-only migration retired it. Run the bench after any refactor that touches imports/bundling.

Settings persistence (data.json)

All plugin settings saved to:

<vault>/.obsidian/plugins/datadeck/data.json

Obsidian writes this automatically via saveData() / loadData(). Persists across sessions, restarts, and devices (travels with vault on iCloud sync). Example:

{
  "defaultMode": "kanban-genre",
  "statusColumn": "status",
  "categoryColumn": "category",
  "notesSubfolder": "Notes",
  "columnWidths": { "Name": 240, "Notes": 400 },
  "selectColumns": ["Category", "Type", "Rating", "Status"],
  "fileConfigs": {
    "Library/books.csv": {
      "categoryColumn": "Category",
      "notesColumn": "Notes",
      "statusColumn": "Status",
      "defaultMode": "kanban-genre"
    }
  }
}

File-rename/delete hooks migrate fileConfigs keys automatically — old Library/Data/... entries in the user's live data.json may still be orphaned from before that fix; hand-edit if you want them back. The SWITCH TO CSV AS MAIN migration also rewrote *.xlsx keys to *.csv in-place.

Manual test checklist

  • Open books.csv → opens in By Genre view.
  • Genre columns render with status subgroups (In progress / Finished / Not started).
  • Hover card → buttons appear; click the note preview → textarea opens inline; blur saves and scroll position restored.
  • Click "⤢ Expand" → NoteExpanderModal opens; fields bar shows all columns; text fields click-to-edit; select chips open picker; click rendered notes to edit; Cancel discards; Save & close commits.
  • Click "+ Add" → AddEntryModal opens; all fields present; select chips work; submit adds row, notice shown.
  • Click "⚙ Columns" → FileConfigModal opens; dropdowns show all headers; checkbox grid for habit cols + cardFields; save persists to data.json.
  • Long chip values truncate with ; full text shown on hover.
  • Switch to Table view; resize columns; click notes cell → NoteExpanderModal opens (no inline editing).
  • Right-click kanban / library / table row → change status, delete (with undo).
  • Delete a row → 6s Notice with Undo button → click Undo, row restored to original index.
  • Click "💾 Backup" → Archive/<basename>_<date>.csv appears (byte-identical to source); same-day repeat refused.
  • Verify csv saved correctly by reopening or checking mtime.