@wallarm-org/design-system 0.22.0 → 0.23.0

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 (51) hide show
  1. package/dist/components/Attribute/Attribute.d.ts +9 -0
  2. package/dist/components/Attribute/Attribute.js +33 -0
  3. package/dist/components/Attribute/AttributeLabel.d.ts +6 -0
  4. package/dist/components/Attribute/AttributeLabel.js +16 -0
  5. package/dist/components/Attribute/AttributeLabelDescription.d.ts +6 -0
  6. package/dist/components/Attribute/AttributeLabelDescription.js +16 -0
  7. package/dist/components/Attribute/AttributeLabelInfo.d.ts +7 -0
  8. package/dist/components/Attribute/AttributeLabelInfo.js +29 -0
  9. package/dist/components/Attribute/AttributeValue.d.ts +6 -0
  10. package/dist/components/Attribute/AttributeValue.js +25 -0
  11. package/dist/components/Attribute/index.d.ts +5 -0
  12. package/dist/components/Attribute/index.js +6 -0
  13. package/dist/components/CodeSnippet/CodeSnippetActions.js +1 -1
  14. package/dist/components/CodeSnippet/CodeSnippetCode.js +21 -16
  15. package/dist/components/CodeSnippet/CodeSnippetContent.js +10 -10
  16. package/dist/components/CodeSnippet/CodeSnippetContext.d.ts +8 -0
  17. package/dist/components/CodeSnippet/CodeSnippetLineNumbers.js +10 -7
  18. package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +4 -1
  19. package/dist/components/CodeSnippet/CodeSnippetRoot.js +54 -3
  20. package/dist/components/CodeSnippet/InlineCodeSnippet.js +1 -1
  21. package/dist/components/CodeSnippet/index.d.ts +2 -0
  22. package/dist/components/CodeSnippet/index.js +2 -1
  23. package/dist/components/CodeSnippet/internal/CodeSnippetHighlights.js +7 -8
  24. package/dist/components/CodeSnippet/internal/ColorStickColumn.d.ts +2 -2
  25. package/dist/components/CodeSnippet/internal/ColorStickColumn.js +8 -8
  26. package/dist/components/CodeSnippet/internal/FoldColumn.d.ts +3 -0
  27. package/dist/components/CodeSnippet/internal/FoldColumn.js +42 -0
  28. package/dist/components/CodeSnippet/internal/FoldSummaryLine.d.ts +8 -0
  29. package/dist/components/CodeSnippet/internal/FoldSummaryLine.js +20 -0
  30. package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +7 -0
  31. package/dist/components/CodeSnippet/internal/FoldToggle.js +19 -0
  32. package/dist/components/CodeSnippet/internal/PrefixColumn.d.ts +2 -2
  33. package/dist/components/CodeSnippet/internal/PrefixColumn.js +8 -8
  34. package/dist/components/CodeSnippet/internal/ShowMoreButton.js +2 -2
  35. package/dist/components/CodeSnippet/lib/foldUtils.d.ts +28 -0
  36. package/dist/components/CodeSnippet/lib/foldUtils.js +80 -0
  37. package/dist/components/CodeSnippet/lib/httpFolds.d.ts +27 -0
  38. package/dist/components/CodeSnippet/lib/httpFolds.js +36 -0
  39. package/dist/components/Flex/Flex.d.ts +1 -1
  40. package/dist/components/SegmentedControl/SegmentedControlSeparator.d.ts +1 -1
  41. package/dist/components/Separator/Separator.d.ts +1 -1
  42. package/dist/components/Skeleton/Skeleton.d.ts +1 -1
  43. package/dist/components/Stack/Stack.d.ts +1 -1
  44. package/dist/hooks/useCopyTooltip.js +5 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +2 -1
  47. package/dist/metadata/components.json +1496 -26
  48. package/dist/theme/index.css +1 -0
  49. package/dist/theme/semantic.css +32 -25
  50. package/dist/theme/utilities/code-snippet-bg.css +8 -0
  51. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ import { type TestableProps } from '../../utils/testId';
