Skip to content

feat: file includes via ![[file.md]] syntax#80

Open
teezeit wants to merge 10 commits intoakonan:mainfrom
teezeit:feat/file-includes
Open

feat: file includes via ![[file.md]] syntax#80
teezeit wants to merge 10 commits intoakonan:mainfrom
teezeit:feat/file-includes

Conversation

@teezeit
Copy link
Copy Markdown
Contributor

@teezeit teezeit commented Apr 17, 2026

⚠️ Depends on #79 — please merge that first. This branch was built on top of the button-link navigation changes.

Summary

  • Adds resolveIncludes(markdown, basePath) to the parser — a pre-parse text substitution that splices referenced .md files inline before parsing
  • Syntax follows Obsidian transclusion (![[file.md]]) — familiar to non-technical users, consistent with wiremd's existing [[...]] nav syntax and markdown's own ! = embed convention
  • Wired into CLI (generateOutput) and VS Code extension (getWebviewContent)

What's included

  • src/parser/index.tsresolveIncludes(): regex replacement, relative path resolution, graceful missing-file warning
  • src/cli/index.ts — resolve includes before parse()
  • vscode-extension/src/preview-provider.ts — resolve includes using document.uri.fsPath
  • tests/includes.test.ts — 10 tests: splice, relative paths, multiple includes, missing file warning, non-.md wikilinks untouched, no-op passthrough, CLI integration
  • docs/guide/syntax.md — new "File Includes" section
  • docs/examples/index.md — multi-page shared-fragments pattern
  • examples/gallery/multi-page-includes/ — 3-page prototype (home/about/pricing) sharing a nav and footer via ![[shared/_nav.md]]

Test plan

  • npm test — 511/511 passing
  • CLI renders included content correctly
  • Missing file produces > ⚠️ Could not include: path.md blockquote
  • ![[image.png]] and ![alt](src) left untouched
  • VS Code extension updated to resolve includes before preview renders

🤖 Generated with Claude Code

teezeit and others added 9 commits April 17, 2026 14:14
- Add `{.grid-N card}` boolean modifier — grid items render with card
  chrome (border/shadow/bg) without changing the structural syntax
- Add `{.col-span-N}` on child headings to span multiple grid columns
- Fix: col-span items now reset to span 1 on mobile across all 7 CSS
  themes, preventing overflow when the grid collapses to 1 column
- Grid heading label is declaration-only and never rendered in output
- Add migration script (scripts/migrate-v0.2.sh) for existing files
- Update all examples, docs, and quick reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: grid card modifier, col-span, and responsive fix
[Button]{href:./page.md} now renders as a styled <a> anchor instead
of a non-navigating <button>. Checks both node.href and node.props.href
so the attribute form {href:url} works naturally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[[Text](url)] renders as a button-styled <a> anchor. CommonMark parses
this as text:"[" + link + text:"]" — the transformer now detects that
3-child pattern and hoists the link url to button.href.

Supports variants: [[Label](url)]* for primary, [[Label](url)]{.cls}
for attributes. Also fixes {href:url} attribute form which was stored
in props.href but renderer only checked node.href.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a.wmd-button gets text-decoration:none and color:inherit so button-links
look identical to regular buttons — no underline, no browser link color.
Applied once in getStyleCSS() so all 7 themes inherit it automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dev server now renders any .md file on demand when its URL is
requested, enabling button-links like [[Docs](./docs.md)] to navigate
between wireframe files in the browser.

- startServer() accepts renderFile callback and rootDir
- GET /page.md → calls renderFile(path) and serves result
- GET /page.html → serves cached HTML or renders from .md
- GET / → serves main watched file (unchanged)
- GET unknown → 404
- startServer() now returns the server instance (testability)
- CLI passes renderFile and rootDir to startServer
- Add href? to button node type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET / now redirects to /{inputFile} (e.g. /agency-site.md) so there
is no special root — every file is addressed by its own path. Links
back to the entry file work the same as links to any other file.

Fallback: when no inputFile is set (programmatic use), / still serves
the pre-rendered outputPath as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nav `[[ ]]` items now fully support links and active states:

- `[text](url)` inside `[[ ]]` creates a navigable nav item with real href.
  Previously the link URL was silently dropped (remark parses links as
  `link` nodes with no `.value`; the inline-containers plugin was using
  `c.value || ''` which discarded them).
  Fix: added `serializeChild` to reconstruct `[text](url)` from MDAST
  link nodes before splitting items on `|`.

- `*text*` inside `[[ ]]` is now treated as the active/current-page item
  (strips asterisks, adds `.active` class with theme-matched styling).
  Previously rendered as literal `*text*`.

- `[text](url)*` in nav renders as a primary-styled button link.

- Multiple `[[Btn](url)]` patterns on the same line now render correctly.
  Previously the second button's URL was dropped and brackets showed as
  literal text. Root cause: `transformParagraph`'s 3-child detection
  only handled a single button-link. For two buttons on one line remark
  produces 5 children (`[`, link, `]* [`, link, `]`).
  Fix: new `tryParseButtonLinkSequence` generalises detection to n ≥ 1
  (replaces the old 3-child check); multiple matches return a
  `button-group` container.

Also adds:
- Active nav-item CSS for all 6 themes (`.wmd-nav-item.wmd-active`)
- `docs/guide/syntax.md` — Button Links section
- `QUICK-REFERENCE.md` — nav link and active-state rows
- `examples/gallery/multi-page/` — three-page navigable prototype
  (home/about/contact sharing a nav with live button-link navigation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds resolveIncludes() — a pre-parse text substitution that splices
the contents of referenced .md files inline before the markdown is
parsed. Syntax follows Obsidian transclusion (![[...]]) so it's
immediately familiar to non-technical users.

- resolveIncludes(markdown, basePath) in src/parser/index.ts
  - regex: /!\[\[([^\]]+\.md)\]\]/g — only matches .md wikilinks
  - paths resolved relative to the source file's directory
  - missing file → visible blockquote warning, no exception thrown
  - standard image syntax ![alt](src) and ![[image.png]] untouched
- CLI (generateOutput): resolves includes before parse()
- VS Code extension (getWebviewContent): resolves includes using
  document.uri.fsPath before parse()
- 10 tests covering: splice, relative paths, multiple includes,
  missing file warning, non-.md wikilinks ignored, no-op passthrough
- Docs: new "File Includes" section in syntax guide + examples index
- Example: examples/gallery/multi-page-includes/ — 3-page prototype
  (home, about, pricing) sharing a nav and footer via ![[shared/]]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix INCLUDE_PATTERN to allow optional whitespace inside brackets:
  ![[  file.md  ]] now resolves correctly (lazy match prevents
  greedy capture from skipping the .md terminator)
- QUICK-REFERENCE.md: add File Include row to component table and
  troubleshooting entry
- SYNTAX-SPEC-v0.1.md: move file includes from deferred to v0.1 included

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant