@intlayer/design-system 8.7.7 → 8.7.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.
Files changed (46) hide show
  1. package/dist/esm/components/HeightResizer/index.mjs +25 -0
  2. package/dist/esm/components/HeightResizer/index.mjs.map +1 -1
  3. package/dist/esm/components/IDE/Code.mjs +96 -13
  4. package/dist/esm/components/IDE/Code.mjs.map +1 -1
  5. package/dist/esm/components/IDE/CodeBlockHighlight.mjs +77 -0
  6. package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -0
  7. package/dist/esm/components/IDE/CodeConditionalRenderer.mjs +15 -4
  8. package/dist/esm/components/IDE/CodeConditionalRenderer.mjs.map +1 -1
  9. package/dist/esm/components/IDE/CodeFormatSelector.mjs +5 -4
  10. package/dist/esm/components/IDE/CodeFormatSelector.mjs.map +1 -1
  11. package/dist/esm/components/IDE/codeTransformer.mjs +228 -0
  12. package/dist/esm/components/IDE/codeTransformer.mjs.map +1 -0
  13. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +101 -0
  14. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -0
  15. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +2 -9
  16. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  17. package/dist/esm/components/WithResizer/index.mjs +24 -0
  18. package/dist/esm/components/WithResizer/index.mjs.map +1 -1
  19. package/dist/esm/hooks/reactQuery.mjs +4 -2
  20. package/dist/esm/hooks/reactQuery.mjs.map +1 -1
  21. package/dist/types/components/Badge/index.d.ts +1 -1
  22. package/dist/types/components/Button/Button.d.ts +2 -2
  23. package/dist/types/components/Command/index.d.ts +2 -2
  24. package/dist/types/components/Container/index.d.ts +2 -2
  25. package/dist/types/components/HeightResizer/index.d.ts.map +1 -1
  26. package/dist/types/components/IDE/Code.d.ts +3 -3
  27. package/dist/types/components/IDE/Code.d.ts.map +1 -1
  28. package/dist/types/components/IDE/CodeBlockHighlight.d.ts +20 -0
  29. package/dist/types/components/IDE/CodeBlockHighlight.d.ts.map +1 -0
  30. package/dist/types/components/IDE/CodeConditionalRenderer.d.ts.map +1 -1
  31. package/dist/types/components/IDE/CodeFormatSelector.d.ts +5 -1
  32. package/dist/types/components/IDE/CodeFormatSelector.d.ts.map +1 -1
  33. package/dist/types/components/IDE/codeTransformer.d.ts +25 -0
  34. package/dist/types/components/IDE/codeTransformer.d.ts.map +1 -0
  35. package/dist/types/components/Input/Checkbox.d.ts +1 -1
  36. package/dist/types/components/Link/Link.d.ts +2 -2
  37. package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts +7 -0
  38. package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts.map +1 -0
  39. package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
  40. package/dist/types/components/Pagination/Pagination.d.ts +1 -1
  41. package/dist/types/components/SwitchSelector/index.d.ts +1 -1
  42. package/dist/types/components/TabSelector/TabSelector.d.ts +1 -1
  43. package/dist/types/components/Tag/index.d.ts +1 -1
  44. package/dist/types/components/WithResizer/index.d.ts.map +1 -1
  45. package/dist/types/hooks/reactQuery.d.ts.map +1 -1
  46. package/package.json +19 -19
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
 
7
7
  //#region src/components/HeightResizer/index.tsx
