@kushagradhawan/kookie-blocks 0.1.5 → 0.1.7
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 +4 -67
- 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/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/package.json +1 -1
- package/src/components/code/CodeBlock.tsx +96 -34
- package/src/components/index.css +4 -63
- package/styles.css +4 -59
package/components.css
CHANGED
|
@@ -18,74 +18,17 @@
|
|
|
18
18
|
position: relative;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/* Code block content (the pre part, below the header) */
|
|
22
|
-
|
|
23
|
-
.code-block-content {
|
|
24
|
-
width: 100%;
|
|
25
|
-
min-width: 0;
|
|
26
|
-
overflow-x: auto;
|
|
27
|
-
/* Hide scrollbars while keeping scroll functionality */
|
|
28
|
-
scrollbar-width: none; /* Firefox */
|
|
29
|
-
-ms-overflow-style: none; /* IE/Edge */
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/* Hide scrollbars for webkit browsers */
|
|
33
|
-
|
|
34
|
-
.code-block-content::-webkit-scrollbar,
|
|
35
|
-
.code-block-content::-webkit-scrollbar-horizontal,
|
|
36
|
-
.code-block-content::-webkit-scrollbar-vertical,
|
|
37
|
-
.code-block-content::-webkit-scrollbar-track,
|
|
38
|
-
.code-block-content::-webkit-scrollbar-thumb,
|
|
39
|
-
.code-block-content::-webkit-scrollbar-corner {
|
|
40
|
-
display: none !important;
|
|
41
|
-
width: 0 !important;
|
|
42
|
-
height: 0 !important;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/* Also hide scrollbars on any pre/code elements inside code content */
|
|
46
|
-
|
|
47
|
-
.code-block-content pre::-webkit-scrollbar,
|
|
48
|
-
.code-block-content pre::-webkit-scrollbar-horizontal,
|
|
49
|
-
.code-block-content pre::-webkit-scrollbar-vertical,
|
|
50
|
-
.code-block-content pre::-webkit-scrollbar-track,
|
|
51
|
-
.code-block-content pre::-webkit-scrollbar-thumb,
|
|
52
|
-
.code-block-content pre::-webkit-scrollbar-corner,
|
|
53
|
-
.code-block-content code::-webkit-scrollbar,
|
|
54
|
-
.code-block-content code::-webkit-scrollbar-horizontal,
|
|
55
|
-
.code-block-content code::-webkit-scrollbar-vertical,
|
|
56
|
-
.code-block-content code::-webkit-scrollbar-track,
|
|
57
|
-
.code-block-content code::-webkit-scrollbar-thumb,
|
|
58
|
-
.code-block-content code::-webkit-scrollbar-corner {
|
|
59
|
-
display: none !important;
|
|
60
|
-
width: 0 !important;
|
|
61
|
-
height: 0 !important;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
21
|
/* Pre elements inside code content */
|
|
65
22
|
|
|
66
|
-
.code-content pre
|
|
67
|
-
.code-block-content pre {
|
|
68
|
-
overflow-x: visible;
|
|
69
|
-
max-width: 100%;
|
|
70
|
-
min-width: 0;
|
|
23
|
+
.code-content pre {
|
|
71
24
|
margin: 0;
|
|
72
25
|
width: 100%;
|
|
73
26
|
box-sizing: border-box;
|
|
74
|
-
scrollbar-width: none;
|
|
75
|
-
-ms-overflow-style: none;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/* Ensure Shiki-generated pre elements fill width */
|
|
79
|
-
|
|
80
|
-
.code-block-content > pre {
|
|
81
|
-
width: 100% !important;
|
|
82
|
-
min-width: 0;
|
|
83
27
|
}
|
|
84
28
|
|
|
85
29
|
/* Code elements inside pre */
|
|
86
30
|
|
|
87
|
-
.code-content pre code
|
|
88
|
-
.code-block-content pre code {
|
|
31
|
+
.code-content pre code {
|
|
89
32
|
font-family: var(--font-mono);
|
|
90
33
|
font-size: var(--font-size-2);
|
|
91
34
|
line-height: 1.75;
|
|
@@ -98,8 +41,7 @@
|
|
|
98
41
|
|
|
99
42
|
/* Shiki line spans */
|
|
100
43
|
|
|
101
|
-
.code-content pre code .line
|
|
102
|
-
.code-block-content pre code .line {
|
|
44
|
+
.code-content pre code .line {
|
|
103
45
|
display: flex;
|
|
104
46
|
align-items: center;
|
|
105
47
|
gap: 0;
|
|
@@ -107,8 +49,7 @@
|
|
|
107
49
|
|
|
108
50
|
/* Line numbers */
|
|
109
51
|
|
|
110
|
-
.code-content pre code .line::before
|
|
111
|
-
.code-block-content pre code .line::before {
|
|
52
|
+
.code-content pre code .line::before {
|
|
112
53
|
counter-increment: line;
|
|
113
54
|
content: counter(line);
|
|
114
55
|
display: inline-block;
|
|
@@ -156,7 +97,6 @@
|
|
|
156
97
|
|
|
157
98
|
/* Default to light theme for all tokens (codeToHtml with defaultColor: false) */
|
|
158
99
|
|
|
159
|
-
.code-block-content pre.shiki span,
|
|
160
100
|
.code-content pre.shiki span {
|
|
161
101
|
color: var(--shiki-light) !important;
|
|
162
102
|
font-style: var(--shiki-light-font-style);
|
|
@@ -167,9 +107,6 @@
|
|
|
167
107
|
|
|
168
108
|
/* Override with dark theme colors when inside a dark context */
|
|
169
109
|
|
|
170
|
-
.dark .code-block-content pre.shiki span,
|
|
171
|
-
.dark-theme .code-block-content pre.shiki span,
|
|
172
|
-
[data-appearance="dark"] .code-block-content pre.shiki span,
|
|
173
110
|
.dark .code-content pre.shiki span,
|
|
174
111
|
.dark-theme .code-content pre.shiki span,
|
|
175
112
|
[data-appearance="dark"] .code-content pre.shiki span {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAiZ9C,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAe,EACf,YAAmB,EACnB,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,GAChB,EAAE,cAAc,qBAyBhB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var B=Object.create;var w=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,$=Object.prototype.hasOwnProperty;var M=(n,o)=>{for(var r in o)w(n,r,{get:o[r],enumerable:!0})},L=(n,o,r,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of A(o))!$.call(n,s)&&s!==r&&w(n,s,{get:()=>o[s],enumerable:!(i=j(o,s))||i.enumerable});return n};var D=(n,o,r)=>(r=n!=null?B(I(n)):{},L(o||!n||!n.__esModule?w(r,"default",{value:n,enumerable:!0}):r,n)),_=n=>L(w({},"__esModule",{value:!0}),n);var V={};M(V,{CodeBlock:()=>Q});module.exports=_(V);var e=D(require("react")),t=require("@kushagradhawan/kookie-ui"),b=require("@hugeicons/react"),d=require("@hugeicons/core-free-icons"),z=require("shiki");const y=360,J="one-light",G="one-dark-pro";function O({children:n,background:o="none",backgroundProps:r={}}){const{dotSize:i=24,color:s="var(--gray-10)",backgroundColor:l="var(--gray-2)",height:a,width:g="100%",radius:u="3"}=r;if(o==="none")return e.default.createElement(t.Card,{size:"1",variant:"soft"},e.default.createElement(t.Flex,{justify:"center",align:"center",py:"4"},e.default.createElement(t.Theme,{fontFamily:"sans"},n)));if(o==="dots"){const f={backgroundImage:`radial-gradient(circle, ${s} 1px, transparent 1px)`,borderRadius:`var(--radius-${u})`,backgroundSize:`${i}px ${i}px`,backgroundPosition:"center",backgroundColor:l,width:g,...a&&{height:a}};return e.default.createElement(t.Card,{size:"1",variant:"soft"},e.default.createElement(t.Flex,{justify:"center",align:"center",py:"4",style:f},e.default.createElement(t.Theme,{fontFamily:"sans"},n)))}const c={backgroundImage:`url(${o})`,backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat",borderRadius:`var(--radius-${u})`,width:g,...a&&{height:a}};return e.default.createElement(t.Card,{size:"1",variant:"soft"},e.default.createElement(t.Flex,{justify:"center",align:"center",py:"4",style:c},e.default.createElement(t.Theme,{fontFamily:"sans"},n)))}const U=(0,e.memo)(function({code:o,language:r,showCopy:i=!0,showLanguage:s=!0,lightTheme:l=J,darkTheme:a=G}){const[g,u]=(0,e.useState)(null),[c,f]=(0,e.useState)(!1),[p,m]=(0,e.useState)(y),[S,T]=(0,e.useState)(!1),C=(0,e.useRef)(null),h=(0,e.useRef)(null),v=p>y;(0,e.useEffect)(()=>{let x=!1;return(0,z.codeToHtml)(o,{lang:r,themes:{light:l,dark:a},defaultColor:!1}).then(N=>{x||u(N)}).catch(()=>{x||u(null)}),()=>{x=!0}},[o,r,l,a]),(0,e.useEffect)(()=>{C.current&&m(C.current.scrollHeight)},[g]),(0,e.useEffect)(()=>()=>{h.current&&clearTimeout(h.current)},[]);const k=(0,e.useCallback)(async()=>{if(o.trim())try{await navigator.clipboard.writeText(o),T(!0),h.current&&clearTimeout(h.current),h.current=setTimeout(()=>T(!1),2e3)}catch{}},[o]),P=r==="text"?"plaintext":r,H=(0,e.useCallback)(()=>{f(x=>!x)},[]),F={maxHeight:c?`${p}px`:`${y}px`};return e.default.createElement(t.Box,{position:"relative"},e.default.createElement(t.Card,{size:"1",variant:"soft"},e.default.createElement(t.Flex,{direction:"column",gap:"3"},e.default.createElement(t.Flex,{gap:"2",justify:"between",align:"start"},s&&e.default.createElement(t.Code,{size:"1",color:"gray",highContrast:!0},P),e.default.createElement(t.Flex,{align:"center",gap:"2",className:"code-action-buttons"},v&&e.default.createElement(t.Button,{size:"2",variant:"ghost",color:"gray",onClick:H,tooltip:c?"Collapse":"Expand","aria-label":c?"Collapse code":"Expand code"},e.default.createElement(b.HugeiconsIcon,{icon:d.ArrowDown01Icon,style:{transform:c?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),i&&e.default.createElement(t.Button,{size:"2",variant:"ghost",color:"gray",onClick:k,tooltip:S?"Copied!":"Copy","aria-label":S?"Copied!":"Copy code"},e.default.createElement(b.HugeiconsIcon,{icon:S?d.Tick01Icon:d.Copy01Icon})," Copy"))),e.default.createElement(t.Box,{ref:C,style:F,className:"code-content"},e.default.createElement(t.ScrollArea,{type:"auto",scrollbars:"horizontal"},g?e.default.createElement(t.Box,{dangerouslySetInnerHTML:{__html:g}}):e.default.createElement("pre",null,e.default.createElement(t.Code,{size:"3"},o)))),v&&!c&&e.default.createElement(t.Box,{className:"code-scroll-shadow visible"}))))});function X(n){const o=r=>{if(typeof r=="string")return r;if(typeof r=="number")return String(r);if(!r)return"";if(Array.isArray(r))return r.map(o).join("");if(typeof r=="object"&&"props"in r){const i=r.props;if(i?.children)return o(i.children)}return""};return o(n)}function E(n){const o=r=>{if(!r)return null;if(typeof r=="object"&&"props"in r){const i=r.props;if(i?.["data-language"])return i["data-language"];const s=i?.className||i?.class||"";if(typeof s=="string"){const l=s.match(/language-([\w-]+)/i);if(l)return l[1]}if(i?.children)if(Array.isArray(i.children))for(const l of i.children){const a=o(l);if(a)return a}else return o(i.children)}return null};return o(n)||"text"}function K(n){return{tsx:"TSX",ts:"TS",jsx:"JSX",js:"JS",javascript:"JS",typescript:"TS",css:"CSS",html:"HTML",json:"JSON",bash:"SH",sh:"SH",shell:"SH",text:"plaintext"}[n.toLowerCase()]||n.toLowerCase()}const q=(0,e.memo)(function({children:o,showCopy:r=!0,showLanguage:i=!0}){const[s,l]=(0,e.useState)(!1),[a,g]=(0,e.useState)(y),[u,c]=(0,e.useState)(!1),f=(0,e.useRef)(null),p=(0,e.useRef)(null),m=X(o),S=E(o),T=K(S),C=a>y;(0,e.useEffect)(()=>{f.current&&g(f.current.scrollHeight)},[o]),(0,e.useEffect)(()=>()=>{p.current&&clearTimeout(p.current)},[]);const h=(0,e.useCallback)(async()=>{if(m.trim())try{await navigator.clipboard.writeText(m),c(!0),p.current&&clearTimeout(p.current),p.current=setTimeout(()=>c(!1),2e3)}catch{}},[m]),v={maxHeight:s?`${a}px`:`${y}px`},k=(0,e.useCallback)(()=>{l(H=>!H)},[]);return e.default.createElement(t.Box,{position:"relative"},e.default.createElement(t.Card,{size:"1",variant:"soft"},e.default.createElement(t.Flex,{direction:"column",gap:"3"},e.default.createElement(t.Flex,{gap:"2",justify:"between",align:"start"},i&&e.default.createElement(t.Code,{size:"1",color:"gray",highContrast:!0},T),e.default.createElement(t.Flex,{align:"center",gap:"2",className:"code-action-buttons"},C&&e.default.createElement(t.Button,{size:"2",variant:"ghost",color:"gray",onClick:k,tooltip:s?"Collapse":"Expand","aria-label":s?"Collapse code":"Expand code"},e.default.createElement(b.HugeiconsIcon,{icon:d.ArrowDown01Icon,style:{transform:s?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),r&&e.default.createElement(t.Button,{size:"2",variant:"ghost",color:"gray",onClick:h,tooltip:u?"Copied!":"Copy","aria-label":u?"Copied!":"Copy code"},e.default.createElement(b.HugeiconsIcon,{icon:u?d.Tick01Icon:d.Copy01Icon})," Copy"))),e.default.createElement(t.Box,{ref:f,style:v,className:"code-content"},e.default.createElement(t.ScrollArea,{type:"auto",scrollbars:"horizontal"},o)),C&&!s&&e.default.createElement(t.Box,{className:"code-scroll-shadow visible"}))))});function Q({children:n,code:o,language:r,preview:i,showCopy:s=!0,showLanguage:l=!0,lightTheme:a,darkTheme:g,background:u,backgroundProps:c}){const f=o||n&&e.default.Children.count(n)>0,p=r||E(n)||"text";return e.default.createElement(t.Box,{className:"docs-code-block",mt:"6",mb:"8"},e.default.createElement(t.Flex,{direction:"column",gap:"2"},i&&e.default.createElement(O,{background:u,backgroundProps:c},i),o&&e.default.createElement(U,{code:o,language:p,showCopy:s,showLanguage:l,lightTheme:a,darkTheme:g}),n&&!o&&e.default.createElement(q,{showCopy:s,showLanguage:l},n)))}
|
|
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 } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon } 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 contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\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 {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 {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\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 if (!children) return \"\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const codeChildren = codeProps?.children;\n if (typeof codeChildren === \"string\") return codeChildren;\n }\n if (typeof childProps?.children === \"string\") return childProps.children;\n }\n if (typeof children === \"string\") return children;\n return \"\";\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n if (!children) return \"text\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const className = codeProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n const className = childProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n return \"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 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 {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 <div className=\"code-block-content\">{children}</div>\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": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAsF,oBACtFC,
|
|
6
|
-
"names": ["CodeBlock_exports", "__export", "CodeBlock", "__toCommonJS", "import_react", "import_kookie_ui", "import_core_free_icons", "import_shiki", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "React", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "contentStyle", "extractCodeFromChildren", "
|
|
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": "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",
|
|
6
|
+
"names": ["CodeBlock_exports", "__export", "CodeBlock", "__toCommonJS", "import_react", "import_kookie_ui", "import_core_free_icons", "import_shiki", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "React", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "handleToggle", "prev", "contentStyle", "extractCodeFromChildren", "extractText", "node", "props", "extractLanguageFromChildren", "findLanguage", "className", "match", "child", "lang", "formatLanguageLabel", "ChildrenCodeSection", "preview", "hasCode"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAiZ9C,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAe,EACf,YAAmB,EACnB,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,GAChB,EAAE,cAAc,qBAyBhB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as C,useRef as
|
|
1
|
+
import e,{useState as C,useRef as H,useEffect as T,useCallback as P,memo as A}from"react";import{Box as f,Card as v,Flex as p,Button as L,Code as N,Theme as B,ScrollArea as I}from"@kushagradhawan/kookie-ui";import{HugeiconsIcon as E}from"@hugeicons/react";import{Copy01Icon as $,Tick01Icon as M,ArrowDown01Icon as D}from"@hugeicons/core-free-icons";import{codeToHtml as O}from"shiki";const m=360,U="one-light",X="one-dark-pro";function K({children:n,background:t="none",backgroundProps:o={}}){const{dotSize:r=24,color:i="var(--gray-10)",backgroundColor:a="var(--gray-2)",height:s,width:u="100%",radius:c="3"}=o;if(t==="none")return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4"},e.createElement(B,{fontFamily:"sans"},n)));if(t==="dots"){const d={backgroundImage:`radial-gradient(circle, ${i} 1px, transparent 1px)`,borderRadius:`var(--radius-${c})`,backgroundSize:`${r}px ${r}px`,backgroundPosition:"center",backgroundColor:a,width:u,...s&&{height:s}};return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4",style:d},e.createElement(B,{fontFamily:"sans"},n)))}const l={backgroundImage:`url(${t})`,backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat",borderRadius:`var(--radius-${c})`,width:u,...s&&{height:s}};return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4",style:l},e.createElement(B,{fontFamily:"sans"},n)))}const q=A(function({code:t,language:o,showCopy:r=!0,showLanguage:i=!0,lightTheme:a=U,darkTheme:s=X}){const[u,c]=C(null),[l,d]=C(!1),[g,S]=C(m),[x,w]=C(!1),y=H(null),h=H(null),k=g>m;T(()=>{let b=!1;return O(t,{lang:o,themes:{light:a,dark:s},defaultColor:!1}).then(G=>{b||c(G)}).catch(()=>{b||c(null)}),()=>{b=!0}},[t,o,a,s]),T(()=>{y.current&&S(y.current.scrollHeight)},[u]),T(()=>()=>{h.current&&clearTimeout(h.current)},[]);const z=P(async()=>{if(t.trim())try{await navigator.clipboard.writeText(t),w(!0),h.current&&clearTimeout(h.current),h.current=setTimeout(()=>w(!1),2e3)}catch{}},[t]),j=o==="text"?"plaintext":o,F=P(()=>{d(b=>!b)},[]),J={maxHeight:l?`${g}px`:`${m}px`};return e.createElement(f,{position:"relative"},e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{direction:"column",gap:"3"},e.createElement(p,{gap:"2",justify:"between",align:"start"},i&&e.createElement(N,{size:"1",color:"gray",highContrast:!0},j),e.createElement(p,{align:"center",gap:"2",className:"code-action-buttons"},k&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:F,tooltip:l?"Collapse":"Expand","aria-label":l?"Collapse code":"Expand code"},e.createElement(E,{icon:D,style:{transform:l?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),r&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:z,tooltip:x?"Copied!":"Copy","aria-label":x?"Copied!":"Copy code"},e.createElement(E,{icon:x?M:$})," Copy"))),e.createElement(f,{ref:y,style:J,className:"code-content"},e.createElement(I,{type:"auto",scrollbars:"horizontal"},u?e.createElement(f,{dangerouslySetInnerHTML:{__html:u}}):e.createElement("pre",null,e.createElement(N,{size:"3"},t)))),k&&!l&&e.createElement(f,{className:"code-scroll-shadow visible"}))))});function Q(n){const t=o=>{if(typeof o=="string")return o;if(typeof o=="number")return String(o);if(!o)return"";if(Array.isArray(o))return o.map(t).join("");if(typeof o=="object"&&"props"in o){const r=o.props;if(r?.children)return t(r.children)}return""};return t(n)}function _(n){const t=o=>{if(!o)return null;if(typeof o=="object"&&"props"in o){const r=o.props;if(r?.["data-language"])return r["data-language"];const i=r?.className||r?.class||"";if(typeof i=="string"){const a=i.match(/language-([\w-]+)/i);if(a)return a[1]}if(r?.children)if(Array.isArray(r.children))for(const a of r.children){const s=t(a);if(s)return s}else return t(r.children)}return null};return t(n)||"text"}function V(n){return{tsx:"TSX",ts:"TS",jsx:"JSX",js:"JS",javascript:"JS",typescript:"TS",css:"CSS",html:"HTML",json:"JSON",bash:"SH",sh:"SH",shell:"SH",text:"plaintext"}[n.toLowerCase()]||n.toLowerCase()}const W=A(function({children:t,showCopy:o=!0,showLanguage:r=!0}){const[i,a]=C(!1),[s,u]=C(m),[c,l]=C(!1),d=H(null),g=H(null),S=Q(t),x=_(t),w=V(x),y=s>m;T(()=>{d.current&&u(d.current.scrollHeight)},[t]),T(()=>()=>{g.current&&clearTimeout(g.current)},[]);const h=P(async()=>{if(S.trim())try{await navigator.clipboard.writeText(S),l(!0),g.current&&clearTimeout(g.current),g.current=setTimeout(()=>l(!1),2e3)}catch{}},[S]),k={maxHeight:i?`${s}px`:`${m}px`},z=P(()=>{a(F=>!F)},[]);return e.createElement(f,{position:"relative"},e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{direction:"column",gap:"3"},e.createElement(p,{gap:"2",justify:"between",align:"start"},r&&e.createElement(N,{size:"1",color:"gray",highContrast:!0},w),e.createElement(p,{align:"center",gap:"2",className:"code-action-buttons"},y&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:z,tooltip:i?"Collapse":"Expand","aria-label":i?"Collapse code":"Expand code"},e.createElement(E,{icon:D,style:{transform:i?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),o&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:h,tooltip:c?"Copied!":"Copy","aria-label":c?"Copied!":"Copy code"},e.createElement(E,{icon:c?M:$})," Copy"))),e.createElement(f,{ref:d,style:k,className:"code-content"},e.createElement(I,{type:"auto",scrollbars:"horizontal"},t)),y&&!i&&e.createElement(f,{className:"code-scroll-shadow visible"}))))});function re({children:n,code:t,language:o,preview:r,showCopy:i=!0,showLanguage:a=!0,lightTheme:s,darkTheme:u,background:c,backgroundProps:l}){const d=t||n&&e.Children.count(n)>0,g=o||_(n)||"text";return e.createElement(f,{className:"docs-code-block",mt:"6",mb:"8"},e.createElement(p,{direction:"column",gap:"2"},r&&e.createElement(K,{background:c,backgroundProps:l},r),t&&e.createElement(q,{code:t,language:g,showCopy:i,showLanguage:a,lightTheme:s,darkTheme:u}),n&&!t&&e.createElement(W,{showCopy:i,showLanguage:a},n)))}export{re as CodeBlock};
|
|
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 } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon } 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 contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\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 {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 {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\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 if (!children) return \"\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const codeChildren = codeProps?.children;\n if (typeof codeChildren === \"string\") return codeChildren;\n }\n if (typeof childProps?.children === \"string\") return childProps.children;\n }\n if (typeof children === \"string\") return children;\n return \"\";\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n if (!children) return \"text\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const className = codeProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n const className = childProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n return \"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 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 {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 <div className=\"code-block-content\">{children}</div>\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,UAAAC,EAAQ,aAAAC,EAAW,eAAAC,EAAa,QAAAC,MAA4B,QACtF,OAAS,OAAAC,EAAK,QAAAC,EAAM,QAAAC,EAAM,UAAAC,EAAQ,QAAAC,EAAM,SAAAC,
|
|
6
|
-
"names": ["React", "useState", "useRef", "useEffect", "useCallback", "memo", "Box", "Card", "Flex", "Button", "Code", "Theme", "HugeiconsIcon", "Copy01Icon", "Tick01Icon", "codeToHtml", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "contentStyle", "extractCodeFromChildren", "
|
|
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": "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",
|
|
6
|
+
"names": ["React", "useState", "useRef", "useEffect", "useCallback", "memo", "Box", "Card", "Flex", "Button", "Code", "Theme", "ScrollArea", "HugeiconsIcon", "Copy01Icon", "Tick01Icon", "ArrowDown01Icon", "codeToHtml", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "handleToggle", "prev", "contentStyle", "extractCodeFromChildren", "extractText", "node", "props", "extractLanguageFromChildren", "findLanguage", "className", "match", "child", "lang", "formatLanguageLabel", "ChildrenCodeSection", "CodeBlock", "preview", "hasCode"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from "react";
|
|
2
|
-
import { Box, Card, Flex, Button, Code, Theme } from "@kushagradhawan/kookie-ui";
|
|
2
|
+
import { Box, Card, Flex, Button, Code, Theme, ScrollArea } from "@kushagradhawan/kookie-ui";
|
|
3
3
|
import { HugeiconsIcon } from "@hugeicons/react";
|
|
4
|
-
import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons";
|
|
4
|
+
import { Copy01Icon, Tick01Icon, ArrowDown01Icon } from "@hugeicons/core-free-icons";
|
|
5
5
|
import { codeToHtml } from "shiki";
|
|
6
6
|
import type { CodeBlockProps } from "./types";
|
|
7
7
|
|
|
@@ -152,10 +152,18 @@ const CodeSection = memo(function CodeSection({
|
|
|
152
152
|
|
|
153
153
|
const displayLanguage = language === "text" ? "plaintext" : language;
|
|
154
154
|
|
|
155
|
+
const handleToggle = useCallback(() => {
|
|
156
|
+
setIsExpanded((prev) => !prev);
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
155
159
|
const contentStyle: React.CSSProperties = {
|
|
156
160
|
maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,
|
|
157
161
|
};
|
|
158
162
|
|
|
163
|
+
const chevronStyle: React.CSSProperties = {
|
|
164
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
165
|
+
};
|
|
166
|
+
|
|
159
167
|
return (
|
|
160
168
|
<Box position="relative">
|
|
161
169
|
<Card size="1" variant="soft">
|
|
@@ -167,6 +175,18 @@ const CodeSection = memo(function CodeSection({
|
|
|
167
175
|
</Code>
|
|
168
176
|
)}
|
|
169
177
|
<Flex align="center" gap="2" className="code-action-buttons">
|
|
178
|
+
{shouldShowToggle && (
|
|
179
|
+
<Button
|
|
180
|
+
size="2"
|
|
181
|
+
variant="ghost"
|
|
182
|
+
color="gray"
|
|
183
|
+
onClick={handleToggle}
|
|
184
|
+
tooltip={isExpanded ? "Collapse" : "Expand"}
|
|
185
|
+
aria-label={isExpanded ? "Collapse code" : "Expand code"}
|
|
186
|
+
>
|
|
187
|
+
<HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className="code-chevron" />
|
|
188
|
+
</Button>
|
|
189
|
+
)}
|
|
170
190
|
{showCopy && (
|
|
171
191
|
<Button
|
|
172
192
|
size="2"
|
|
@@ -183,13 +203,15 @@ const CodeSection = memo(function CodeSection({
|
|
|
183
203
|
</Flex>
|
|
184
204
|
|
|
185
205
|
<Box ref={contentRef} style={contentStyle} className="code-content">
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
<ScrollArea type="auto" scrollbars="horizontal">
|
|
207
|
+
{highlighted ? (
|
|
208
|
+
<Box dangerouslySetInnerHTML={{ __html: highlighted }} />
|
|
209
|
+
) : (
|
|
210
|
+
<pre>
|
|
211
|
+
<Code size="3">{code}</Code>
|
|
212
|
+
</pre>
|
|
213
|
+
)}
|
|
214
|
+
</ScrollArea>
|
|
193
215
|
</Box>
|
|
194
216
|
|
|
195
217
|
{shouldShowToggle && !isExpanded && <Box className="code-scroll-shadow visible" />}
|
|
@@ -210,35 +232,53 @@ interface ChildrenCodeSectionProps {
|
|
|
210
232
|
}
|
|
211
233
|
|
|
212
234
|
function extractCodeFromChildren(children?: ReactNode): string {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
235
|
+
const extractText = (node: any): string => {
|
|
236
|
+
if (typeof node === "string") return node;
|
|
237
|
+
if (typeof node === "number") return String(node);
|
|
238
|
+
if (!node) return "";
|
|
239
|
+
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
240
|
+
if (typeof node === "object" && "props" in node) {
|
|
241
|
+
const props = node.props;
|
|
242
|
+
if (props?.children) return extractText(props.children);
|
|
220
243
|
}
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return "";
|
|
244
|
+
return "";
|
|
245
|
+
};
|
|
246
|
+
return extractText(children);
|
|
225
247
|
}
|
|
226
248
|
|
|
227
249
|
function extractLanguageFromChildren(children?: ReactNode): string {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
250
|
+
const findLanguage = (node: any): string | null => {
|
|
251
|
+
if (!node) return null;
|
|
252
|
+
if (typeof node === "object" && "props" in node) {
|
|
253
|
+
const props = node.props;
|
|
254
|
+
|
|
255
|
+
// Check data-language attribute (rehype-pretty-code)
|
|
256
|
+
if (props?.["data-language"]) {
|
|
257
|
+
return props["data-language"];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check className for language-xxx
|
|
261
|
+
const className = props?.className || props?.class || "";
|
|
262
|
+
if (typeof className === "string") {
|
|
263
|
+
const match = className.match(/language-([\w-]+)/i);
|
|
264
|
+
if (match) return match[1];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Recursively check children
|
|
268
|
+
if (props?.children) {
|
|
269
|
+
if (Array.isArray(props.children)) {
|
|
270
|
+
for (const child of props.children) {
|
|
271
|
+
const lang = findLanguage(child);
|
|
272
|
+
if (lang) return lang;
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
return findLanguage(props.children);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
236
278
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
return "text";
|
|
279
|
+
return null;
|
|
280
|
+
};
|
|
281
|
+
return findLanguage(children) || "text";
|
|
242
282
|
}
|
|
243
283
|
|
|
244
284
|
function formatLanguageLabel(lang: string): string {
|
|
@@ -301,6 +341,14 @@ const ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCo
|
|
|
301
341
|
maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,
|
|
302
342
|
};
|
|
303
343
|
|
|
344
|
+
const handleToggle = useCallback(() => {
|
|
345
|
+
setIsExpanded((prev) => !prev);
|
|
346
|
+
}, []);
|
|
347
|
+
|
|
348
|
+
const chevronStyle: React.CSSProperties = {
|
|
349
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
350
|
+
};
|
|
351
|
+
|
|
304
352
|
return (
|
|
305
353
|
<Box position="relative">
|
|
306
354
|
<Card size="1" variant="soft">
|
|
@@ -312,6 +360,18 @@ const ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCo
|
|
|
312
360
|
</Code>
|
|
313
361
|
)}
|
|
314
362
|
<Flex align="center" gap="2" className="code-action-buttons">
|
|
363
|
+
{shouldShowToggle && (
|
|
364
|
+
<Button
|
|
365
|
+
size="2"
|
|
366
|
+
variant="ghost"
|
|
367
|
+
color="gray"
|
|
368
|
+
onClick={handleToggle}
|
|
369
|
+
tooltip={isExpanded ? "Collapse" : "Expand"}
|
|
370
|
+
aria-label={isExpanded ? "Collapse code" : "Expand code"}
|
|
371
|
+
>
|
|
372
|
+
<HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className="code-chevron" />
|
|
373
|
+
</Button>
|
|
374
|
+
)}
|
|
315
375
|
{showCopy && (
|
|
316
376
|
<Button
|
|
317
377
|
size="2"
|
|
@@ -328,7 +388,9 @@ const ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCo
|
|
|
328
388
|
</Flex>
|
|
329
389
|
|
|
330
390
|
<Box ref={contentRef} style={contentStyle} className="code-content">
|
|
331
|
-
<
|
|
391
|
+
<ScrollArea type="auto" scrollbars="horizontal">
|
|
392
|
+
{children}
|
|
393
|
+
</ScrollArea>
|
|
332
394
|
</Box>
|
|
333
395
|
|
|
334
396
|
{shouldShowToggle && !isExpanded && <Box className="code-scroll-shadow visible" />}
|
package/src/components/index.css
CHANGED
|
@@ -16,68 +16,15 @@
|
|
|
16
16
|
position: relative;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/* Code block content (the pre part, below the header) */
|
|
20
|
-
.code-block-content {
|
|
21
|
-
width: 100%;
|
|
22
|
-
min-width: 0;
|
|
23
|
-
overflow-x: auto;
|
|
24
|
-
/* Hide scrollbars while keeping scroll functionality */
|
|
25
|
-
scrollbar-width: none; /* Firefox */
|
|
26
|
-
-ms-overflow-style: none; /* IE/Edge */
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/* Hide scrollbars for webkit browsers */
|
|
30
|
-
.code-block-content::-webkit-scrollbar,
|
|
31
|
-
.code-block-content::-webkit-scrollbar-horizontal,
|
|
32
|
-
.code-block-content::-webkit-scrollbar-vertical,
|
|
33
|
-
.code-block-content::-webkit-scrollbar-track,
|
|
34
|
-
.code-block-content::-webkit-scrollbar-thumb,
|
|
35
|
-
.code-block-content::-webkit-scrollbar-corner {
|
|
36
|
-
display: none !important;
|
|
37
|
-
width: 0 !important;
|
|
38
|
-
height: 0 !important;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* Also hide scrollbars on any pre/code elements inside code content */
|
|
42
|
-
.code-block-content pre::-webkit-scrollbar,
|
|
43
|
-
.code-block-content pre::-webkit-scrollbar-horizontal,
|
|
44
|
-
.code-block-content pre::-webkit-scrollbar-vertical,
|
|
45
|
-
.code-block-content pre::-webkit-scrollbar-track,
|
|
46
|
-
.code-block-content pre::-webkit-scrollbar-thumb,
|
|
47
|
-
.code-block-content pre::-webkit-scrollbar-corner,
|
|
48
|
-
.code-block-content code::-webkit-scrollbar,
|
|
49
|
-
.code-block-content code::-webkit-scrollbar-horizontal,
|
|
50
|
-
.code-block-content code::-webkit-scrollbar-vertical,
|
|
51
|
-
.code-block-content code::-webkit-scrollbar-track,
|
|
52
|
-
.code-block-content code::-webkit-scrollbar-thumb,
|
|
53
|
-
.code-block-content code::-webkit-scrollbar-corner {
|
|
54
|
-
display: none !important;
|
|
55
|
-
width: 0 !important;
|
|
56
|
-
height: 0 !important;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
19
|
/* Pre elements inside code content */
|
|
60
|
-
.code-content pre
|
|
61
|
-
.code-block-content pre {
|
|
62
|
-
overflow-x: visible;
|
|
63
|
-
max-width: 100%;
|
|
64
|
-
min-width: 0;
|
|
20
|
+
.code-content pre {
|
|
65
21
|
margin: 0;
|
|
66
22
|
width: 100%;
|
|
67
23
|
box-sizing: border-box;
|
|
68
|
-
scrollbar-width: none;
|
|
69
|
-
-ms-overflow-style: none;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* Ensure Shiki-generated pre elements fill width */
|
|
73
|
-
.code-block-content > pre {
|
|
74
|
-
width: 100% !important;
|
|
75
|
-
min-width: 0;
|
|
76
24
|
}
|
|
77
25
|
|
|
78
26
|
/* Code elements inside pre */
|
|
79
|
-
.code-content pre code
|
|
80
|
-
.code-block-content pre code {
|
|
27
|
+
.code-content pre code {
|
|
81
28
|
font-family: var(--font-mono);
|
|
82
29
|
font-size: var(--font-size-2);
|
|
83
30
|
line-height: 1.75;
|
|
@@ -89,16 +36,14 @@
|
|
|
89
36
|
}
|
|
90
37
|
|
|
91
38
|
/* Shiki line spans */
|
|
92
|
-
.code-content pre code .line
|
|
93
|
-
.code-block-content pre code .line {
|
|
39
|
+
.code-content pre code .line {
|
|
94
40
|
display: flex;
|
|
95
41
|
align-items: center;
|
|
96
42
|
gap: 0;
|
|
97
43
|
}
|
|
98
44
|
|
|
99
45
|
/* Line numbers */
|
|
100
|
-
.code-content pre code .line::before
|
|
101
|
-
.code-block-content pre code .line::before {
|
|
46
|
+
.code-content pre code .line::before {
|
|
102
47
|
counter-increment: line;
|
|
103
48
|
content: counter(line);
|
|
104
49
|
display: inline-block;
|
|
@@ -140,7 +85,6 @@
|
|
|
140
85
|
}
|
|
141
86
|
|
|
142
87
|
/* Default to light theme for all tokens (codeToHtml with defaultColor: false) */
|
|
143
|
-
.code-block-content pre.shiki span,
|
|
144
88
|
.code-content pre.shiki span {
|
|
145
89
|
color: var(--shiki-light) !important;
|
|
146
90
|
font-style: var(--shiki-light-font-style);
|
|
@@ -149,9 +93,6 @@
|
|
|
149
93
|
}
|
|
150
94
|
|
|
151
95
|
/* Override with dark theme colors when inside a dark context */
|
|
152
|
-
.dark .code-block-content pre.shiki span,
|
|
153
|
-
.dark-theme .code-block-content pre.shiki span,
|
|
154
|
-
[data-appearance="dark"] .code-block-content pre.shiki span,
|
|
155
96
|
.dark .code-content pre.shiki span,
|
|
156
97
|
.dark-theme .code-content pre.shiki span,
|
|
157
98
|
[data-appearance="dark"] .code-content pre.shiki span {
|
package/styles.css
CHANGED
|
@@ -13,63 +13,14 @@
|
|
|
13
13
|
transition: max-height 0.3s ease-in-out;
|
|
14
14
|
position: relative;
|
|
15
15
|
}
|
|
16
|
-
/* Code block content (the pre part, below the header) */
|
|
17
|
-
.code-block-content {
|
|
18
|
-
width: 100%;
|
|
19
|
-
min-width: 0;
|
|
20
|
-
overflow-x: auto;
|
|
21
|
-
/* Hide scrollbars while keeping scroll functionality */
|
|
22
|
-
scrollbar-width: none; /* Firefox */
|
|
23
|
-
-ms-overflow-style: none; /* IE/Edge */
|
|
24
|
-
}
|
|
25
|
-
/* Hide scrollbars for webkit browsers */
|
|
26
|
-
.code-block-content::-webkit-scrollbar,
|
|
27
|
-
.code-block-content::-webkit-scrollbar-horizontal,
|
|
28
|
-
.code-block-content::-webkit-scrollbar-vertical,
|
|
29
|
-
.code-block-content::-webkit-scrollbar-track,
|
|
30
|
-
.code-block-content::-webkit-scrollbar-thumb,
|
|
31
|
-
.code-block-content::-webkit-scrollbar-corner {
|
|
32
|
-
display: none !important;
|
|
33
|
-
width: 0 !important;
|
|
34
|
-
height: 0 !important;
|
|
35
|
-
}
|
|
36
|
-
/* Also hide scrollbars on any pre/code elements inside code content */
|
|
37
|
-
.code-block-content pre::-webkit-scrollbar,
|
|
38
|
-
.code-block-content pre::-webkit-scrollbar-horizontal,
|
|
39
|
-
.code-block-content pre::-webkit-scrollbar-vertical,
|
|
40
|
-
.code-block-content pre::-webkit-scrollbar-track,
|
|
41
|
-
.code-block-content pre::-webkit-scrollbar-thumb,
|
|
42
|
-
.code-block-content pre::-webkit-scrollbar-corner,
|
|
43
|
-
.code-block-content code::-webkit-scrollbar,
|
|
44
|
-
.code-block-content code::-webkit-scrollbar-horizontal,
|
|
45
|
-
.code-block-content code::-webkit-scrollbar-vertical,
|
|
46
|
-
.code-block-content code::-webkit-scrollbar-track,
|
|
47
|
-
.code-block-content code::-webkit-scrollbar-thumb,
|
|
48
|
-
.code-block-content code::-webkit-scrollbar-corner {
|
|
49
|
-
display: none !important;
|
|
50
|
-
width: 0 !important;
|
|
51
|
-
height: 0 !important;
|
|
52
|
-
}
|
|
53
16
|
/* Pre elements inside code content */
|
|
54
|
-
.code-content pre
|
|
55
|
-
.code-block-content pre {
|
|
56
|
-
overflow-x: visible;
|
|
57
|
-
max-width: 100%;
|
|
58
|
-
min-width: 0;
|
|
17
|
+
.code-content pre {
|
|
59
18
|
margin: 0;
|
|
60
19
|
width: 100%;
|
|
61
20
|
box-sizing: border-box;
|
|
62
|
-
scrollbar-width: none;
|
|
63
|
-
-ms-overflow-style: none;
|
|
64
|
-
}
|
|
65
|
-
/* Ensure Shiki-generated pre elements fill width */
|
|
66
|
-
.code-block-content > pre {
|
|
67
|
-
width: 100% !important;
|
|
68
|
-
min-width: 0;
|
|
69
21
|
}
|
|
70
22
|
/* Code elements inside pre */
|
|
71
|
-
.code-content pre code
|
|
72
|
-
.code-block-content pre code {
|
|
23
|
+
.code-content pre code {
|
|
73
24
|
font-family: var(--font-mono);
|
|
74
25
|
font-size: var(--font-size-2);
|
|
75
26
|
line-height: 1.75;
|
|
@@ -80,15 +31,13 @@
|
|
|
80
31
|
counter-reset: line;
|
|
81
32
|
}
|
|
82
33
|
/* Shiki line spans */
|
|
83
|
-
.code-content pre code .line
|
|
84
|
-
.code-block-content pre code .line {
|
|
34
|
+
.code-content pre code .line {
|
|
85
35
|
display: flex;
|
|
86
36
|
align-items: center;
|
|
87
37
|
gap: 0;
|
|
88
38
|
}
|
|
89
39
|
/* Line numbers */
|
|
90
|
-
.code-content pre code .line::before
|
|
91
|
-
.code-block-content pre code .line::before {
|
|
40
|
+
.code-content pre code .line::before {
|
|
92
41
|
counter-increment: line;
|
|
93
42
|
content: counter(line);
|
|
94
43
|
display: inline-block;
|
|
@@ -127,7 +76,6 @@
|
|
|
127
76
|
transition: transform 0.2s ease-in-out;
|
|
128
77
|
}
|
|
129
78
|
/* Default to light theme for all tokens (codeToHtml with defaultColor: false) */
|
|
130
|
-
.code-block-content pre.shiki span,
|
|
131
79
|
.code-content pre.shiki span {
|
|
132
80
|
color: var(--shiki-light) !important;
|
|
133
81
|
font-style: var(--shiki-light-font-style);
|
|
@@ -136,9 +84,6 @@
|
|
|
136
84
|
text-decoration: var(--shiki-light-text-decoration);
|
|
137
85
|
}
|
|
138
86
|
/* Override with dark theme colors when inside a dark context */
|
|
139
|
-
.dark .code-block-content pre.shiki span,
|
|
140
|
-
.dark-theme .code-block-content pre.shiki span,
|
|
141
|
-
[data-appearance="dark"] .code-block-content pre.shiki span,
|
|
142
87
|
.dark .code-content pre.shiki span,
|
|
143
88
|
.dark-theme .code-content pre.shiki span,
|
|
144
89
|
[data-appearance="dark"] .code-content pre.shiki span {
|