Inline view (src/inline-view.ts): a csv-view code block renders an editable
table/cards/kanban view of a CSV inside any note. InlineCardHost satisfies the
renderers' duck-typed surface so renderTable/renderLibrary/renderKanbanGenre are
reused unchanged; edits write back via vault.modify and re-sync off the modify
event. Block directives: file/mode/height/collapse.
- Cards/Kanban thumbnails: getImageCol() auto-detects Image/Cover/Poster/…
columns; resolveImageSrc() handles vault paths, wikilinks, md-images, URLs.
- Card title + context-menu "Open entry" now open the expander even without a
notes column (NoteExpanderModal hides its notes section when absent), so
notes-less files (e.g. applications trackers) are editable from cards.
- collapse: directive + fileCfg.collapsedGroups: Cards groups collapse by
default and remember manual toggles (full-page too).
- Habit add-form toggles for 0/1 columns; pseudo-categorical columns (few
distinct values) get option dropdowns in the add + edit forms via shared
looksCategorical() helper.
- Kanban: empty group value → "—" column (was dropped in default grouping);
rows with blank/unknown status render ungrouped instead of vanishing while
the column header still counted them.
- Dark-mode table row hover: explicit text-tint overlay (was near-invisible).
- Docs: csv-view syntax in README; architecture + open follow-ups in handoff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Notes
- openOrCreateNotes now tags spawned notes with the row's project on
tasks/projects files: `tags: [project-<slug>]` via a new tagify() helper,
reviving the old per-project dashboard tag-scan. Gated to hasTaskColumns
files so books/movies notes don't grow project tags.
Anki sync (src/view/anki.ts)
- New 🎴 Anki toolbar button (+ ⋯ menu) pushes rows to Anki via AnkiConnect
(127.0.0.1:8765). One row → one Basic note: configurable front column
(⚙ Columns → "Anki card front", FileConfig.ankiFrontCol), other columns on
the back, HTML-escaped. Deck = file basename, auto-created. allowDuplicate
off → idempotent re-sync. Clear notice when Anki isn't reachable.
Palette commands
- "Create tasks/travel/habit tracker file" scaffold a new CSV with that view's
canonical columns, pin defaultMode, and open it (FILE_TEMPLATES +
createTemplateFile + PromptModal).
Tasks view + Add-entry modal
- Split the Tasks view into three peer sections: Tasks / Notes / Ideas.
- Add-entry modal: Type and Priority render as native <select>s prefilled with
Task/Note/Idea and Low/Medium/High on tasks files; Due gets a date picker
(isDateCol now matches due/deadline). Native selects fix the focus-trap bug
where the body-appended picker bounced focus back to the title, leaving
empty project/type/etc. columns unfillable.
Docs + tests
- README Anki-sync section + setup, updated feature/mode notes; architecture
module map. New coverage: tagify, anki sync (4), 3-section split, date-col
due/deadline. Full suite green (csv 6, logic 115, mobile 21, smoke 62).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First pass at this used appearance:none + vertical padding + line-height,
which made the label taller than the control and clipped it at the top.
Mirror how Obsidian's own dropdowns center: a fixed `height` with zero
top/bottom padding lets the native control flex-center the label
(line-height is irrelevant). Keeps the custom chevron and per-class
horizontal padding.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Styled native <select>s rendered their label vertically off (top
clipping the border) on macOS/Electron — a long-standing wart on the
view-mode picker, the library/tasks filter selects, and the per-file
config modal. Strip the native appearance so CSS box centering applies,
draw our own chevron in place of the native arrow, and switch the
per-class fills to background-color so the shared chevron image isn't
reset by the background shorthand.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clicking a task/note name now opens the expander overview modal (same as
tapping a kanban card title) — a quick read/edit with no filesystem
touch. The 📄/+ icon is the only thing that opens/creates the backing
.md. Falls back to open/create-page when the file has no notes column to
host the expander body. +2 smoke cases (57 passing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Six jsdom cases over src/view/tasks.ts: task/note split + project
grouping, done→priority→due sort order, overdue flagging, the
vocab-aware done toggle, the no-type-column "all tasks" path, and the
hasTaskColumns availability gate (incl. rejecting a genre "Type" column).
Full suite green: 112 + 55 + 21 + 6, 0 failures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- resolveDoneWord: the done-toggle reuses the file's existing finished
word (Completed/Finished/…) instead of forcing "done" into the column
- priority cell is click-to-edit via showSelectPicker (file's own values
+ high/medium/low); re-sorts on change
- editable-cell hover affordance
- README view-modes table (Eight → Nine) + handoff follow-up entry,
including the phase-2 cross-vault aggregate TODO
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A project dashboard that renders a tasks CSV directly, replacing the
fragile DataviewJS-over-index-pages pattern. One row = a task/note/idea/
reference; grouped by a project column into a Tasks section (sorted
done → priority → due, overdue highlighted) and a Notes & Ideas section.
Each row opens/creates an optional backing .md page, reusing the Library
view's notes infra.
- src/view/tasks.ts: renderer + column resolution + hasTaskColumns gate
- wired into ViewMode, availableModes (gated on due/priority/type cols),
render dispatch, and the onLoadFile mode guard
- taskProjectFilter / taskTypeFilter instance state
- styles + sample-data/tasks.csv
Cross-vault aggregate mode left as a phase-2 TODO in the renderer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Multi-select for list columns (Category/Genres/Tags/Themes/Topics, by
name via isMultiValueColName): the picker becomes a toggle list with
✓ checkmarks, live-commits the comma-joined string on every toggle,
stays open until Done/Esc/outside-click, and splits comma-joined data
values into individual options. Wired at all five call sites (table
cells, kanban/focus chips, add-entry + expander modals). Closes the
"Multi-value select for Category" backlog item.
- Sticky table header: table mode reuses --no-yscroll so the table
wrapper becomes the y-scroller (sticky needs a scrollport — the
wrapper's implicit overflow-y:auto made sticky a no-op before).
Header rule drawn as box-shadow since border-collapse drops sticky
borders. Scroll restore now snapshots the wrapper's scrollTop.
- csv-random code block: a random entry card (quote/word of the day)
for daily-note templates, with attribution, source label, and a ↻
re-roll button. file:/field: options, same path resolution as
csv-add. Duck-typed TFile check so it's testable with a stub vault.
- Stats bars click through: status/category bars jump to the library
pre-filtered to that value.
- Palette commands: "Add entry to current CSV" and "Cycle view mode"
(availableModes extracted from the toolbar as the single source of
truth), both hotkey-able.
8 new smoke tests (38 total; 156 across all suites). jsdom env gains a
scrollIntoView no-op polyfill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Country selection: map shapes, Countries-table rows, and timeline
segments are all click targets. Selecting highlights the country on the
map (.cp-selected), dims other countries' timeline segments, and opens
a detail panel under the map listing every trip there (confirmed +
photo-inferred, newest first, with city/visa). Re-click, ✕, or clicking
an unvisited country clears. Selection lives in a renderTravel closure —
no view state, resets naturally on re-render.
Stats upgrades:
- "📍 Currently in X — since DATE (day N)" banner when today falls
inside a confirmed trip. Blank date_left counts as ongoing; partial
dates (2022-06-??) deliberately don't, so historic undated rows can't
show a stale location. currentStay in travel-data, 5 unit tests with
a fixed today.
- Cities and Longest-trip stat tiles.
- Timeline years get "Nd · M countries" summaries (confirmed days
clipped to the calendar year).
4 new smoke tests for the selection flow. Docs updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New views (both for non-date, non-travel files):
- Stats: pure-CSS bar charts — status/category breakdowns, rating
histogram + average, entries per year, top authors. No Chart.js;
respects the toolbar search. src/view/stats.ts.
- Focus: one entry at a time with big typography, prev/random/next
nav and arrow-key navigation. Gives quotes/dictionary files
(previously table-only) a purpose-built reading view; notes render
as markdown via a new CardView.renderMarkdownInto helper.
src/view/focus.ts.
UX improvements:
- Table headers are click-to-sort (asc → desc → off) with numeric-aware
comparison and empties-last; session-only, the persisted date toggle
remains the default. Comparator extracted as sortRowsByColumn in utils.
- Library search now matches all fields — "search by author" worked in
kanban/table but silently failed in the library (title-only).
- No-results empty states in library/kanban/focus get a Clear
filters/search button instead of a dead end.
Both new modes are offered in the per-file default-view dropdown.
11 new test cases across the smoke suite (26 total). Bundle +8 KB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- README.md: features, the six view modes + auto-detection, the flat travel
CSV format, the residency-rule editor (with field reference), install, and
the src/ layout + test surface.
- Bump manifest + package version to 1.1.0 and broaden the manifest
description to mention the dashboard / library / travel views.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mobile-dashboard generation moves out as generateMobileFiles(view); the toolbar
calls it directly and the now-unused mobile-templates import is dropped from
main.ts. 1 smoke case (habit dashboard written to Mobile/<file>.md with a
mocked vault).
main.ts 857 → 767. tsc clean; test:all green (view 15).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
renderDashboard + renderHabitTimeline (~490 lines) move out, taking the loadChart
lazy Chart.js loader with them — so non-dashboard sessions still never load
Chart.js, and main.ts no longer carries the chart plumbing (keeps only the
ChartType import for the chartInstance field + its destroy on unload). Widened
selectedDate/selectedHabit/chartInstance/timelineYear + getDateCol/
getBooleanColumns/formatDate/isTruthy/parseDate to public.
Smoke: 3 cases (full render incl. post-chart stats, per-habit timeline, empty
state) using a chart.js stub aliased in the bundler (jsdom has no canvas ctx).
main.ts 1354 → 857. tsc clean; test:all green (view 14).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The toolbar (mode buttons, search bar, sort toggle, Columns/Mobile/Backup/
+Add/⋯ actions) moves out as renderToolbar(view, root). Widened saveFileCfg/
renderViewPreservingScroll/autoDetectBooleanColumns/generateMobileFiles/
backupToArchive/openAddModal/hasDateColumn/isTravelFile to public. toolbar.ts
declares its own __BUILD_TIME__; the smoke loader passes a define for it.
1 smoke case (mode buttons + search + row count + Add).
main.ts 1548 → 1354. tsc clean; test:all green (view 11).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
renderKanbanGenre + its renderKanbanCard helper (incl. the inline-notes editor)
move out as free functions taking the view. Widened getTitle/getSubtitle/
getColumnValues to public (visibility only). 2 smoke cases (columns-per-genre +
empty state); the column case also documents that status-grouped kanban only
cards rows whose status is among the present statuses.
main.ts 1761 → 1548. tsc clean; test:all green (view 10).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cards-by-genre renderer moves out as renderLibrary(view, container). Widened
getCategoryCol/getStatusCol/titleKey/authorKey/resolveCol/getNotesCol/fileCfg/
renderView + libraryStatus/GenreFilter to public (visibility only). The filter
state fields stay on CardView. 2 smoke cases added (grouping + empty state).
main.ts 1999 → 1761. tsc clean; test:all green (view 8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First view-renderer extraction, proving the pattern: renderTable + its
makeEditable helper move out as free functions taking the CardView instance
(type-only import → no cycle). The ~12 members they reach (getFilteredRows,
isNotesCol, openNoteExpander, scheduleSave, persistSettings, …) are widened
from private to public — visibility only, no behavior change. Dispatch calls
renderTable(this, content).
Guarded by 2 new smoke cases in test-view-smoke.mjs driven by a hand-built
view stub (no CardView/FileView instance needed). main.ts 2103 → 1999.
tsc clean; test:all green (csv 6, logic 107, view 6).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The regression net for the main.ts → src/view/* split. test-support/dom-env.mjs
sets up jsdom and polyfills Obsidian's Element extensions (createEl/createDiv/
setText/empty/addClass/setAttr/… on Element.prototype, so SVG nodes get them
too). test-support/obsidian-stub.mjs satisfies the obsidian imports so view
code bundles under node (esbuild alias). test-view-smoke.mjs renders a view
into a real DOM tree and asserts structure — 4 cases over renderTravel (core
sections, residency gauge, empty state, SVG map coloring). Wired into
test:view + test:all. Adds jsdom devDependency.
Next renderers extracted should add a case here (drive with a fake `view`
stub) before committing. test:all green (csv 6, logic 107, view 4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
renderAddEntryForm (~370 lines) only depended on this.app, so it moves out as
a free function renderAddEntryForm(app, source, el, ctx). Call site passes
this.app. Drops now-unused imports (resolvePath, MarkdownRenderer,
MarkdownPostProcessorContext) from main.ts.
main.ts 2476 → 2103. tsc clean, 107/107, build green. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First step of the main.ts modularization. The settings tab + residency-rule
editor only touch this.plugin (settings + saveSettings), so they move out
cleanly with a type-only import of CardViewPlugin (no runtime cycle). Drops the
now-unused Setting/PluginSettingTab imports from main.ts.
main.ts 2569 → 2476. tsc clean, 107/107, build green. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read-only "travel" view mode for flat travel CSVs (country + date range +
source columns): interactive world choropleth (gold=confirmed, blue=photo-only),
stats row, per-country day totals, year-by-year timeline, instant hover
tooltips, and an editable confirmed-trips table. Plus configurable residency /
threshold day-gauges with an in-app rule editor in Settings, native yyyy-mm-dd
date pickers and non-strict source/resolved dropdowns in the add/edit modals,
and a mobile-responsive layout.
Modules: src/travel-data.ts (pure analysis + country reference data),
src/travel-view.ts (render), src/residency.ts (rule evaluation),
src/field-types.ts (modal field heuristics). world-map.svg ships beside the
bundle and loads at runtime (main.js stays lean). 107 unit tests over synthetic
fixtures (sample-data/travel_flat.csv). Default residency rule is a single
neutral Schengen example; user rules live in data.json only.
History squashed from the travel-feature commits into one clean commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two small follow-ups to the mobile search work.
Scroll-preserve audit: six in-place-edit call sites that were calling
plain renderView (which empties the content area and resets scrollTop
to 0) now go through renderViewPreservingScroll. The list:
- deleteWithUndo: row deletion + the undo restore
- context-menu Mark-as status change
- AddEntryModal onSubmit
- Table sort-order toggle
- Dashboard habit-checkbox toggle
Sites that genuinely warrant a scroll reset (mode switch, file open,
dashboard date navigation, FileConfigModal which may change the mode)
keep plain renderView.
× clear button restored on mobile, but properly:
- Input now lives in a relative wrapper (.csv-search-input-wrap) so
the × can position:absolute over the input's right edge without
affecting any sibling. The Done button no longer shifts when ×
appears mid-typing — × never participates in layout in the first
place.
- Visibility flipped via an `is-hidden` class on the button (opacity
+ pointer-events: none) instead of display:none, so there's no
even-momentary reflow on the toggle.
- Pill styling: 22×22 circle with surface-2 background, hover bumps
contrast. Sits inside the input on the right, indistinguishable
pattern from any native search field.
- × now only clears the query and refocuses (so the user can keep
typing), not collapses the bar. Done handles bar-collapse when
pressed on an empty input.
Behavioral summary for the mobile search row:
[search-input(×)] [Done]
- Type → underlying view filters live (debounced 120ms)
- × → clear query, keep bar open + focused
- Done with text → dismiss keyboard, keep bar + filter
- Done with empty input → collapse bar back to normal toolbar
- Return key → same as Done with text
Tests: 88 + 21 green. Typecheck clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iterative pass on mobile search UX after the first try (modal with preview
list) felt heavier than warranted. End state is closer to the desktop
inline search but adapted for the iOS keyboard.
Mobile search flow:
- Tap 🔍 → toolbar transforms inline: mode group / + Add / ⋯ hide and the
search input + Done button fill the row. No modal, no fixed positioning
acrobatics, no extra layer.
- Type → underlying view filters live in whatever space iOS leaves above
the keyboard. With the keyboard up that's small, but it's enough to see
the result count and a row or two.
- Tap Done or hit Return → keyboard dismisses, WebView returns to full
size, the filtered view is fully visible. Search bar stays open with
the query, so the user can edit or close from there.
- Done is multi-purpose: with text in the input it dismisses the keyboard
(filter persists); with empty input it collapses the search bar back
to the normal toolbar. Single exit affordance — no separate × needed,
which previously caused Done to shift position mid-typing.
- The Return key is relabeled "Search" via enterkeyhint, and inputmode
is set to "search" for the search-style on-screen keyboard.
Also during this cycle:
- An intermediate SearchModal-with-preview-list approach (still exported
in src/modals.ts for future use) which proved heavier than the inline
approach. Left in tree as it carries the visualViewport + .modal-bg
transparency patterns and could be useful for other things.
- "Found X of Y entries" hidden on mobile while search is open — it
clipped weirdly when iOS shrunk the WebView and was redundant with
the input being visible at the top.
- Build-time timestamp injected via esbuild's `define`. Surfaced as
"Built YYYY-MM-DD HH:mm" in the ⋯ menu and as a small ⓘ button on
desktop. Useful confirmation on iPhone that iCloud has synced the
latest deploy.
Dashboard scroll-preserve:
- Tapping a habit card to open its timeline (and tapping ✕ / changing
the year selector) now goes through renderViewPreservingScroll
instead of plain renderView. Without this, opening the timeline below
the fold yanks the user back to y=0 and they have to scroll down to
find the card again to see what they just opened.
Bug fixes / reverts from earlier in the session:
- Reverted `will-change: scroll-position` on .csv-table-wrapper —
intermittent iOS WebKit bug where the promoted compositor layer was
clipped outside the parent bounds, making rows invisible. The scroll
smoothness from the earlier cleanup (per-td ::after + position:relative
scoped to .csv-cell--clipped) was enough on its own.
- Modal centering fix: don't override the transform (Obsidian centers
the modal via parent flex, not a transform on the modal); use
align-self: flex-start + margin-top: 10px on mobile so the modal
top-anchors without breaking horizontal centering.
- Library view stopped rendering its own duplicate search input.
- Kanban notes preview gets overflow-wrap: anywhere so a URL or hash
doesn't push the card past the column.
- Mode labels renamed: Library → Cards, By Genre → Kanban. Generic for
non-library files; also shorter so the toolbar fits one row on mobile.
Tests: 88 plugin + 21 mobile-dashboard, all green. Typecheck clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A multi-pass mobile session. Each change addresses a specific iOS report,
but they hang together because they're all the same shape of bug: the
plugin is rendered in WKWebView, which has its own opinions about
keyboards, scroll, hover, layer compositing, and table layout.
— Kanban card: bottom hover-button row removed. Title-tap opens the
expander (already wired); small +/📄 in the title row creates or opens
the sidecar .md. One affordance per action; recovers vertical density.
— Mobile inline-note edit routes to the expander modal instead of the
inline textarea. iOS keyboard pop scrolls focused inputs into view,
which yanks the user's y-position. The modal owns its own viewport so
the keyboard resizes the modal, not the underlying card list.
— Modal keyboard handling. Two stacked fixes after the obvious ones
didn't take:
1. visualViewport.height (not dvh, which iOS Obsidian doesn't honour)
written into --csv-modal-vh on resize+scroll, used in max-height.
2. On mobile, align-self: flex-start + margin-top: 10px instead of
overriding the transform — Obsidian centers via parent flex, not
a transform on the modal, so translating the modal kicks it off
the left edge. align-self leaves horizontal centering intact and
top-anchors the modal so its sticky-bottom footer stays above the
keyboard.
3. .modal-content overflow-y: auto + .csv-expander-footer position:
sticky bottom: 0 — modal scrolls if content overflows, footer
floats above the scroll content and is always reachable.
— Mobile toolbar: search input collapses to a 🔍 toggle that overlays
the toolbar (position: absolute, not flex displacement — displacement
pushed Cards/Kanban/Table off the visible toolbar area and read as
'the view disappeared'). Expanded state holds while the query is
non-empty; empty-blur recollapses. Library renamed → Cards, By Genre
→ Kanban so the labels read correctly for non-library files and the
toolbar fits one row.
— renderLibrary used to render its own .csv-library-search alongside
the toolbar one — both wrote to this.searchQuery, pure duplicate.
Removed.
— Table lag on iPhone (was the worst report — 'scrolls to notes column
and freezes'). Three causes:
1. Per-row requestAnimationFrame for clip detection: 200 rows × N
cells × forced reflow each. Batched to one rAF, skipped on touch.
2. td::after gradient pseudo on every td (opacity:0 by default) =
N×M compositor pseudos. Scoped to .csv-cell--clipped only; on
touch the class is never added, so mobile pays zero.
3. position:relative on every td = N×M stacking contexts. Also
scoped to .csv-cell--clipped only.
4. -webkit-line-clamp on the td itself overrode display:table-cell
and broke row borders. Moved the clamp onto the inner span so
the td stays a normal table-cell.
5. tr:hover gated behind @media (hover: hover) — iOS sticky-hover
was leaving the last-touched row tinted and forcing a recomposite
on every scroll frame. Also dropped contain: content on tbody tr;
CSS containment on table-internal elements is spec-undefined and
iOS WebKit was occasionally dropping rows from layout.
6. .csv-table-wrapper gets will-change: scroll-position so iOS
promotes it to its own compositor layer.
— Mobile table search input debounced by 120ms so each keystroke
doesn't trigger a full content re-render. Search-focus also restores
window scroll on the next frame — iOS WKWebView auto-scrolls focused
inputs into view even with preventScroll, pushing the table below
the keyboard.
— Kanban notes preview: overflow-wrap: anywhere so a URL or hash in
the note doesn't push the card wider than the column.
— Build-time timestamp baked into the bundle via esbuild's define;
surfaced as 'Built YYYY-MM-DD HH:mm' in the ⋯ menu + a small ⓘ
button on desktop. Useful on iPhone where iCloud sync of the
deployed bundle can lag and there's no native way to confirm the
new code arrived.
— Transition durations capped: 0.3s → 0.15s on the habit progress bar,
0.2s → 0.1s elsewhere. UI feels snappier; transitions are compositor-
driven so shortening doesn't slow anything down.
Tests: 88 plugin + 21 mobile-dashboard, all green. Typecheck clean.
Bundle: 300 KB minified (unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two kanban-on-mobile bugs:
(1) Tapping the inline note preview opened the editor with the caret at
the end of the textarea (default behavior after setting .value
programmatically). On long notes this scrolled the textarea's internal
viewport past all the content, so the user saw an empty area "much
down" and assumed the editor opened blank. Also: scrollHeight read
inline (before layout) returned ~0, so the height clamped to the 120 px
floor regardless of content length.
Fix: caret at position 0, scrollTop = 0, defer height calc to next
frame so scrollHeight reflects laid-out content.
(2) Swipes between scroll-snap kanban columns (and momentum scrolls
inside a column) occasionally registered as taps on the underlying
card — opening the note editor or expander modal when the user just
wanted to scroll.
Fix: installTouchScrollGuard on .csv-content-area. Tracks
touchstart→touchmove distance; if the finger moves more than 10 px
(iOS's own tap threshold) before touchend, swallows the subsequent
click in capture phase before any descendant handler runs. Pure
touchmove-based, so programmatic scrolls (e.g. the scroll-restore
inside closeInlineEditor) don't create false lockouts. Touch-only
via matchMedia gate; desktop behavior unchanged.
Single install at the content-area level covers kanban, library,
and table views.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Class-internal rename. The CARD_VIEW_TYPE string registered with Obsidian
is unchanged, so the rename is purely cosmetic from the API's perspective.
Sweeps 8 sites: class def + instantiation in main.ts, plus mention-only
references in test-plugin-logic.mjs, xlsx-to-csv-roundtrip.mjs,
src/mobile-templates.ts, docs/architecture.md, docs/dev-workflow.md.
Closes the follow-up flagged in handoff.md after SWITCH TO CSV AS MAIN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plugin is CSV-only. registerExtensions(["csv"]); SheetJS gone; the
_csv_helpers/ mirror collapses to a single canonical CSV. Bundle drops
720 KB → 297 KB (-59%); parse+eval 0.9 ms → 0.5 ms.
Migration plan was empirically validated before any data was touched:
xlsx-to-csv-roundtrip.mjs reads each xlsx, Papa.unparse → Papa.parse,
cell-by-cell compares the result to the source. All 10 checks passed
across books / movies / quotes / dictionary / habit_tracker — proves
the round-trip is lossless including multiline notes, stars, embedded
punctuation, and unicode.
migrate-xlsx-to-csv.mjs is the cutover: writes canonical csv, moves
xlsx → Archive/<name>_pre-csv-migration.xlsx (recoverable), removes
the _csv_helpers/ folder, rewrites data.json fileConfigs keys from
*.xlsx → *.csv so per-file overrides survive.
Code surface:
- main.ts: onLoadFile / doSave / generateMobileFiles / renderAddEntryForm
all collapsed to the csv-only path; loadXLSX + isXlsx removed.
- mobile dashboards: csv-add and dataviewjs now point at the same
canonical csv (used to be split because Dataview couldn't read xlsx).
- regenerate-mobile-dashboards.mjs: drops the helper-folder path.
- package.json: xlsx dependency removed (9 transitive packages gone).
Also included in this commit (carried over from the same session):
- mobile toolbar: ⚙ Columns / 📱 Mobile / 💾 Backup collapse into a
⋯ overflow menu on screens ≤ 600 px so the toolbar fits on one row.
- mobile compact density: pure-CSS media-query switch for Library
(2-col compact grid + mobile-md card style), tighter Kanban cards,
smaller Table cells.
Tests: 88 plugin + 21 mobile-dashboard, all green. Typecheck clean.
Verified manually: open books.csv, edit a note (saves debounce), open
on iPhone (compact library grid, ⋯ menu, csv-add form pre-fills).
Class name XLSXCardView left in place to keep this commit focused on
behavior; rename to CardView is a noted follow-up in handoff.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>