Commit Graph

580 Commits

Author SHA1 Message Date
streamdown-github-app[bot] 90f6bb67ac Version Packages (#431)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@streamdown/code@1.1.0 remend@1.2.2 streamdown@2.4.0
2026-03-05 11:01:39 -08:00
Hayden Bleasel 0f2b56f31b Improve changesets 2026-03-04 10:46:58 -08:00
Hayden Bleasel 0dff8de003 Build fixes 2026-03-04 10:39:59 -08:00
Hayden Bleasel 5a35a09cd8 Build fixes 2026-03-04 10:37:03 -08:00
Rich Haines 9d78590b2d Make sitemap.md static via generateStaticParams (#432) 2026-03-04 10:34:00 -08:00
Hayden Bleasel 2cf559d500 Resolves #191 2026-03-03 18:10:56 -08:00
Hayden Bleasel 781178b99c Resolves #272 2026-03-03 17:05:57 -08:00
Hayden Bleasel 71184cce30 Resolves #329 2026-03-03 16:58:12 -08:00
Hayden Bleasel 401b90183f Create ten-loops-unite.md 2026-03-03 16:52:15 -08:00
Hayden Bleasel ad21f7d69e Resolves #317 2026-03-03 16:51:44 -08:00
Hayden Bleasel bd167da49e Run linter/formatter 2026-03-03 16:24:55 -08:00
Hayden Bleasel 32a2a9817f Bump root deps 2026-03-03 16:23:46 -08:00
Udit Kumar fb76275ef4 Feat/table fullscreen (#390)
* feat(streamdown): add fullscreen support for tables

* test(streamdown): add fullscreen behavior tests

* docs: document fullscreen styling and usage

* add: changeset

* fix: prevent unintended fullscreen close

* fix: resolve shiki theme style mapping

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:16:10 -08:00
Jarvis f2a7e51ecb fix(code-block): preserve empty lines in code blocks (#420)
* fix(code-block): preserve empty lines in code blocks

## Summary

Fix empty lines collapsing in code blocks by inserting a newline character for empty rows.

## Problem

When rendering code blocks with empty lines, the `<span>` elements for empty rows had no content, causing them to collapse to zero height. This affected both visual display and copy behavior.

## Solution

Insert `"\n"` for empty lines instead of rendering an empty span. This ensures:
- **Visual**: Empty lines maintain proper height
- **Copy**: Copied text includes the empty lines correctly

## Changes

- `packages/streamdown/lib/code-block/body.tsx`: Add condition to detect empty rows (`row.length === 0` or single token with empty content) and render `"\n"` instead

* Fix lint issues

* Add tests

* Create fix-empty-lines.md

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
2026-03-03 15:58:29 -08:00
Hayden Bleasel f5ce9fb6c6 Update custom-shiki-themes.md 2026-03-03 15:48:04 -08:00
Hayden Bleasel ff390bb114 Update docs 2026-03-03 15:43:20 -08:00
Hayden Bleasel d5af95b18a Run linter/formatter 2026-03-03 15:38:08 -08:00
Dmitrii c4c86fabd1 feat: automatic RTL direction support (#411)
* feat: automatic RTL direction support

Add per-block RTL/LTR text direction detection using the 'first strong
character' Unicode algorithm.

- New `dir` prop on StreamdownProps: 'auto' | 'ltr' | 'rtl'
- When dir='auto', each block's direction is detected automatically
- Exported detectTextDirection() utility for custom usage
- Zero extra DOM elements when dir is not set

Closes #311

* fix: lint errors — sort imports, hoist regex, use block statements

* Add changeset for RTL direction support

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:30:49 -08:00
Dmitrii b392fbefe0 feat: add literalTagContent prop for plain-text custom tag children (#423)
* feat: add literalTagContent prop for plain-text custom tag children

* fix: run preprocessLiteralTagContent before preprocessCustomTags to prevent HTML comment corruption

When a tag appears in both allowedTags and literalTagContent and its
content contains blank lines, the '<!---->' HTML comment inserted by
preprocessCustomTags was subsequently corrupted to '<\!\-\-\-\->' by
preprocessLiteralTagContent (which escapes '!' and '-' as markdown
metacharacters).

Fix: swap the execution order so preprocessLiteralTagContent runs first,
then preprocessCustomTags inserts its markers. The HTML comments are
never seen by the markdown escaper.

* Add changeset for literalTagContent

* fix: address review issues for literalTagContent

- changeset patch→minor (new feature)
- add literalTagContent to Streamdown memo comparison
- narrow escape regex to inline-only metacharacters
- use ElementContent type in collectText for proper HAST coverage
- document double-escape limitation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:22:43 -08:00
Dmitrii e129f09aa5 feat: add translations prop for i18n support (#415)
* feat: add translations/i18n support via translations prop

Implements #190 - adds a `translations` prop to the Streamdown component
that allows customizing all UI strings for internationalization.

Changes:
- Add `packages/streamdown/lib/translations-context.tsx` with
  StreamdownTranslations type, defaultTranslations, TranslationsContext,
  and useTranslations hook
- Add `translations?: Partial<StreamdownTranslations>` prop to Streamdown
- Wrap render output with TranslationsContext.Provider (both static and
  streaming modes)
- Export StreamdownTranslations type and defaultTranslations from index
- Replace all hardcoded UI strings in sub-components with translations:
  - code-block/copy-button: copyCode
  - code-block/download-button: downloadFile
  - mermaid/download-button: downloadDiagram, downloadDiagramAs{Svg,Png,Mmd},
    mermaidFormat{Svg,Png,Mmd}
  - mermaid/fullscreen-button: viewFullscreen, exitFullscreen
  - table/copy-dropdown: copyTable, copyTableAs{Markdown,Csv,Tsv},
    tableFormat{Markdown,Csv,Tsv}
  - table/download-dropdown: downloadTable, downloadTableAs{Csv,Markdown},
    tableFormat{Csv,Markdown}
  - image: imageNotAvailable, downloadImage
  - link-modal: openExternalLink, externalLinkWarning, close, copyLink,
    copied, openLink
- Add __tests__/translations.test.tsx with 12 tests covering defaults,
  custom translations, partial overrides, and context access
- Update download-dropdown.test.tsx to match new translation-based title

All 804 tests pass.

* Add changeset for translations prop

* Fix misplaced comments in translations type and broken test type assertion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use value equality for translations memo to support inline objects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:17:11 -08:00
Dmitrii 57cd3b5a07 feat: allow customizing icons via icons prop (#414)
* feat: support custom starting line number in code blocks

Add support for specifying a custom starting line number via the code
fence meta string, e.g.:

```js startLine=10
const x = 1;
```

Implementation:
- Add `remarkCodeMeta` remark plugin that forwards the fenced-code meta
  string to hast as the `metastring` property so it is available to the
  custom React code component.
- Update the rehype-sanitize default schema to allow the `metastring`
  attribute on `code` elements.
- Parse `startLine=N` from the meta string in `CodeComponent` and pass
  the value down as a `startLine` prop.
- In `CodeBlockBody`, apply `counter-reset: line N-1` as an inline style
  when `startLine > 1`, which overrides the Tailwind counter-reset class
  and makes CSS counters begin from the specified number.
- Pass `startLine` through the `CodeBlock` and
  `HighlightedCodeBlockBody` call chains.
- Add 12 tests covering the remark plugin, CodeBlockBody prop, and
  CodeBlock integration.

Closes: resolves #287

* fix: lint errors — use literal keys, hoist regex, sort imports

* feat: allow customizing icons via icons prop

Add an IconContext-based system that lets users override any of the
built-in icons (CheckIcon, CopyIcon, DownloadIcon, etc.) by passing
a Partial<IconMap> via the new `icons` prop on <Streamdown>.

- Created lib/icon-context.tsx with IconMap type, IconProvider, and useIcons hook
- Updated all sub-components to consume icons via useIcons() instead of direct imports
- Added icons prop to StreamdownProps with IconProvider wrapper
- Exported IconMap type from package entry

Closes #412

* style: fix biome lint and formatting errors

* Merge branch 'main' into feat/custom-icons

* fix: review fixes for icons and startLine features

- Add missing changeset for icons feature (minor)
- Fix IconComponent type to include size prop matching actual usage
- Fix IconProvider memoization: use shallow comparison instead of
  referential equality so inline icon objects don't cause re-renders
- Move START_LINE_PATTERN constant below all imports
- Add bounds checking for startLine (must be >= 1)
- Convert dynamic imports to top-level imports in startLine tests
- Add icon-context tests (5 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:04:26 -08:00
Dmitrii 01d27e944c feat: support custom shiki themes in code blocks (#416)
* feat: support custom shiki themes in code blocks

Add support for custom shiki theme objects (ThemeRegistrationAny-compatible)
in addition to built-in BundledTheme string names.

- Add CustomTheme interface and ThemeInput type alias
- Widen theme types across CodePluginOptions, CodeHighlighterPlugin,
  HighlightOptions, StreamdownProps, and StreamdownContextType
- Extract theme names from objects for cache keys and codeToTokens
- Pass theme objects to createHighlighter for registration
- Add tests for custom theme objects and mixed themes
- Full backward compatibility with existing BundledTheme usage

Closes #409

* fix: pass per-call themes to getHighlighter instead of defaultThemes

* fix: resolve duplicate interface properties and type errors in CodeHighlighterPlugin

* Add changeset for custom Shiki themes

* fix: use shiki's ThemeRegistrationAny, deduplicate types, tighten API

- Replace custom CustomTheme interface with shiki's ThemeRegistrationAny
- Remove duplicated type definitions across packages
- Simplify HighlightOptions.themes to [ThemeInput, ThemeInput]
- Remove unsafe cast in createHighlighter call
- Bump changeset to minor (new feature, not patch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:38:11 -08:00
Dmitrii 9ba8511f5a fix: prevent ordered list animation retrigger during streaming (#417)
* fix: use HAST char count for prevContentLength instead of raw markdown length

The animate plugin's charCounter counts HAST text node characters (rendered
text, without markdown syntax). Previously, prevContentLengthRef stored
content.length (raw markdown), causing a unit mismatch: markdown syntax
characters (**, #, `, etc.) inflate the raw length vs the HAST count.

This mismatch caused new streaming content to incorrectly skip animation
when prevContentLength (raw) exceeded the actual HAST character count.

Fix: expose getLastRenderCharCount() on AnimatePlugin that returns the
total HAST character count from the last render. Block now uses this value
instead of content.length so both sides measure the same units.

Also fix lint issues in list-animation-retrigger.test.tsx:
- Replace async () => {} with () => Promise.resolve() for empty act() calls
- Remove async from act callbacks that don't use await
- Remove unused renderCount variable

* fix: correct timing of prevContentLength for animate plugin

The previous implementation called resetPrevContentLength() in Block's
function body, but Markdown (which calls processor.runSync synchronously)
renders as a child component — AFTER Block's function body returns. This
meant the animate plugin always saw prevContentLength=0 on every render.

Fix:
- Remove manual resetPrevContentLength() from Block's render body
- Add self-reset inside rehypeAnimate after each run, so sibling blocks
  start clean (depth-first rendering ensures Markdown1 runs before Block2)
- Read getLastRenderCharCount() at the TOP of Block's render body: since
  React renders depth-first, this value is from the PREVIOUS Markdown run
  (exactly the prevContentLength needed for the current render)
- Remove stale useLayoutEffect approach (not needed with depth-first timing)

* fix: reset lastRenderCharCount after reading to prevent sibling block leakage

When a single AnimatePlugin instance is shared across sibling Block
components, getLastRenderCharCount() was returning the accumulated
char count from the previously-rendered Block instead of 0. This
caused subsequent Block components to incorrectly skip animation for
their initial content.

Fix: reset config.lastRenderCharCount to 0 after returning the value
in getLastRenderCharCount(). Since React renders depth-first, each
Block reads this value, uses it as prevContentLength, then its
Markdown child runs rehype (setting a new lastRenderCharCount).
After the read-reset, the next sibling Block starts clean.

Addresses VADE review comment on PR #417.

* fix: resolve animate plugin issues with memo'd components

- Give each animatePlugin instance a unique rehype function name to
  prevent ProcessorCache collisions across instances
- Separate immutable AnimateConfig from mutable AnimateRenderState
- Stabilize animatePlugin useMemo deps using value-equality key
  (JSON.stringify) instead of reference equality
- Bypass startTransition when animatePlugin is active so displayBlocks
  updates synchronously
- Rewrite failing test: the 0ms approach only applies when memo'd
  components re-render (node position changes), so test with a
  text-growing scenario instead of new-item-added scenario
- Fix lint issues (sorted interfaces, block statements)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:29:55 -08:00
Dmitrii 00872f0e10 feat: add Tailwind CSS prefix support (#408)
* feat: add Tailwind CSS prefix support

* fix: prevent double-prefixing of already-prefixed CSS classes

When cn() output (e.g. 'tw:size-full') is passed as className to a child
component that also calls cn() with the same prefix, prefixClasses() was
naively prepending the prefix again, producing 'tw:tw:size-full'.

Fix: skip classes that already start with the prefix+colon sentinel.

Add tests for both the all-already-prefixed and mixed cases.

* fix: lint errors — sort imports, format, hoist regex constants

* Add changeset for Tailwind prefix support

* fix: review fixes for tailwind prefix PR

- Bump changeset to minor (new feature, not patch)
- Rename shadowed cn import to baseCn in code-block/body.tsx
- Document that user className values are also prefixed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:39:50 -08:00
Hayden Bleasel 80bcc5f29d Improve table docs 2026-03-03 13:19:49 -08:00
Hayden Bleasel dbcfa12857 Update docs 2026-03-03 13:18:50 -08:00
Lucas Estevão 27c7b037d0 feat: export table action components and utilities (#421)
Export TableCopyDropdown, TableDownloadButton, TableDownloadDropdown,
and table utility functions (extractTableDataFromElement, tableDataToCSV,
tableDataToTSV, tableDataToMarkdown, escapeMarkdownTableCell, TableData)
from the main entry point, enabling custom table overrides to preserve
copy/download interactivity.

Also fixes a type error in table components where `size` prop was passed
to SVG icons that only accept standard SVG attributes (width/height).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 13:17:26 -08:00
Hayden Bleasel cb4746b256 Improve a725579 2026-03-03 13:16:35 -08:00
Hayden Bleasel 5dd31999a7 Explicit null ref, callback refs, remove callbacks from memo comparator 2026-03-03 13:15:31 -08:00
Dmitrii f3986111f2 feat: add onAnimationStart and onAnimationEnd callback props (#425)
* feat: add onAnimationStart and onAnimationEnd callback props

* chore: add changeset for animation callbacks

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
2026-03-03 13:10:50 -08:00
Hayden Bleasel a69d77ef46 Revert "Create require-changeset.yml"
This reverts commit 67c4f457be.
2026-03-03 13:10:10 -08:00
Dmitrii a72557988a fix(remend): don't complete emphasis/strikethrough markers inside complete inline code spans (#426)
* fix(remend): don't complete bold/italic/strikethrough inside complete inline code spans

Bold, italic, and strikethrough handlers were using isWithinCodeBlock()
which only detects triple-backtick fenced code blocks. Markers inside
single-backtick inline code spans (e.g. `**bold`) were incorrectly
completed, leaking outside the code span.

Fix:
- Add isWithinCompleteInlineCode() to code-block-utils.ts that returns
  true only when a position is inside a *complete* (both delimiters
  present) inline code span. This preserves the existing streaming
  behavior where emphasis inside an *incomplete* inline code span
  (still being streamed) continues to be closed correctly.
- Apply the check in handleIncompleteBold, handleIncompleteBoldItalic,
  handleIncompleteDoubleUnderscoreItalic, handleIncompleteSingleAsterisk-
  Italic, and handleIncompleteStrikethrough.

Fixes #424

* fix(remend): add isWithinCompleteInlineCode guard to handleIncompleteSingleUnderscoreItalic

* chore: add changeset for inline code emphasis fix

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
2026-03-03 13:09:08 -08:00
Hayden Bleasel 7b404379d9 Fix plugin docs 2026-03-03 13:09:00 -08:00
Aradhya C P 5edff75c13 docs: add code, cjk, math, and mermaid to @source (#430)
* docs: update Tailwind CSS setup instructions to include additional Streamdown modules in README.md

* docs: update FAQ to include additional Streamdown module sources in globals.css

* docs: update Tailwind CSS configuration to include additional Streamdown module sources in getting-started.mdx

* docs: update migration guide to include additional Streamdown module sources in globals.css

* docs: update Tailwind CSS configuration in SKILL.md to include additional Streamdown module sources

* chore: add changeset for @source documentation updates

* docs: update README to include optional Streamdown plugin installation instructions

* docs: update FAQ to include instructions for optional Streamdown plugins in globals.css

* docs: update Tailwind CSS configuration in getting-started.mdx to clarify optional plugin integration

* docs: add Tailwind CSS configuration instructions for @streamdown/code plugin

* docs: update migration guide to include plugin source path instructions for Tailwind CSS

* docs: add Tailwind CSS integration instructions for @streamdown/cjk plugin

* docs: add Tailwind CSS integration instructions for @streamdown/math plugin

* docs: add Tailwind CSS integration instructions for @streamdown/mermaid plugin

* docs: update the changeset

* docs: update Tailwind CSS configuration instructions for clarity

* docs: update links for plugin documentation
2026-03-03 13:07:22 -08:00
Hayden Bleasel 67c4f457be Create require-changeset.yml 2026-03-03 12:11:36 -08:00
Rich Haines 559274fe29 Add sitemap.md route for LLM-friendly documentation indexing (#429) 2026-03-03 10:16:05 -08:00
Hayden Bleasel e4c37b73c0 Create migration guide 2026-02-25 11:58:57 -08:00
Hayden Bleasel 2d929d021e Update website logos 2026-02-25 09:47:47 -08:00
Hayden Bleasel 89b44cd106 Fix double overflow issue on homepage 2026-02-19 10:35:39 -08:00
streamdown-github-app[bot] b46781c928 Version Packages (#397)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@streamdown/code@1.0.3 remend@1.2.1 streamdown@2.3.0
2026-02-19 08:39:50 -08:00
Carles Andrés bb4e0c2ea1 docs: add build step to contributing setup instructions (#406) 2026-02-19 08:32:47 -08:00
Hayden Bleasel 9562177cb8 Update index.tsx 2026-02-18 21:10:34 -08:00
Hayden Bleasel d892b140d8 Misc fixes 2026-02-18 21:07:39 -08:00
Hayden Bleasel 1102f1855e Fix all Biome lint and formatting errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 20:57:01 -08:00
Hayden Bleasel 858ff5de0c Update Biome and Ultracite (fix security issues) 2026-02-18 20:50:27 -08:00
Hayden Bleasel 847132d73c Remove ts-router-chat-test 2026-02-18 20:47:58 -08:00
Hayden Bleasel 6618620f97 Update index.tsx 2026-02-18 20:45:25 -08:00
Hayden Bleasel 184a4976ec Improve Mermaid error design 2026-02-18 20:45:04 -08:00
Hayden Bleasel b12332252d Hide carets when rendering code blocks and tables 2026-02-18 20:43:33 -08:00
Hayden Bleasel 0987479ebf Resolves #402 2026-02-18 20:21:55 -08:00