8
+ const HANDLE_DOUBLE_CLICK_ZONE_PX = 16;
8
9
  /**
9
10
  * HeightResizer Component
10
11
  *
@@ -77,6 +78,7 @@ const HeightResizer = ({ initialHeight, maxHeight, minHeight = 0, children, clas
77
78
  const containerRef = useRef(null);
78
79
  const [height, setHeight] = useState(initialHeight);
79
80
  const [isResizing, setIsResizing] = useState(false);
81
+ const lastExpandedHeightRef = useRef(initialHeight);
80
82
  /**
81
83
  * Handler to initiate the resizing process
82
84
  * Prevents default browser behavior and sets the resizing state
@@ -133,6 +135,27 @@ const HeightResizer = ({ initialHeight, maxHeight, minHeight = 0, children, clas
133
135
  window.removeEventListener("touchend", stopResizing);
134
136
  };
135
137
  }, [resize, stopResizing]);
138
+ useEffect(() => {
139
+ if (height > minHeight) lastExpandedHeightRef.current = height;
140
+ }, [height, minHeight]);
141
+ const handleDoubleClick = useCallback((event) => {
142
+ const el = containerRef.current;
143
+ if (!el) return;
144
+ const { top } = el.getBoundingClientRect();
145
+ if (event.clientY - top > HANDLE_DOUBLE_CLICK_ZONE_PX) return;
146
+ event.preventDefault();
147
+ event.stopPropagation();
148
+ if (height > minHeight) {
149
+ setHeight(minHeight);
150
+ return;
151
+ }
152
+ const capped = maxHeight !== void 0 ? Math.min(lastExpandedHeightRef.current, maxHeight) : lastExpandedHeightRef.current;
153
+ setHeight(Math.max(capped, minHeight));
154
+ }, [
155
+ height,
156
+ maxHeight,
157
+ minHeight
158
+ ]);
136
159
  return /* @__PURE__ */ jsx("div", {
137
160
  className: cn("relative h-full max-h-[80%] w-full cursor-ns-resize border-neutral-200 border-t-[2px] transition dark:border-neutral-950", "before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-2 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-neutral-200 before:transition before:content-[\"\"] dark:before:bg-neutral-950", "active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600", className),
138
161
  style: {
@@ -143,6 +166,7 @@ const HeightResizer = ({ initialHeight, maxHeight, minHeight = 0, children, clas
143
166
  ref: containerRef,
144
167
  onMouseDown: startResizing,
145
168
  onTouchStart: startResizing,
169
+ onDoubleClick: handleDoubleClick,
146
170
  "aria-valuemin": minHeight,
147
171
  "aria-valuemax": maxHeight,
148
172
  "aria-valuenow": height,
@@ -151,6 +175,7 @@ const HeightResizer = ({ initialHeight, maxHeight, minHeight = 0, children, clas
151
175
  tabIndex: 0,
152
176
  ...props,
153
177
  children: /* @__PURE__ */ jsx("div", {
178
+ role: "presentation",
154
179
  className: "absolute top-0 left-0 size-full cursor-default overflow-hidden",
155
180
  onMouseDown: (e) => e.stopPropagation(),
156
181
  onTouchStart: (e) => e.stopPropagation(),
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/HeightResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type React from 'react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type HTMLAttributes,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Props for the HeightResizer component\n *\n * @interface HeightResizerProps\n */\ntype HeightResizerProps = {\n /**\n * Initial height in pixels for the resizable container\n * Sets the default size when the component first loads\n * @example 200\n */\n initialHeight: number;\n\n /**\n * Maximum height in pixels that the user can resize to (optional)\n * When undefined, no maximum limit is enforced\n * @example 500\n */\n maxHeight?: number;\n\n /**\n * Minimum height in pixels that the user can resize to (optional)\n * Prevents the container from being resized below this threshold\n * @default 0\n * @example 50\n */\n minHeight?: number;\n} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;\n\n/**\n * HeightResizer Component\n *\n * A resizable container component that allows users to dynamically adjust the height\n * by dragging a visual handle at the top. Provides smooth resizing with optional\n * minimum and maximum height constraints.\n *\n * ## Key Features\n * - **Interactive Resizing**: Drag handle to resize container vertically\n * - **Touch Support**: Full support for touch devices and mobile interactions\n * - **Height Constraints**: Optional minimum and maximum height limits\n * - **Visual Feedback**: Handle with hover and active states for clear interaction\n * - **Accessibility**: ARIA slider role with value announcements for screen readers\n * - **Smooth Animation**: CSS transitions for polished user experience\n *\n * ## Use Cases\n * - Code editors with resizable panels\n * - Chat interfaces with adjustable message areas\n * - Dashboard widgets with user-customizable sizes\n * - Documentation viewers with resizable content panes\n * - Settings panels with expandable sections\n *\n * ## Interaction Model\n * The component uses a drag interaction model where users click and drag the visual\n * handle (rounded bar) at the top of the container. The resize calculation is based\n * on the difference between the current cursor position and the container's top edge.\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for assistive technologies\n * - **Value Announcements**: Current, min, and max values announced to screen readers\n * - **Keyboard Navigation**: Focusable with standard slider keyboard support\n * - **Visual Indicators**: Clear visual handle for drag interaction\n *\n * @component\n * @example\n * ```tsx\n * // Basic usage\n * <HeightResizer initialHeight={200}>\n * <div>Your resizable content here</div>\n * </HeightResizer>\n *\n * // With height constraints\n * <HeightResizer\n * initialHeight={300}\n * minHeight={100}\n * maxHeight={600}\n * >\n * <div>Content with size limits</div>\n * </HeightResizer>\n *\n * // In a code editor context\n * <HeightResizer\n * initialHeight={400}\n * minHeight={150}\n * className=\"border rounded-lg\"\n * >\n * <CodeEditor />\n * </HeightResizer>\n * ```\n *\n * @param props - HeightResizer component props\n * @param props.initialHeight - Starting height in pixels\n * @param props.minHeight - Optional minimum height constraint\n * @param props.maxHeight - Optional maximum height constraint\n * @param props.children - Content to display in the resizable container\n * @param props.className - Additional CSS classes for styling\n * @returns Interactive resizable container component\n */\nexport const HeightResizer: FC<PropsWithChildren<HeightResizerProps>> = ({\n initialHeight,\n maxHeight,\n minHeight = 0,\n children,\n className,\n ...props\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState(initialHeight);\n const [isResizing, setIsResizing] = useState(false);\n\n /**\n * Handler to initiate the resizing process\n * Prevents default browser behavior and sets the resizing state\n *\n * @param mouseDownEvent - Mouse or touch event from the drag handle\n */\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n setIsResizing(true);\n mouseDownEvent.preventDefault();\n },\n []\n );\n\n /**\n * Handler to stop the resizing process\n * Resets the resizing state when user releases the drag handle\n */\n const stopResizing = useCallback(() => {\n setIsResizing(false);\n }, []);\n\n /**\n * Core resize logic that calculates new height based on cursor position\n * Handles both mouse and touch events with boundary checking\n *\n * @param mouseMoveEvent - Mouse or touch move event during drag\n */\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n const container = containerRef.current;\n if (isResizing && container) {\n const { height: containerHeight, top: containerTop } =\n container.getBoundingClientRect();\n\n let clientY = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientY = mouseMoveEvent.clientY;\n } else if (mouseMoveEvent instanceof TouchEvent) {\n clientY = mouseMoveEvent.touches[0].clientY;\n }\n\n const resizeDifference = clientY - containerTop;\n const newHeight = containerHeight - resizeDifference;\n\n // Apply height constraints\n let correctedHeight = Math.max(newHeight, minHeight);\n if (maxHeight !== undefined) {\n correctedHeight = Math.min(correctedHeight, maxHeight);\n }\n\n setHeight(correctedHeight);\n }\n },\n [isResizing, minHeight, maxHeight]\n );\n\n /**\n * Effect to manage global event listeners for resize interactions\n * Handles both mouse and touch events with proper cleanup\n */\n useEffect(() => {\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing]);\n\n return (\n <div\n className={cn(\n 'relative h-full max-h-[80%] w-full cursor-ns-resize border-neutral-200 border-t-[2px] transition dark:border-neutral-950',\n 'before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-2 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-neutral-200 before:transition before:content-[\"\"] dark:before:bg-neutral-950',\n 'active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600',\n className\n )}\n style={{\n height: `${height}px`,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n minHeight: `${minHeight}px`,\n }}\n ref={containerRef}\n onMouseDown={startResizing}\n onTouchStart={startResizing}\n aria-valuemin={minHeight}\n aria-valuemax={maxHeight}\n aria-valuenow={height}\n aria-label=\"Resizable component - drag the handle to adjust height\"\n role=\"slider\"\n tabIndex={0}\n {...props}\n >\n <div\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgHA,MAAa,iBAA4D,EACvE,eACA,WACA,YAAY,GACZ,UACA,WACA,GAAG,YACC;CACJ,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,CAAC,QAAQ,aAAa,SAAS,cAAc;CACnD,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;;;;;;;CAQnD,MAAM,gBAAgB,aAElB,mBAGG;AACH,gBAAc,KAAK;AACnB,iBAAe,gBAAgB;IAEjC,EAAE,CACH;;;;;CAMD,MAAM,eAAe,kBAAkB;AACrC,gBAAc,MAAM;IACnB,EAAE,CAAC;;;;;;;CAQN,MAAM,SAAS,aACZ,mBAA4C;EAC3C,MAAM,YAAY,aAAa;AAC/B,MAAI,cAAc,WAAW;GAC3B,MAAM,EAAE,QAAQ,iBAAiB,KAAK,iBACpC,UAAU,uBAAuB;GAEnC,IAAI,UAAU;AACd,OAAI,0BAA0B,WAC5B,WAAU,eAAe;YAChB,0BAA0B,WACnC,WAAU,eAAe,QAAQ,GAAG;GAItC,MAAM,YAAY,mBADO,UAAU;GAInC,IAAI,kBAAkB,KAAK,IAAI,WAAW,UAAU;AACpD,OAAI,cAAc,OAChB,mBAAkB,KAAK,IAAI,iBAAiB,UAAU;AAGxD,aAAU,gBAAgB;;IAG9B;EAAC;EAAY;EAAW;EAAU,CACnC;;;;;AAMD,iBAAgB;AACd,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,WAAW,aAAa;AAChD,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,YAAY,aAAa;AAEjD,eAAa;AACX,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,WAAW,aAAa;AACnD,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,YAAY,aAAa;;IAErD,CAAC,QAAQ,aAAa,CAAC;AAE1B,QACE,oBAAC,OAAD;EACE,WAAW,GACT,4HACA,sSACA,2HACA,UACD;EACD,OAAO;GACL,QAAQ,GAAG,OAAO;GAClB,WAAW,YAAY,GAAG,UAAU,MAAM;GAC1C,WAAW,GAAG,UAAU;GACzB;EACD,KAAK;EACL,aAAa;EACb,cAAc;EACd,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,cAAW;EACX,MAAK;EACL,UAAU;EACV,GAAI;YAEJ,oBAAC,OAAD;GACE,WAAU;GACV,cAAc,MAAM,EAAE,iBAAiB;GACvC,eAAe,MAAM,EAAE,iBAAiB;GAEvC;GACG;EACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/HeightResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type React from 'react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type HTMLAttributes,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst HANDLE_DOUBLE_CLICK_ZONE_PX = 16;\n\n/**\n * Props for the HeightResizer component\n *\n * @interface HeightResizerProps\n */\ntype HeightResizerProps = {\n /**\n * Initial height in pixels for the resizable container\n * Sets the default size when the component first loads\n * @example 200\n */\n initialHeight: number;\n\n /**\n * Maximum height in pixels that the user can resize to (optional)\n * When undefined, no maximum limit is enforced\n * @example 500\n */\n maxHeight?: number;\n\n /**\n * Minimum height in pixels that the user can resize to (optional)\n * Prevents the container from being resized below this threshold\n * @default 0\n * @example 50\n */\n minHeight?: number;\n} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;\n\n/**\n * HeightResizer Component\n *\n * A resizable container component that allows users to dynamically adjust the height\n * by dragging a visual handle at the top. Provides smooth resizing with optional\n * minimum and maximum height constraints.\n *\n * ## Key Features\n * - **Interactive Resizing**: Drag handle to resize container vertically\n * - **Touch Support**: Full support for touch devices and mobile interactions\n * - **Height Constraints**: Optional minimum and maximum height limits\n * - **Visual Feedback**: Handle with hover and active states for clear interaction\n * - **Accessibility**: ARIA slider role with value announcements for screen readers\n * - **Smooth Animation**: CSS transitions for polished user experience\n *\n * ## Use Cases\n * - Code editors with resizable panels\n * - Chat interfaces with adjustable message areas\n * - Dashboard widgets with user-customizable sizes\n * - Documentation viewers with resizable content panes\n * - Settings panels with expandable sections\n *\n * ## Interaction Model\n * The component uses a drag interaction model where users click and drag the visual\n * handle (rounded bar) at the top of the container. The resize calculation is based\n * on the difference between the current cursor position and the container's top edge.\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for assistive technologies\n * - **Value Announcements**: Current, min, and max values announced to screen readers\n * - **Keyboard Navigation**: Focusable with standard slider keyboard support\n * - **Visual Indicators**: Clear visual handle for drag interaction\n *\n * @component\n * @example\n * ```tsx\n * // Basic usage\n * <HeightResizer initialHeight={200}>\n * <div>Your resizable content here</div>\n * </HeightResizer>\n *\n * // With height constraints\n * <HeightResizer\n * initialHeight={300}\n * minHeight={100}\n * maxHeight={600}\n * >\n * <div>Content with size limits</div>\n * </HeightResizer>\n *\n * // In a code editor context\n * <HeightResizer\n * initialHeight={400}\n * minHeight={150}\n * className=\"border rounded-lg\"\n * >\n * <CodeEditor />\n * </HeightResizer>\n * ```\n *\n * @param props - HeightResizer component props\n * @param props.initialHeight - Starting height in pixels\n * @param props.minHeight - Optional minimum height constraint\n * @param props.maxHeight - Optional maximum height constraint\n * @param props.children - Content to display in the resizable container\n * @param props.className - Additional CSS classes for styling\n * @returns Interactive resizable container component\n */\nexport const HeightResizer: FC<PropsWithChildren<HeightResizerProps>> = ({\n initialHeight,\n maxHeight,\n minHeight = 0,\n children,\n className,\n ...props\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState(initialHeight);\n const [isResizing, setIsResizing] = useState(false);\n const lastExpandedHeightRef = useRef(initialHeight);\n\n /**\n * Handler to initiate the resizing process\n * Prevents default browser behavior and sets the resizing state\n *\n * @param mouseDownEvent - Mouse or touch event from the drag handle\n */\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n setIsResizing(true);\n mouseDownEvent.preventDefault();\n },\n []\n );\n\n /**\n * Handler to stop the resizing process\n * Resets the resizing state when user releases the drag handle\n */\n const stopResizing = useCallback(() => {\n setIsResizing(false);\n }, []);\n\n /**\n * Core resize logic that calculates new height based on cursor position\n * Handles both mouse and touch events with boundary checking\n *\n * @param mouseMoveEvent - Mouse or touch move event during drag\n */\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n const container = containerRef.current;\n if (isResizing && container) {\n const { height: containerHeight, top: containerTop } =\n container.getBoundingClientRect();\n\n let clientY = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientY = mouseMoveEvent.clientY;\n } else if (mouseMoveEvent instanceof TouchEvent) {\n clientY = mouseMoveEvent.touches[0].clientY;\n }\n\n const resizeDifference = clientY - containerTop;\n const newHeight = containerHeight - resizeDifference;\n\n // Apply height constraints\n let correctedHeight = Math.max(newHeight, minHeight);\n if (maxHeight !== undefined) {\n correctedHeight = Math.min(correctedHeight, maxHeight);\n }\n\n setHeight(correctedHeight);\n }\n },\n [isResizing, minHeight, maxHeight]\n );\n\n /**\n * Effect to manage global event listeners for resize interactions\n * Handles both mouse and touch events with proper cleanup\n */\n useEffect(() => {\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing]);\n\n useEffect(() => {\n if (height > minHeight) {\n lastExpandedHeightRef.current = height;\n }\n }, [height, minHeight]);\n\n const handleDoubleClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const el = containerRef.current;\n if (!el) return;\n\n const { top } = el.getBoundingClientRect();\n if (event.clientY - top > HANDLE_DOUBLE_CLICK_ZONE_PX) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n if (height > minHeight) {\n setHeight(minHeight);\n return;\n }\n\n const capped =\n maxHeight !== undefined\n ? Math.min(lastExpandedHeightRef.current, maxHeight)\n : lastExpandedHeightRef.current;\n setHeight(Math.max(capped, minHeight));\n },\n [height, maxHeight, minHeight]\n );\n\n return (\n <div\n className={cn(\n 'relative h-full max-h-[80%] w-full cursor-ns-resize border-neutral-200 border-t-[2px] transition dark:border-neutral-950',\n 'before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-2 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-neutral-200 before:transition before:content-[\"\"] dark:before:bg-neutral-950',\n 'active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600',\n className\n )}\n style={{\n height: `${height}px`,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n minHeight: `${minHeight}px`,\n }}\n ref={containerRef}\n onMouseDown={startResizing}\n onTouchStart={startResizing}\n onDoubleClick={handleDoubleClick}\n aria-valuemin={minHeight}\n aria-valuemax={maxHeight}\n aria-valuenow={height}\n aria-label=\"Resizable component - drag the handle to adjust height\"\n role=\"slider\"\n tabIndex={0}\n {...props}\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Stops content clicks from triggering resize on the parent slider */}\n <div\n role=\"presentation\"\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAeA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGpC,MAAa,iBAA4D,EACvE,eACA,WACA,YAAY,GACZ,UACA,WACA,GAAG,YACC;CACJ,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,CAAC,QAAQ,aAAa,SAAS,cAAc;CACnD,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,wBAAwB,OAAO,cAAc;;;;;;;CAQnD,MAAM,gBAAgB,aAElB,mBAGG;AACH,gBAAc,KAAK;AACnB,iBAAe,gBAAgB;IAEjC,EAAE,CACH;;;;;CAMD,MAAM,eAAe,kBAAkB;AACrC,gBAAc,MAAM;IACnB,EAAE,CAAC;;;;;;;CAQN,MAAM,SAAS,aACZ,mBAA4C;EAC3C,MAAM,YAAY,aAAa;AAC/B,MAAI,cAAc,WAAW;GAC3B,MAAM,EAAE,QAAQ,iBAAiB,KAAK,iBACpC,UAAU,uBAAuB;GAEnC,IAAI,UAAU;AACd,OAAI,0BAA0B,WAC5B,WAAU,eAAe;YAChB,0BAA0B,WACnC,WAAU,eAAe,QAAQ,GAAG;GAItC,MAAM,YAAY,mBADO,UAAU;GAInC,IAAI,kBAAkB,KAAK,IAAI,WAAW,UAAU;AACpD,OAAI,cAAc,OAChB,mBAAkB,KAAK,IAAI,iBAAiB,UAAU;AAGxD,aAAU,gBAAgB;;IAG9B;EAAC;EAAY;EAAW;EAAU,CACnC;;;;;AAMD,iBAAgB;AACd,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,WAAW,aAAa;AAChD,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,YAAY,aAAa;AAEjD,eAAa;AACX,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,WAAW,aAAa;AACnD,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,YAAY,aAAa;;IAErD,CAAC,QAAQ,aAAa,CAAC;AAE1B,iBAAgB;AACd,MAAI,SAAS,UACX,uBAAsB,UAAU;IAEjC,CAAC,QAAQ,UAAU,CAAC;CAEvB,MAAM,oBAAoB,aACvB,UAA4C;EAC3C,MAAM,KAAK,aAAa;AACxB,MAAI,CAAC,GAAI;EAET,MAAM,EAAE,QAAQ,GAAG,uBAAuB;AAC1C,MAAI,MAAM,UAAU,MAAM,4BAA6B;AAEvD,QAAM,gBAAgB;AACtB,QAAM,iBAAiB;AAEvB,MAAI,SAAS,WAAW;AACtB,aAAU,UAAU;AACpB;;EAGF,MAAM,SACJ,cAAc,SACV,KAAK,IAAI,sBAAsB,SAAS,UAAU,GAClD,sBAAsB;AAC5B,YAAU,KAAK,IAAI,QAAQ,UAAU,CAAC;IAExC;EAAC;EAAQ;EAAW;EAAU,CAC/B;AAED,QACE,oBAAC,OAAD;EACE,WAAW,GACT,4HACA,sSACA,2HACA,UACD;EACD,OAAO;GACL,QAAQ,GAAG,OAAO;GAClB,WAAW,YAAY,GAAG,UAAU,MAAM;GAC1C,WAAW,GAAG,UAAU;GACzB;EACD,KAAK;EACL,aAAa;EACb,cAAc;EACd,eAAe;EACf,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,cAAW;EACX,MAAK;EACL,UAAU;EACV,GAAI;YAGJ,oBAAC,OAAD;GACE,MAAK;GACL,WAAU;GACV,cAAc,MAAM,EAAE,iBAAiB;GACvC,eAAe,MAAM,EAAE,iBAAiB;GAEvC;GACG;EACF"}
@@ -1,54 +1,137 @@
1
+ 'use client';
2
+
1
3
  import { cn } from "../../utils/cn.mjs";
2
4
  import { Container } from "../Container/index.mjs";
3
5
  import { ExpandCollapse } from "../ExpandCollapse/ExpandCollapse.mjs";
4
6
  import { CodeBlock } from "./CodeBlockClient.mjs";
7
+ import { CodeBlockHighlight } from "./CodeBlockHighlight.mjs";
8
+ import { useCodeContext } from "./CodeContext.mjs";
5
9
  import { CodeConditionalRender } from "./CodeConditionalRenderer.mjs";
6
10
  import { CodeFormatSelector } from "./CodeFormatSelector.mjs";
7
11
  import { ContentDeclarationFormatSelector } from "./ContentDeclarationFormatSelector.mjs";
8
12
  import { CopyCode } from "./CopyCode.mjs";
9
13
  import { PackageManagerSelector } from "./PackageManagerSelector.mjs";
10
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
+ import { useEffect, useMemo, useState } from "react";
15
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
16
 
12
17
  //#region src/components/IDE/Code.tsx
13
18
  const MIN_HEIGHT = 700;
14
- const Code = ({ children, language, isDarkMode, showHeader = true, showLineNumbers = true, className, fileName, packageManager, codeFormat, contentDeclarationFormat, isRollable = true, ...props }) => {
15
- const code = children?.endsWith("\n") ? children.slice(0, -1) : children;
16
- const hadSelectInHeader = packageManager || codeFormat || contentDeclarationFormat;
19
+ /** Languages that use JSX syntax CommonJS doesn't make sense for these. */
20
+ const JSX_LANGUAGES = new Set(["tsx", "jsx"]);
21
+ /** Parse a codeFormat prop that may be a single value or a JSON-array string. */
22
+ function parseFormats(raw) {
23
+ if (!raw) return void 0;
24
+ if (raw.startsWith("[")) try {
25
+ const parsed = JSON.parse(raw);
26
+ if (Array.isArray(parsed)) return parsed;
27
+ } catch {}
28
+ return [raw];
29
+ }
30
+ const Code = ({ children, language, isDarkMode, showHeader = true, showLineNumbers = true, className, fileName, packageManager, codeFormat: rawCodeFormat, contentDeclarationFormat: rawContentDeclarationFormat, isRollable = true, ...props }) => {
31
+ const { codeFormat: selectedCodeFormat, contentDeclarationFormat: selectedContentDeclarationFormat } = useCodeContext();
32
+ const codeFormats = useMemo(() => parseFormats(rawCodeFormat), [rawCodeFormat]);
33
+ const contentFormats = useMemo(() => parseFormats(rawContentDeclarationFormat), [rawContentDeclarationFormat]);
34
+ const isMultiCodeFormat = codeFormats !== void 0 && codeFormats.length > 1 && codeFormats.includes("typescript");
35
+ const isMultiContentFormat = contentFormats !== void 0 && contentFormats.length > 1 && contentFormats.includes("typescript");
36
+ const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;
37
+ const selectedFormat = isMultiContentFormat ? selectedContentDeclarationFormat : selectedCodeFormat;
38
+ const effectiveFormats = useMemo(() => {
39
+ const base = isMultiContentFormat ? contentFormats : codeFormats;
40
+ if (!base) return base;
41
+ let filtered = base.filter((f) => f !== "json");
42
+ if (JSX_LANGUAGES.has(language)) filtered = filtered.filter((f) => f !== "commonjs");
43
+ return filtered;
44
+ }, [
45
+ isMultiContentFormat,
46
+ contentFormats,
47
+ codeFormats,
48
+ language
49
+ ]);
50
+ const resolvedFormat = useMemo(() => {
51
+ if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) return selectedFormat;
52
+ return effectiveFormats[effectiveFormats.length - 1] ?? "typescript";
53
+ }, [effectiveFormats, selectedFormat]);
54
+ const [displayedFileName, setDisplayedFileName] = useState(fileName);
55
+ useEffect(() => {
56
+ if (!isMultiFormat || resolvedFormat === "typescript") {
57
+ setDisplayedFileName(fileName);
58
+ return;
59
+ }
60
+ if (!fileName) return;
61
+ let cancelled = false;
62
+ (async () => {
63
+ const { deriveFileName } = await import("./codeTransformer.mjs");
64
+ if (!cancelled) setDisplayedFileName(deriveFileName(fileName, resolvedFormat));
65
+ })();
66
+ return () => {
67
+ cancelled = true;
68
+ };
69
+ }, [
70
+ fileName,
71
+ isMultiFormat,
72
+ resolvedFormat
73
+ ]);
74
+ const rawCode = useMemo(() => children?.endsWith("\n") ? children.slice(0, -1) : children, [children]);
75
+ const [copyCode, setCopyCode] = useState(rawCode);
76
+ useEffect(() => {
77
+ if (!isMultiFormat || resolvedFormat === "typescript") {
78
+ setCopyCode(rawCode);
79
+ return;
80
+ }
81
+ let cancelled = false;
82
+ (async () => {
83
+ const { transformCode } = await import("./codeTransformer.mjs");
84
+ if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));
85
+ })();
86
+ return () => {
87
+ cancelled = true;
88
+ };
89
+ }, [
90
+ rawCode,
91
+ isMultiFormat,
92
+ resolvedFormat
93
+ ]);
94
+ const hadSelectInHeader = packageManager || rawCodeFormat || rawContentDeclarationFormat;
17
95
  return /* @__PURE__ */ jsx(CodeConditionalRender, {
18
96
  packageManager,
19
- codeFormat,
20
- contentDeclarationFormat,
97
+ codeFormat: rawCodeFormat,
98
+ contentDeclarationFormat: rawContentDeclarationFormat,
21
99
  children: /* @__PURE__ */ jsxs(Container, {
22
100
  className: cn("relative min-w-0 max-w-full text-sm leading-6", showLineNumbers && "with-line-number ml-0", className),
23
101
  transparency: "lg",
24
102
  ...props,
25
- children: [showHeader && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
103
+ children: [showHeader && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
26
104
  className: "grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs",
27
105
  children: [/* @__PURE__ */ jsx("span", {
28
106
  className: "truncate",
29
- children: fileName ?? language
107
+ children: displayedFileName ?? language
30
108
  }), /* @__PURE__ */ jsxs("div", {
31
109
  className: "flex items-center gap-2",
32
110
  children: [
33
111
  packageManager && /* @__PURE__ */ jsx(PackageManagerSelector, {}),
34
- codeFormat && /* @__PURE__ */ jsx(CodeFormatSelector, {}),
35
- contentDeclarationFormat && /* @__PURE__ */ jsx(ContentDeclarationFormatSelector, {})
112
+ rawCodeFormat && /* @__PURE__ */ jsx(CodeFormatSelector, { availableFormats: effectiveFormats }),
113
+ rawContentDeclarationFormat && /* @__PURE__ */ jsx(ContentDeclarationFormatSelector, {})
36
114
  ]
37
115
  })]
38
116
  }), /* @__PURE__ */ jsx("div", {
39
117
  className: "sticky top-46 z-20",
40
118
  children: /* @__PURE__ */ jsx("div", {
41
119
  className: cn("absolute right-2 bottom-0 flex h-7 items-center", hadSelectInHeader && "h-11"),
42
- children: /* @__PURE__ */ jsx(CopyCode, { code })
120
+ children: /* @__PURE__ */ jsx(CopyCode, { code: copyCode })
43
121
  })
44
122
  })] }), /* @__PURE__ */ jsx(ExpandCollapse, {
45
123
  minHeight: MIN_HEIGHT,
46
124
  isRollable,
47
125
  className: "min-w-0 max-w-full overflow-x-auto p-2",
48
- children: /* @__PURE__ */ jsx(CodeBlock, {
126
+ children: isMultiFormat ? /* @__PURE__ */ jsx(CodeBlockHighlight, {
127
+ originalLang: language,
128
+ targetFormat: resolvedFormat,
129
+ isDarkMode,
130
+ children: rawCode
131
+ }) : /* @__PURE__ */ jsx(CodeBlock, {
49
132
  lang: language,
50
133
  isDarkMode,
51
- children: code
134
+ children: rawCode
52
135
  })
53
136
  })]
54
137
  })
@@ -1 +1 @@
1
- {"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n codeFormat?: CodeFormat;\n contentDeclarationFormat?: ContentDeclarationFormat;\n};\n\ntype CodeCompProps = {\n children: string;\n fileName?: string;\n language: BundledLanguage;\n isDarkMode?: boolean;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n isDarkMode,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat,\n contentDeclarationFormat,\n isRollable = true,\n ...props\n}) => {\n const code = children?.endsWith('\\n') ? children.slice(0, -1) : children;\n\n const hadSelectInHeader =\n packageManager || codeFormat || contentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={codeFormat}\n contentDeclarationFormat={contentDeclarationFormat}\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs\">\n <span className=\"truncate\">{fileName ?? language}</span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {codeFormat && <CodeFormatSelector />}\n {contentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={code} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n <CodeBlock lang={language} isDarkMode={isDarkMode}>\n {code}\n </CodeBlock>\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAmCA,MAAM,aAAa;AAEnB,MAAa,QAA2B,EACtC,UACA,UACA,YACA,aAAa,MACb,kBAAkB,MAClB,WACA,UACA,gBACA,YACA,0BACA,aAAa,MACb,GAAG,YACC;CACJ,MAAM,OAAO,UAAU,SAAS,KAAK,GAAG,SAAS,MAAM,GAAG,GAAG,GAAG;CAEhE,MAAM,oBACJ,kBAAkB,cAAc;AAElC,QACE,oBAAC,uBAAD;EACkB;EACJ;EACc;YAE1B,qBAAC,WAAD;GACE,WAAW,GACT,iDACA,mBAAmB,yBACnB,UACD;GACD,cAAa;GACb,GAAI;aAPN,CASG,cACC,4CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KAAM,WAAU;eAAY,YAAY;KAAgB,GACxD,qBAAC,OAAD;KAAK,WAAU;eAAf;MACG,kBAAkB,oBAAC,wBAAD,EAA0B;MAC5C,cAAc,oBAAC,oBAAD,EAAsB;MACpC,4BACC,oBAAC,kCAAD,EAAoC;MAElC;OACF;OACN,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAW,GACT,mDACA,qBAAqB,OACtB;eAED,oBAAC,UAAD,EAAgB,MAAQ;KACpB;IACF,EACL,KAEL,oBAAC,gBAAD;IACE,WAAW;IACC;IACZ,WAAU;cAEV,oBAAC,WAAD;KAAW,MAAM;KAAsB;eACpC;KACS;IACG,EACP;;EACU"}
1
+ {"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeBlockHighlight } from './CodeBlockHighlight';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { useCodeContext } from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n /** Single format, or a JSON-array string like `[\"typescript\",\"esm\",\"commonjs\"]`. */\n codeFormat?: CodeFormat | string;\n contentDeclarationFormat?: ContentDeclarationFormat | string;\n};\n\ntype CodeCompProps = {\n children: string;\n fileName?: string;\n language: BundledLanguage;\n isDarkMode?: boolean;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\n/** Languages that use JSX syntax — CommonJS doesn't make sense for these. */\nconst JSX_LANGUAGES = new Set(['tsx', 'jsx']);\n\n/** Parse a codeFormat prop that may be a single value or a JSON-array string. */\nfunction parseFormats(raw: string | undefined): CodeFormat[] | undefined {\n if (!raw) return undefined;\n if (raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n /* ignore */\n }\n }\n return [raw as CodeFormat];\n}\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n isDarkMode,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat: rawCodeFormat,\n contentDeclarationFormat: rawContentDeclarationFormat,\n isRollable = true,\n ...props\n}) => {\n const {\n codeFormat: selectedCodeFormat,\n contentDeclarationFormat: selectedContentDeclarationFormat,\n } = useCodeContext();\n\n // Parse whichever attribute is present as an array of formats.\n const codeFormats = useMemo(\n () => parseFormats(rawCodeFormat as string | undefined),\n [rawCodeFormat]\n );\n const contentFormats = useMemo(\n () => parseFormats(rawContentDeclarationFormat as string | undefined),\n [rawContentDeclarationFormat]\n );\n\n // A block is \"multi-format\" when it has multiple formats including TypeScript\n // (the canonical source). Such blocks transform at runtime.\n const isMultiCodeFormat =\n codeFormats !== undefined &&\n codeFormats.length > 1 &&\n codeFormats.includes('typescript');\n\n const isMultiContentFormat =\n contentFormats !== undefined &&\n contentFormats.length > 1 &&\n contentFormats.includes('typescript');\n\n const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;\n\n // Determine which context format drives this block's selection.\n // content declaration blocks use selectedContentDeclarationFormat;\n // regular code blocks use selectedCodeFormat.\n const selectedFormat: CodeFormat = isMultiContentFormat\n ? (selectedContentDeclarationFormat as CodeFormat)\n : selectedCodeFormat;\n\n // The formats actually relevant for transformation (no 'json', no 'commonjs' for JSX).\n const effectiveFormats = useMemo<CodeFormat[] | undefined>(() => {\n const base = isMultiContentFormat ? contentFormats : codeFormats;\n if (!base) return base;\n let filtered = base.filter((f) => f !== 'json') as CodeFormat[];\n if (JSX_LANGUAGES.has(language as string)) {\n filtered = filtered.filter((f) => f !== 'commonjs');\n }\n return filtered;\n }, [isMultiContentFormat, contentFormats, codeFormats, language]);\n\n // When the globally-selected format isn't valid for this block\n // (e.g. CJS selected but this is a JSX file), fall back to the last valid one.\n const resolvedFormat: Exclude<CodeFormat, 'json'> = useMemo(() => {\n if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) {\n return selectedFormat as Exclude<CodeFormat, 'json'>;\n }\n return (\n effectiveFormats[effectiveFormats.length - 1] ?? 'typescript'\n ) as Exclude<CodeFormat, 'json'>;\n }, [effectiveFormats, selectedFormat]);\n\n // ── Async filename derivation (dynamic import of transformer) ──────────────\n // We derive the displayed fileName so the header updates when format changes.\n // deriveFileName is tiny but it lives inside codeTransformer, which is only\n // imported when actually needed (non-TypeScript selection).\n const [displayedFileName, setDisplayedFileName] = useState<\n string | undefined\n >(fileName);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setDisplayedFileName(fileName);\n return;\n }\n if (!fileName) return;\n\n let cancelled = false;\n (async () => {\n const { deriveFileName } = await import('./codeTransformer');\n if (!cancelled) setDisplayedFileName(deriveFileName(fileName, resolvedFormat));\n })();\n return () => { cancelled = true; };\n }, [fileName, isMultiFormat, resolvedFormat]);\n\n // ── Async copy text (transformed code for CopyCode) ───────────────────────\n const rawCode = useMemo(\n () => (children?.endsWith('\\n') ? children.slice(0, -1) : children),\n [children]\n );\n\n const [copyCode, setCopyCode] = useState<string>(rawCode);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setCopyCode(rawCode);\n return;\n }\n let cancelled = false;\n (async () => {\n const { transformCode } = await import('./codeTransformer');\n if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));\n })();\n return () => { cancelled = true; };\n }, [rawCode, isMultiFormat, resolvedFormat]);\n\n const hadSelectInHeader =\n packageManager || rawCodeFormat || rawContentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={rawCodeFormat as string | undefined}\n contentDeclarationFormat={\n rawContentDeclarationFormat as string | undefined\n }\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs\">\n <span className=\"truncate\">\n {displayedFileName ?? language}\n </span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {rawCodeFormat && (\n <CodeFormatSelector availableFormats={effectiveFormats} />\n )}\n {rawContentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={copyCode} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n {isMultiFormat ? (\n /*\n * Multi-format: CodeBlockHighlight manages both the transformation\n * (dynamic import of codeTransformer) and the Shiki highlighting in\n * a single useEffect. The previous highlighted output stays visible\n * while the new one loads — no white-text flash.\n */\n <CodeBlockHighlight\n originalLang={language}\n targetFormat={resolvedFormat}\n isDarkMode={isDarkMode}\n >\n {rawCode}\n </CodeBlockHighlight>\n ) : (\n /*\n * Single-format: use the original Suspense-based async Shiki renderer\n * (good for SSR / static content).\n */\n <CodeBlock lang={language} isDarkMode={isDarkMode}>\n {rawCode}\n </CodeBlock>\n )}\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAyCA,MAAM,aAAa;;AAGnB,MAAM,gBAAgB,IAAI,IAAI,CAAC,OAAO,MAAM,CAAC;;AAG7C,SAAS,aAAa,KAAmD;AACvE,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI,IAAI,WAAW,IAAI,CACrB,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,MAAM,QAAQ,OAAO,CAAE,QAAO;SAC5B;AAIV,QAAO,CAAC,IAAkB;;AAG5B,MAAa,QAA2B,EACtC,UACA,UACA,YACA,aAAa,MACb,kBAAkB,MAClB,WACA,UACA,gBACA,YAAY,eACZ,0BAA0B,6BAC1B,aAAa,MACb,GAAG,YACC;CACJ,MAAM,EACJ,YAAY,oBACZ,0BAA0B,qCACxB,gBAAgB;CAGpB,MAAM,cAAc,cACZ,aAAa,cAAoC,EACvD,CAAC,cAAc,CAChB;CACD,MAAM,iBAAiB,cACf,aAAa,4BAAkD,EACrE,CAAC,4BAA4B,CAC9B;CAID,MAAM,oBACJ,gBAAgB,UAChB,YAAY,SAAS,KACrB,YAAY,SAAS,aAAa;CAEpC,MAAM,uBACJ,mBAAmB,UACnB,eAAe,SAAS,KACxB,eAAe,SAAS,aAAa;CAEvC,MAAM,gBAAgB,qBAAqB;CAK3C,MAAM,iBAA6B,uBAC9B,mCACD;CAGJ,MAAM,mBAAmB,cAAwC;EAC/D,MAAM,OAAO,uBAAuB,iBAAiB;AACrD,MAAI,CAAC,KAAM,QAAO;EAClB,IAAI,WAAW,KAAK,QAAQ,MAAM,MAAM,OAAO;AAC/C,MAAI,cAAc,IAAI,SAAmB,CACvC,YAAW,SAAS,QAAQ,MAAM,MAAM,WAAW;AAErD,SAAO;IACN;EAAC;EAAsB;EAAgB;EAAa;EAAS,CAAC;CAIjE,MAAM,iBAA8C,cAAc;AAChE,MAAI,CAAC,oBAAoB,iBAAiB,SAAS,eAAe,CAChE,QAAO;AAET,SACE,iBAAiB,iBAAiB,SAAS,MAAM;IAElD,CAAC,kBAAkB,eAAe,CAAC;CAMtC,MAAM,CAAC,mBAAmB,wBAAwB,SAEhD,SAAS;AAEX,iBAAgB;AACd,MAAI,CAAC,iBAAiB,mBAAmB,cAAc;AACrD,wBAAqB,SAAS;AAC9B;;AAEF,MAAI,CAAC,SAAU;EAEf,IAAI,YAAY;AAChB,GAAC,YAAY;GACX,MAAM,EAAE,mBAAmB,MAAM,OAAO;AACxC,OAAI,CAAC,UAAW,sBAAqB,eAAe,UAAU,eAAe,CAAC;MAC5E;AACJ,eAAa;AAAE,eAAY;;IAC1B;EAAC;EAAU;EAAe;EAAe,CAAC;CAG7C,MAAM,UAAU,cACP,UAAU,SAAS,KAAK,GAAG,SAAS,MAAM,GAAG,GAAG,GAAG,UAC1D,CAAC,SAAS,CACX;CAED,MAAM,CAAC,UAAU,eAAe,SAAiB,QAAQ;AAEzD,iBAAgB;AACd,MAAI,CAAC,iBAAiB,mBAAmB,cAAc;AACrD,eAAY,QAAQ;AACpB;;EAEF,IAAI,YAAY;AAChB,GAAC,YAAY;GACX,MAAM,EAAE,kBAAkB,MAAM,OAAO;AACvC,OAAI,CAAC,UAAW,aAAY,cAAc,SAAS,eAAe,CAAC;MACjE;AACJ,eAAa;AAAE,eAAY;;IAC1B;EAAC;EAAS;EAAe;EAAe,CAAC;CAE5C,MAAM,oBACJ,kBAAkB,iBAAiB;AAErC,QACE,oBAAC,uBAAD;EACkB;EAChB,YAAY;EACZ,0BACE;YAGF,qBAAC,WAAD;GACE,WAAW,GACT,iDACA,mBAAmB,yBACnB,UACD;GACD,cAAa;GACb,GAAI;aAPN,CASG,cACC,8CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KAAM,WAAU;eACb,qBAAqB;KACjB,GACP,qBAAC,OAAD;KAAK,WAAU;eAAf;MACG,kBAAkB,oBAAC,wBAAD,EAA0B;MAC5C,iBACC,oBAAC,oBAAD,EAAoB,kBAAkB,kBAAoB;MAE3D,+BACC,oBAAC,kCAAD,EAAoC;MAElC;OACF;OACN,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAW,GACT,mDACA,qBAAqB,OACtB;eAED,oBAAC,UAAD,EAAU,MAAM,UAAY;KACxB;IACF,EACL,KAEL,oBAAC,gBAAD;IACE,WAAW;IACC;IACZ,WAAU;cAET,gBAOC,oBAAC,oBAAD;KACE,cAAc;KACd,cAAc;KACF;eAEX;KACkB,IAMrB,oBAAC,WAAD;KAAW,MAAM;KAAsB;eACpC;KACS;IAEC,EACP;;EACU"}
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+
6
+ //#region src/components/IDE/CodeBlockHighlight.tsx
7
+ /**
8
+ * Client-side Shiki highlighter that also handles TypeScript→ESM/CJS transformation.
9
+ *
10
+ * Everything runs inside a single useEffect so that:
11
+ * - The transformer is only dynamically imported when a non-TypeScript format is selected.
12
+ * - The previous highlighted HTML stays visible while the new one loads (no white-text flash).
13
+ */
14
+ /**
15
+ * Map display language names to Shiki grammar identifiers.
16
+ * Shiki's web bundle does not ship a separate 'jsx' grammar — tsx handles both.
17
+ */
18
+ const toShikiLang = (lang) => {
19
+ switch (lang) {
20
+ case "jsx": return "tsx";
21
+ case "mjs":
22
+ case "cjs": return "javascript";
23
+ default: return lang;
24
+ }
25
+ };
26
+ const CodeBlockHighlight = ({ children, originalLang, targetFormat, isDarkMode }) => {
27
+ const [html, setHtml] = useState(null);
28
+ const prevHtml = useRef(null);
29
+ useEffect(() => {
30
+ let cancelled = false;
31
+ (async () => {
32
+ try {
33
+ let code = children;
34
+ let shikiLang = toShikiLang(originalLang);
35
+ if (targetFormat !== "typescript") {
36
+ const { transformCode, deriveLanguage } = await import("./codeTransformer.mjs");
37
+ if (cancelled) return;
38
+ code = transformCode(children, targetFormat);
39
+ shikiLang = toShikiLang(deriveLanguage(originalLang, targetFormat));
40
+ }
41
+ const { codeToHtml } = await import("shiki/bundle/web");
42
+ if (cancelled) return;
43
+ const out = await codeToHtml(code, {
44
+ lang: shikiLang,
45
+ theme: isDarkMode ? "github-dark" : "github-light"
46
+ });
47
+ if (!cancelled) {
48
+ prevHtml.current = out;
49
+ setHtml(out);
50
+ }
51
+ } catch {
52
+ if (!cancelled) setHtml("");
53
+ }
54
+ })();
55
+ return () => {
56
+ cancelled = true;
57
+ };
58
+ }, [
59
+ children,
60
+ originalLang,
61
+ targetFormat,
62
+ isDarkMode
63
+ ]);
64
+ const display = html ?? prevHtml.current;
65
+ if (!display) return /* @__PURE__ */ jsx("pre", {
66
+ className: "min-w-0 max-w-full overflow-x-auto",
67
+ children: /* @__PURE__ */ jsx("code", { children })
68
+ });
69
+ return /* @__PURE__ */ jsx("div", {
70
+ dangerouslySetInnerHTML: { __html: display },
71
+ style: { backgroundColor: "transparent" }
72
+ });
73
+ };
74
+
75
+ //#endregion
76
+ export { CodeBlockHighlight };
77
+ //# sourceMappingURL=CodeBlockHighlight.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlockHighlight.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockHighlight.tsx"],"sourcesContent":["'use client';\n\n/**\n * Client-side Shiki highlighter that also handles TypeScript→ESM/CJS transformation.\n *\n * Everything runs inside a single useEffect so that:\n * - The transformer is only dynamically imported when a non-TypeScript format is selected.\n * - The previous highlighted HTML stays visible while the new one loads (no white-text flash).\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport type { CodeFormat } from './CodeContext';\n\ntype Props = {\n /** Raw TypeScript source code (the canonical \"source of truth\"). */\n children: string;\n /** Language of the source (e.g. 'tsx', 'typescript'). */\n originalLang: BundledLanguage;\n /** Currently selected format: 'typescript' | 'esm' | 'commonjs'. */\n targetFormat: Exclude<CodeFormat, 'json'>;\n isDarkMode?: boolean;\n};\n\n/**\n * Map display language names to Shiki grammar identifiers.\n * Shiki's web bundle does not ship a separate 'jsx' grammar — tsx handles both.\n */\nconst toShikiLang = (lang: string): string => {\n switch (lang) {\n case 'jsx':\n return 'tsx';\n case 'mjs':\n case 'cjs':\n return 'javascript';\n default:\n return lang;\n }\n};\n\nexport const CodeBlockHighlight = ({\n children,\n originalLang,\n targetFormat,\n isDarkMode,\n}: Props) => {\n const [html, setHtml] = useState<string | null>(null);\n const prevHtml = useRef<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n let code = children;\n let shikiLang = toShikiLang(originalLang);\n\n // Only import the transformer when we actually need it.\n if (targetFormat !== 'typescript') {\n const { transformCode, deriveLanguage } = await import(\n './codeTransformer'\n );\n if (cancelled) return;\n code = transformCode(children, targetFormat);\n shikiLang = toShikiLang(deriveLanguage(originalLang, targetFormat));\n }\n\n const { codeToHtml } = await import('shiki/bundle/web');\n if (cancelled) return;\n\n const out = await codeToHtml(code, {\n lang: shikiLang,\n theme: isDarkMode ? 'github-dark' : 'github-light',\n });\n\n if (!cancelled) {\n prevHtml.current = out;\n setHtml(out);\n }\n } catch {\n // Shiki failed (unknown language, etc.) — fall through to plain-text.\n if (!cancelled) setHtml('');\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [children, originalLang, targetFormat, isDarkMode]);\n\n // Keep the previous highlighted output visible while the new one is loading.\n // This prevents the white-text flash on format switches.\n const display = html ?? prevHtml.current;\n\n if (!display) {\n return (\n <pre className=\"min-w-0 max-w-full overflow-x-auto\">\n <code>{children}</code>\n </pre>\n );\n }\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: display }}\n style={{ backgroundColor: 'transparent' }}\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,MAAM,eAAe,SAAyB;AAC5C,SAAQ,MAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK;EACL,KAAK,MACH,QAAO;EACT,QACE,QAAO;;;AAIb,MAAa,sBAAsB,EACjC,UACA,cACA,cACA,iBACW;CACX,MAAM,CAAC,MAAM,WAAW,SAAwB,KAAK;CACrD,MAAM,WAAW,OAAsB,KAAK;AAE5C,iBAAgB;EACd,IAAI,YAAY;AAEhB,GAAC,YAAY;AACX,OAAI;IACF,IAAI,OAAO;IACX,IAAI,YAAY,YAAY,aAAa;AAGzC,QAAI,iBAAiB,cAAc;KACjC,MAAM,EAAE,eAAe,mBAAmB,MAAM,OAC9C;AAEF,SAAI,UAAW;AACf,YAAO,cAAc,UAAU,aAAa;AAC5C,iBAAY,YAAY,eAAe,cAAc,aAAa,CAAC;;IAGrE,MAAM,EAAE,eAAe,MAAM,OAAO;AACpC,QAAI,UAAW;IAEf,MAAM,MAAM,MAAM,WAAW,MAAM;KACjC,MAAM;KACN,OAAO,aAAa,gBAAgB;KACrC,CAAC;AAEF,QAAI,CAAC,WAAW;AACd,cAAS,UAAU;AACnB,aAAQ,IAAI;;WAER;AAEN,QAAI,CAAC,UAAW,SAAQ,GAAG;;MAE3B;AAEJ,eAAa;AACX,eAAY;;IAEb;EAAC;EAAU;EAAc;EAAc;EAAW,CAAC;CAItD,MAAM,UAAU,QAAQ,SAAS;AAEjC,KAAI,CAAC,QACH,QACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,QAAD,EAAO,UAAgB;EACnB;AAIV,QACE,oBAAC,OAAD;EACE,yBAAyB,EAAE,QAAQ,SAAS;EAC5C,OAAO,EAAE,iBAAiB,eAAe;EACzC"}
@@ -4,14 +4,25 @@ import { useCodeContext } from "./CodeContext.mjs";
4
4
  import { Fragment, jsx } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/components/IDE/CodeConditionalRenderer.tsx
7
+ /** Parse a codeFormat prop that may be a single value or a JSON array string. */
8
+ const parseCodeFormats = (raw) => {
9
+ if (raw === void 0) return void 0;
10
+ if (raw.startsWith("[")) try {
11
+ const parsed = JSON.parse(raw);
12
+ if (Array.isArray(parsed)) return parsed;
13
+ } catch {}
14
+ return [raw];
15
+ };
7
16
  const CodeConditionalRender = ({ children, ...props }) => {
8
17
  const { packageManager, codeFormat, contentDeclarationFormat } = useCodeContext();
9
18
  const isPackageManagerUndefined = typeof props.packageManager === "undefined";
10
19
  const isPackageManagerSelected = packageManager === props.packageManager;
11
- const isCodeFormatUndefined = typeof props.codeFormat === "undefined";
12
- const isCodeFormatSelected = codeFormat === props.codeFormat;
13
- const isContentDeclarationFormatUndefined = typeof props.contentDeclarationFormat === "undefined";
14
- const isContentDeclarationFormatSelected = contentDeclarationFormat === props.contentDeclarationFormat;
20
+ const formats = parseCodeFormats(props.codeFormat);
21
+ const isCodeFormatUndefined = formats === void 0;
22
+ const isCodeFormatSelected = formats ? formats.includes(codeFormat) : false;
23
+ const contentFormats = parseCodeFormats(props.contentDeclarationFormat);
24
+ const isContentDeclarationFormatUndefined = contentFormats === void 0;
25
+ const isContentDeclarationFormatSelected = contentFormats ? contentFormats.includes(contentDeclarationFormat) : false;
15
26
  if ((isPackageManagerUndefined || isPackageManagerSelected) && (isCodeFormatUndefined || isCodeFormatSelected) && (isContentDeclarationFormatUndefined || isContentDeclarationFormatSelected)) return children;
16
27
  return /* @__PURE__ */ jsx(Fragment, {});
17
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"CodeConditionalRenderer.mjs","names":[],"sources":["../../../../src/components/IDE/CodeConditionalRenderer.tsx"],"sourcesContent":["'use client';\n\nimport type { FC, PropsWithChildren } from 'react';\nimport type { CodeCompAttributes } from './Code';\nimport { useCodeContext } from './CodeContext';\n\nexport const CodeConditionalRender: FC<\n PropsWithChildren<CodeCompAttributes>\n> = ({ children, ...props }) => {\n const { packageManager, codeFormat, contentDeclarationFormat } =\n useCodeContext();\n\n const isPackageManagerUndefined = typeof props.packageManager === 'undefined';\n const isPackageManagerSelected = packageManager === props.packageManager;\n\n const isCodeFormatUndefined = typeof props.codeFormat === 'undefined';\n const isCodeFormatSelected = codeFormat === props.codeFormat;\n\n const isContentDeclarationFormatUndefined =\n typeof props.contentDeclarationFormat === 'undefined';\n const isContentDeclarationFormatSelected =\n contentDeclarationFormat === props.contentDeclarationFormat;\n\n if (\n (isPackageManagerUndefined || isPackageManagerSelected) &&\n (isCodeFormatUndefined || isCodeFormatSelected) &&\n (isContentDeclarationFormatUndefined || isContentDeclarationFormatSelected)\n ) {\n return children;\n }\n\n return <></>;\n};\n"],"mappings":";;;;;;AAMA,MAAa,yBAER,EAAE,UAAU,GAAG,YAAY;CAC9B,MAAM,EAAE,gBAAgB,YAAY,6BAClC,gBAAgB;CAElB,MAAM,4BAA4B,OAAO,MAAM,mBAAmB;CAClE,MAAM,2BAA2B,mBAAmB,MAAM;CAE1D,MAAM,wBAAwB,OAAO,MAAM,eAAe;CAC1D,MAAM,uBAAuB,eAAe,MAAM;CAElD,MAAM,sCACJ,OAAO,MAAM,6BAA6B;CAC5C,MAAM,qCACJ,6BAA6B,MAAM;AAErC,MACG,6BAA6B,8BAC7B,yBAAyB,0BACzB,uCAAuC,oCAExC,QAAO;AAGT,QAAO,gCAAK"}
1
+ {"version":3,"file":"CodeConditionalRenderer.mjs","names":[],"sources":["../../../../src/components/IDE/CodeConditionalRenderer.tsx"],"sourcesContent":["'use client';\n\nimport type { FC, PropsWithChildren } from 'react';\nimport type { CodeCompAttributes } from './Code';\nimport type { CodeFormat } from './CodeContext';\nimport { useCodeContext } from './CodeContext';\n\n/** Parse a codeFormat prop that may be a single value or a JSON array string. */\nconst parseCodeFormats = (\n raw: string | undefined\n): CodeFormat[] | undefined => {\n if (raw === undefined) return undefined;\n // JSON array string: `[\"typescript\",\"esm\",\"commonjs\"]`\n if (raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n // fall through\n }\n }\n return [raw as CodeFormat];\n};\n\nexport const CodeConditionalRender: FC<\n PropsWithChildren<CodeCompAttributes>\n> = ({ children, ...props }) => {\n const { packageManager, codeFormat, contentDeclarationFormat } =\n useCodeContext();\n\n const isPackageManagerUndefined = typeof props.packageManager === 'undefined';\n const isPackageManagerSelected = packageManager === props.packageManager;\n\n const formats = parseCodeFormats(props.codeFormat as string | undefined);\n const isCodeFormatUndefined = formats === undefined;\n const isCodeFormatSelected = formats ? formats.includes(codeFormat) : false;\n\n const contentFormats = parseCodeFormats(\n props.contentDeclarationFormat as string | undefined\n );\n const isContentDeclarationFormatUndefined = contentFormats === undefined;\n const isContentDeclarationFormatSelected = contentFormats\n ? contentFormats.includes(contentDeclarationFormat as CodeFormat)\n : false;\n\n if (\n (isPackageManagerUndefined || isPackageManagerSelected) &&\n (isCodeFormatUndefined || isCodeFormatSelected) &&\n (isContentDeclarationFormatUndefined || isContentDeclarationFormatSelected)\n ) {\n return children;\n }\n\n return <></>;\n};\n"],"mappings":";;;;;;;AAQA,MAAM,oBACJ,QAC6B;AAC7B,KAAI,QAAQ,OAAW,QAAO;AAE9B,KAAI,IAAI,WAAW,IAAI,CACrB,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,MAAM,QAAQ,OAAO,CAAE,QAAO;SAC5B;AAIV,QAAO,CAAC,IAAkB;;AAG5B,MAAa,yBAER,EAAE,UAAU,GAAG,YAAY;CAC9B,MAAM,EAAE,gBAAgB,YAAY,6BAClC,gBAAgB;CAElB,MAAM,4BAA4B,OAAO,MAAM,mBAAmB;CAClE,MAAM,2BAA2B,mBAAmB,MAAM;CAE1D,MAAM,UAAU,iBAAiB,MAAM,WAAiC;CACxE,MAAM,wBAAwB,YAAY;CAC1C,MAAM,uBAAuB,UAAU,QAAQ,SAAS,WAAW,GAAG;CAEtE,MAAM,iBAAiB,iBACrB,MAAM,yBACP;CACD,MAAM,sCAAsC,mBAAmB;CAC/D,MAAM,qCAAqC,iBACvC,eAAe,SAAS,yBAAuC,GAC/D;AAEJ,MACG,6BAA6B,8BAC7B,yBAAyB,0BACzB,uCAAuC,oCAExC,QAAO;AAGT,QAAO,gCAAK"}
@@ -6,9 +6,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { useIntlayer } from "react-intlayer";
7
7
 
8
8
  //#region src/components/IDE/CodeFormatSelector.tsx
9
- const CodeFormatSelector = () => {
9
+ const CodeFormatSelector = ({ availableFormats }) => {
10
10
  const { codeFormat, setCodeFormat, setContentDeclarationFormat } = useCodeContext();
11
11
  const content = useIntlayer("code-selectors");
12
+ const show = (format) => !availableFormats || availableFormats.includes(format);
12
13
  return /* @__PURE__ */ jsxs(Select, {
13
14
  value: codeFormat,
14
15
  onValueChange: (value) => {
@@ -20,15 +21,15 @@ const CodeFormatSelector = () => {
20
21
  "aria-label": content.codeFormat.label.value,
21
22
  children: /* @__PURE__ */ jsx(Select.Value, { placeholder: content.codeFormat.placeholder.value })
22
23
  }), /* @__PURE__ */ jsxs(Select.Content, { children: [
23
- /* @__PURE__ */ jsx(Select.Item, {
24
+ show("typescript") && /* @__PURE__ */ jsx(Select.Item, {
24
25
  value: "typescript",
25
26
  children: "TypeScript"
26
27
  }),
27
- /* @__PURE__ */ jsx(Select.Item, {
28
+ show("esm") && /* @__PURE__ */ jsx(Select.Item, {
28
29
  value: "esm",
29
30
  children: "ESM"
30
31
  }),
31
- /* @__PURE__ */ jsx(Select.Item, {
32
+ show("commonjs") && /* @__PURE__ */ jsx(Select.Item, {
32
33
  value: "commonjs",
33
34
  children: "CommonJS"
34
35
  })
@@ -1 +1 @@
1
- {"version":3,"file":"CodeFormatSelector.mjs","names":[],"sources":["../../../../src/components/IDE/CodeFormatSelector.tsx"],"sourcesContent":["'use client';\n\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Select } from '../Select';\nimport { useCodeContext } from './CodeContext';\n\nexport const CodeFormatSelector: FC = () => {\n const { codeFormat, setCodeFormat, setContentDeclarationFormat } =\n useCodeContext();\n const content = useIntlayer('code-selectors');\n\n return (\n <Select\n value={codeFormat}\n onValueChange={(value) => {\n setCodeFormat(value as typeof codeFormat);\n setContentDeclarationFormat(value as typeof codeFormat);\n }}\n >\n <Select.Trigger\n className=\"py-1!\"\n aria-label={content.codeFormat.label.value}\n >\n <Select.Value placeholder={content.codeFormat.placeholder.value} />\n </Select.Trigger>\n <Select.Content>\n <Select.Item value=\"typescript\">TypeScript</Select.Item>\n <Select.Item value=\"esm\">ESM</Select.Item>\n <Select.Item value=\"commonjs\">CommonJS</Select.Item>\n </Select.Content>\n </Select>\n );\n};\n"],"mappings":";;;;;;;;AAOA,MAAa,2BAA+B;CAC1C,MAAM,EAAE,YAAY,eAAe,gCACjC,gBAAgB;CAClB,MAAM,UAAU,YAAY,iBAAiB;AAE7C,QACE,qBAAC,QAAD;EACE,OAAO;EACP,gBAAgB,UAAU;AACxB,iBAAc,MAA2B;AACzC,+BAA4B,MAA2B;;YAJ3D,CAOE,oBAAC,OAAO,SAAR;GACE,WAAU;GACV,cAAY,QAAQ,WAAW,MAAM;aAErC,oBAAC,OAAO,OAAR,EAAc,aAAa,QAAQ,WAAW,YAAY,OAAS;GACpD,GACjB,qBAAC,OAAO,SAAR;GACE,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAa;IAAwB;GACxD,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAM;IAAiB;GAC1C,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAW;IAAsB;GACrC,IACV"}
1
+ {"version":3,"file":"CodeFormatSelector.mjs","names":[],"sources":["../../../../src/components/IDE/CodeFormatSelector.tsx"],"sourcesContent":["'use client';\n\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Select } from '../Select';\nimport type { CodeFormat } from './CodeContext';\nimport { useCodeContext } from './CodeContext';\n\ntype CodeFormatSelectorProps = {\n /** When provided, only the listed formats are shown as options. */\n availableFormats?: CodeFormat[];\n};\n\nexport const CodeFormatSelector: FC<CodeFormatSelectorProps> = ({\n availableFormats,\n}) => {\n const { codeFormat, setCodeFormat, setContentDeclarationFormat } =\n useCodeContext();\n const content = useIntlayer('code-selectors');\n\n const show = (format: CodeFormat) =>\n !availableFormats || availableFormats.includes(format);\n\n return (\n <Select\n value={codeFormat}\n onValueChange={(value) => {\n setCodeFormat(value as typeof codeFormat);\n setContentDeclarationFormat(value as typeof codeFormat);\n }}\n >\n <Select.Trigger\n className=\"py-1!\"\n aria-label={content.codeFormat.label.value}\n >\n <Select.Value placeholder={content.codeFormat.placeholder.value} />\n </Select.Trigger>\n <Select.Content>\n {show('typescript') && (\n <Select.Item value=\"typescript\">TypeScript</Select.Item>\n )}\n {show('esm') && <Select.Item value=\"esm\">ESM</Select.Item>}\n {show('commonjs') && (\n <Select.Item value=\"commonjs\">CommonJS</Select.Item>\n )}\n </Select.Content>\n </Select>\n );\n};\n"],"mappings":";;;;;;;;AAaA,MAAa,sBAAmD,EAC9D,uBACI;CACJ,MAAM,EAAE,YAAY,eAAe,gCACjC,gBAAgB;CAClB,MAAM,UAAU,YAAY,iBAAiB;CAE7C,MAAM,QAAQ,WACZ,CAAC,oBAAoB,iBAAiB,SAAS,OAAO;AAExD,QACE,qBAAC,QAAD;EACE,OAAO;EACP,gBAAgB,UAAU;AACxB,iBAAc,MAA2B;AACzC,+BAA4B,MAA2B;;YAJ3D,CAOE,oBAAC,OAAO,SAAR;GACE,WAAU;GACV,cAAY,QAAQ,WAAW,MAAM;aAErC,oBAAC,OAAO,OAAR,EAAc,aAAa,QAAQ,WAAW,YAAY,OAAS;GACpD,GACjB,qBAAC,OAAO,SAAR;GACG,KAAK,aAAa,IACjB,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAa;IAAwB;GAEzD,KAAK,MAAM,IAAI,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAM;IAAiB;GACzD,KAAK,WAAW,IACf,oBAAC,OAAO,MAAR;IAAa,OAAM;cAAW;IAAsB;GAEvC,IACV"}