3
+ export interface AttributeProps extends HTMLAttributes<HTMLDivElement>, TestableProps {
4
+ ref?: Ref<HTMLDivElement>;
5
+ /** Show skeleton placeholders instead of children */
6
+ loading?: boolean;
7
+ children?: ReactNode;
8
+ }
9
+ export declare const Attribute: FC<AttributeProps>;
@@ -0,0 +1,33 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
3
+ import { TestIdProvider } from "../../utils/testId.js";
4
+ import { Skeleton } from "../Skeleton/index.js";
5
+ const Attribute = ({ ref, loading = false, children, className, 'data-testid': testId, ...props })=>/*#__PURE__*/ jsx(TestIdProvider, {
6
+ value: testId,
7
+ children: /*#__PURE__*/ jsx("div", {
8
+ ...props,
9
+ ref: ref,
10
+ "data-testid": testId,
11
+ "data-slot": "attribute",
12
+ className: cn('flex flex-col', className),
13
+ children: loading ? /*#__PURE__*/ jsxs(Fragment, {
14
+ children: [
15
+ /*#__PURE__*/ jsx(Skeleton, {
16
+ width: "82px",
17
+ height: "16px",
18
+ rounded: 6
19
+ }),
20
+ /*#__PURE__*/ jsx("div", {
21
+ className: "pt-4",
22
+ children: /*#__PURE__*/ jsx(Skeleton, {
23
+ width: "100%",
24
+ height: "24px",
25
+ rounded: 6
26
+ })
27
+ })
28
+ ]
29
+ }) : children
30
+ })
31
+ });
32
+ Attribute.displayName = 'Attribute';
33
+ export { Attribute };
@@ -0,0 +1,6 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface AttributeLabelProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ children?: ReactNode;
5
+ }
6
+ export declare const AttributeLabel: FC<AttributeLabelProps>;
@@ -0,0 +1,16 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ const AttributeLabel = ({ ref, children, className, ...props })=>{
5
+ const testId = useTestId('label');
6
+ return /*#__PURE__*/ jsx("div", {
7
+ ...props,
8
+ ref: ref,
9
+ "data-testid": testId,
10
+ "data-slot": "attribute-label",
11
+ className: cn('flex items-center gap-4 flex-wrap font-sans-display text-sm font-normal text-text-secondary', className),
12
+ children: children
13
+ });
14
+ };
15
+ AttributeLabel.displayName = 'AttributeLabel';
16
+ export { AttributeLabel };
@@ -0,0 +1,6 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface AttributeLabelDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {
3
+ ref?: Ref<HTMLParagraphElement>;
4
+ children?: ReactNode;
5
+ }
6
+ export declare const AttributeLabelDescription: FC<AttributeLabelDescriptionProps>;
@@ -0,0 +1,16 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ const AttributeLabelDescription = ({ ref, children, className, ...props })=>{
5
+ const testId = useTestId("label-description");
6
+ return /*#__PURE__*/ jsx("p", {
7
+ ...props,
8
+ ref: ref,
9
+ "data-testid": testId,
10
+ "data-slot": "attribute-label-description",
11
+ className: cn('basis-full', className),
12
+ children: children
13
+ });
14
+ };
15
+ AttributeLabelDescription.displayName = "AttributeLabelDescription";
16
+ export { AttributeLabelDescription };
@@ -0,0 +1,7 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface AttributeLabelInfoProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
3
+ ref?: Ref<HTMLSpanElement>;
4
+ /** Tooltip content */
5
+ children: ReactNode;
6
+ }
7
+ export declare const AttributeLabelInfo: FC<AttributeLabelInfoProps>;
@@ -0,0 +1,29 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Info } from "../../icons/index.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
5
+ const AttributeLabelInfo = ({ ref, children, ...props })=>{
6
+ const testId = useTestId('label-info');
7
+ return /*#__PURE__*/ jsxs(Tooltip, {
8
+ children: [
9
+ /*#__PURE__*/ jsx(TooltipTrigger, {
10
+ asChild: true,
11
+ children: /*#__PURE__*/ jsx("span", {
12
+ ...props,
13
+ ref: ref,
14
+ "data-testid": testId,
15
+ "data-slot": "attribute-label-info",
16
+ className: "inline-flex cursor-help",
17
+ children: /*#__PURE__*/ jsx(Info, {
18
+ size: "md"
19
+ })
20
+ })
21
+ }),
22
+ /*#__PURE__*/ jsx(TooltipContent, {
23
+ children: children
24
+ })
25
+ ]
26
+ });
27
+ };
28
+ AttributeLabelInfo.displayName = 'AttributeLabelInfo';
29
+ export { AttributeLabelInfo };
@@ -0,0 +1,6 @@
1
+ import { type FC, type HTMLAttributes, type ReactNode, type Ref } from 'react';
2
+ export interface AttributeValueProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ children?: ReactNode;
5
+ }
6
+ export declare const AttributeValue: FC<AttributeValueProps>;
@@ -0,0 +1,25 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Children } from "react";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { useTestId } from "../../utils/testId.js";
5
+ import { Text } from "../Text/index.js";
6
+ function isEmpty(children) {
7
+ return null == children || false === children || 0 === Children.count(children);
8
+ }
9
+ const AttributeValue = ({ ref, children, className, ...props })=>{
10
+ const testId = useTestId('value');
11
+ return /*#__PURE__*/ jsx("div", {
12
+ ...props,
13
+ ref: ref,
14
+ "data-testid": testId,
15
+ "data-slot": "attribute-value",
16
+ className: cn('pt-4 min-h-[28px] flex items-center', className),
17
+ children: isEmpty(children) ? /*#__PURE__*/ jsx(Text, {
18
+ size: "sm",
19
+ color: "secondary",
20
+ children: "—"
21
+ }) : children
22
+ });
23
+ };
24
+ AttributeValue.displayName = 'AttributeValue';
25
+ export { AttributeValue };
@@ -0,0 +1,5 @@
1
+ export { Attribute, type AttributeProps } from './Attribute';
2
+ export { AttributeLabel, type AttributeLabelProps } from './AttributeLabel';
3
+ export { AttributeLabelDescription, type AttributeLabelDescriptionProps, } from './AttributeLabelDescription';
4
+ export { AttributeLabelInfo, type AttributeLabelInfoProps } from './AttributeLabelInfo';
5
+ export { AttributeValue, type AttributeValueProps } from './AttributeValue';
@@ -0,0 +1,6 @@
1
+ import { Attribute } from "./Attribute.js";
2
+ import { AttributeLabel } from "./AttributeLabel.js";
3
+ import { AttributeLabelDescription } from "./AttributeLabelDescription.js";
4
+ import { AttributeLabelInfo } from "./AttributeLabelInfo.js";
5
+ import { AttributeValue } from "./AttributeValue.js";
6
+ export { Attribute, AttributeLabel, AttributeLabelDescription, AttributeLabelInfo, AttributeValue };
@@ -7,7 +7,7 @@ const CodeSnippetActions = ({ className, ref, ...props })=>{
7
7
  ref: ref,
8
8
  "data-slot": "code-snippet-actions",
9
9
  "data-testid": testId,
10
- className: cn('flex items-center gap-4 ml-auto px-6 rounded-tr-6', className),
10
+ className: cn('flex items-center gap-4 ml-auto px-6 rounded-tr-6 code-snippet-bg', className),
11
11
  ...props
12
12
  });
