feat(macro): run a user script from a note's ```js code block (#1065)#1341
feat(macro): run a user script from a note's ```js code block (#1065)#1341chhoumann wants to merge 2 commits into
Conversation
User scripts can now live in a Markdown note instead of only a standalone .js file — handy on mobile, where Obsidian can't open .js files. QuickAdd runs the first ```js (or ```javascript) fence in the note and ignores the surrounding prose and any other code blocks. - extractScriptFromMarkdown: a pure CommonMark-aware line scanner (first js fence wins; trailing-content lines don't close the block; CRLF preserved; leading blank-line padding keeps error line numbers near the note's). - getUserScript branches on .md and feeds the extracted body to the same CommonJS Function shim, so exports/settings/entry/::member and all five callers (macro command, conditional script, member access, single-macro entry, preflight) work unchanged. The .js path stays byte-identical. A note with no runnable fence shows a Notice and returns undefined. - Script pickers (macro builder + conditional modal) offer a unified list of .js files and notes-with-a-code-block (metadata-cache pre-filter) and validate the fence on select. Notes resolve by path so a bare basename never picks a note over a same-named .js. - packagePreview: a bundled .md whose content has a runnable js fence is now treated as executable, closing a disclosure-gate bypass for note scripts. - packageImportService: keep a note script's command name (= its path) in sync when the asset is written to a new destination on import. Docs updated (Next). Verified in the dev vault: a note script runs with a persisted side effect (decoy ```python block ignored) and a no-fence note is graceful (macro continues).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds support for user scripts defined in markdown note code fences, updates macro editing and condition UI to discover and validate those scripts, extends package preview/import logic for note-backed scripts, and refreshes tests and documentation to describe the new script formats and selection rules. ChangesNote-backed user scripts
Sequence Diagram(s)sequenceDiagram
participant User
participant MacroEditor as Macro UI
participant Candidates as loadScriptCandidates
participant Validator as noteScriptError
participant Vault
participant Extractor as extractScriptFromMarkdown
User->>MacroEditor: Type or browse script
MacroEditor->>Candidates: discover candidates
Candidates-->>MacroEditor: js files + markdown notes
MacroEditor->>MacroEditor: resolve selector
MacroEditor->>Validator: validate if markdown
Validator->>Vault: read note
Vault-->>Validator: content
Validator->>Extractor: extract first js fence
Extractor-->>Validator: code or error
Validator-->>MacroEditor: ok or show notice
MacroEditor-->>User: save UserScript path/name
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying quickadd with
|
| Latest commit: |
6202dc2
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://85b08a4f.quickadd.pages.dev |
| Branch Preview URL: | https://chhoumann-1065-userscript-in.quickadd.pages.dev |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 404b2cd9a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/docs/Choices/MacroChoice.md`:
- Around line 83-86: Update the QuickAdd macro docs in MacroChoice.md so the
fence-language guidance matches runtime support: the section around the “first
code block runs” wording should mention both `js` and `javascript` fences. Use
the existing “Create a new script file…” paragraph and revise it to say the
first matching JavaScript fence runs, so users are not misled about supported
note formats.
In `@src/gui/MacroGUIs/CommandSequenceEditor.ts`:
- Around line 95-97: Refresh script candidates at interaction time instead of
caching them only in CommandSequenceEditor’s initialization: update the
CommandSequenceEditor flow so the resolve path and picker path both reload
scriptCandidates immediately before use, and apply the same stale-data pattern
fix in ConditionalCommandSettingsModal as well. Use the existing
loadScriptCandidates, the script resolution logic around command selection, and
the script picker population logic as the touchpoints so interactive macro
commands always reflect the current vault/index state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1f002c80-a752-42d2-87c7-c4d333932ed3
📒 Files selected for processing (16)
docs/docs/Choices/MacroChoice.mddocs/docs/UserScripts.mdsrc/gui/MacroGUIs/CommandSequenceEditor.tssrc/gui/MacroGUIs/ConditionalCommandSettingsModal.tssrc/gui/MacroGUIs/noScriptsFoundNotice.tssrc/gui/MacroGUIs/scriptCandidates.test.tssrc/gui/MacroGUIs/scriptCandidates.tssrc/gui/apiModernization.test.tssrc/services/packageImportService.test.tssrc/services/packageImportService.tssrc/services/packagePreview.test.tssrc/services/packagePreview.tssrc/utilityObsidian.test.tssrc/utilityObsidian.tssrc/utils/extractScriptFromMarkdown.test.tssrc/utils/extractScriptFromMarkdown.ts
…resh script candidates on interaction, docs note ```javascript fences
Closes #1065.
What & why
A Macro user script currently must be a standalone
.jsfile, which Obsidian can't open or edit natively — especially painful on mobile. This lets a user script live in a regular note instead: QuickAdd runs the first```js(or```javascript) fenced block and ignores the surrounding prose and any other code blocks.Verified in the dev vault (QuickAdd 2.13.1) that the gap was real before this change: the picker's
/\.js$/filter excluded notes, and pointing a command at a.mdfed raw markdown tonew Function→SyntaxError.How
extractScriptFromMarkdown(new, pure, App-free line scanner): first js fence wins; CommonMark fence rules (a line with trailing content likeconsole.log("```")doesn't close the block; wrap with 4+ backticks to embed a bare```); CRLF preserved byte-for-byte; empty-vs-missing fence distinguished; leading blank-line padding keeps runtime error line numbers near the note's own.getUserScriptbranches on.mdand feeds the extracted body to the same CommonJSFunctionshim. Somodule.exports/exports.default/settings/entry/name::memberand all fivegetUserScriptcallers (macro command, conditional script-condition,::member, single-macro entry, and the one-page preflight scanner) work unchanged. The.jspath is byte-identical → zero regression. A note with no runnable fence shows aNoticeand the macro continues (no silent failure, no crash)..jsfiles and notes-that-have-a-code-block (cheap metadata-cache pre-filter), with a validate-on-select backstop that reads the note and reports the precise reason if there's no runnable fence. Notes resolve by path so a bare basename never picks a note over a same-named.js; a direct-vault fallback covers a cold cache.packagePreview): a bundled.mdwhose content has a runnable js fence is now treated as executable, closing a disclosure-gate bypass (an orphan note labeledtemplatewould otherwise slip the gate, land on disk, and run via any macro pointing at its path). Plain.mdtemplates are not flagged.packageImportService): a note script's command name (= its vault path, optionallypath::member) is kept in sync when the asset is written to a new destination on import.Design & review
Designed and reviewed before implementation (gap confirmed live; design challenged by an ultracode multi-lens pass + adversarial reviewers). Decisions: unified single picker; no-fence ⇒ Notice + continue; precise decode-and-extract for the transparency gate. The implementation diff was then put through a second adversarial review — its findings (typed-resolution basename collision, import name staleness, and not documenting the feature in the stable 2.13.1 docs snapshot) are all addressed here.
Tests
extractScriptFromMarkdown.test.ts— 16 cases (selection rule, fence edges, CRLF, frontmatter, callout/tilde rejected, empty vs missing, padding).getUserScript—.mdload +::member+ no-fence Notice + non-module.exportsbody.scriptCandidates.test.ts— resolver picks.jsover a same-named note; path/cold-cache resolution; validate-on-select reasons.packagePreview.test.ts— orphan.mdwith a js fence is critical/review-required; plain.mdtemplate is not.packageImportService.test.ts— note-scriptnametracks a remapped asset path.tsc+ ESLint clean; production build OK; docs build (Next) OK.Verification (dev vault, real Obsidian)
```jsblock; the side effect persisted and the decoy```pythonblock was correctly ignored.Notes
main.js/styles.cssare gitignored here and built in CI/release, so they're not in this diff.docs/docs/(Next) only; they'll snapshot into the version docs when the next release is cut (documenting it in the stable 2.13.1 snapshot would claim support that release doesn't have).Summary by CodeRabbit
.jsfiles or inside a note’s firstjs/javascriptcode block.