@kushagradhawan/kookie-blocks 0.1.39 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/markdown/streaming-markdown.d.ts.map +1 -1
- package/dist/cjs/components/markdown/streaming-markdown.js +1 -1
- package/dist/cjs/components/markdown/streaming-markdown.js.map +3 -3
- package/dist/esm/components/markdown/streaming-markdown.d.ts.map +1 -1
- package/dist/esm/components/markdown/streaming-markdown.js +1 -1
- package/dist/esm/components/markdown/streaming-markdown.js.map +3 -3
- package/package.json +2 -2
- package/src/components/markdown/streaming-markdown.tsx +19 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/streaming-markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAsB,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOhE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAa3D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG;IAChE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3D;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC,CAAC;AA6CF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAY,EAAE,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"streaming-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/streaming-markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAsB,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOhE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAa3D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG;IAChE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3D;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC,CAAC;AA6CF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAY,EAAE,EAAE,sBAAsB,4BA2DtF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";"use client";var
|
|
1
|
+
"use strict";"use client";var E=Object.create;var l=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var R=(o,n)=>{for(var t in n)l(o,t,{get:n[t],enumerable:!0})},y=(o,n,t,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of L(n))!F.call(o,r)&&r!==t&&l(o,r,{get:()=>n[r],enumerable:!(i=x(n,r))||i.enumerable});return o};var s=(o,n,t)=>(t=o!=null?E(A(o)):{},y(n||!o||!o.__esModule?l(t,"default",{value:o,enumerable:!0}):t,o)),_=o=>y(l({},"__esModule",{value:!0}),o);var $={};R($,{StreamingMarkdown:()=>X});module.exports=_($);var e=s(require("react")),C=s(require("react-markdown")),b=s(require("remark-gfm")),B=s(require("rehype-raw")),p=s(require("harden-react-markdown")),d=require("@kushagradhawan/kookie-ui"),I=require("./create-markdown-components.js"),c=require("./utils/markdown-streaming.js");const D=typeof p.default=="function"?p.default:p.default.default||p.default,N=D(C.default),G=["https://","http://","/"],H=["https://","http://","/","data:"],z=["mailto:","tel:","data:","http:","https:"],T=typeof window<"u"&&window.location?.origin?window.location.origin:"https://app.kookie.ai";function U(o){return o||T}const g=(0,e.memo)(({content:o,defaultOrigin:n,components:t})=>e.default.createElement(d.Box,{width:"100%"},e.default.createElement(N,{defaultOrigin:n,allowedLinkPrefixes:G,allowedImagePrefixes:H,allowedProtocols:z,allowDataImages:!0,components:t,remarkPlugins:[b.default],rehypePlugins:[B.default]},o)),(o,n)=>o.content===n.content&&o.defaultOrigin===n.defaultOrigin&&o.components===n.components);g.displayName="MarkdownBlock";function X({content:o,id:n,options:t={}}){const{defaultOrigin:i,enableBlockMemoization:r=!0,blockParser:k,components:f,codeBlockCollapsible:w=!1,imageComponent:u,inlineCodeHighContrast:M=!0,spacing:O="spacious"}=t,P=(0,e.useMemo)(()=>U(i),[i]),h=(0,e.useMemo)(()=>({...(0,I.createMarkdownComponents)({codeBlockCollapsible:w,imageComponent:u,inlineCodeHighContrast:M,spacing:O}),...f}),[w,u,M,O,f]),m=(0,e.useMemo)(()=>{if(!r||!k){const a=(0,c.completeUnterminatedMarkdown)(o);return a.trim()?[a]:[]}return(0,c.parseMarkdownIntoBlocks)(o,k)},[o,r,k]);return m.length?m.length===1?e.default.createElement(g,{content:m[0],defaultOrigin:P,components:h}):e.default.createElement(d.Flex,{direction:"column",gap:"2",width:"100%"},m.map((a,S)=>e.default.createElement(g,{key:`${n}-block-${S}`,content:a,defaultOrigin:P,components:h}))):null}
|
|
2
2
|
//# sourceMappingURL=streaming-markdown.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/markdown/streaming-markdown.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport React, { memo, useMemo, type ReactNode } from \"react\";\nimport ReactMarkdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport hardenReactMarkdownModule from \"harden-react-markdown\";\nimport { Box, Flex } from \"@kushagradhawan/kookie-ui\";\nimport { createMarkdownComponents } from \"./create-markdown-components.js\";\nimport { completeUnterminatedMarkdown, parseMarkdownIntoBlocks } from \"./utils/markdown-streaming.js\";\nimport type { MarkdownComponentOptions } from \"./types.js\";\n\n// Handle different export formats\nconst hardenReactMarkdown =\n typeof hardenReactMarkdownModule === \"function\" ? hardenReactMarkdownModule : (hardenReactMarkdownModule as any).default || hardenReactMarkdownModule;\n\nconst HardenedMarkdown = hardenReactMarkdown(ReactMarkdown);\n\nconst LINK_PREFIXES = [\"https://\", \"http://\", \"/\"];\nconst IMAGE_PREFIXES = [\"https://\", \"http://\", \"/\", \"data:\"];\nconst ALLOWED_PROTOCOLS = [\"mailto:\", \"tel:\", \"data:\", \"http:\", \"https:\"];\nconst DEFAULT_APP_ORIGIN = typeof window !== \"undefined\" && window.location?.origin ? window.location.origin : \"https://app.kookie.ai\";\n\n/**\n * Options for StreamingMarkdown component\n */\nexport type StreamingMarkdownOptions = MarkdownComponentOptions & {\n /**\n * Security origin for link/image validation\n * @default window.location.origin or \"https://app.kookie.ai\"\n */\n defaultOrigin?: string;\n\n /**\n * Whether to enable block-level memoization for performance\n * Recommended for streaming scenarios where content updates frequently\n * @default true\n */\n enableBlockMemoization?: boolean;\n\n /**\n * Custom parser for splitting content into blocks\n * If not provided, content will be rendered as a single block\n * For optimal streaming performance, use marked.lexer with GFM enabled\n */\n blockParser?: (content: string) => Array<{ raw?: string }>;\n\n /**\n * Override default component mappings\n */\n components?: Partial<Components>;\n};\n\ntype StreamingMarkdownProps = {\n /**\n * Markdown content to render (supports streaming/incomplete markdown)\n */\n content: string;\n\n /**\n * Unique identifier for this markdown instance (used for keys)\n */\n id: string;\n\n /**\n * Optional configuration\n */\n options?: StreamingMarkdownOptions;\n};\n\ntype MarkdownBlockProps = {\n content: string;\n defaultOrigin: string;\n components: Components;\n};\n\n/**\n * Resolves the default origin for security validation\n */\nfunction resolveDefaultOrigin(customOrigin?: string): string {\n if (customOrigin) {\n return customOrigin;\n }\n return DEFAULT_APP_ORIGIN;\n}\n\n/**\n * Memoized markdown block component for efficient streaming rendering\n */\nconst MarkdownBlock = memo(\n ({ content, defaultOrigin, components }: MarkdownBlockProps) => {\n return (\n <Box width=\"100%\">\n <HardenedMarkdown\n defaultOrigin={defaultOrigin}\n allowedLinkPrefixes={LINK_PREFIXES}\n allowedImagePrefixes={IMAGE_PREFIXES}\n allowedProtocols={ALLOWED_PROTOCOLS}\n allowDataImages\n components={components}\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n >\n {content}\n </HardenedMarkdown>\n </Box>\n );\n },\n (previous, next) => previous.content === next.content && previous.defaultOrigin === next.defaultOrigin && previous.components === next.components\n);\n\nMarkdownBlock.displayName = \"MarkdownBlock\";\n\n/**\n * StreamingMarkdown - A drop-in markdown renderer designed for AI streaming.\n *\n * Features:\n * - Unterminated block parsing (handles incomplete markdown during streaming)\n * - Block-level memoization for performance\n * - Security hardening (validates links/images)\n * - GitHub Flavored Markdown support\n * - KookieUI component integration\n * - Code syntax highlighting via CodeBlock\n *\n * @example\n * ```tsx\n * import { StreamingMarkdown } from '@kushagradhawan/kookie-blocks';\n * import { marked } from 'marked';\n *\n * function ChatMessage({ message }) {\n * return (\n * <StreamingMarkdown\n * content={message.content}\n * id={message.id}\n * options={{\n * blockParser: (content) => marked.lexer(content, { gfm: true }),\n * enableBlockMemoization: true,\n * }}\n * />\n * );\n * }\n * ```\n */\nexport function StreamingMarkdown({ content, id, options = {} }: StreamingMarkdownProps) {\n const {
|
|
5
|
-
"mappings": "ukBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,IAAA,eAAAC,EAAAH,GAEA,IAAAI,EAAqD,oBACrDC,EAA+C,6BAC/CC,EAAsB,yBACtBC,EAAsB,yBACtBC,EAAsC,oCACtCC,EAA0B,qCAC1BC,EAAyC,2CACzCC,EAAsE,yCAItE,MAAMC,EACJ,OAAO,EAAAC,SAA8B,WAAa,EAAAA,QAA6B,EAAAA,QAAkC,SAAW,EAAAA,QAExHC,EAAmBF,EAAoB,EAAAG,OAAa,EAEpDC,EAAgB,CAAC,WAAY,UAAW,GAAG,EAC3CC,EAAiB,CAAC,WAAY,UAAW,IAAK,OAAO,EACrDC,EAAoB,CAAC,UAAW,OAAQ,QAAS,QAAS,QAAQ,EAClEC,EAAqB,OAAO,OAAW,KAAe,OAAO,UAAU,OAAS,OAAO,SAAS,OAAS,wBA0D/G,SAASC,EAAqBC,EAA+B,CAC3D,OAAIA,GAGGF,CACT,CAKA,MAAMG,KAAgB,QACpB,CAAC,CAAE,QAAAC,EAAS,cAAAC,EAAe,WAAAC,CAAW,IAElC,EAAAC,QAAA,cAAC,OAAI,MAAM,QACT,EAAAA,QAAA,cAACZ,EAAA,CACC,cAAeU,EACf,oBAAqBR,EACrB,qBAAsBC,EACtB,iBAAkBC,EAClB,gBAAe,GACf,WAAYO,EACZ,cAAe,CAAC,EAAAE,OAAS,EACzB,cAAe,CAAC,EAAAC,OAAS,GAExBL,CACH,CACF,EAGJ,CAACM,EAAUC,IAASD,EAAS,UAAYC,EAAK,SAAWD,EAAS,gBAAkBC,EAAK,eAAiBD,EAAS,aAAeC,EAAK,UACzI,EAEAR,EAAc,YAAc,gBAgCrB,SAASpB,EAAkB,CAAE,QAAAqB,EAAS,GAAAQ,EAAI,QAAAC,EAAU,CAAC,CAAE,EAA2B,CACvF,KAAM,
|
|
6
|
-
"names": ["streaming_markdown_exports", "__export", "StreamingMarkdown", "__toCommonJS", "import_react", "import_react_markdown", "import_remark_gfm", "import_rehype_raw", "import_harden_react_markdown", "import_kookie_ui", "import_create_markdown_components", "import_markdown_streaming", "hardenReactMarkdown", "hardenReactMarkdownModule", "HardenedMarkdown", "ReactMarkdown", "LINK_PREFIXES", "IMAGE_PREFIXES", "ALLOWED_PROTOCOLS", "DEFAULT_APP_ORIGIN", "resolveDefaultOrigin", "customOrigin", "MarkdownBlock", "content", "defaultOrigin", "components", "React", "remarkGfm", "rehypeRaw", "previous", "next", "id", "options", "enableBlockMemoization", "blockParser", "customComponents", "
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport React, { memo, useMemo, type ReactNode } from \"react\";\nimport ReactMarkdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport hardenReactMarkdownModule from \"harden-react-markdown\";\nimport { Box, Flex } from \"@kushagradhawan/kookie-ui\";\nimport { createMarkdownComponents } from \"./create-markdown-components.js\";\nimport { completeUnterminatedMarkdown, parseMarkdownIntoBlocks } from \"./utils/markdown-streaming.js\";\nimport type { MarkdownComponentOptions } from \"./types.js\";\n\n// Handle different export formats\nconst hardenReactMarkdown =\n typeof hardenReactMarkdownModule === \"function\" ? hardenReactMarkdownModule : (hardenReactMarkdownModule as any).default || hardenReactMarkdownModule;\n\nconst HardenedMarkdown = hardenReactMarkdown(ReactMarkdown);\n\nconst LINK_PREFIXES = [\"https://\", \"http://\", \"/\"];\nconst IMAGE_PREFIXES = [\"https://\", \"http://\", \"/\", \"data:\"];\nconst ALLOWED_PROTOCOLS = [\"mailto:\", \"tel:\", \"data:\", \"http:\", \"https:\"];\nconst DEFAULT_APP_ORIGIN = typeof window !== \"undefined\" && window.location?.origin ? window.location.origin : \"https://app.kookie.ai\";\n\n/**\n * Options for StreamingMarkdown component\n */\nexport type StreamingMarkdownOptions = MarkdownComponentOptions & {\n /**\n * Security origin for link/image validation\n * @default window.location.origin or \"https://app.kookie.ai\"\n */\n defaultOrigin?: string;\n\n /**\n * Whether to enable block-level memoization for performance\n * Recommended for streaming scenarios where content updates frequently\n * @default true\n */\n enableBlockMemoization?: boolean;\n\n /**\n * Custom parser for splitting content into blocks\n * If not provided, content will be rendered as a single block\n * For optimal streaming performance, use marked.lexer with GFM enabled\n */\n blockParser?: (content: string) => Array<{ raw?: string }>;\n\n /**\n * Override default component mappings\n */\n components?: Partial<Components>;\n};\n\ntype StreamingMarkdownProps = {\n /**\n * Markdown content to render (supports streaming/incomplete markdown)\n */\n content: string;\n\n /**\n * Unique identifier for this markdown instance (used for keys)\n */\n id: string;\n\n /**\n * Optional configuration\n */\n options?: StreamingMarkdownOptions;\n};\n\ntype MarkdownBlockProps = {\n content: string;\n defaultOrigin: string;\n components: Components;\n};\n\n/**\n * Resolves the default origin for security validation\n */\nfunction resolveDefaultOrigin(customOrigin?: string): string {\n if (customOrigin) {\n return customOrigin;\n }\n return DEFAULT_APP_ORIGIN;\n}\n\n/**\n * Memoized markdown block component for efficient streaming rendering\n */\nconst MarkdownBlock = memo(\n ({ content, defaultOrigin, components }: MarkdownBlockProps) => {\n return (\n <Box width=\"100%\">\n <HardenedMarkdown\n defaultOrigin={defaultOrigin}\n allowedLinkPrefixes={LINK_PREFIXES}\n allowedImagePrefixes={IMAGE_PREFIXES}\n allowedProtocols={ALLOWED_PROTOCOLS}\n allowDataImages\n components={components}\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n >\n {content}\n </HardenedMarkdown>\n </Box>\n );\n },\n (previous, next) => previous.content === next.content && previous.defaultOrigin === next.defaultOrigin && previous.components === next.components\n);\n\nMarkdownBlock.displayName = \"MarkdownBlock\";\n\n/**\n * StreamingMarkdown - A drop-in markdown renderer designed for AI streaming.\n *\n * Features:\n * - Unterminated block parsing (handles incomplete markdown during streaming)\n * - Block-level memoization for performance\n * - Security hardening (validates links/images)\n * - GitHub Flavored Markdown support\n * - KookieUI component integration\n * - Code syntax highlighting via CodeBlock\n *\n * @example\n * ```tsx\n * import { StreamingMarkdown } from '@kushagradhawan/kookie-blocks';\n * import { marked } from 'marked';\n *\n * function ChatMessage({ message }) {\n * return (\n * <StreamingMarkdown\n * content={message.content}\n * id={message.id}\n * options={{\n * blockParser: (content) => marked.lexer(content, { gfm: true }),\n * enableBlockMemoization: true,\n * }}\n * />\n * );\n * }\n * ```\n */\nexport function StreamingMarkdown({ content, id, options = {} }: StreamingMarkdownProps) {\n const {\n defaultOrigin: customOrigin,\n enableBlockMemoization = true,\n blockParser,\n components: customComponents,\n // Extract individual options as primitives for stable useMemo deps\n codeBlockCollapsible = false,\n imageComponent,\n inlineCodeHighContrast = true,\n spacing = \"spacious\",\n } = options;\n\n // Resolve security origin\n const defaultOrigin = useMemo(() => resolveDefaultOrigin(customOrigin), [customOrigin]);\n\n // Create component mappings with custom overrides\n // Uses primitive deps to avoid recreating on every render\n const markdownComponents = useMemo(() => {\n const baseComponents = createMarkdownComponents({\n codeBlockCollapsible,\n imageComponent,\n inlineCodeHighContrast,\n spacing,\n });\n return {\n ...baseComponents,\n ...customComponents,\n };\n }, [codeBlockCollapsible, imageComponent, inlineCodeHighContrast, spacing, customComponents]);\n\n // Parse content into blocks for memoization (if enabled and parser provided)\n const blocks = useMemo(() => {\n if (!enableBlockMemoization || !blockParser) {\n // No block splitting - just complete unterminated markdown\n const completed = completeUnterminatedMarkdown(content);\n return completed.trim() ? [completed] : [];\n }\n\n return parseMarkdownIntoBlocks(content, blockParser);\n }, [content, enableBlockMemoization, blockParser]);\n\n if (!blocks.length) {\n return null;\n }\n\n // Single block - no need for wrapper\n if (blocks.length === 1) {\n return <MarkdownBlock content={blocks[0]} defaultOrigin={defaultOrigin} components={markdownComponents} />;\n }\n\n // Multiple blocks - render with flex wrapper\n return (\n <Flex direction=\"column\" gap=\"2\" width=\"100%\">\n {blocks.map((block, index) => (\n <MarkdownBlock key={`${id}-block-${index}`} content={block} defaultOrigin={defaultOrigin} components={markdownComponents} />\n ))}\n </Flex>\n );\n}\n"],
|
|
5
|
+
"mappings": "ukBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,IAAA,eAAAC,EAAAH,GAEA,IAAAI,EAAqD,oBACrDC,EAA+C,6BAC/CC,EAAsB,yBACtBC,EAAsB,yBACtBC,EAAsC,oCACtCC,EAA0B,qCAC1BC,EAAyC,2CACzCC,EAAsE,yCAItE,MAAMC,EACJ,OAAO,EAAAC,SAA8B,WAAa,EAAAA,QAA6B,EAAAA,QAAkC,SAAW,EAAAA,QAExHC,EAAmBF,EAAoB,EAAAG,OAAa,EAEpDC,EAAgB,CAAC,WAAY,UAAW,GAAG,EAC3CC,EAAiB,CAAC,WAAY,UAAW,IAAK,OAAO,EACrDC,EAAoB,CAAC,UAAW,OAAQ,QAAS,QAAS,QAAQ,EAClEC,EAAqB,OAAO,OAAW,KAAe,OAAO,UAAU,OAAS,OAAO,SAAS,OAAS,wBA0D/G,SAASC,EAAqBC,EAA+B,CAC3D,OAAIA,GAGGF,CACT,CAKA,MAAMG,KAAgB,QACpB,CAAC,CAAE,QAAAC,EAAS,cAAAC,EAAe,WAAAC,CAAW,IAElC,EAAAC,QAAA,cAAC,OAAI,MAAM,QACT,EAAAA,QAAA,cAACZ,EAAA,CACC,cAAeU,EACf,oBAAqBR,EACrB,qBAAsBC,EACtB,iBAAkBC,EAClB,gBAAe,GACf,WAAYO,EACZ,cAAe,CAAC,EAAAE,OAAS,EACzB,cAAe,CAAC,EAAAC,OAAS,GAExBL,CACH,CACF,EAGJ,CAACM,EAAUC,IAASD,EAAS,UAAYC,EAAK,SAAWD,EAAS,gBAAkBC,EAAK,eAAiBD,EAAS,aAAeC,EAAK,UACzI,EAEAR,EAAc,YAAc,gBAgCrB,SAASpB,EAAkB,CAAE,QAAAqB,EAAS,GAAAQ,EAAI,QAAAC,EAAU,CAAC,CAAE,EAA2B,CACvF,KAAM,CACJ,cAAeX,EACf,uBAAAY,EAAyB,GACzB,YAAAC,EACA,WAAYC,EAEZ,qBAAAC,EAAuB,GACvB,eAAAC,EACA,uBAAAC,EAAyB,GACzB,QAAAC,EAAU,UACZ,EAAIP,EAGER,KAAgB,WAAQ,IAAMJ,EAAqBC,CAAY,EAAG,CAACA,CAAY,CAAC,EAIhFmB,KAAqB,WAAQ,KAO1B,CACL,MAPqB,4BAAyB,CAC9C,qBAAAJ,EACA,eAAAC,EACA,uBAAAC,EACA,QAAAC,CACF,CAAC,EAGC,GAAGJ,CACL,GACC,CAACC,EAAsBC,EAAgBC,EAAwBC,EAASJ,CAAgB,CAAC,EAGtFM,KAAS,WAAQ,IAAM,CAC3B,GAAI,CAACR,GAA0B,CAACC,EAAa,CAE3C,MAAMQ,KAAY,gCAA6BnB,CAAO,EACtD,OAAOmB,EAAU,KAAK,EAAI,CAACA,CAAS,EAAI,CAAC,CAC3C,CAEA,SAAO,2BAAwBnB,EAASW,CAAW,CACrD,EAAG,CAACX,EAASU,EAAwBC,CAAW,CAAC,EAEjD,OAAKO,EAAO,OAKRA,EAAO,SAAW,EACb,EAAAf,QAAA,cAACJ,EAAA,CAAc,QAASmB,EAAO,CAAC,EAAG,cAAejB,EAAe,WAAYgB,EAAoB,EAKxG,EAAAd,QAAA,cAAC,QAAK,UAAU,SAAS,IAAI,IAAI,MAAM,QACpCe,EAAO,IAAI,CAACE,EAAOC,IAClB,EAAAlB,QAAA,cAACJ,EAAA,CAAc,IAAK,GAAGS,CAAE,UAAUa,CAAK,GAAI,QAASD,EAAO,cAAenB,EAAe,WAAYgB,EAAoB,CAC3H,CACH,EAdO,IAgBX",
|
|
6
|
+
"names": ["streaming_markdown_exports", "__export", "StreamingMarkdown", "__toCommonJS", "import_react", "import_react_markdown", "import_remark_gfm", "import_rehype_raw", "import_harden_react_markdown", "import_kookie_ui", "import_create_markdown_components", "import_markdown_streaming", "hardenReactMarkdown", "hardenReactMarkdownModule", "HardenedMarkdown", "ReactMarkdown", "LINK_PREFIXES", "IMAGE_PREFIXES", "ALLOWED_PROTOCOLS", "DEFAULT_APP_ORIGIN", "resolveDefaultOrigin", "customOrigin", "MarkdownBlock", "content", "defaultOrigin", "components", "React", "remarkGfm", "rehypeRaw", "previous", "next", "id", "options", "enableBlockMemoization", "blockParser", "customComponents", "codeBlockCollapsible", "imageComponent", "inlineCodeHighContrast", "spacing", "markdownComponents", "blocks", "completed", "block", "index"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/streaming-markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAsB,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOhE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAa3D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG;IAChE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3D;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC,CAAC;AA6CF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAY,EAAE,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"streaming-markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/streaming-markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAsB,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOhE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAa3D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG;IAChE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3D;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC,CAAC;AA6CF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAY,EAAE,EAAE,sBAAsB,4BA2DtF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import e,{memo as
|
|
1
|
+
"use client";import e,{memo as P,useMemo as p}from"react";import h from"react-markdown";import y from"remark-gfm";import C from"rehype-raw";import i from"harden-react-markdown";import{Box as b,Flex as B}from"@kushagradhawan/kookie-ui";import{createMarkdownComponents as I}from"./create-markdown-components.js";import{completeUnterminatedMarkdown as S,parseMarkdownIntoBlocks as E}from"./utils/markdown-streaming.js";const x=typeof i=="function"?i:i.default||i,L=x(h),A=["https://","http://","/"],F=["https://","http://","/","data:"],R=["mailto:","tel:","data:","http:","https:"],_=typeof window<"u"&&window.location?.origin?window.location.origin:"https://app.kookie.ai";function D(o){return o||_}const m=P(({content:o,defaultOrigin:n,components:a})=>e.createElement(b,{width:"100%"},e.createElement(L,{defaultOrigin:n,allowedLinkPrefixes:A,allowedImagePrefixes:F,allowedProtocols:R,allowDataImages:!0,components:a,remarkPlugins:[y],rehypePlugins:[C]},o)),(o,n)=>o.content===n.content&&o.defaultOrigin===n.defaultOrigin&&o.components===n.components);m.displayName="MarkdownBlock";function j({content:o,id:n,options:a={}}){const{defaultOrigin:l,enableBlockMemoization:d=!0,blockParser:s,components:c,codeBlockCollapsible:k=!1,imageComponent:g,inlineCodeHighContrast:f=!0,spacing:w="spacious"}=a,u=p(()=>D(l),[l]),M=p(()=>({...I({codeBlockCollapsible:k,imageComponent:g,inlineCodeHighContrast:f,spacing:w}),...c}),[k,g,f,w,c]),r=p(()=>{if(!d||!s){const t=S(o);return t.trim()?[t]:[]}return E(o,s)},[o,d,s]);return r.length?r.length===1?e.createElement(m,{content:r[0],defaultOrigin:u,components:M}):e.createElement(B,{direction:"column",gap:"2",width:"100%"},r.map((t,O)=>e.createElement(m,{key:`${n}-block-${O}`,content:t,defaultOrigin:u,components:M}))):null}export{j as StreamingMarkdown};
|
|
2
2
|
//# sourceMappingURL=streaming-markdown.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/markdown/streaming-markdown.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport React, { memo, useMemo, type ReactNode } from \"react\";\nimport ReactMarkdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport hardenReactMarkdownModule from \"harden-react-markdown\";\nimport { Box, Flex } from \"@kushagradhawan/kookie-ui\";\nimport { createMarkdownComponents } from \"./create-markdown-components.js\";\nimport { completeUnterminatedMarkdown, parseMarkdownIntoBlocks } from \"./utils/markdown-streaming.js\";\nimport type { MarkdownComponentOptions } from \"./types.js\";\n\n// Handle different export formats\nconst hardenReactMarkdown =\n typeof hardenReactMarkdownModule === \"function\" ? hardenReactMarkdownModule : (hardenReactMarkdownModule as any).default || hardenReactMarkdownModule;\n\nconst HardenedMarkdown = hardenReactMarkdown(ReactMarkdown);\n\nconst LINK_PREFIXES = [\"https://\", \"http://\", \"/\"];\nconst IMAGE_PREFIXES = [\"https://\", \"http://\", \"/\", \"data:\"];\nconst ALLOWED_PROTOCOLS = [\"mailto:\", \"tel:\", \"data:\", \"http:\", \"https:\"];\nconst DEFAULT_APP_ORIGIN = typeof window !== \"undefined\" && window.location?.origin ? window.location.origin : \"https://app.kookie.ai\";\n\n/**\n * Options for StreamingMarkdown component\n */\nexport type StreamingMarkdownOptions = MarkdownComponentOptions & {\n /**\n * Security origin for link/image validation\n * @default window.location.origin or \"https://app.kookie.ai\"\n */\n defaultOrigin?: string;\n\n /**\n * Whether to enable block-level memoization for performance\n * Recommended for streaming scenarios where content updates frequently\n * @default true\n */\n enableBlockMemoization?: boolean;\n\n /**\n * Custom parser for splitting content into blocks\n * If not provided, content will be rendered as a single block\n * For optimal streaming performance, use marked.lexer with GFM enabled\n */\n blockParser?: (content: string) => Array<{ raw?: string }>;\n\n /**\n * Override default component mappings\n */\n components?: Partial<Components>;\n};\n\ntype StreamingMarkdownProps = {\n /**\n * Markdown content to render (supports streaming/incomplete markdown)\n */\n content: string;\n\n /**\n * Unique identifier for this markdown instance (used for keys)\n */\n id: string;\n\n /**\n * Optional configuration\n */\n options?: StreamingMarkdownOptions;\n};\n\ntype MarkdownBlockProps = {\n content: string;\n defaultOrigin: string;\n components: Components;\n};\n\n/**\n * Resolves the default origin for security validation\n */\nfunction resolveDefaultOrigin(customOrigin?: string): string {\n if (customOrigin) {\n return customOrigin;\n }\n return DEFAULT_APP_ORIGIN;\n}\n\n/**\n * Memoized markdown block component for efficient streaming rendering\n */\nconst MarkdownBlock = memo(\n ({ content, defaultOrigin, components }: MarkdownBlockProps) => {\n return (\n <Box width=\"100%\">\n <HardenedMarkdown\n defaultOrigin={defaultOrigin}\n allowedLinkPrefixes={LINK_PREFIXES}\n allowedImagePrefixes={IMAGE_PREFIXES}\n allowedProtocols={ALLOWED_PROTOCOLS}\n allowDataImages\n components={components}\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n >\n {content}\n </HardenedMarkdown>\n </Box>\n );\n },\n (previous, next) => previous.content === next.content && previous.defaultOrigin === next.defaultOrigin && previous.components === next.components\n);\n\nMarkdownBlock.displayName = \"MarkdownBlock\";\n\n/**\n * StreamingMarkdown - A drop-in markdown renderer designed for AI streaming.\n *\n * Features:\n * - Unterminated block parsing (handles incomplete markdown during streaming)\n * - Block-level memoization for performance\n * - Security hardening (validates links/images)\n * - GitHub Flavored Markdown support\n * - KookieUI component integration\n * - Code syntax highlighting via CodeBlock\n *\n * @example\n * ```tsx\n * import { StreamingMarkdown } from '@kushagradhawan/kookie-blocks';\n * import { marked } from 'marked';\n *\n * function ChatMessage({ message }) {\n * return (\n * <StreamingMarkdown\n * content={message.content}\n * id={message.id}\n * options={{\n * blockParser: (content) => marked.lexer(content, { gfm: true }),\n * enableBlockMemoization: true,\n * }}\n * />\n * );\n * }\n * ```\n */\nexport function StreamingMarkdown({ content, id, options = {} }: StreamingMarkdownProps) {\n const {
|
|
5
|
-
"mappings": "aAEA,OAAOA,GAAS,QAAAC,EAAM,WAAAC,MAA+B,QACrD,OAAOC,MAAwC,iBAC/C,OAAOC,MAAe,aACtB,OAAOC,MAAe,aACtB,OAAOC,MAA+B,wBACtC,OAAS,OAAAC,EAAK,QAAAC,MAAY,4BAC1B,OAAS,4BAAAC,MAAgC,kCACzC,OAAS,gCAAAC,EAA8B,2BAAAC,MAA+B,gCAItE,MAAMC,EACJ,OAAON,GAA8B,WAAaA,EAA6BA,EAAkC,SAAWA,EAExHO,EAAmBD,EAAoBT,CAAa,EAEpDW,EAAgB,CAAC,WAAY,UAAW,GAAG,EAC3CC,EAAiB,CAAC,WAAY,UAAW,IAAK,OAAO,EACrDC,EAAoB,CAAC,UAAW,OAAQ,QAAS,QAAS,QAAQ,EAClEC,EAAqB,OAAO,OAAW,KAAe,OAAO,UAAU,OAAS,OAAO,SAAS,OAAS,wBA0D/G,SAASC,EAAqBC,EAA+B,CAC3D,OAAIA,GAGGF,CACT,CAKA,MAAMG,EAAgBnB,EACpB,CAAC,CAAE,QAAAoB,EAAS,cAAAC,EAAe,WAAAC,CAAW,IAElCvB,EAAA,cAACO,EAAA,CAAI,MAAM,QACTP,EAAA,cAACa,EAAA,CACC,cAAeS,EACf,oBAAqBR,EACrB,qBAAsBC,EACtB,iBAAkBC,EAClB,gBAAe,GACf,WAAYO,EACZ,cAAe,CAACnB,CAAS,EACzB,cAAe,CAACC,CAAS,GAExBgB,CACH,CACF,EAGJ,CAACG,EAAUC,IAASD,EAAS,UAAYC,EAAK,SAAWD,EAAS,gBAAkBC,EAAK,eAAiBD,EAAS,aAAeC,EAAK,UACzI,EAEAL,EAAc,YAAc,gBAgCrB,SAASM,EAAkB,CAAE,QAAAL,EAAS,GAAAM,EAAI,QAAAC,EAAU,CAAC,CAAE,EAA2B,CACvF,KAAM,
|
|
6
|
-
"names": ["React", "memo", "useMemo", "ReactMarkdown", "remarkGfm", "rehypeRaw", "hardenReactMarkdownModule", "Box", "Flex", "createMarkdownComponents", "completeUnterminatedMarkdown", "parseMarkdownIntoBlocks", "hardenReactMarkdown", "HardenedMarkdown", "LINK_PREFIXES", "IMAGE_PREFIXES", "ALLOWED_PROTOCOLS", "DEFAULT_APP_ORIGIN", "resolveDefaultOrigin", "customOrigin", "MarkdownBlock", "content", "defaultOrigin", "components", "previous", "next", "StreamingMarkdown", "id", "options", "enableBlockMemoization", "blockParser", "customComponents", "
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport React, { memo, useMemo, type ReactNode } from \"react\";\nimport ReactMarkdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport hardenReactMarkdownModule from \"harden-react-markdown\";\nimport { Box, Flex } from \"@kushagradhawan/kookie-ui\";\nimport { createMarkdownComponents } from \"./create-markdown-components.js\";\nimport { completeUnterminatedMarkdown, parseMarkdownIntoBlocks } from \"./utils/markdown-streaming.js\";\nimport type { MarkdownComponentOptions } from \"./types.js\";\n\n// Handle different export formats\nconst hardenReactMarkdown =\n typeof hardenReactMarkdownModule === \"function\" ? hardenReactMarkdownModule : (hardenReactMarkdownModule as any).default || hardenReactMarkdownModule;\n\nconst HardenedMarkdown = hardenReactMarkdown(ReactMarkdown);\n\nconst LINK_PREFIXES = [\"https://\", \"http://\", \"/\"];\nconst IMAGE_PREFIXES = [\"https://\", \"http://\", \"/\", \"data:\"];\nconst ALLOWED_PROTOCOLS = [\"mailto:\", \"tel:\", \"data:\", \"http:\", \"https:\"];\nconst DEFAULT_APP_ORIGIN = typeof window !== \"undefined\" && window.location?.origin ? window.location.origin : \"https://app.kookie.ai\";\n\n/**\n * Options for StreamingMarkdown component\n */\nexport type StreamingMarkdownOptions = MarkdownComponentOptions & {\n /**\n * Security origin for link/image validation\n * @default window.location.origin or \"https://app.kookie.ai\"\n */\n defaultOrigin?: string;\n\n /**\n * Whether to enable block-level memoization for performance\n * Recommended for streaming scenarios where content updates frequently\n * @default true\n */\n enableBlockMemoization?: boolean;\n\n /**\n * Custom parser for splitting content into blocks\n * If not provided, content will be rendered as a single block\n * For optimal streaming performance, use marked.lexer with GFM enabled\n */\n blockParser?: (content: string) => Array<{ raw?: string }>;\n\n /**\n * Override default component mappings\n */\n components?: Partial<Components>;\n};\n\ntype StreamingMarkdownProps = {\n /**\n * Markdown content to render (supports streaming/incomplete markdown)\n */\n content: string;\n\n /**\n * Unique identifier for this markdown instance (used for keys)\n */\n id: string;\n\n /**\n * Optional configuration\n */\n options?: StreamingMarkdownOptions;\n};\n\ntype MarkdownBlockProps = {\n content: string;\n defaultOrigin: string;\n components: Components;\n};\n\n/**\n * Resolves the default origin for security validation\n */\nfunction resolveDefaultOrigin(customOrigin?: string): string {\n if (customOrigin) {\n return customOrigin;\n }\n return DEFAULT_APP_ORIGIN;\n}\n\n/**\n * Memoized markdown block component for efficient streaming rendering\n */\nconst MarkdownBlock = memo(\n ({ content, defaultOrigin, components }: MarkdownBlockProps) => {\n return (\n <Box width=\"100%\">\n <HardenedMarkdown\n defaultOrigin={defaultOrigin}\n allowedLinkPrefixes={LINK_PREFIXES}\n allowedImagePrefixes={IMAGE_PREFIXES}\n allowedProtocols={ALLOWED_PROTOCOLS}\n allowDataImages\n components={components}\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n >\n {content}\n </HardenedMarkdown>\n </Box>\n );\n },\n (previous, next) => previous.content === next.content && previous.defaultOrigin === next.defaultOrigin && previous.components === next.components\n);\n\nMarkdownBlock.displayName = \"MarkdownBlock\";\n\n/**\n * StreamingMarkdown - A drop-in markdown renderer designed for AI streaming.\n *\n * Features:\n * - Unterminated block parsing (handles incomplete markdown during streaming)\n * - Block-level memoization for performance\n * - Security hardening (validates links/images)\n * - GitHub Flavored Markdown support\n * - KookieUI component integration\n * - Code syntax highlighting via CodeBlock\n *\n * @example\n * ```tsx\n * import { StreamingMarkdown } from '@kushagradhawan/kookie-blocks';\n * import { marked } from 'marked';\n *\n * function ChatMessage({ message }) {\n * return (\n * <StreamingMarkdown\n * content={message.content}\n * id={message.id}\n * options={{\n * blockParser: (content) => marked.lexer(content, { gfm: true }),\n * enableBlockMemoization: true,\n * }}\n * />\n * );\n * }\n * ```\n */\nexport function StreamingMarkdown({ content, id, options = {} }: StreamingMarkdownProps) {\n const {\n defaultOrigin: customOrigin,\n enableBlockMemoization = true,\n blockParser,\n components: customComponents,\n // Extract individual options as primitives for stable useMemo deps\n codeBlockCollapsible = false,\n imageComponent,\n inlineCodeHighContrast = true,\n spacing = \"spacious\",\n } = options;\n\n // Resolve security origin\n const defaultOrigin = useMemo(() => resolveDefaultOrigin(customOrigin), [customOrigin]);\n\n // Create component mappings with custom overrides\n // Uses primitive deps to avoid recreating on every render\n const markdownComponents = useMemo(() => {\n const baseComponents = createMarkdownComponents({\n codeBlockCollapsible,\n imageComponent,\n inlineCodeHighContrast,\n spacing,\n });\n return {\n ...baseComponents,\n ...customComponents,\n };\n }, [codeBlockCollapsible, imageComponent, inlineCodeHighContrast, spacing, customComponents]);\n\n // Parse content into blocks for memoization (if enabled and parser provided)\n const blocks = useMemo(() => {\n if (!enableBlockMemoization || !blockParser) {\n // No block splitting - just complete unterminated markdown\n const completed = completeUnterminatedMarkdown(content);\n return completed.trim() ? [completed] : [];\n }\n\n return parseMarkdownIntoBlocks(content, blockParser);\n }, [content, enableBlockMemoization, blockParser]);\n\n if (!blocks.length) {\n return null;\n }\n\n // Single block - no need for wrapper\n if (blocks.length === 1) {\n return <MarkdownBlock content={blocks[0]} defaultOrigin={defaultOrigin} components={markdownComponents} />;\n }\n\n // Multiple blocks - render with flex wrapper\n return (\n <Flex direction=\"column\" gap=\"2\" width=\"100%\">\n {blocks.map((block, index) => (\n <MarkdownBlock key={`${id}-block-${index}`} content={block} defaultOrigin={defaultOrigin} components={markdownComponents} />\n ))}\n </Flex>\n );\n}\n"],
|
|
5
|
+
"mappings": "aAEA,OAAOA,GAAS,QAAAC,EAAM,WAAAC,MAA+B,QACrD,OAAOC,MAAwC,iBAC/C,OAAOC,MAAe,aACtB,OAAOC,MAAe,aACtB,OAAOC,MAA+B,wBACtC,OAAS,OAAAC,EAAK,QAAAC,MAAY,4BAC1B,OAAS,4BAAAC,MAAgC,kCACzC,OAAS,gCAAAC,EAA8B,2BAAAC,MAA+B,gCAItE,MAAMC,EACJ,OAAON,GAA8B,WAAaA,EAA6BA,EAAkC,SAAWA,EAExHO,EAAmBD,EAAoBT,CAAa,EAEpDW,EAAgB,CAAC,WAAY,UAAW,GAAG,EAC3CC,EAAiB,CAAC,WAAY,UAAW,IAAK,OAAO,EACrDC,EAAoB,CAAC,UAAW,OAAQ,QAAS,QAAS,QAAQ,EAClEC,EAAqB,OAAO,OAAW,KAAe,OAAO,UAAU,OAAS,OAAO,SAAS,OAAS,wBA0D/G,SAASC,EAAqBC,EAA+B,CAC3D,OAAIA,GAGGF,CACT,CAKA,MAAMG,EAAgBnB,EACpB,CAAC,CAAE,QAAAoB,EAAS,cAAAC,EAAe,WAAAC,CAAW,IAElCvB,EAAA,cAACO,EAAA,CAAI,MAAM,QACTP,EAAA,cAACa,EAAA,CACC,cAAeS,EACf,oBAAqBR,EACrB,qBAAsBC,EACtB,iBAAkBC,EAClB,gBAAe,GACf,WAAYO,EACZ,cAAe,CAACnB,CAAS,EACzB,cAAe,CAACC,CAAS,GAExBgB,CACH,CACF,EAGJ,CAACG,EAAUC,IAASD,EAAS,UAAYC,EAAK,SAAWD,EAAS,gBAAkBC,EAAK,eAAiBD,EAAS,aAAeC,EAAK,UACzI,EAEAL,EAAc,YAAc,gBAgCrB,SAASM,EAAkB,CAAE,QAAAL,EAAS,GAAAM,EAAI,QAAAC,EAAU,CAAC,CAAE,EAA2B,CACvF,KAAM,CACJ,cAAeT,EACf,uBAAAU,EAAyB,GACzB,YAAAC,EACA,WAAYC,EAEZ,qBAAAC,EAAuB,GACvB,eAAAC,EACA,uBAAAC,EAAyB,GACzB,QAAAC,EAAU,UACZ,EAAIP,EAGEN,EAAgBpB,EAAQ,IAAMgB,EAAqBC,CAAY,EAAG,CAACA,CAAY,CAAC,EAIhFiB,EAAqBlC,EAAQ,KAO1B,CACL,GAPqBO,EAAyB,CAC9C,qBAAAuB,EACA,eAAAC,EACA,uBAAAC,EACA,QAAAC,CACF,CAAC,EAGC,GAAGJ,CACL,GACC,CAACC,EAAsBC,EAAgBC,EAAwBC,EAASJ,CAAgB,CAAC,EAGtFM,EAASnC,EAAQ,IAAM,CAC3B,GAAI,CAAC2B,GAA0B,CAACC,EAAa,CAE3C,MAAMQ,EAAY5B,EAA6BW,CAAO,EACtD,OAAOiB,EAAU,KAAK,EAAI,CAACA,CAAS,EAAI,CAAC,CAC3C,CAEA,OAAO3B,EAAwBU,EAASS,CAAW,CACrD,EAAG,CAACT,EAASQ,EAAwBC,CAAW,CAAC,EAEjD,OAAKO,EAAO,OAKRA,EAAO,SAAW,EACbrC,EAAA,cAACoB,EAAA,CAAc,QAASiB,EAAO,CAAC,EAAG,cAAef,EAAe,WAAYc,EAAoB,EAKxGpC,EAAA,cAACQ,EAAA,CAAK,UAAU,SAAS,IAAI,IAAI,MAAM,QACpC6B,EAAO,IAAI,CAACE,EAAOC,IAClBxC,EAAA,cAACoB,EAAA,CAAc,IAAK,GAAGO,CAAE,UAAUa,CAAK,GAAI,QAASD,EAAO,cAAejB,EAAe,WAAYc,EAAoB,CAC3H,CACH,EAdO,IAgBX",
|
|
6
|
+
"names": ["React", "memo", "useMemo", "ReactMarkdown", "remarkGfm", "rehypeRaw", "hardenReactMarkdownModule", "Box", "Flex", "createMarkdownComponents", "completeUnterminatedMarkdown", "parseMarkdownIntoBlocks", "hardenReactMarkdown", "HardenedMarkdown", "LINK_PREFIXES", "IMAGE_PREFIXES", "ALLOWED_PROTOCOLS", "DEFAULT_APP_ORIGIN", "resolveDefaultOrigin", "customOrigin", "MarkdownBlock", "content", "defaultOrigin", "components", "previous", "next", "StreamingMarkdown", "id", "options", "enableBlockMemoization", "blockParser", "customComponents", "codeBlockCollapsible", "imageComponent", "inlineCodeHighContrast", "spacing", "markdownComponents", "blocks", "completed", "block", "index"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kushagradhawan/kookie-blocks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/KushagraDhawan1997/kookie-blocks.git"
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"shiki": "^1.24.2"
|
|
103
103
|
},
|
|
104
104
|
"peerDependencies": {
|
|
105
|
-
"@kushagradhawan/kookie-ui": ">=0.1.
|
|
105
|
+
"@kushagradhawan/kookie-ui": ">=0.1.111",
|
|
106
106
|
"react": "^18.0.0 || ^19.0.0",
|
|
107
107
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
108
108
|
},
|
|
@@ -142,19 +142,35 @@ MarkdownBlock.displayName = "MarkdownBlock";
|
|
|
142
142
|
* ```
|
|
143
143
|
*/
|
|
144
144
|
export function StreamingMarkdown({ content, id, options = {} }: StreamingMarkdownProps) {
|
|
145
|
-
const {
|
|
145
|
+
const {
|
|
146
|
+
defaultOrigin: customOrigin,
|
|
147
|
+
enableBlockMemoization = true,
|
|
148
|
+
blockParser,
|
|
149
|
+
components: customComponents,
|
|
150
|
+
// Extract individual options as primitives for stable useMemo deps
|
|
151
|
+
codeBlockCollapsible = false,
|
|
152
|
+
imageComponent,
|
|
153
|
+
inlineCodeHighContrast = true,
|
|
154
|
+
spacing = "spacious",
|
|
155
|
+
} = options;
|
|
146
156
|
|
|
147
157
|
// Resolve security origin
|
|
148
158
|
const defaultOrigin = useMemo(() => resolveDefaultOrigin(customOrigin), [customOrigin]);
|
|
149
159
|
|
|
150
160
|
// Create component mappings with custom overrides
|
|
161
|
+
// Uses primitive deps to avoid recreating on every render
|
|
151
162
|
const markdownComponents = useMemo(() => {
|
|
152
|
-
const baseComponents = createMarkdownComponents(
|
|
163
|
+
const baseComponents = createMarkdownComponents({
|
|
164
|
+
codeBlockCollapsible,
|
|
165
|
+
imageComponent,
|
|
166
|
+
inlineCodeHighContrast,
|
|
167
|
+
spacing,
|
|
168
|
+
});
|
|
153
169
|
return {
|
|
154
170
|
...baseComponents,
|
|
155
171
|
...customComponents,
|
|
156
172
|
};
|
|
157
|
-
}, [
|
|
173
|
+
}, [codeBlockCollapsible, imageComponent, inlineCodeHighContrast, spacing, customComponents]);
|
|
158
174
|
|
|
159
175
|
// Parse content into blocks for memoization (if enabled and parser provided)
|
|
160
176
|
const blocks = useMemo(() => {
|