13
13
  };
@@ -1,57 +1,62 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useTestId } from "../../utils/testId.js";
3
- import { MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
4
3
  import { useCodeSnippet } from "./hooks/index.js";
5
4
  import { CodeContent, CodeLine, TokenizedCodeLine } from "./internal/index.js";
5
+ import { FoldSummaryLine } from "./internal/FoldSummaryLine.js";
6
6
  import { SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
7
7
  import { splitTextByRanges } from "./lib/lineUtils.js";
8
8
  const CodeSnippetCode = ({ className, ...props })=>{
9
9
  const testId = useTestId('code');
10
- const { code, tokens, isLoading, wrapLines, startingLineNumber, lines, size, inlineGutter, showLineNumbers, totalLines, maxLines, isExpanded } = useCodeSnippet();
10
+ const { code, tokens, isLoading, wrapLines, lines, size, inlineGutter, showLineNumbers, visibleDisplayItems, toggleFold } = useCodeSnippet();
11
11
  const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
12
- const hiddenLines = totalLines - maxLines;
13
- const shouldClip = maxLines > 0 && !isExpanded && hiddenLines >= MIN_HIDDEN_LINES_THRESHOLD;
12
+ const renderFoldSummary = (item)=>/*#__PURE__*/ jsx(FoldSummaryLine, {
13
+ fold: item.fold,
14
+ lineCount: item.lineCount,
15
+ lineHeightClass: lineHeightClass,
16
+ onToggle: ()=>toggleFold(item.fold.id)
17
+ }, `fold-${item.fold.id}`);
14
18
  if (isLoading || !tokens) {
15
19
  const codeLines = code.split('\n');
16
- const visibleLines = shouldClip ? codeLines.slice(0, maxLines) : codeLines;
17
20
  return /*#__PURE__*/ jsx(CodeContent, {
18
21
  wrapLines: wrapLines,
19
22
  className: className,
20
23
  ...props,
21
24
  "data-testid": testId,
22
- children: visibleLines.map((line, index)=>{
23
- const lineNumber = startingLineNumber + index;
24
- const lineConfig = lines.get(lineNumber);
25
+ children: visibleDisplayItems.map((item)=>{
26
+ if ('fold-summary' === item.type) return renderFoldSummary(item);
27
+ const line = codeLines[item.index] ?? '';
28
+ const lineConfig = lines.get(item.lineNumber);
25
29
  const ranges = lineConfig?.ranges;
26
30
  const hasRanges = ranges && ranges.length > 0;
27
31
  return /*#__PURE__*/ jsx(CodeLine, {
28
32
  lineConfig: lineConfig,
29
33
  lineHeightClass: lineHeightClass,
30
34
  showInlineGutter: inlineGutter,
31
- lineNumber: showLineNumbers ? lineNumber : void 0,
35
+ lineNumber: showLineNumbers ? item.lineNumber : void 0,
32
36
  children: hasRanges ? splitTextByRanges(line, ranges, lineConfig?.color).map((segment, i)=>/*#__PURE__*/ jsx("span", {
33
37
  className: segment.rangeColor,
34
38
  children: segment.content
35
39
  }, i)) : line
36
- }, lineNumber);
40
+ }, item.lineNumber);
37
41
  })
38
42
  });
39
43
  }
40
- const visibleTokens = shouldClip ? tokens.slice(0, maxLines) : tokens;
41
44
  return /*#__PURE__*/ jsx(CodeContent, {
42
45
  wrapLines: wrapLines,
43
46
  className: className,
44
47
  ...props,
45
48
  "data-testid": testId,
46
- children: visibleTokens.map((lineTokens, index)=>{
47
- const lineNumber = startingLineNumber + index;
49
+ children: visibleDisplayItems.map((item)=>{
50
+ if ('fold-summary' === item.type) return renderFoldSummary(item);
51
+ const lineTokens = tokens[item.index];
52
+ if (!lineTokens) return null;
48
53
  return /*#__PURE__*/ jsx(TokenizedCodeLine, {
49
54
  tokens: lineTokens,
50
- lineConfig: lines.get(lineNumber),
55
+ lineConfig: lines.get(item.lineNumber),
51
56
  lineHeightClass: lineHeightClass,
52
57
  showInlineGutter: inlineGutter,
53
- lineNumber: showLineNumbers ? lineNumber : void 0
54
- }, lineNumber);
58
+ lineNumber: showLineNumbers ? item.lineNumber : void 0
59
+ }, item.lineNumber);
55
60
  })
56
61
  });
57
62
  };
@@ -7,21 +7,22 @@ import { CodeSnippetContext } from "./CodeSnippetContext.js";
7
7
  import { useCodeSnippet } from "./hooks/index.js";
8
8
  import { CodeSnippetHighlights } from "./internal/CodeSnippetHighlights.js";
9
9
  import { ColorStickColumn } from "./internal/ColorStickColumn.js";
10
+ import { FoldColumn } from "./internal/FoldColumn.js";
10
11
  import { PrefixColumn } from "./internal/PrefixColumn.js";
11
12
  import { SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
12
13
  const CodeSnippetContent = ({ className, children, nativeScroll = false, ...props })=>{
13
14
  const testId = useTestId('content');
14
15
  const context = useCodeSnippet();
15
- const { wrapLines, lines, totalLines, startingLineNumber, size } = context;
16
+ const { wrapLines, lines, visibleDisplayItems, folds, size } = context;
17
+ const hasFolds = folds.length > 0;
16
18
  const hasHighlights = Array.from(lines.values()).some((config)=>null != config.color);
17
19
  const hasAnyPrefix = Array.from(lines.values()).some((config)=>null != config.prefix);
18
- const lineCount = totalLines;
19
20
  const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
20
21
  const childrenArray = Children.toArray(children);
21
22
  const lineNumbersElement = childrenArray.find((child)=>/*#__PURE__*/ isValidElement(child) && child.type?.displayName === 'CodeSnippetLineNumbers');
22
23
  const otherChildren = childrenArray.filter((child)=>!/*#__PURE__*/ isValidElement(child) || child.type?.displayName !== 'CodeSnippetLineNumbers');
23
24
  const hasLineNumbers = Boolean(lineNumbersElement);
24
- const needsGutterElements = hasHighlights || hasLineNumbers || hasAnyPrefix;
25
+ const needsGutterElements = hasHighlights || hasLineNumbers || hasAnyPrefix || hasFolds;
25
26
  const useInlineGutter = wrapLines && needsGutterElements;
26
27
  const hasGutter = !wrapLines && needsGutterElements;
27
28
  const overriddenContext = useMemo(()=>({
@@ -39,18 +40,17 @@ const CodeSnippetContent = ({ className, children, nativeScroll = false, ...prop
39
40
  !wrapLines && hasHighlights && /*#__PURE__*/ jsx(CodeSnippetHighlights, {}),
40
41
  hasGutter && /*#__PURE__*/ jsxs("div", {
41
42
  "data-slot": "code-snippet-gutter",
42
- className: "sticky left-0 z-20 flex shrink-0 py-8 -my-8 mr-8 bg-bg-primary [contain:paint]",
43
+ className: "sticky left-0 z-20 flex shrink-0 py-8 -my-8 mr-8 code-snippet-bg [contain:paint]",
43
44
  children: [
44
45
  hasHighlights && /*#__PURE__*/ jsx(ColorStickColumn, {
45
- lineCount: lineCount,
46
- startingLineNumber: startingLineNumber,
46
+ visibleDisplayItems: visibleDisplayItems,
47
47
  lines: lines,
48
48
  lineHeightClass: lineHeightClass
49
49
  }),
50
50
  lineNumbersElement,
51
+ hasFolds && /*#__PURE__*/ jsx(FoldColumn, {}),
51
52
  hasAnyPrefix && /*#__PURE__*/ jsx(PrefixColumn, {
52
- lineCount: lineCount,
53
- startingLineNumber: startingLineNumber,
53
+ visibleDisplayItems: visibleDisplayItems,
54
54
  lines: lines,
55
55
  lineHeightClass: lineHeightClass
56
56
  })
@@ -68,14 +68,14 @@ const CodeSnippetContent = ({ className, children, nativeScroll = false, ...prop
68
68
  if (nativeScroll) return /*#__PURE__*/ jsx("div", {
69
69
  "data-slot": "code-snippet-content",
70
70
  "data-testid": testId,
71
- className: cn('min-h-0', wrapLines ? 'overflow-y-auto overflow-x-hidden' : 'overflow-auto', className),
71
+ className: cn('min-h-0 overscroll-none', wrapLines ? 'overflow-y-auto overflow-x-hidden' : 'overflow-auto', className),
72
72
  ...props,
73
73
  children: innerContent
74
74
  });
75
75
  return /*#__PURE__*/ jsx("div", {
76
76
  "data-slot": "code-snippet-content",
77
77
  "data-testid": testId,
78
- className: cn('min-h-0', className),
78
+ className: cn('min-h-0 [&_[data-part=viewport]]:overscroll-none', className),
79
79
  ...props,
80
80
  children: /*#__PURE__*/ jsxs(ScrollArea, {
81
81
  children: [
@@ -1,5 +1,6 @@
1
1
  import type { CSSProperties, ReactNode } from 'react';
2
2
  import type { SyntaxAdapter, Token } from './adapters/types';
3
+ import type { DisplayItem, FoldRegion } from './lib/foldUtils';
3
4
  export type CodeSnippetSize = 'sm' | 'md' | 'lg';
4
5
  export type LineColor = 'danger' | 'warning' | 'info' | 'success' | 'brand' | 'ai' | 'neutral';
5
6
  export type LineTextStyle = 'regular' | 'medium' | 'italic';
@@ -39,6 +40,13 @@ export type CodeSnippetContextValue<TLanguage extends string = string> = {
39
40
  showLineNumbers: boolean;
40
41
  lines: Map<number, LineConfig>;
41
42
  totalLines: number;
43
+ /** All display items (post-fold, pre-clip). Used for ShowMore counting. */
44
+ displayItems: DisplayItem[];
45
+ /** Display items clipped to maxLines. All rendering components should iterate this. */
46
+ visibleDisplayItems: DisplayItem[];
47
+ folds: FoldRegion[];
48
+ collapsedFolds: Set<string>;
49
+ toggleFold: (foldId: string) => void;
42
50
  isExpanded: boolean;
43
51
  maxLines: number;
44
52
  isFullscreen: boolean;
@@ -5,7 +5,7 @@ import { useCodeSnippet } from "./hooks/index.js";
5
5
  import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
6
6
  const CodeSnippetLineNumbers = ({ className, ...props })=>{
7
7
  const testId = useTestId('line-numbers');
8
- const { tokens, startingLineNumber, lines, size } = useCodeSnippet();
8
+ const { tokens, visibleDisplayItems, lines, size } = useCodeSnippet();
9
9
  const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
10
10
  if (!tokens) return null;
11
11
  return /*#__PURE__*/ jsx("div", {
@@ -13,14 +13,17 @@ const CodeSnippetLineNumbers = ({ className, ...props })=>{
13
13
  "data-testid": testId,
14
14
  className: cn('flex flex-col text-text-secondary select-none text-right', className),
15
15
  ...props,
16
- children: tokens.map((_, index)=>{
17
- const lineNumber = startingLineNumber + index;
18
- const lineConfig = lines.get(lineNumber);
16
+ children: visibleDisplayItems.map((item)=>{
17
+ if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
18
+ className: cn(lineHeightClass, 'px-8'),
19
+ children: item.fold.startLine
20
+ }, `fold-${item.fold.id}`);
21
+ const lineConfig = lines.get(item.lineNumber);
19
22
  const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
20
23
  return /*#__PURE__*/ jsx("span", {
21
- className: cn(lineHeightClass, 'px-8 bg-bg-primary', colorStyles?.text, colorStyles?.bg),
22
- children: lineNumber
23
- }, lineNumber);
24
+ className: cn(lineHeightClass, 'px-8', colorStyles?.text, colorStyles?.bg),
25
+ children: item.lineNumber
26
+ }, item.lineNumber);
24
27
  })
25
28
  });
26
29
  };
@@ -2,6 +2,7 @@ import type { HTMLAttributes, ReactNode, Ref } from 'react';
2
2
  import { type VariantProps } from 'class-variance-authority';
3
3
  import { type TestableProps } from '../../utils/testId';
4
4
  import { type LineConfig } from './CodeSnippetContext';
5
+ import { type FoldRegion } from './lib/foldUtils';
5
6
  declare const codeSnippetRootVariants: (props?: ({
6
7
  size?: "sm" | "md" | "lg" | null | undefined;
7
8
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -21,13 +22,15 @@ export type CodeSnippetRootProps<TLanguage extends string = string> = CodeSnippe
21
22
  wrapLines?: boolean;
22
23
  /** Max lines before collapsing (for ShowMore) */
23
24
  maxLines?: number;
25
+ /** Foldable regions that can be collapsed/expanded */
26
+ folds?: FoldRegion[];
24
27
  /** Callback when code is copied */
25
28
  onCopy?: (code: string) => void;
26
29
  /** Child components */
27
30
  children?: ReactNode;
28
31
  };
29
32
  export declare const CodeSnippetRoot: {
30
- <TLanguage extends string = string>({ code, language, size, lines, startingLineNumber, wrapLines: initialWrapLines, maxLines, onCopy, className, children, "data-testid": testId, ...props }: CodeSnippetRootProps<TLanguage>): import("react/jsx-runtime").JSX.Element;
33
+ <TLanguage extends string = string>({ code, language, size, lines, startingLineNumber, wrapLines: initialWrapLines, maxLines, folds: foldsProp, onCopy, className, children, "data-testid": testId, ...props }: CodeSnippetRootProps<TLanguage>): import("react/jsx-runtime").JSX.Element;
31
34
  displayName: string;
32
35
  };
33
36
  export {};
@@ -5,10 +5,11 @@ import { cva } from "class-variance-authority";
5
5
  import { cn } from "../../utils/cn.js";
6
6
  import { TestIdProvider } from "../../utils/testId.js";
7
7
  import { plainAdapter } from "./adapters/plain.js";
8
- import { CodeSnippetContext } from "./CodeSnippetContext.js";
8
+ import { CodeSnippetContext, MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
9
9
  import { useAdapter } from "./hooks/index.js";
10
10
  import { ShowMoreButton } from "./internal/ShowMoreButton.js";
11
- const codeSnippetRootVariants = cva("relative bg-component-code-snippet-bg rounded-6 font-mono text-syntax-no-syntax overflow-hidden flex flex-col [&::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&::selection]:text-[var(--color-syntax-highlight-selected-code)] [&_*::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&_*::selection]:text-[var(--color-syntax-highlight-selected-code)] [&>[data-slot=code-snippet-actions]]:absolute [&>[data-slot=code-snippet-actions]]:right-0 [&>[data-slot=code-snippet-actions]]:top-0 [&>[data-slot=code-snippet-actions]]:z-30 [&>[data-slot=code-snippet-actions]]:p-6 [&>[data-slot=code-snippet-actions]]:rounded-br-6 [&>[data-slot=code-snippet-actions]]:rounded-tl-6", {
11
+ import { buildDisplayItems, validateFolds } from "./lib/foldUtils.js";
12
+ const codeSnippetRootVariants = cva("relative code-snippet-bg rounded-6 font-mono text-syntax-no-syntax overflow-hidden flex flex-col [&::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&::selection]:text-[var(--color-syntax-highlight-selected-code)] [&_*::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&_*::selection]:text-[var(--color-syntax-highlight-selected-code)] [&>[data-slot=code-snippet-actions]]:absolute [&>[data-slot=code-snippet-actions]]:right-0 [&>[data-slot=code-snippet-actions]]:top-0 [&>[data-slot=code-snippet-actions]]:z-30 [&>[data-slot=code-snippet-actions]]:p-6 [&>[data-slot=code-snippet-actions]]:rounded-br-6 [&>[data-slot=code-snippet-actions]]:rounded-tl-6", {
12
13
  variants: {
13
14
  size: {
14
15
  sm: 'text-xs leading-sm',
@@ -20,7 +21,8 @@ const codeSnippetRootVariants = cva("relative bg-component-code-snippet-bg round
20
21
  size: 'sm'
21
22
  }
22
23
  });
23
- const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, startingLineNumber = 1, wrapLines: initialWrapLines = false, maxLines = 0, onCopy, className, children, 'data-testid': testId, ...props })=>{
24
+ const EMPTY_LINES = {};
25
+ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_LINES, startingLineNumber = 1, wrapLines: initialWrapLines = false, maxLines = 0, folds: foldsProp, onCopy, className, children, 'data-testid': testId, ...props })=>{
24
26
  const adapterContext = useAdapter();
25
27
  const [wrapLines, setWrapLines] = useState(initialWrapLines);
26
28
  const [isExpanded, setIsExpanded] = useState(false);
@@ -79,6 +81,45 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
79
81
  onCopy
80
82
  ]);
81
83
  const totalLines = tokens?.length ?? code.split('\n').length;
84
+ const validatedFolds = useMemo(()=>foldsProp ? validateFolds(foldsProp, totalLines, startingLineNumber) : [], [
85
+ foldsProp,
86
+ totalLines,
87
+ startingLineNumber
88
+ ]);
89
+ const computeCollapsedFolds = useCallback((folds)=>{
90
+ if (!folds) return new Set();
91
+ return new Set(folds.filter((f)=>false !== f.defaultCollapsed).map((f)=>f.id));
92
+ }, []);
93
+ const [collapsedFolds, setCollapsedFolds] = useState(()=>computeCollapsedFolds(foldsProp));
94
+ useEffect(()=>{
95
+ setCollapsedFolds(computeCollapsedFolds(foldsProp));
96
+ }, [
97
+ foldsProp,
98
+ computeCollapsedFolds
99
+ ]);
100
+ const toggleFold = useCallback((foldId)=>{
101
+ setCollapsedFolds((prev)=>{
102
+ const next = new Set(prev);
103
+ if (next.has(foldId)) next.delete(foldId);
104
+ else next.add(foldId);
105
+ return next;
106
+ });
107
+ }, []);
108
+ const displayItems = useMemo(()=>buildDisplayItems(totalLines, validatedFolds, collapsedFolds, startingLineNumber), [
109
+ totalLines,
110
+ validatedFolds,
111
+ collapsedFolds,
112
+ startingLineNumber
113
+ ]);
114
+ const visibleDisplayItems = useMemo(()=>{
115
+ const hiddenRows = displayItems.length - maxLines;
116
+ const shouldClip = maxLines > 0 && !isExpanded && hiddenRows >= MIN_HIDDEN_LINES_THRESHOLD;
117
+ return shouldClip ? displayItems.slice(0, maxLines) : displayItems;
118
+ }, [
119
+ displayItems,
120
+ maxLines,
121
+ isExpanded
122
+ ]);
82
123
  const contextValue = useMemo(()=>({
83
124
  code,
84
125
  language,
@@ -94,6 +135,11 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
94
135
  v
95
136
  ])),
96
137
  totalLines,
138
+ displayItems,
139
+ visibleDisplayItems,
140
+ folds: validatedFolds,
141
+ collapsedFolds,
142
+ toggleFold,
97
143
  isExpanded,
98
144
  maxLines,
99
145
  isFullscreen,
@@ -112,6 +158,11 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
112
158
  startingLineNumber,
113
159
  lines,
114
160
  totalLines,
161
+ displayItems,
162
+ visibleDisplayItems,
163
+ validatedFolds,
164
+ collapsedFolds,
165
+ toggleFold,
115
166
  isExpanded,
116
167
  maxLines,
117
168
  isFullscreen,
@@ -4,7 +4,7 @@ import { cva } from "class-variance-authority";
4
4
  import { useCopyTooltip } from "../../hooks/index.js";
5
5
  import { cn } from "../../utils/cn.js";
6
6
  import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
7
- const inlineCodeSnippetVariants = cva("inline-flex items-center bg-component-code-snippet-bg rounded-6 font-mono font-normal text-syntax-no-syntax", {
7
+ const inlineCodeSnippetVariants = cva("inline-flex items-center code-snippet-bg rounded-6 font-mono font-normal text-syntax-no-syntax", {
8
8
  variants: {
9
9
  size: {
10
10
  sm: 'text-xs px-4 py-2',
@@ -16,3 +16,5 @@ export { CodeSnippetTitle, type CodeSnippetTitleProps } from './CodeSnippetTitle
16
16
  export { CodeSnippetWrapButton, type CodeSnippetWrapButtonProps } from './CodeSnippetWrapButton';
17
17
  export { useAdapter, useCodeSnippet } from './hooks';
18
18
  export { InlineCodeSnippet, type InlineCodeSnippetProps } from './InlineCodeSnippet';
19
+ export { type FoldRegion } from './lib/foldUtils';
20
+ export { getHttpFolds, HTTP_FOLD_ID, type HttpFoldOptions, type HttpFoldSectionOptions, } from './lib/httpFolds';
@@ -14,4 +14,5 @@ import { CodeSnippetTitle } from "./CodeSnippetTitle.js";
14
14
  import { CodeSnippetWrapButton } from "./CodeSnippetWrapButton.js";
15
15
  import { useAdapter, useCodeSnippet } from "./hooks/index.js";
16
16
  import { InlineCodeSnippet } from "./InlineCodeSnippet.js";
17
- export { CodeSnippetActions, CodeSnippetAdapterProvider, CodeSnippetCode, CodeSnippetContent, CodeSnippetCopyButton, CodeSnippetFullscreenButton, CodeSnippetHeader, CodeSnippetLineNumbers, CodeSnippetRoot, CodeSnippetTab, CodeSnippetTabs, CodeSnippetTitle, CodeSnippetWrapButton, InlineCodeSnippet, loadHighlightJsAdapter, loadPrismAdapter, loadShikiAdapter, plainAdapter, useAdapter, useCodeSnippet };
17
+ import { HTTP_FOLD_ID, getHttpFolds } from "./lib/httpFolds.js";
18
+ export { CodeSnippetActions, CodeSnippetAdapterProvider, CodeSnippetCode, CodeSnippetContent, CodeSnippetCopyButton, CodeSnippetFullscreenButton, CodeSnippetHeader, CodeSnippetLineNumbers, CodeSnippetRoot, CodeSnippetTab, CodeSnippetTabs, CodeSnippetTitle, CodeSnippetWrapButton, HTTP_FOLD_ID, InlineCodeSnippet, getHttpFolds, loadHighlightJsAdapter, loadPrismAdapter, loadShikiAdapter, plainAdapter, useAdapter, useCodeSnippet };
@@ -3,22 +3,21 @@ import { cn } from "../../../utils/cn.js";
3
3
  import { useCodeSnippet } from "../hooks/index.js";
4
4
  import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "../lib/lineStyles.js";
5
5
  const CodeSnippetHighlights = ()=>{
6
- const { totalLines, startingLineNumber, lines, size } = useCodeSnippet();
7
- const lineCount = totalLines;
6
+ const { visibleDisplayItems, lines, size } = useCodeSnippet();
8
7
  const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
9
8
  return /*#__PURE__*/ jsx("div", {
10
9
  "data-slot": "code-snippet-highlights",
11
10
  className: "absolute inset-0 z-0 flex flex-col py-8 pointer-events-none",
12
11
  "aria-hidden": "true",
13
- children: Array.from({
14
- length: lineCount
15
- }, (_, index)=>{
16
- const lineNumber = startingLineNumber + index;
17
- const lineConfig = lines.get(lineNumber);
12
+ children: visibleDisplayItems.map((item)=>{
13
+ if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("div", {
14
+ className: lineHeightClass
15
+ }, `fold-${item.fold.id}`);
16
+ const lineConfig = lines.get(item.lineNumber);
18
17
  const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : null;
19
18
  return /*#__PURE__*/ jsx("div", {
20
19
  className: cn(lineHeightClass, colorStyles?.bg)
21
- }, lineNumber);
20
+ }, item.lineNumber);
22
21
  })
23
22
  });
24
23
  };
@@ -1,9 +1,9 @@
1
1
  import type { FC } from 'react';
2
2
  import type { LineConfig } from '../CodeSnippetContext';
3
+ import type { DisplayItem } from '../lib/foldUtils';
3
4
  /** Color stick column component - renders color indicators for each line */
4
5
  export declare const ColorStickColumn: FC<{
5
- lineCount: number;
6
- startingLineNumber: number;
6
+ visibleDisplayItems: DisplayItem[];
7
7
  lines: Map<number, LineConfig>;
8
8
  lineHeightClass: string;
9
9
  }>;
@@ -1,18 +1,18 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../../../utils/cn.js";
3
3
  import { LINE_COLOR_STYLES } from "../lib/lineStyles.js";
4
- const ColorStickColumn = ({ lineCount, startingLineNumber, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
4
+ const ColorStickColumn = ({ visibleDisplayItems, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
5
5
  className: "flex flex-col",
6
6
  "data-slot": "code-snippet-color-stick",
7
- children: Array.from({
8
- length: lineCount
9
- }, (_, index)=>{
10
- const lineNumber = startingLineNumber + index;
11
- const lineConfig = lines.get(lineNumber);
7
+ children: visibleDisplayItems.map((item)=>{
8
+ if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
9
+ className: cn(lineHeightClass, 'border-l-2 pl-12 border-transparent')
10
+ }, `fold-${item.fold.id}`);
11
+ const lineConfig = lines.get(item.lineNumber);
12
12
  const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
13
13
  return /*#__PURE__*/ jsx("span", {
14
- className: cn(lineHeightClass, 'border-l-2 pl-12', 'bg-bg-primary', colorStyles?.border ?? 'border-transparent', colorStyles?.bg)
15
- }, lineNumber);
14
+ className: cn(lineHeightClass, 'border-l-2 pl-12', colorStyles?.border ?? 'border-transparent', colorStyles?.bg)
15
+ }, item.lineNumber);
16
16
  })
17
17
  });
18
18
  export { ColorStickColumn };
@@ -0,0 +1,3 @@
1
+ import type { FC } from 'react';
2
+ /** Fold toggle column — renders fold chevrons for each row */
3
+ export declare const FoldColumn: FC;