Commit Graph

607 Commits

Author SHA1 Message Date
streamdown-github-app[bot] 15ba1aee86 Version Packages (#457)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@streamdown/cjk@1.0.3 @streamdown/code@1.1.1 remend@1.3.0 streamdown@2.5.0
2026-03-17 10:32:35 -07:00
Hayden Bleasel 38aae89ee3 Update broken-markdown-variants.test.ts 2026-03-17 09:06:16 -07:00
Hayden Bleasel d566c819d4 Create broken-markdown-variants.test.ts 2026-03-16 14:31:18 -07:00
Sora Morimoto b752b44a80 fix(streamdown): move mermaid from devDependencies to dependencies (#466)
The published type declarations (dist/index.d.ts) import MermaidConfig
from the mermaid package, making it part of the public API. Having it as
a devDependency meant consumers could end up resolving different mermaid
versions for streamdown and @streamdown/mermaid, causing type errors due
to incompatible MermaidConfig types.
2026-03-16 10:39:55 -07:00
仓鼠 d6263515f4 fix(playground-editor): add full height class to ConversationContent (#465)
* fix(playground-editor): add full height class to ConversationContent

* docs: add changeset

* fix(playground-editor): update Textarea class for consistent field sizing

* Delete .changeset/fix-playground-safari-textarea.md

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
2026-03-16 10:39:24 -07:00
Hayden Bleasel 90a7b58ca8 Run fix 2026-03-12 17:35:48 -07:00
ziyadev 9e6f991f0d fix(streamdown): increase dropdown z-index (#463)
* fix(streamdown): increase dropdown z-index

* chore: rm duplicate class

* chore: increase dropdown z-index

* add patch changeset for dropdown z-index fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-12 17:34:25 -07:00
Aradhya C P 2a83c97c1f Fix: Fix streaming of custom markdown input in PlaygroundEditor (#462)
* Implement markdown output state for streaming simulation

* Update streaming simulation to use markdownOutput conditionally
2026-03-12 17:32:34 -07:00
Dmitrii d6666b6de3 feat: add lineNumbers prop to disable line numbers in code blocks (#460)
* feat: add lineNumbers prop to disable line numbers in code blocks

* docs: add lineNumbers prop and noLineNumbers meta string

Co-Authored-By: Claude Opus 4.6 (1M context) <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 (1M context) <noreply@anthropic.com>
2026-03-12 17:31:46 -07:00
Dmitrii 6a79e04690 fix(streamdown): preserve double newlines in literalTagContent tags (#459)
* fix(streamdown): preserve double newlines in literalTagContent tags

When a tag is in literalTagContent mode, double newlines (\n\n) inside
the tag content were incorrectly treated as paragraph separators by the
markdown parser, causing the content after the first blank line to be
excluded from the tag's children.

Fix: in preprocessLiteralTagContent, after escaping markdown metacharacters,
replace \n\n with &#10;&#10; HTML entities. This prevents the markdown
parser from interpreting blank lines as paragraph breaks while still
preserving the newline characters (rehype-raw decodes &#10; back to \n).

Fixes vercel/streamdown#456

* Run pnpm fix to resolve formatting issues

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

* Fix pre-existing formatting issue in code-block/index.tsx

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

* Fix lint and formatting issues

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-11 15:41:03 -07:00
Hayden Bleasel b9796c8e91 Improve test coverage 2026-03-11 15:19:40 -07:00
Hayden Bleasel 6b49db1a20 Fix polynomial regular expression issue 2026-03-10 16:58:24 -07:00
Hayden Bleasel 6d4100a861 Update utils.test.ts 2026-03-10 16:53:48 -07:00
Hayden Bleasel 9f8ed4cdb3 Lint fixes 2026-03-10 16:53:02 -07:00
Hayden Bleasel ac8d83978a Resolves #437 2026-03-10 16:50:39 -07:00
Hayden Bleasel 51f5ba6cf7 Update remend priorities 2026-03-10 16:47:15 -07:00
Hayden Bleasel 716a5f05a9 Resolves #445 2026-03-10 16:46:10 -07:00
Hayden Bleasel 7b62e9a88d Resolves #454 2026-03-10 16:35:12 -07:00
Hayden Bleasel b105c64378 Resolves #456 2026-03-10 16:30:53 -07:00
Tatsunori Uchino 6f1ea07b22 chore(deps): update remark-cjk-friendly and remark-cjk-friendly-gfm-strikethrough to v2.x (#455)
* Update remark-cjk-friendly and remark-cjk-friendly-gfm-strikethrough to v2.x

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Tatsunori Uchino <tats.u@live.jp>

* Fix wrong changeset level (minor -> patch)

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2026-03-10 16:06:46 -07:00
Dmitrii 9c18748f00 docs: document required CSS custom properties (shadcn/ui design tokens) (#451)
* docs: document required CSS custom properties (shadcn/ui design tokens)

* docs: document required CSS custom properties in README

---------

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
2026-03-10 16:04:54 -07:00
Dmitrii 651873df54 fix(streamdown-code): fall back to plain text for unknown/truncated language identifiers (#434)
* fix(streamdown-code): fall back to plain text for unknown/truncated language identifiers

When a code block's language identifier is unrecognised by Shiki (e.g.
'rus' from a network-truncated 'rust'), createHighlighter throws a
ShikiError. Previously the .catch() block only logged the error and
left subscribers hanging, causing a permanent React render-cycle stall.

Fix: resolve the language to the 'text' fallback *before* calling
getHighlighter() when the identifier is not in the bundledLanguages
set. This prevents the exception entirely and ensures the code block
renders with plain-text highlighting instead of breaking.

Fixes #433

* Add changeset for unknown language fallback fix

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

* Add test for unknown/truncated language fallback

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

* Fix TypeScript error: widen safeLanguage type to include SpecialLanguage

`'text'` is a SpecialLanguage in Shiki, not a BundledLanguage, so the
type annotations for getHighlighter and getHighlighterCacheKey need to
accept both.

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

* Fix formatting: remove trailing comma, use double quotes

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-10 16:04:19 -07:00
Dmitrii d4ec6c0e96 feat: add meta prop to custom renderer props (#447)
Custom renderers now receive the raw metastring from code fences via a
new optional `meta` prop. Everything after the language identifier is
passed through (e.g. ```rust {1} title="foo"``` → meta = '{1} title="foo"').

- Added `meta?: string` to `CustomRendererProps` in `plugin-types.ts`
- Pass `meta={metastring}` to custom renderers in `components.tsx`
- Added tests for meta prop presence and absence
- Added minor changeset

Closes #443

Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
2026-03-10 16:04:02 -07:00
shloimy-wiesel 8b1c262982 fix(save): prepend UTF-8 BOM to CSV blobs for Excel compatibility (#441)
* fix(save): prepend UTF-8 BOM to CSV blobs for Excel compatibility

Closes #440

* fix(download): refactor TableDownloadButton to use save() for CSV downloads and ensure UTF-8 BOM compatibility
2026-03-10 16:02:51 -07:00
Wu Tianwei 75845c0820 fix: Optimize code block rendering in streaming mode to prevent unnecessary re-renders (#436) 2026-03-10 16:01:31 -07:00
Gregory Moskaliuk e50b0c4399 feat(remend): add opt-in inline KaTeX completion (#446)
* feat(remend): add opt-in inline KaTeX completion

* docs(remend): update README to include inline KaTeX completion and clarify option defaults

* fix(remend): complete partial closing $ in block math without duplicating

* Add changeset for inline KaTeX completion feature

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

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:00:17 -07:00
Dmitrii add537414b fix(code-block): enable horizontal scrolling for long lines in code blocks (#439)
* fix(code-block): enable horizontal scrolling for long lines in code blocks

Change overflow-hidden to overflow-x-auto on the code block body container
so that long single-line comments and other long code lines are scrollable
horizontally instead of being silently clipped.

Fixes #438

* Add changeset for code block horizontal scroll fix

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-10 15:59:05 -07:00
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