feat: add configurable download filenames through unified controls - #559
Conversation
…and context - Introduced CodeDownloadConfig interface to define optional baseFileName for code downloads. - Updated StreamdownProps to include codeDownload property. - Enhanced StreamdownContextType to support codeDownload configuration. - Ensured default values are set for new properties in the context.
… codeDownload configuration - Modified filename logic in CodeBlockDownloadButton to utilize baseFileName from StreamdownContext. - Ensured fallback to default filename if baseFileName is not provided.
…lename scenarios - Added tests to verify the functionality of custom baseFileName in the download button. - Included cases for handling undefined codeDownload, unknown languages, and special characters in filenames. - Ensured that the button is enabled and the correct filename is used during the download process.
|
@aradhyacp is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
…nd component usage - Simplified the rendering of CodeBlockDownloadButton by consolidating props into a single line. - Updated test assertions for expected save calls to improve readability and maintainability.
|
Thanks for the PR @aradhyacp, |
|
Thanks for asking @farnabaz ! I did consider putting controls={{
table: {
copy: true, // Show table copy button
download: true, // Show table download button
fullscreen: true, // Show table fullscreen button
},
code: {
copy: true, // Show code copy button
download: true, // Show code download button
},
}}
That's the reasoning behind the current API choice, but I'm happy to discuss it if you think controls is the better fit here. |
|
@aradhyacp I felt the same way at first, but I think In other words, having a single place to control and configure these behaviors creates better DX, since users don’t need to look through multiple props to find the right setting. Also, imagine if we want to add similar customization for tables in the future, if we use separate props, we’ll eventually need to introduce something like a |
|
Yeah, I see what you mean, especially the concern about ending up with separate props like My concern is more about keeping the responsibilities of controls?: {
code?: {
copy?: boolean;
download?: boolean;
};
table?: {
copy?: boolean;
download?: boolean;
fullscreen?: boolean;
};
mermaid?: {
download?: boolean;
copy?: boolean;
fullscreen?: boolean;
panZoom?: boolean;
};
};Because of that, I’d prefer to keep Instead, perhaps we could introduce a global <Streamdown
controls={{
code: { download: true },
table: { download: true },
}}
downloadConfig={{
code: {
baseFileName: "component",
},
table: {
baseFileName: "data",
},
}}
/>This would still give us a single place for download-related configuration across the different components, while keeping the UI layer separate from the underlying download behavior. It would also avoid having separate So I agree with the underlying concern about having a single place for this configuration, I just think that place should be separate from |
|
@aradhyacp For me controls?: {
code?: boolean | {
copy?: boolean;
download?: boolean | { filename: string }
};
table?: boolean | {
copy?: boolean;
download?: boolean | { filename: string };
fullscreen?: boolean;
};
mermaid?: boolean | {
download?: boolean | { filename: string };
copy?: boolean;
fullscreen?: boolean;
panZoom?: boolean;
};
};This way controls?: {
code?: boolean | {
copy?: boolean;
download?: boolean | { filename: string };
};
table?: boolean | {
copy?: boolean;
download?: boolean | { filename: string };
fullscreen?: boolean;
csvSeparator: "," | ";" | "\t" | "auto" // CSV separator from #524
};
mermaid?: boolean | {
download?: boolean | { filename: string };
copy?: boolean;
fullscreen?: boolean;
panZoom?: boolean;
};
}; |
|
Thanks @farnabaz for taking the time to review both PRs and explaining the direction I’ll update both PRs accordingly. Appreciate the guidance 🙌🏻 |
…wnProps - Introduced DownloadControlConfig type to enhance download configuration options. - Updated ControlsConfig to utilize DownloadControlConfig for download properties. - Removed deprecated codeDownload property from StreamdownProps and context for cleaner API.
- Added a new utility function `getDownloadFilename` to retrieve configurable download filenames based on the provided controls configuration. - Updated `CodeBlockDownloadButton`, `MermaidDownloadDropdown`, and `TableDownloadButton` components to utilize the new filename generation logic, enhancing flexibility for download filenames. - Ensured fallback options are in place for scenarios where configuration is not defined.
… download components - Updated tests for `CodeBlockDownloadButton`, `MermaidDownloadDropdown`, and `TableDownloadButton` to verify the use of custom filenames from controls. - Added new test cases to ensure correct behavior when filenames are configured or omitted, including scenarios for fallback options. - Improved assertions for clarity and maintainability across the test suite.
- Added sections on setting custom download filenames for code blocks, tables, and mermaid diagrams across multiple documentation files.
- Updated descriptions to clarify the use of the `download: { filename }` configuration option.
- Provided code examples demonstrating how to implement custom filenames in the `Streamdown` component.
- Clarified the `controls` configuration in the Streamdown component to include custom download filenames for tables, code blocks, and mermaid diagrams.
- Updated examples to demonstrate the use of `download: { filename }` for setting specific filenames during downloads.
- Enhanced descriptions in the API and features documentation to reflect the new capabilities and usage scenarios.
- Enhanced the documentation to reflect the removal of the `codeDownload` prop in favor of a unified `controls` API for customizing download filenames.
- Clarified the configuration options for downloads, including the new `download: { filename: "customName" }` format while preserving automatic file-extension mapping.
- Updated examples to demonstrate the new capabilities for code, table, and mermaid downloads.
- Added .pnpm-store/ to the .gitignore file to prevent pnpm store files from being tracked in the repository.
|
@farnabaz Please have a look at the latest changes whenever you’re free. I’ve updated the implementation based on your guidance and moved the download filename configuration into the unified I’ve also updated the tests, documentation, and changeset accordingly. Please let me know if any further changes are required. Thanks again 🤗 |
Keep the fork's animation timeline, animateCodeBlocks path, and ControlsConfig in streamdown-context. Take unique upstream bits: inline-code animation (vercel#595), configurable download filenames (vercel#559), and table csvSeparator (vercel#524). Co-authored-by: Cursor <cursoragent@cursor.com>
Description
Adds support for configuring custom base filenames for Streamdown's built-in download controls through the unified
controlsAPI.The original implementation focused specifically on code block downloads and introduced a dedicated
codeDownloadconfiguration. Following maintainer feedback and guidance, the implementation was revised to provide a more consistent and extensible API across all Streamdown components that support downloads.Download filename configuration now lives under
controls, allowing consumers to configure custom filenames for code blocks, tables, and Mermaid diagrams from a single place:The configured
filenameis treated as a base filename. Streamdown continues to determine and append the appropriate extension automatically.For example:
myScript.js,myScript.py,myScript.tsx, etc.report.csvorreport.mdflowchart.svg,flowchart.png, orflowchart.mmdWhen no filename is configured, the existing default filenames remain unchanged (
file.<ext>,table.<ext>, anddiagram.<ext>respectively).This approach follows the maintainer's guidance to make download filename configuration part of the existing unified controls API rather than introducing a code-specific prop. It also provides a consistent foundation for download customization across the different downloadable content types supported by Streamdown.
Type of Change
Related Issues
Fixes #558
Closes #558
Related to #558
Changes Made
Unified download configuration
codeDownloadprop.DownloadControlConfigfor download-specific configuration.ControlsConfigto support configurable download filenames.controls.Custom filenames
Consumers can configure a base filename using:
Supported through:
The extension continues to be inferred automatically:
Existing behavior
Existing boolean configuration remains supported:
download: trueshows the download control with the default filename.download: falsehides the download control.code: falsehides all code controls.When no custom filename is supplied, existing default naming behavior is preserved.
Implementation
getDownloadFilenameutility for resolving configured filenames.CodeBlockDownloadButtonto use the unified filename configuration.TableDownloadButtonto use the unified filename configuration.MermaidDownloadDropdownto use the unified filename configuration.codeDownloadproperty fromStreamdownPropsandStreamdownContext.Tests
CodeBlockDownloadButton,TableDownloadButton, andMermaidDownloadDropdown.Documentation
controlsapproach.controlsAPI instead of the previouscodeDownloadAPI.Testing
Test Coverage
Verified custom filenames for the supported download controls:
Code:
myScript.jsmyScript.pymyScript.tsxTable:
report.csvreport.mdMermaid:
flowchart.svgflowchart.pngflowchart.mmdWhen no custom filename is provided, existing defaults remain unchanged:
file.<ext>for codetable.<ext>for tablesdiagram.<ext>for MermaidTest component:
Screenshots/Demos
Checklist
pnpm changeset)Changeset
Additional Notes
The initial version of this work introduced a dedicated
codeDownloadprop for configuring code block filenames.Based on maintainer feedback, the implementation was revised so that filename customization is handled through Streamdown's existing
controlsconfiguration. This avoids introducing a separate code-specific API and makes the behavior consistent across all current downloadable content types.The resulting API is intentionally simple:
This controls the base filename while Streamdown remains responsible for determining the appropriate extension.
The change therefore provides a unified mechanism for code, table, and Mermaid downloads while preserving the existing download behavior for consumers who do not configure a custom filename.
Future download-related customization can build on the unified
controlsAPI without requiring separate configuration props for individual content types.