Commit graph

5 commits

Author SHA1 Message Date
mpstaton
c70885d6a4 align(api): align frontmatter parsing with Obsidian recent API changes
Replace the in-tree hand-rolled YAML parser with Obsidian's first-party
frontmatter APIs (`app.metadataCache.getFileCache(file)?.frontmatter`
for reads, `app.fileManager.processFrontMatter(file, fn)` for writes).
Closes Phase 4.3-B of the Obsidian community-plugin publishing-prep plan
at context-v/plans/2026-05-03_Assuring-Obsidian-Community-Plugin-Requirements.md.

Why
---
The earlier publishing-prep pass typed away the parser's `any` to
satisfy ObsidianReviewBot, which kept lint green but left the underlying
parser intact. The hand-rolled parser had real edge-case correctness
bugs the type system could not catch: URL values containing colons
were mis-split, multi-line strings were truncated, YAML anchors and
list-of-maps were not understood, and the in-tree YAML emitter would
produce `[object Object]` output for plain-object values. Obsidian's
own implementation handles all of those correctly and is the path the
Obsidian community-plugin guidelines explicitly point at. Carrying our
own parser meant carrying the bug surface forever; deleting it
externalizes that work.

Files
-----
Deleted:
- src/utils/yamlFrontmatter.ts (176 lines: parser + emitter +
  updateFileFrontmatter helper). No replacement file in our codebase
  — the responsibility moves to Obsidian's APIs.

Modified:
- src/modals/CurrentFileModal.ts — three usages refactored
- src/modals/ConvertLocalImagesForCurrentFile.ts — three usages
  refactored, including the largest restructure (see below)
- context-v/changelogs/2026-05-03_01.md — appended a §6 covering this
  phase, updated the front-matter title and summary intro to claim
  completion of every blocking publishing-prep phase, added the parser
  to the deleted-files inventory at the bottom

Call-site categorization (5 sites total)
----------------------------------------
Pure reads → `metadataCache.getFileCache(file)?.frontmatter`:
- CurrentFileModal.loadExistingPrompt — was reading `image_prompt`
  to seed the modal textarea. Bonus: removes a `vault.read` (the cache
  is in-memory and synchronous) so the modal opens marginally faster
  on files without an existing prompt.
- ConvertLocalImagesForCurrentFile.analyzeCurrentFile — discovers
  image properties on file open.
- ConvertLocalImagesForCurrentFile.handleConvert — snapshots
  frontmatter once at the top of the loop for tag extraction. Tags
  are only used as upload metadata, not as a source of truth, so a
  one-time snapshot is fine and avoids re-fetching from cache on
  every iteration.

Single-key writes → `app.fileManager.processFrontMatter(file, fn)`:
- CurrentFileModal.updateFrontmatter — writes `image_prompt`.
- CurrentFileModal.updateImagePathInFrontmatter — writes
  `<size>_image` keys with the URL of a generated image.

Both single-key writes now follow the uniform pattern:

    await this.app.fileManager.processFrontMatter(file, (fm) => {
        fm[key] = value;
    });

processFrontMatter reads, mutates, and writes atomically via
Obsidian's own YAML emitter — no hand-rolled vault.read /
formatFrontmatter / vault.modify cycle, no race window between read
and re-write.

The harder case: handleConvert's mixed-mutation loop
----------------------------------------------------
The original handleConvert read the file once, parsed frontmatter into
an object, peeled off the body, then ran two long async loops:

  1. For each frontmatter image: upload to ImageKit, then
     `frontmatter[key] = uploadResult.url` in the in-memory object.
  2. For each markdown body image: upload, then
     `markdownContent = markdownContent.replace(...)` in the in-memory
     string.

After both loops, it reassembled the file as
`---\n${formatFrontmatter(frontmatter)}---\n\n${markdownContent}` and
wrote the whole thing with vault.modify. The formatFrontmatter step
was where the hand-rolled YAML emitter was doing its damage.

Restructured so frontmatter and body live on independent code paths:

