@kushagradhawan/kookie-blocks 0.1.7 → 0.1.8
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/components.css +67 -1
- package/dist/cjs/components/code/CodeBlock.d.ts +2 -1
- package/dist/cjs/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/cjs/components/code/CodeBlock.js +1 -1
- package/dist/cjs/components/code/CodeBlock.js.map +3 -3
- package/dist/cjs/components/code/LanguageBadge.d.ts +9 -0
- package/dist/cjs/components/code/LanguageBadge.d.ts.map +1 -1
- package/dist/cjs/components/code/LanguageBadge.js +1 -1
- package/dist/cjs/components/code/LanguageBadge.js.map +3 -3
- package/dist/cjs/components/code/index.d.ts +5 -2
- package/dist/cjs/components/code/index.d.ts.map +1 -1
- package/dist/cjs/components/code/index.js +1 -1
- package/dist/cjs/components/code/index.js.map +3 -3
- package/dist/cjs/components/code/types.d.ts +132 -13
- package/dist/cjs/components/code/types.d.ts.map +1 -1
- package/dist/cjs/components/code/types.js +1 -1
- package/dist/cjs/components/code/types.js.map +3 -3
- package/dist/cjs/components/code/useCodeCard.d.ts +20 -0
- package/dist/cjs/components/code/useCodeCard.d.ts.map +1 -0
- package/dist/cjs/components/code/useCodeCard.js +2 -0
- package/dist/cjs/components/code/useCodeCard.js.map +7 -0
- package/dist/esm/components/code/CodeBlock.d.ts +2 -1
- package/dist/esm/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/esm/components/code/CodeBlock.js +1 -1
- package/dist/esm/components/code/CodeBlock.js.map +3 -3
- package/dist/esm/components/code/LanguageBadge.d.ts +9 -0
- package/dist/esm/components/code/LanguageBadge.d.ts.map +1 -1
- package/dist/esm/components/code/LanguageBadge.js +1 -1
- package/dist/esm/components/code/LanguageBadge.js.map +3 -3
- package/dist/esm/components/code/index.d.ts +5 -2
- package/dist/esm/components/code/index.d.ts.map +1 -1
- package/dist/esm/components/code/index.js +1 -1
- package/dist/esm/components/code/index.js.map +3 -3
- package/dist/esm/components/code/types.d.ts +132 -13
- package/dist/esm/components/code/types.d.ts.map +1 -1
- package/dist/esm/components/code/types.js +1 -0
- package/dist/esm/components/code/types.js.map +4 -4
- package/dist/esm/components/code/useCodeCard.d.ts +20 -0
- package/dist/esm/components/code/useCodeCard.d.ts.map +1 -0
- package/dist/esm/components/code/useCodeCard.js +2 -0
- package/dist/esm/components/code/useCodeCard.js.map +7 -0
- package/package.json +1 -1
- package/src/components/code/CodeBlock.tsx +250 -341
- package/src/components/code/LanguageBadge.tsx +65 -18
- package/src/components/code/index.ts +6 -3
- package/src/components/code/types.ts +219 -27
- package/src/components/code/useCodeCard.ts +82 -0
- package/src/components/index.css +62 -1
- package/styles.css +57 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface UseCodeCardOptions {
|
|
2
|
+
code: string;
|
|
3
|
+
collapsedHeight: number;
|
|
4
|
+
}
|
|
5
|
+
interface UseCodeCardReturn {
|
|
6
|
+
isExpanded: boolean;
|
|
7
|
+
shouldShowToggle: boolean;
|
|
8
|
+
copied: boolean;
|
|
9
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
10
|
+
contentMaxHeight: number;
|
|
11
|
+
handleToggle: () => void;
|
|
12
|
+
handleCopy: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Shared hook for code card UI behavior.
|
|
16
|
+
* Handles expand/collapse state and copy functionality.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useCodeCard({ code, collapsedHeight }: UseCodeCardOptions): UseCodeCardReturn;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=useCodeCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCodeCard.d.ts","sourceRoot":"","sources":["../../../../src/components/code/useCodeCard.ts"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,iBAAiB;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACnD,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,kBAAkB,GAAG,iBAAiB,CA0D5F"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var l=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var R=(o,e)=>{for(var r in e)l(o,r,{get:e[r],enumerable:!0})},b=(o,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of h(e))!E.call(o,n)&&n!==r&&l(o,n,{get:()=>e[n],enumerable:!(s=T(e,n))||s.enumerable});return o};var v=o=>b(l({},"__esModule",{value:!0}),o);var H={};R(H,{useCodeCard:()=>y});module.exports=v(H);var t=require("react");const x=2e3;function y({code:o,collapsedHeight:e}){const[r,s]=(0,t.useState)(!1),[n,d]=(0,t.useState)(e),[f,a]=(0,t.useState)(!1),i=(0,t.useRef)(null),c=(0,t.useRef)(null),p=n>e,C=r?n:e;(0,t.useEffect)(()=>{i.current&&d(i.current.scrollHeight)}),(0,t.useEffect)(()=>()=>{c.current&&clearTimeout(c.current)},[]);const g=(0,t.useCallback)(()=>{s(u=>!u)},[]),m=(0,t.useCallback)(async()=>{if(o.trim())try{await navigator.clipboard.writeText(o),a(!0),c.current&&clearTimeout(c.current),c.current=setTimeout(()=>a(!1),x)}catch{}},[o]);return{isExpanded:r,shouldShowToggle:p,copied:f,contentRef:i,contentMaxHeight:C,handleToggle:g,handleCopy:m}}
|
|
2
|
+
//# sourceMappingURL=useCodeCard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/components/code/useCodeCard.ts"],
|
|
4
|
+
"sourcesContent": ["import { useState, useRef, useEffect, useCallback } from \"react\";\n\nconst COPY_FEEDBACK_DURATION_MS = 2000;\n\ninterface UseCodeCardOptions {\n code: string;\n collapsedHeight: number;\n}\n\ninterface UseCodeCardReturn {\n isExpanded: boolean;\n shouldShowToggle: boolean;\n copied: boolean;\n contentRef: React.RefObject<HTMLDivElement | null>;\n contentMaxHeight: number;\n handleToggle: () => void;\n handleCopy: () => Promise<void>;\n}\n\n/**\n * Shared hook for code card UI behavior.\n * Handles expand/collapse state and copy functionality.\n */\nexport function useCodeCard({ code, collapsedHeight }: UseCodeCardOptions): UseCodeCardReturn {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(collapsedHeight);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement | null>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > collapsedHeight;\n const contentMaxHeight = isExpanded ? contentHeight : collapsedHeight;\n\n // Measure content height on mount and when content changes\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n });\n\n // Cleanup timeout on unmount\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) {\n clearTimeout(resetTimeoutRef.current);\n }\n };\n }, []);\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n\n if (resetTimeoutRef.current) {\n clearTimeout(resetTimeoutRef.current);\n }\n resetTimeoutRef.current = setTimeout(() => setCopied(false), COPY_FEEDBACK_DURATION_MS);\n } catch (error) {\n // Log error in development, fail gracefully in production\n if (process.env.NODE_ENV === \"development\") {\n console.error(\"[CodeBlock] Failed to copy to clipboard:\", error);\n }\n }\n }, [code]);\n\n return {\n isExpanded,\n shouldShowToggle,\n copied,\n contentRef,\n contentMaxHeight,\n handleToggle,\n handleCopy,\n };\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAyD,iBAEzD,MAAMC,EAA4B,IAqB3B,SAASH,EAAY,CAAE,KAAAI,EAAM,gBAAAC,CAAgB,EAA0C,CAC5F,KAAM,CAACC,EAAYC,CAAa,KAAI,YAAS,EAAK,EAC5C,CAACC,EAAeC,CAAgB,KAAI,YAASJ,CAAe,EAC5D,CAACK,EAAQC,CAAS,KAAI,YAAS,EAAK,EACpCC,KAAa,UAA8B,IAAI,EAC/CC,KAAkB,UAA6C,IAAI,EAEnEC,EAAmBN,EAAgBH,EACnCU,EAAmBT,EAAaE,EAAgBH,KAGtD,aAAU,IAAM,CACVO,EAAW,SACbH,EAAiBG,EAAW,QAAQ,YAAY,CAEpD,CAAC,KAGD,aAAU,IACD,IAAM,CACPC,EAAgB,SAClB,aAAaA,EAAgB,OAAO,CAExC,EACC,CAAC,CAAC,EAEL,MAAMG,KAAe,eAAY,IAAM,CACrCT,EAAeU,GAAS,CAACA,CAAI,CAC/B,EAAG,CAAC,CAAC,EAECC,KAAa,eAAY,SAAY,CACzC,GAAKd,EAAK,KAAK,EAEf,GAAI,CACF,MAAM,UAAU,UAAU,UAAUA,CAAI,EACxCO,EAAU,EAAI,EAEVE,EAAgB,SAClB,aAAaA,EAAgB,OAAO,EAEtCA,EAAgB,QAAU,WAAW,IAAMF,EAAU,EAAK,EAAGR,CAAyB,CACxF,MAAgB,CAKhB,CACF,EAAG,CAACC,CAAI,CAAC,EAET,MAAO,CACL,WAAAE,EACA,iBAAAQ,EACA,OAAAJ,EACA,WAAAE,EACA,iBAAAG,EACA,aAAAC,EACA,WAAAE,CACF,CACF",
|
|
6
|
+
"names": ["useCodeCard_exports", "__export", "useCodeCard", "__toCommonJS", "import_react", "COPY_FEEDBACK_DURATION_MS", "code", "collapsedHeight", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "contentMaxHeight", "handleToggle", "prev", "handleCopy"]
|
|
7
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { CodeBlockProps } from "./types";
|
|
3
|
-
export declare function CodeBlock({ children, code, language, preview, showCopy, showLanguage,
|
|
3
|
+
export declare function CodeBlock({ children, code, language, preview, showCopy, showLanguage, showLineNumbers, shikiConfig, background, backgroundProps, collapsible, collapsedHeight, file, }: CodeBlockProps): React.JSX.Element;
|
|
4
|
+
export declare function useCodeBlockContext(): boolean;
|
|
4
5
|
//# sourceMappingURL=CodeBlock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwF,MAAM,OAAO,CAAC;AAK7G,OAAO,KAAK,EAAE,cAAc,EAAuC,MAAM,SAAS,CAAC;AA6RnF,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAe,EACf,YAAmB,EACnB,eAAsB,EACtB,WAAW,EACX,UAAU,EACV,eAAe,EACf,WAAkB,EAClB,eAA0C,EAC1C,IAAI,GACL,EAAE,cAAc,qBA2ChB;AAED,wBAAgB,mBAAmB,YAElC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as
|
|
1
|
+
import e,{useState as k,useEffect as F,useMemo as S,memo as y,createContext as I,useContext as A}from"react";import{Box as m,Card as L,Flex as f,Button as _,Text as z,Theme as D,ScrollArea as $,IconButton as M}from"@kushagradhawan/kookie-ui";import{HugeiconsIcon as P}from"@hugeicons/react";import{Copy01Icon as W,Tick01Icon as O,ArrowDown01Icon as U}from"@hugeicons/core-free-icons";import{codeToHtml as j}from"shiki";import{extractTextFromChildren as G,extractLanguageFromChildren as B}from"./types";import{useCodeCard as K}from"./useCodeCard";import{LanguageBadge as V}from"./LanguageBadge";const v=I(!1),q=360,J="one-light",Q="one-dark-pro";function X({children:u,background:o="none",backgroundProps:t={}}){const{dotSize:a=24,color:l="var(--gray-10)",backgroundColor:i="var(--gray-2)",height:r,width:d="100%",radius:s="3"}=t,n=S(()=>{if(o!=="none")return o==="dots"?{backgroundImage:`radial-gradient(circle, ${l} 1px, transparent 1px)`,borderRadius:`var(--radius-${s})`,backgroundSize:`${a}px ${a}px`,backgroundPosition:"center",backgroundColor:i,width:d,...r&&{height:r}}:{backgroundImage:`url(${o})`,backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat",borderRadius:`var(--radius-${s})`,width:d,...r&&{height:r}}},[o,l,i,a,r,d,s]);return e.createElement(L,{size:"1",variant:"soft"},e.createElement(f,{justify:"center",align:"center",py:"4",style:n},e.createElement(D,{fontFamily:"sans"},u)))}function Y(){return e.createElement(m,{className:"code-skeleton"},["85%","70%","90%","60%","75%","80%"].map((o,t)=>e.createElement(m,{key:t,className:"code-skeleton-line",style:{width:o}})))}const T=y(function({code:o,language:t,showCopy:a,showLanguage:l,showLineNumbers:i,collapsible:r,collapsedHeight:d,file:s,isLoading:n=!1,children:p}){const{isExpanded:c,shouldShowToggle:C,copied:g,contentRef:x,contentMaxHeight:h,handleToggle:b,handleCopy:N}=K({code:o,collapsedHeight:d}),w=r&&C,H=c?"rotate(180deg)":"rotate(0deg)",E=i?"code-content":"code-content hide-line-numbers";return e.createElement(m,{position:"relative"},e.createElement(L,{size:"1",variant:"soft"},e.createElement(f,{direction:"column"},e.createElement(f,{justify:"between",align:"start",gap:"2"},e.createElement(f,{align:"center",gap:"2"},l&&e.createElement(V,{language:t}),s&&e.createElement(z,{size:"1",color:"gray",highContrast:!0},s)),e.createElement(f,{align:"center",className:"code-action-buttons"},w&&e.createElement(M,{size:"2",variant:"ghost",color:"gray",highContrast:!0,onClick:b,tooltip:c?"Collapse":"Expand","aria-label":c?"Collapse code":"Expand code"},e.createElement(P,{icon:U,style:{transform:H},className:"code-chevron",strokeWidth:1.75})),a&&e.createElement(_,{size:"2",variant:"ghost",color:"gray",highContrast:!0,onClick:N,tooltip:g?"Copied!":"Copy","aria-label":g?"Copied!":"Copy code"},e.createElement(P,{icon:g?O:W,strokeWidth:1.75})," Copy"))),e.createElement(m,{ref:x,style:{maxHeight:r?`${h}px`:void 0},className:E},e.createElement($,{type:"auto",scrollbars:"horizontal"},n?e.createElement(Y,null):p)),w&&!c&&e.createElement(m,{className:"code-scroll-shadow visible"}))))}),Z=y(function({code:o,language:t,showCopy:a,showLanguage:l,showLineNumbers:i,collapsible:r,collapsedHeight:d,file:s,shikiConfig:n}){const[p,c]=k(null),[C,g]=k(!0),x=S(()=>{const h=n?.themes?.light||J,b=n?.themes?.dark||Q;return{lang:t,themes:{light:h,dark:b},defaultColor:!1,langAlias:n?.langAlias,transformers:n?.transformers,meta:n?.meta?{__raw:n.meta}:void 0}},[t,n?.themes?.light,n?.themes?.dark,n?.langAlias,n?.transformers,n?.meta]);return F(()=>{let h=!1;return g(!0),j(o,x).then(b=>{h||(c(b),g(!1))}).catch(b=>{h||(c(null),g(!1))}),()=>{h=!0}},[o,x]),e.createElement(T,{code:o,language:t,showCopy:a,showLanguage:l,showLineNumbers:i,collapsible:r,collapsedHeight:d,file:s,isLoading:C},p?e.createElement(m,{dangerouslySetInnerHTML:{__html:p}}):null)}),R=y(function({children:o,showCopy:t,showLanguage:a,showLineNumbers:l,collapsible:i,collapsedHeight:r,file:d}){const s=G(o),n=B(o);return e.createElement(T,{code:s,language:n,showCopy:t,showLanguage:a,showLineNumbers:l,collapsible:i,collapsedHeight:r,file:d},o)});function de({children:u,code:o,language:t,preview:a,showCopy:l=!0,showLanguage:i=!0,showLineNumbers:r=!0,shikiConfig:d,background:s,backgroundProps:n,collapsible:p=!0,collapsedHeight:c=q,file:C}){const g=t||B(u)||"text";return e.createElement(v.Provider,{value:!0},e.createElement(m,{className:"docs-code-block",mt:"6",mb:"8"},e.createElement(f,{direction:"column",gap:"2"},a&&e.createElement(X,{background:s,backgroundProps:n},a),o&&e.createElement(Z,{code:o,language:g,showCopy:l,showLanguage:i,showLineNumbers:r,collapsible:p,collapsedHeight:c,file:C,shikiConfig:d}),u&&!o&&e.createElement(R,{showCopy:l,showLanguage:i,showLineNumbers:r,collapsible:p,collapsedHeight:c,file:C},u))))}function ce(){return A(v)}export{de as CodeBlock,ce as useCodeBlockContext};
|
|
2
2
|
//# sourceMappingURL=CodeBlock.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/code/CodeBlock.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from \"react\";\nimport { Box, Card, Flex, Button, Code, Theme, ScrollArea } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon, ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml } from \"shiki\";\nimport type { CodeBlockProps } from \"./types\";\n\nconst COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\n// ============================================\n// Preview Section\n// ============================================\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: {\n dotSize?: number;\n color?: string;\n backgroundColor?: string;\n height?: string;\n width?: string;\n radius?: string;\n };\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n if (background === \"none\") {\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\">\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n if (background === \"dots\") {\n const dotsStyle: React.CSSProperties = {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={dotsStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n const imageStyle: React.CSSProperties = {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={imageStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\n// ============================================\n// Code Section (for runtime highlighting)\n// ============================================\n\ninterface CodeSectionProps {\n code: string;\n language: string;\n showCopy?: boolean;\n showLanguage?: boolean;\n lightTheme?: string;\n darkTheme?: string;\n}\n\nconst CodeSection = memo(function CodeSection({\n code,\n language,\n showCopy = true,\n showLanguage = true,\n lightTheme = DEFAULT_LIGHT_THEME,\n darkTheme = DEFAULT_DARK_THEME,\n}: CodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n let cancelled = false;\n codeToHtml(code, {\n lang: language,\n themes: { light: lightTheme, dark: darkTheme },\n defaultColor: false,\n })\n .then((html) => {\n if (!cancelled) setHighlighted(html);\n })\n .catch(() => {\n if (!cancelled) setHighlighted(null);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language, lightTheme, darkTheme]);\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [highlighted]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const displayLanguage = language === \"text\" ? \"plaintext\" : language;\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <ScrollArea type=\"auto\" scrollbars=\"horizontal\">\n {highlighted ? (\n <Box dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre>\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\n </ScrollArea>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Children Code Section (for pre-highlighted MDX)\n// ============================================\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy?: boolean;\n showLanguage?: boolean;\n}\n\nfunction extractCodeFromChildren(children?: ReactNode): string {\n const extractText = (node: any): string => {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (!node) return \"\";\n if (Array.isArray(node)) return node.map(extractText).join(\"\");\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n if (props?.children) return extractText(props.children);\n }\n return \"\";\n };\n return extractText(children);\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n const findLanguage = (node: any): string | null => {\n if (!node) return null;\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n\n // Check data-language attribute (rehype-pretty-code)\n if (props?.[\"data-language\"]) {\n return props[\"data-language\"];\n }\n\n // Check className for language-xxx\n const className = props?.className || props?.class || \"\";\n if (typeof className === \"string\") {\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n\n // Recursively check children\n if (props?.children) {\n if (Array.isArray(props.children)) {\n for (const child of props.children) {\n const lang = findLanguage(child);\n if (lang) return lang;\n }\n } else {\n return findLanguage(props.children);\n }\n }\n }\n return null;\n };\n return findLanguage(children) || \"text\";\n}\n\nfunction formatLanguageLabel(lang: string): string {\n const aliasMap: Record<string, string> = {\n tsx: \"TSX\",\n ts: \"TS\",\n jsx: \"JSX\",\n js: \"JS\",\n javascript: \"JS\",\n typescript: \"TS\",\n css: \"CSS\",\n html: \"HTML\",\n json: \"JSON\",\n bash: \"SH\",\n sh: \"SH\",\n shell: \"SH\",\n text: \"plaintext\",\n };\n return aliasMap[lang.toLowerCase()] || lang.toLowerCase();\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCopy = true, showLanguage = true }: ChildrenCodeSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const code = extractCodeFromChildren(children);\n const language = extractLanguageFromChildren(children);\n const displayLanguage = formatLanguageLabel(language);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [children]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <ScrollArea type=\"auto\" scrollbars=\"horizontal\">\n {children}\n </ScrollArea>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Main CodeBlock Component\n// ============================================\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n lightTheme,\n darkTheme,\n background,\n backgroundProps,\n}: CodeBlockProps) {\n const hasCode = code || (children && React.Children.count(children) > 0);\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <CodeSection code={code} language={displayLanguage} showCopy={showCopy} showLanguage={showLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />\n )}\n\n {children && !code && (\n <ChildrenCodeSection showCopy={showCopy} showLanguage={showLanguage}>\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n );\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAOA,GAAS,YAAAC,EAAU,
|
|
6
|
-
"names": ["React", "useState", "
|
|
4
|
+
"sourcesContent": ["import React, { useState, useEffect, useMemo, memo, createContext, useContext, type ReactNode } from \"react\";\nimport { Box, Card, Code, Flex, Button, Text, Theme, ScrollArea, IconButton } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon, ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml, type BundledLanguage, type BundledTheme } from \"shiki\";\nimport type { CodeBlockProps, ShikiConfig, PreviewBackgroundProps } from \"./types\";\nimport { extractTextFromChildren, extractLanguageFromChildren } from \"./types\";\nimport { useCodeCard } from \"./useCodeCard\";\nimport { LanguageBadge } from \"./LanguageBadge\";\n\nconst CodeBlockContext = createContext<boolean>(false);\n\nconst DEFAULT_COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: PreviewBackgroundProps;\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n const backgroundStyle = useMemo((): React.CSSProperties | undefined => {\n if (background === \"none\") return undefined;\n\n if (background === \"dots\") {\n return {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n }\n\n // Image background\n return {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n }, [background, color, backgroundColor, dotSize, height, width, radius]);\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={backgroundStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\ninterface CodeCardProps {\n code: string;\n language: string;\n showCopy: boolean;\n showLanguage: boolean;\n showLineNumbers: boolean;\n collapsible: boolean;\n collapsedHeight: number;\n file?: string;\n isLoading?: boolean;\n children: ReactNode;\n}\n\nfunction CodeSkeleton() {\n // Generate varied line widths for visual interest\n const lineWidths = [\"85%\", \"70%\", \"90%\", \"60%\", \"75%\", \"80%\"];\n\n return (\n <Box className=\"code-skeleton\">\n {lineWidths.map((width, index) => (\n <Box key={index} className=\"code-skeleton-line\" style={{ width }} />\n ))}\n </Box>\n );\n}\n\nconst CodeCard = memo(function CodeCard({\n code,\n language,\n showCopy,\n showLanguage,\n showLineNumbers,\n collapsible,\n collapsedHeight,\n file,\n isLoading = false,\n children,\n}: CodeCardProps) {\n const { isExpanded, shouldShowToggle, copied, contentRef, contentMaxHeight, handleToggle, handleCopy } = useCodeCard({\n code,\n collapsedHeight,\n });\n\n const showToggle = collapsible && shouldShowToggle;\n const chevronRotation = isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\";\n const contentClassName = showLineNumbers ? \"code-content\" : \"code-content hide-line-numbers\";\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\">\n <Flex justify=\"between\" align=\"start\" gap=\"2\">\n <Flex align=\"center\" gap=\"2\">\n {showLanguage && <LanguageBadge language={language} />}\n {file && (\n <Text size=\"1\" color=\"gray\" highContrast>\n {file}\n </Text>\n )}\n </Flex>\n\n <Flex align=\"center\" className=\"code-action-buttons\">\n {showToggle && (\n <IconButton\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n highContrast\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={{ transform: chevronRotation }} className=\"code-chevron\" strokeWidth={1.75} />\n </IconButton>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n highContrast\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} strokeWidth={1.75} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={{ maxHeight: collapsible ? `${contentMaxHeight}px` : undefined }} className={contentClassName}>\n <ScrollArea type=\"auto\" scrollbars=\"horizontal\">\n {isLoading ? <CodeSkeleton /> : children}\n </ScrollArea>\n </Box>\n\n {showToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\ninterface RuntimeCodeSectionProps {\n code: string;\n language: string;\n showCopy: boolean;\n showLanguage: boolean;\n showLineNumbers: boolean;\n collapsible: boolean;\n collapsedHeight: number;\n file?: string;\n shikiConfig?: ShikiConfig;\n}\n\nconst RuntimeCodeSection = memo(function RuntimeCodeSection({\n code,\n language,\n showCopy,\n showLanguage,\n showLineNumbers,\n collapsible,\n collapsedHeight,\n file,\n shikiConfig,\n}: RuntimeCodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n\n // Memoize Shiki config to prevent unnecessary re-highlights\n const shikiOptions = useMemo(() => {\n const lightTheme = shikiConfig?.themes?.light || DEFAULT_LIGHT_THEME;\n const darkTheme = shikiConfig?.themes?.dark || DEFAULT_DARK_THEME;\n\n return {\n lang: language as BundledLanguage,\n themes: {\n light: lightTheme as BundledTheme,\n dark: darkTheme as BundledTheme,\n },\n defaultColor: false as const,\n langAlias: shikiConfig?.langAlias,\n transformers: shikiConfig?.transformers,\n meta: shikiConfig?.meta ? { __raw: shikiConfig.meta } : undefined,\n };\n }, [language, shikiConfig?.themes?.light, shikiConfig?.themes?.dark, shikiConfig?.langAlias, shikiConfig?.transformers, shikiConfig?.meta]);\n\n useEffect(() => {\n let cancelled = false;\n setIsLoading(true);\n\n codeToHtml(code, shikiOptions)\n .then((html) => {\n if (!cancelled) {\n setHighlighted(html);\n setIsLoading(false);\n }\n })\n .catch((error) => {\n if (!cancelled) {\n setHighlighted(null);\n setIsLoading(false);\n if (process.env.NODE_ENV === \"development\") {\n console.error(\"[CodeBlock] Shiki highlighting failed:\", error);\n }\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, shikiOptions]);\n\n return (\n <CodeCard\n code={code}\n language={language}\n showCopy={showCopy}\n showLanguage={showLanguage}\n showLineNumbers={showLineNumbers}\n collapsible={collapsible}\n collapsedHeight={collapsedHeight}\n file={file}\n isLoading={isLoading}\n >\n {highlighted ? <Box dangerouslySetInnerHTML={{ __html: highlighted }} /> : null}\n </CodeCard>\n );\n});\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy: boolean;\n showLanguage: boolean;\n showLineNumbers: boolean;\n collapsible: boolean;\n collapsedHeight: number;\n file?: string;\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({\n children,\n showCopy,\n showLanguage,\n showLineNumbers,\n collapsible,\n collapsedHeight,\n file,\n}: ChildrenCodeSectionProps) {\n const code = extractTextFromChildren(children);\n const language = extractLanguageFromChildren(children);\n\n return (\n <CodeCard\n code={code}\n language={language}\n showCopy={showCopy}\n showLanguage={showLanguage}\n showLineNumbers={showLineNumbers}\n collapsible={collapsible}\n collapsedHeight={collapsedHeight}\n file={file}\n >\n {children}\n </CodeCard>\n );\n});\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n showLineNumbers = true,\n shikiConfig,\n background,\n backgroundProps,\n collapsible = true,\n collapsedHeight = DEFAULT_COLLAPSED_HEIGHT,\n file,\n}: CodeBlockProps) {\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <CodeBlockContext.Provider value={true}>\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <RuntimeCodeSection\n code={code}\n language={displayLanguage}\n showCopy={showCopy}\n showLanguage={showLanguage}\n showLineNumbers={showLineNumbers}\n collapsible={collapsible}\n collapsedHeight={collapsedHeight}\n file={file}\n shikiConfig={shikiConfig}\n />\n )}\n\n {children && !code && (\n <ChildrenCodeSection\n showCopy={showCopy}\n showLanguage={showLanguage}\n showLineNumbers={showLineNumbers}\n collapsible={collapsible}\n collapsedHeight={collapsedHeight}\n file={file}\n >\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n </CodeBlockContext.Provider>\n );\n}\n\nexport function useCodeBlockContext() {\n return useContext(CodeBlockContext);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["React", "useState", "useEffect", "useMemo", "memo", "createContext", "useContext", "Box", "Card", "Flex", "Button", "Text", "Theme", "ScrollArea", "IconButton", "HugeiconsIcon", "Copy01Icon", "Tick01Icon", "ArrowDown01Icon", "codeToHtml", "extractTextFromChildren", "extractLanguageFromChildren", "useCodeCard", "LanguageBadge", "CodeBlockContext", "DEFAULT_COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "backgroundStyle", "CodeSkeleton", "index", "CodeCard", "code", "language", "showCopy", "showLanguage", "showLineNumbers", "collapsible", "collapsedHeight", "file", "isLoading", "isExpanded", "shouldShowToggle", "copied", "contentRef", "contentMaxHeight", "handleToggle", "handleCopy", "showToggle", "chevronRotation", "contentClassName", "RuntimeCodeSection", "shikiConfig", "highlighted", "setHighlighted", "setIsLoading", "shikiOptions", "lightTheme", "darkTheme", "cancelled", "html", "error", "ChildrenCodeSection", "CodeBlock", "preview", "displayLanguage", "useCodeBlockContext"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Formats a language identifier for display in the badge.
|
|
4
|
+
* Converts technical identifiers to human-readable labels.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatLanguageForDisplay(lang: string): string;
|
|
2
7
|
interface LanguageBadgeProps {
|
|
3
8
|
language: string;
|
|
4
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Displays the programming language as a small badge.
|
|
12
|
+
* Uses Kookie UI's Code component with consistent styling.
|
|
13
|
+
*/
|
|
5
14
|
export declare function LanguageBadge({ language }: LanguageBadgeProps): React.JSX.Element;
|
|
6
15
|
export {};
|
|
7
16
|
//# sourceMappingURL=LanguageBadge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageBadge.d.ts","sourceRoot":"","sources":["../../../../src/components/code/LanguageBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"LanguageBadge.d.ts","sourceRoot":"","sources":["../../../../src/components/code/LanguageBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAsD1B;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,kBAAkB,qBAQ7D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"react";import{Code as
|
|
1
|
+
import e from"react";import{Code as r}from"@kushagradhawan/kookie-ui";const a={js:"JS",javascript:"JS",jsx:"JSX",ts:"TS",typescript:"TS",tsx:"TSX",py:"Python",python:"Python",rb:"Ruby",ruby:"Ruby",sh:"Shell",bash:"Shell",shell:"Shell",zsh:"Shell",css:"CSS",scss:"SCSS",sass:"Sass",less:"Less",html:"HTML",xml:"XML",json:"JSON",yaml:"YAML",yml:"YAML",md:"Markdown",markdown:"Markdown",mdx:"MDX",sql:"SQL",graphql:"GraphQL",gql:"GraphQL",go:"Go",rust:"Rust",rs:"Rust",swift:"Swift",kotlin:"Kotlin",java:"Java",cpp:"C++",c:"C",cs:"C#",csharp:"C#",php:"PHP",vue:"Vue",svelte:"Svelte",astro:"Astro",dockerfile:"Docker",docker:"Docker",text:"Text",plaintext:"Text",txt:"Text"};function o(s){const t=s.toLowerCase().trim();return a[t]||s.toUpperCase()}function g({language:s}){const t=o(s);return e.createElement(r,{size:"1",color:"gray",highContrast:!0},t)}export{g as LanguageBadge,o as formatLanguageForDisplay};
|
|
2
2
|
//# sourceMappingURL=LanguageBadge.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/code/LanguageBadge.tsx"],
|
|
4
|
-
"sourcesContent": ["import React from \"react\";\nimport { Code } from \"@kushagradhawan/kookie-ui\";\n\
|
|
5
|
-
"mappings": "AAAA,OAAOA,MAAW,QAClB,OAAS,QAAAC,MAAY,
|
|
6
|
-
"names": ["React", "Code", "
|
|
4
|
+
"sourcesContent": ["import React from \"react\";\nimport { Code } from \"@kushagradhawan/kookie-ui\";\n\nconst LANGUAGE_DISPLAY_MAP: Record<string, string> = {\n js: \"JS\",\n javascript: \"JS\",\n jsx: \"JSX\",\n ts: \"TS\",\n typescript: \"TS\",\n tsx: \"TSX\",\n py: \"Python\",\n python: \"Python\",\n rb: \"Ruby\",\n ruby: \"Ruby\",\n sh: \"Shell\",\n bash: \"Shell\",\n shell: \"Shell\",\n zsh: \"Shell\",\n css: \"CSS\",\n scss: \"SCSS\",\n sass: \"Sass\",\n less: \"Less\",\n html: \"HTML\",\n xml: \"XML\",\n json: \"JSON\",\n yaml: \"YAML\",\n yml: \"YAML\",\n md: \"Markdown\",\n markdown: \"Markdown\",\n mdx: \"MDX\",\n sql: \"SQL\",\n graphql: \"GraphQL\",\n gql: \"GraphQL\",\n go: \"Go\",\n rust: \"Rust\",\n rs: \"Rust\",\n swift: \"Swift\",\n kotlin: \"Kotlin\",\n java: \"Java\",\n cpp: \"C++\",\n c: \"C\",\n cs: \"C#\",\n csharp: \"C#\",\n php: \"PHP\",\n vue: \"Vue\",\n svelte: \"Svelte\",\n astro: \"Astro\",\n dockerfile: \"Docker\",\n docker: \"Docker\",\n text: \"Text\",\n plaintext: \"Text\",\n txt: \"Text\",\n};\n\n/**\n * Formats a language identifier for display in the badge.\n * Converts technical identifiers to human-readable labels.\n */\nexport function formatLanguageForDisplay(lang: string): string {\n const normalized = lang.toLowerCase().trim();\n return LANGUAGE_DISPLAY_MAP[normalized] || lang.toUpperCase();\n}\n\ninterface LanguageBadgeProps {\n language: string;\n}\n\n/**\n * Displays the programming language as a small badge.\n * Uses Kookie UI's Code component with consistent styling.\n */\nexport function LanguageBadge({ language }: LanguageBadgeProps) {\n const displayLanguage = formatLanguageForDisplay(language);\n\n return (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n );\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAOA,MAAW,QAClB,OAAS,QAAAC,MAAY,4BAErB,MAAMC,EAA+C,CACnD,GAAI,KACJ,WAAY,KACZ,IAAK,MACL,GAAI,KACJ,WAAY,KACZ,IAAK,MACL,GAAI,SACJ,OAAQ,SACR,GAAI,OACJ,KAAM,OACN,GAAI,QACJ,KAAM,QACN,MAAO,QACP,IAAK,QACL,IAAK,MACL,KAAM,OACN,KAAM,OACN,KAAM,OACN,KAAM,OACN,IAAK,MACL,KAAM,OACN,KAAM,OACN,IAAK,OACL,GAAI,WACJ,SAAU,WACV,IAAK,MACL,IAAK,MACL,QAAS,UACT,IAAK,UACL,GAAI,KACJ,KAAM,OACN,GAAI,OACJ,MAAO,QACP,OAAQ,SACR,KAAM,OACN,IAAK,MACL,EAAG,IACH,GAAI,KACJ,OAAQ,KACR,IAAK,MACL,IAAK,MACL,OAAQ,SACR,MAAO,QACP,WAAY,SACZ,OAAQ,SACR,KAAM,OACN,UAAW,OACX,IAAK,MACP,EAMO,SAASC,EAAyBC,EAAsB,CAC7D,MAAMC,EAAaD,EAAK,YAAY,EAAE,KAAK,EAC3C,OAAOF,EAAqBG,CAAU,GAAKD,EAAK,YAAY,CAC9D,CAUO,SAASE,EAAc,CAAE,SAAAC,CAAS,EAAuB,CAC9D,MAAMC,EAAkBL,EAAyBI,CAAQ,EAEzD,OACEP,EAAA,cAACC,EAAA,CAAK,KAAK,IAAI,MAAM,OAAO,aAAY,IACrCO,CACH,CAEJ",
|
|
6
|
+
"names": ["React", "Code", "LANGUAGE_DISPLAY_MAP", "formatLanguageForDisplay", "lang", "normalized", "LanguageBadge", "language", "displayLanguage"]
|
|
7
7
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { CodeBlock } from "./CodeBlock";
|
|
2
|
-
export
|
|
1
|
+
export { CodeBlock, useCodeBlockContext } from "./CodeBlock";
|
|
2
|
+
export { useCodeCard } from "./useCodeCard";
|
|
3
|
+
export { LanguageBadge, formatLanguageForDisplay } from "./LanguageBadge";
|
|
4
|
+
export type { CodeBlockProps, ShikiConfig, PreviewBackgroundProps } from "./types";
|
|
5
|
+
export { extractTextFromChildren, extractLanguageFromChildren, isReactElement } from "./types";
|
|
3
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/code/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/code/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACnF,OAAO,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{CodeBlock as
|
|
1
|
+
import{CodeBlock as r,useCodeBlockContext as t}from"./CodeBlock";import{useCodeCard as p}from"./useCodeCard";import{LanguageBadge as g,formatLanguageForDisplay as m}from"./LanguageBadge";import{extractTextFromChildren as x,extractLanguageFromChildren as C,isReactElement as i}from"./types";export{r as CodeBlock,g as LanguageBadge,C as extractLanguageFromChildren,x as extractTextFromChildren,m as formatLanguageForDisplay,i as isReactElement,t as useCodeBlockContext,p as useCodeCard};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/code/index.ts"],
|
|
4
|
-
"sourcesContent": ["// Public API
|
|
5
|
-
"mappings": "AACA,OAAS,aAAAA,
|
|
6
|
-
"names": ["CodeBlock"]
|
|
4
|
+
"sourcesContent": ["// Public API\nexport { CodeBlock, useCodeBlockContext } from \"./CodeBlock\";\nexport { useCodeCard } from \"./useCodeCard\";\nexport { LanguageBadge, formatLanguageForDisplay } from \"./LanguageBadge\";\nexport type { CodeBlockProps, ShikiConfig, PreviewBackgroundProps } from \"./types\";\nexport { extractTextFromChildren, extractLanguageFromChildren, isReactElement } from \"./types\";\n"],
|
|
5
|
+
"mappings": "AACA,OAAS,aAAAA,EAAW,uBAAAC,MAA2B,cAC/C,OAAS,eAAAC,MAAmB,gBAC5B,OAAS,iBAAAC,EAAe,4BAAAC,MAAgC,kBAExD,OAAS,2BAAAC,EAAyB,+BAAAC,EAA6B,kBAAAC,MAAsB",
|
|
6
|
+
"names": ["CodeBlock", "useCodeBlockContext", "useCodeCard", "LanguageBadge", "formatLanguageForDisplay", "extractTextFromChildren", "extractLanguageFromChildren", "isReactElement"]
|
|
7
7
|
}
|
|
@@ -1,24 +1,143 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ReactNode, ReactElement } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Shiki syntax highlighting configuration.
|
|
4
|
+
* Controls themes and advanced highlighting options.
|
|
5
|
+
*/
|
|
6
|
+
export interface ShikiConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Theme names for light and dark modes.
|
|
9
|
+
* Uses Shiki's dual-theme mode for automatic theme switching.
|
|
10
|
+
* @default { light: 'one-light', dark: 'one-dark-pro' }
|
|
11
|
+
*/
|
|
12
|
+
themes?: {
|
|
13
|
+
light?: string;
|
|
14
|
+
dark?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Language aliases for custom language mapping.
|
|
18
|
+
* @example { js: 'javascript', ts: 'typescript' }
|
|
19
|
+
*/
|
|
20
|
+
langAlias?: Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Shiki transformers for custom code transformations.
|
|
23
|
+
* Useful for line highlighting, diff syntax, etc.
|
|
24
|
+
* @see https://shiki.style/guide/transformers
|
|
25
|
+
*/
|
|
26
|
+
transformers?: any[];
|
|
27
|
+
/**
|
|
28
|
+
* Metadata string passed to transformers.
|
|
29
|
+
* Often used for line highlighting syntax like `{1,3-5}`.
|
|
30
|
+
*/
|
|
31
|
+
meta?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Background configuration for preview section.
|
|
35
|
+
*/
|
|
36
|
+
export interface PreviewBackgroundProps {
|
|
37
|
+
/** Dot size in pixels (for dots background) */
|
|
38
|
+
dotSize?: number;
|
|
39
|
+
/** Dot color (CSS value) */
|
|
40
|
+
color?: string;
|
|
41
|
+
/** Background color (CSS value) */
|
|
42
|
+
backgroundColor?: string;
|
|
43
|
+
/** Preview section height */
|
|
44
|
+
height?: string;
|
|
45
|
+
/** Preview section width */
|
|
46
|
+
width?: string;
|
|
47
|
+
/** Border radius token (e.g., "3" for var(--radius-3)) */
|
|
48
|
+
radius?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* CodeBlock Component Props
|
|
52
|
+
*
|
|
53
|
+
* A unified code block component supporting:
|
|
54
|
+
* - Runtime syntax highlighting via Shiki (`code` + `language`)
|
|
55
|
+
* - Build-time highlighted content from MDX/rehype-pretty-code (`children`)
|
|
56
|
+
* - Optional preview section for documentation
|
|
57
|
+
*/
|
|
2
58
|
export interface CodeBlockProps {
|
|
3
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Raw code string for runtime Shiki highlighting.
|
|
61
|
+
* Mutually exclusive with `children`.
|
|
62
|
+
*/
|
|
4
63
|
code?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Language identifier for syntax highlighting.
|
|
66
|
+
* Required when using `code` prop.
|
|
67
|
+
* @example "typescript", "tsx", "python", "bash"
|
|
68
|
+
*/
|
|
5
69
|
language?: string;
|
|
6
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Pre-highlighted content from MDX/rehype-pretty-code.
|
|
72
|
+
* Mutually exclusive with `code` prop.
|
|
73
|
+
* Language is auto-detected from `data-language` or `className="language-xxx"`.
|
|
74
|
+
*/
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
/**
|
|
77
|
+
* Shiki syntax highlighting configuration.
|
|
78
|
+
* Only applies when using `code` prop (runtime highlighting).
|
|
79
|
+
*/
|
|
80
|
+
shikiConfig?: ShikiConfig;
|
|
81
|
+
/**
|
|
82
|
+
* Show copy-to-clipboard button.
|
|
83
|
+
* @default true
|
|
84
|
+
*/
|
|
7
85
|
showCopy?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Show language badge.
|
|
88
|
+
* @default true
|
|
89
|
+
*/
|
|
8
90
|
showLanguage?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Show line numbers.
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
showLineNumbers?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* File path displayed above the code block.
|
|
98
|
+
* @example "src/components/Button.tsx"
|
|
99
|
+
*/
|
|
9
100
|
file?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Enable expand/collapse for long code blocks.
|
|
103
|
+
* @default true
|
|
104
|
+
*/
|
|
10
105
|
collapsible?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Height threshold (in pixels) before code is collapsed.
|
|
108
|
+
* @default 360
|
|
109
|
+
*/
|
|
11
110
|
collapsedHeight?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Preview component displayed above the code block.
|
|
113
|
+
* Useful for showcasing component examples in documentation.
|
|
114
|
+
*/
|
|
115
|
+
preview?: ReactNode;
|
|
116
|
+
/**
|
|
117
|
+
* Background style for preview section.
|
|
118
|
+
* - `"none"`: Plain card background
|
|
119
|
+
* - `"dots"`: Dotted pattern background
|
|
120
|
+
* - `string`: Image URL for custom background
|
|
121
|
+
* @default "none"
|
|
122
|
+
*/
|
|
12
123
|
background?: "none" | "dots" | string;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
height?: string;
|
|
18
|
-
width?: string;
|
|
19
|
-
radius?: string;
|
|
20
|
-
};
|
|
21
|
-
lightTheme?: string;
|
|
22
|
-
darkTheme?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Customization for preview background.
|
|
126
|
+
*/
|
|
127
|
+
backgroundProps?: PreviewBackgroundProps;
|
|
23
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Type guard for React elements with props.
|
|
131
|
+
*/
|
|
132
|
+
export declare function isReactElement(node: unknown): node is ReactElement;
|
|
133
|
+
/**
|
|
134
|
+
* Extracts plain text content from React children.
|
|
135
|
+
* Used to get copyable code from pre-highlighted MDX content.
|
|
136
|
+
*/
|
|
137
|
+
export declare function extractTextFromChildren(children: ReactNode): string;
|
|
138
|
+
/**
|
|
139
|
+
* Extracts language identifier from pre-highlighted MDX children.
|
|
140
|
+
* Checks `data-language` attribute and `className="language-xxx"` pattern.
|
|
141
|
+
*/
|
|
142
|
+
export declare function extractLanguageFromChildren(children: ReactNode): string;
|
|
24
143
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/code/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/code/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IAEF;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEnC;;;;OAIG;IAEH,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAErB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAEtC;;OAEG;IACH,eAAe,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAElE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,SAAS,GAAG,MAAM,CAiBnE;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,SAAS,GAAG,MAAM,CAyCvE"}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
+
function s(r){return typeof r=="object"&&r!==null&&"props"in r&&typeof r.props=="object"}function g(r){if(typeof r=="string")return r;if(typeof r=="number")return String(r);if(!r)return"";if(Array.isArray(r))return r.map(g).join("");if(s(r)){const t=r.props;if(t.children)return g(t.children)}return""}function c(r){const t=o=>{if(!o)return null;if(s(o)){const e=o.props;if(e["data-language"])return e["data-language"];const i=e.className||e.class||"";if(typeof i=="string"){const n=i.match(/language-([\w-]+)/i);if(n)return n[1]}if(e.children)if(Array.isArray(e.children))for(const n of e.children){const a=t(n);if(a)return a}else return t(e.children)}return null};return t(r)||"text"}export{c as extractLanguageFromChildren,g as extractTextFromChildren,s as isReactElement};
|
|
1
2
|
//# sourceMappingURL=types.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": [],
|
|
4
|
-
"sourcesContent": [],
|
|
5
|
-
"mappings": "",
|
|
6
|
-
"names": []
|
|
3
|
+
"sources": ["../../../../src/components/code/types.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ReactNode, ReactElement } from \"react\";\n\n/**\n * Shiki syntax highlighting configuration.\n * Controls themes and advanced highlighting options.\n */\nexport interface ShikiConfig {\n /**\n * Theme names for light and dark modes.\n * Uses Shiki's dual-theme mode for automatic theme switching.\n * @default { light: 'one-light', dark: 'one-dark-pro' }\n */\n themes?: {\n light?: string;\n dark?: string;\n };\n\n /**\n * Language aliases for custom language mapping.\n * @example { js: 'javascript', ts: 'typescript' }\n */\n langAlias?: Record<string, string>;\n\n /**\n * Shiki transformers for custom code transformations.\n * Useful for line highlighting, diff syntax, etc.\n * @see https://shiki.style/guide/transformers\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transformers?: any[];\n\n /**\n * Metadata string passed to transformers.\n * Often used for line highlighting syntax like `{1,3-5}`.\n */\n meta?: string;\n}\n\n/**\n * Background configuration for preview section.\n */\nexport interface PreviewBackgroundProps {\n /** Dot size in pixels (for dots background) */\n dotSize?: number;\n /** Dot color (CSS value) */\n color?: string;\n /** Background color (CSS value) */\n backgroundColor?: string;\n /** Preview section height */\n height?: string;\n /** Preview section width */\n width?: string;\n /** Border radius token (e.g., \"3\" for var(--radius-3)) */\n radius?: string;\n}\n\n/**\n * CodeBlock Component Props\n *\n * A unified code block component supporting:\n * - Runtime syntax highlighting via Shiki (`code` + `language`)\n * - Build-time highlighted content from MDX/rehype-pretty-code (`children`)\n * - Optional preview section for documentation\n */\nexport interface CodeBlockProps {\n /**\n * Raw code string for runtime Shiki highlighting.\n * Mutually exclusive with `children`.\n */\n code?: string;\n\n /**\n * Language identifier for syntax highlighting.\n * Required when using `code` prop.\n * @example \"typescript\", \"tsx\", \"python\", \"bash\"\n */\n language?: string;\n\n /**\n * Pre-highlighted content from MDX/rehype-pretty-code.\n * Mutually exclusive with `code` prop.\n * Language is auto-detected from `data-language` or `className=\"language-xxx\"`.\n */\n children?: ReactNode;\n\n /**\n * Shiki syntax highlighting configuration.\n * Only applies when using `code` prop (runtime highlighting).\n */\n shikiConfig?: ShikiConfig;\n\n /**\n * Show copy-to-clipboard button.\n * @default true\n */\n showCopy?: boolean;\n\n /**\n * Show language badge.\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * Show line numbers.\n * @default true\n */\n showLineNumbers?: boolean;\n\n /**\n * File path displayed above the code block.\n * @example \"src/components/Button.tsx\"\n */\n file?: string;\n\n /**\n * Enable expand/collapse for long code blocks.\n * @default true\n */\n collapsible?: boolean;\n\n /**\n * Height threshold (in pixels) before code is collapsed.\n * @default 360\n */\n collapsedHeight?: number;\n\n /**\n * Preview component displayed above the code block.\n * Useful for showcasing component examples in documentation.\n */\n preview?: ReactNode;\n\n /**\n * Background style for preview section.\n * - `\"none\"`: Plain card background\n * - `\"dots\"`: Dotted pattern background\n * - `string`: Image URL for custom background\n * @default \"none\"\n */\n background?: \"none\" | \"dots\" | string;\n\n /**\n * Customization for preview background.\n */\n backgroundProps?: PreviewBackgroundProps;\n}\n\n/**\n * Type guard for React elements with props.\n */\nexport function isReactElement(node: unknown): node is ReactElement {\n return typeof node === \"object\" && node !== null && \"props\" in node && typeof (node as ReactElement).props === \"object\";\n}\n\n/**\n * Extracts plain text content from React children.\n * Used to get copyable code from pre-highlighted MDX content.\n */\nexport function extractTextFromChildren(children: ReactNode): string {\n if (typeof children === \"string\") return children;\n if (typeof children === \"number\") return String(children);\n if (!children) return \"\";\n\n if (Array.isArray(children)) {\n return children.map(extractTextFromChildren).join(\"\");\n }\n\n if (isReactElement(children)) {\n const props = children.props as { children?: ReactNode };\n if (props.children) {\n return extractTextFromChildren(props.children);\n }\n }\n\n return \"\";\n}\n\n/**\n * Extracts language identifier from pre-highlighted MDX children.\n * Checks `data-language` attribute and `className=\"language-xxx\"` pattern.\n */\nexport function extractLanguageFromChildren(children: ReactNode): string {\n const findLanguage = (node: ReactNode): string | null => {\n if (!node) return null;\n\n if (isReactElement(node)) {\n const props = node.props as {\n \"data-language\"?: string;\n className?: string;\n class?: string;\n children?: ReactNode;\n };\n\n // Check data-language attribute (rehype-pretty-code)\n if (props[\"data-language\"]) {\n return props[\"data-language\"];\n }\n\n // Check className for language-xxx pattern\n const className = props.className || props.class || \"\";\n if (typeof className === \"string\") {\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n\n // Recursively check children\n if (props.children) {\n if (Array.isArray(props.children)) {\n for (const child of props.children) {\n const lang = findLanguage(child);\n if (lang) return lang;\n }\n } else {\n return findLanguage(props.children);\n }\n }\n }\n\n return null;\n };\n\n return findLanguage(children) || \"text\";\n}\n"],
|
|
5
|
+
"mappings": "AAuJO,SAASA,EAAeC,EAAqC,CAClE,OAAO,OAAOA,GAAS,UAAYA,IAAS,MAAQ,UAAWA,GAAQ,OAAQA,EAAsB,OAAU,QACjH,CAMO,SAASC,EAAwBC,EAA6B,CACnE,GAAI,OAAOA,GAAa,SAAU,OAAOA,EACzC,GAAI,OAAOA,GAAa,SAAU,OAAO,OAAOA,CAAQ,EACxD,GAAI,CAACA,EAAU,MAAO,GAEtB,GAAI,MAAM,QAAQA,CAAQ,EACxB,OAAOA,EAAS,IAAID,CAAuB,EAAE,KAAK,EAAE,EAGtD,GAAIF,EAAeG,CAAQ,EAAG,CAC5B,MAAMC,EAAQD,EAAS,MACvB,GAAIC,EAAM,SACR,OAAOF,EAAwBE,EAAM,QAAQ,CAEjD,CAEA,MAAO,EACT,CAMO,SAASC,EAA4BF,EAA6B,CACvE,MAAMG,EAAgBL,GAAmC,CACvD,GAAI,CAACA,EAAM,OAAO,KAElB,GAAID,EAAeC,CAAI,EAAG,CACxB,MAAMG,EAAQH,EAAK,MAQnB,GAAIG,EAAM,eAAe,EACvB,OAAOA,EAAM,eAAe,EAI9B,MAAMG,EAAYH,EAAM,WAAaA,EAAM,OAAS,GACpD,GAAI,OAAOG,GAAc,SAAU,CACjC,MAAMC,EAAQD,EAAU,MAAM,oBAAoB,EAClD,GAAIC,EAAO,OAAOA,EAAM,CAAC,CAC3B,CAGA,GAAIJ,EAAM,SACR,GAAI,MAAM,QAAQA,EAAM,QAAQ,EAC9B,UAAWK,KAASL,EAAM,SAAU,CAClC,MAAMM,EAAOJ,EAAaG,CAAK,EAC/B,GAAIC,EAAM,OAAOA,CACnB,KAEA,QAAOJ,EAAaF,EAAM,QAAQ,CAGxC,CAEA,OAAO,IACT,EAEA,OAAOE,EAAaH,CAAQ,GAAK,MACnC",
|
|
6
|
+
"names": ["isReactElement", "node", "extractTextFromChildren", "children", "props", "extractLanguageFromChildren", "findLanguage", "className", "match", "child", "lang"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface UseCodeCardOptions {
|
|
2
|
+
code: string;
|
|
3
|
+
collapsedHeight: number;
|
|
4
|
+
}
|
|
5
|
+
interface UseCodeCardReturn {
|
|
6
|
+
isExpanded: boolean;
|
|
7
|
+
shouldShowToggle: boolean;
|
|
8
|
+
copied: boolean;
|
|
9
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
10
|
+
contentMaxHeight: number;
|
|
11
|
+
handleToggle: () => void;
|
|
12
|
+
handleCopy: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Shared hook for code card UI behavior.
|
|
16
|
+
* Handles expand/collapse state and copy functionality.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useCodeCard({ code, collapsedHeight }: UseCodeCardOptions): UseCodeCardReturn;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=useCodeCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCodeCard.d.ts","sourceRoot":"","sources":["../../../../src/components/code/useCodeCard.ts"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,iBAAiB;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACnD,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,kBAAkB,GAAG,iBAAiB,CA0D5F"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useState as r,useRef as a,useEffect as u,useCallback as d}from"react";const E=2e3;function b({code:t,collapsedHeight:o}){const[c,f]=r(!1),[s,p]=r(o),[C,i]=r(!1),n=a(null),e=a(null),g=s>o,m=c?s:o;u(()=>{n.current&&p(n.current.scrollHeight)}),u(()=>()=>{e.current&&clearTimeout(e.current)},[]);const T=d(()=>{f(l=>!l)},[]),h=d(async()=>{if(t.trim())try{await navigator.clipboard.writeText(t),i(!0),e.current&&clearTimeout(e.current),e.current=setTimeout(()=>i(!1),E)}catch{}},[t]);return{isExpanded:c,shouldShowToggle:g,copied:C,contentRef:n,contentMaxHeight:m,handleToggle:T,handleCopy:h}}export{b as useCodeCard};
|
|
2
|
+
//# sourceMappingURL=useCodeCard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/components/code/useCodeCard.ts"],
|
|
4
|
+
"sourcesContent": ["import { useState, useRef, useEffect, useCallback } from \"react\";\n\nconst COPY_FEEDBACK_DURATION_MS = 2000;\n\ninterface UseCodeCardOptions {\n code: string;\n collapsedHeight: number;\n}\n\ninterface UseCodeCardReturn {\n isExpanded: boolean;\n shouldShowToggle: boolean;\n copied: boolean;\n contentRef: React.RefObject<HTMLDivElement | null>;\n contentMaxHeight: number;\n handleToggle: () => void;\n handleCopy: () => Promise<void>;\n}\n\n/**\n * Shared hook for code card UI behavior.\n * Handles expand/collapse state and copy functionality.\n */\nexport function useCodeCard({ code, collapsedHeight }: UseCodeCardOptions): UseCodeCardReturn {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(collapsedHeight);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement | null>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > collapsedHeight;\n const contentMaxHeight = isExpanded ? contentHeight : collapsedHeight;\n\n // Measure content height on mount and when content changes\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n });\n\n // Cleanup timeout on unmount\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) {\n clearTimeout(resetTimeoutRef.current);\n }\n };\n }, []);\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n\n if (resetTimeoutRef.current) {\n clearTimeout(resetTimeoutRef.current);\n }\n resetTimeoutRef.current = setTimeout(() => setCopied(false), COPY_FEEDBACK_DURATION_MS);\n } catch (error) {\n // Log error in development, fail gracefully in production\n if (process.env.NODE_ENV === \"development\") {\n console.error(\"[CodeBlock] Failed to copy to clipboard:\", error);\n }\n }\n }, [code]);\n\n return {\n isExpanded,\n shouldShowToggle,\n copied,\n contentRef,\n contentMaxHeight,\n handleToggle,\n handleCopy,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAS,YAAAA,EAAU,UAAAC,EAAQ,aAAAC,EAAW,eAAAC,MAAmB,QAEzD,MAAMC,EAA4B,IAqB3B,SAASC,EAAY,CAAE,KAAAC,EAAM,gBAAAC,CAAgB,EAA0C,CAC5F,KAAM,CAACC,EAAYC,CAAa,EAAIT,EAAS,EAAK,EAC5C,CAACU,EAAeC,CAAgB,EAAIX,EAASO,CAAe,EAC5D,CAACK,EAAQC,CAAS,EAAIb,EAAS,EAAK,EACpCc,EAAab,EAA8B,IAAI,EAC/Cc,EAAkBd,EAA6C,IAAI,EAEnEe,EAAmBN,EAAgBH,EACnCU,EAAmBT,EAAaE,EAAgBH,EAGtDL,EAAU,IAAM,CACVY,EAAW,SACbH,EAAiBG,EAAW,QAAQ,YAAY,CAEpD,CAAC,EAGDZ,EAAU,IACD,IAAM,CACPa,EAAgB,SAClB,aAAaA,EAAgB,OAAO,CAExC,EACC,CAAC,CAAC,EAEL,MAAMG,EAAef,EAAY,IAAM,CACrCM,EAAeU,GAAS,CAACA,CAAI,CAC/B,EAAG,CAAC,CAAC,EAECC,EAAajB,EAAY,SAAY,CACzC,GAAKG,EAAK,KAAK,EAEf,GAAI,CACF,MAAM,UAAU,UAAU,UAAUA,CAAI,EACxCO,EAAU,EAAI,EAEVE,EAAgB,SAClB,aAAaA,EAAgB,OAAO,EAEtCA,EAAgB,QAAU,WAAW,IAAMF,EAAU,EAAK,EAAGT,CAAyB,CACxF,MAAgB,CAKhB,CACF,EAAG,CAACE,CAAI,CAAC,EAET,MAAO,CACL,WAAAE,EACA,iBAAAQ,EACA,OAAAJ,EACA,WAAAE,EACA,iBAAAG,EACA,aAAAC,EACA,WAAAE,CACF,CACF",
|
|
6
|
+
"names": ["useState", "useRef", "useEffect", "useCallback", "COPY_FEEDBACK_DURATION_MS", "useCodeCard", "code", "collapsedHeight", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "contentMaxHeight", "handleToggle", "prev", "handleCopy"]
|
|
7
|
+
}
|