- Frontmatter mutations: each upload's frontmatter assignment now
  calls processFrontMatter directly — atomic per upload, written by
  Obsidian's emitter.
- Body mutations: accumulated into a `bodyMutations: Array<{from, to}>`
  list during the markdown-image loop. After both loops complete, a
  single vault.read(file) + N string replacements + one
  vault.modify(file, updated) flushes them. The body re-read happens
  *after* all processFrontMatter calls so it picks up the latest
  version (including the frontmatter changes those calls just made);
  no stale-content / lost-update race.

Also pulled `const file = this.currentFile;` to a local once at the
top of handleConvert so TypeScript's narrowing survives across
awaits — without the alias, the `if (!this.currentFile) return;`
guard is invalidated at every await boundary because modal lifecycle
could in principle null out currentFile.

Verification
------------
- pnpm build green: ESLint (the bot-mirror flat config from the
  prior commit) + tsc strict + esbuild production.
- git grep -nE ": any\\b|as any\\b|<any>|any\\[\\]" -- "*.ts" empty.
- git grep -n "innerHTML" -- "*.ts" empty.
- main.js bundle 54 KB → 52 KB (the deleted parser was being bundled
  in; net -2 KB shipped to users).
- All four version markers still in sync at 0.1.0
  (manifest.json, package.json, versions.json key + value).

Risk worth knowing about
------------------------
processFrontMatter's callback signature is `(frontmatter: any) => void`
per Obsidian's type definitions. We mutate `fm[key] = value` directly
without a cast — that's the path of least friction, and the bot's
no-explicit-any rule does not fire because we are not *writing* `any`
in our source (we are receiving it from a third-party API). If a
future Obsidian update tightens the type to `Record<string, unknown>`,
our callsites will keep compiling. If it tightens to a stricter shape,
narrowing at the callsite is a small change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 02:03:06 -05:00
mpstaton
e117ceaf54 update(logs): update console log hygiene across many files
Changes to be committed:
	deleted:    .eslintignore
	deleted:    .eslintrc
	modified:   README.md
	new file:   context-v/changelogs/2026-05-03_01.md
	new file:   context-v/plans/2026-05-03_Assuring-Obsidian-Community-Plugin-Requirements.md
	new file:   eslint.config.mjs
	modified:   main.ts
	modified:   manifest.json
	modified:   package.json
	modified:   src/modals/BatchDirectoryConvertLocalToRemote.ts
	modified:   src/modals/ConvertLocalImagesForCurrentFile.ts
	modified:   src/modals/CurrentFileModal.ts
	modified:   src/modals/MagnificModal.ts
	modified:   src/services/imageCacheService.ts
	modified:   src/services/imagekitService.ts
	modified:   src/services/magnificService.ts
	modified:   src/services/recraftImageService.ts
	modified:   src/settings/settings.ts
	modified:   src/types/index.ts
	modified:   src/types/obsidian.d.ts
	new file:   src/utils/coerce.ts
	modified:   src/utils/logger.ts
	modified:   src/utils/yamlFrontmatter.ts
	modified:   versions.json
2026-05-03 01:35:13 -05:00
mpstaton
0cb935d55b improve(modals): improve modal for Magnific image selection, improve
image response rate, embedding image in document
2026-05-03 00:52:34 -05:00
mpstaton
09868ddd1f feat(uploads): uploads local files found in markdown text and replaces
with unique url from image service, for now ImageKit

 On branch development
 Your branch is up to date with 'origin/development'.
 Changes to be committed:
	modified:   README.md
	new file:   add
	new file:   git
	modified:   src/modals/ConvertLocalImagesForCurrentFile.ts
	modified:   styles.css
2025-07-30 02:27:28 +03:00
mpstaton
45396f8924 milestone(modal): command launching local to remote images now works
with user set image parameters

 On branch development
 Your branch is up to date with 'origin/development'.
 Changes to be committed:
	modified:   main.ts
	new file:   src/modals/ConvertLocalImagesForCurrentFile.ts
	modified:   src/services/imagekitService.ts
	modified:   src/settings/settings.ts
2025-07-21 01:39:47 +03:00