@wallarm-org/design-system 0.22.0-rc-feature-WDS-74-attribute.2 → 0.22.1

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 (53) hide show
  1. package/dist/components/CodeSnippet/CodeSnippetActions.js +1 -1
  2. package/dist/components/CodeSnippet/CodeSnippetCode.js +21 -16
  3. package/dist/components/CodeSnippet/CodeSnippetContent.js +10 -10
  4. package/dist/components/CodeSnippet/CodeSnippetContext.d.ts +8 -0
  5. package/dist/components/CodeSnippet/CodeSnippetLineNumbers.js +10 -7
  6. package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +4 -1
  7. package/dist/components/CodeSnippet/CodeSnippetRoot.js +54 -3
  8. package/dist/components/CodeSnippet/InlineCodeSnippet.js +1 -1
  9. package/dist/components/CodeSnippet/index.d.ts +2 -0
  10. package/dist/components/CodeSnippet/index.js +2 -1
  11. package/dist/components/CodeSnippet/internal/CodeSnippetHighlights.js +7 -8
  12. package/dist/components/CodeSnippet/internal/ColorStickColumn.d.ts +2 -2
  13. package/dist/components/CodeSnippet/internal/ColorStickColumn.js +8 -8
  14. package/dist/components/CodeSnippet/internal/FoldColumn.d.ts +3 -0
  15. package/dist/components/CodeSnippet/internal/FoldColumn.js +42 -0
  16. package/dist/components/CodeSnippet/internal/FoldSummaryLine.d.ts +8 -0
  17. package/dist/components/CodeSnippet/internal/FoldSummaryLine.js +20 -0
  18. package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +7 -0
  19. package/dist/components/CodeSnippet/internal/FoldToggle.js +19 -0
  20. package/dist/components/CodeSnippet/internal/PrefixColumn.d.ts +2 -2
  21. package/dist/components/CodeSnippet/internal/PrefixColumn.js +8 -8
  22. package/dist/components/CodeSnippet/internal/ShowMoreButton.js +2 -2
  23. package/dist/components/CodeSnippet/lib/foldUtils.d.ts +28 -0
  24. package/dist/components/CodeSnippet/lib/foldUtils.js +80 -0
  25. package/dist/components/CodeSnippet/lib/httpFolds.d.ts +27 -0
  26. package/dist/components/CodeSnippet/lib/httpFolds.js +36 -0
  27. package/dist/components/Flex/Flex.d.ts +1 -1
  28. package/dist/components/SegmentedControl/SegmentedControlSeparator.d.ts +1 -1
  29. package/dist/components/Separator/Separator.d.ts +1 -1
  30. package/dist/components/Skeleton/Skeleton.d.ts +1 -1
  31. package/dist/components/SplitButton/SplitButton.d.ts +6 -0
  32. package/dist/components/SplitButton/SplitButton.js +18 -0
  33. package/dist/components/SplitButton/classes.d.ts +1 -0
  34. package/dist/components/SplitButton/classes.js +7 -0
  35. package/dist/components/SplitButton/index.d.ts +1 -0
  36. package/dist/components/SplitButton/index.js +2 -0
  37. package/dist/components/Stack/Stack.d.ts +1 -1
  38. package/dist/hooks/useCopyTooltip.js +5 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +2 -2
  41. package/dist/metadata/components.json +658 -1251
  42. package/dist/theme/index.css +1 -0
  43. package/dist/theme/semantic.css +32 -25
  44. package/dist/theme/utilities/code-snippet-bg.css +8 -0
  45. package/package.json +1 -1
  46. package/dist/components/Attribute/Attribute.d.ts +0 -9
  47. package/dist/components/Attribute/Attribute.js +0 -33
  48. package/dist/components/Attribute/AttributeLabel.d.ts +0 -15
  49. package/dist/components/Attribute/AttributeLabel.js +0 -58
  50. package/dist/components/Attribute/AttributeValue.d.ts +0 -6
  51. package/dist/components/Attribute/AttributeValue.js +0 -25
  52. package/dist/components/Attribute/index.d.ts +0 -3
  53. package/dist/components/Attribute/index.js +0 -4
@@ -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;
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { cn } from "../../../utils/cn.js";
4
+ import { useCodeSnippet } from "../hooks/index.js";
5
+ import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "../lib/lineStyles.js";
6
+ import { FoldToggle } from "./FoldToggle.js";
7
+ const FoldColumn = ()=>{
8
+ const { visibleDisplayItems, folds, collapsedFolds, toggleFold, size, lines } = useCodeSnippet();
9
+ const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
10
+ const foldByStartLine = useMemo(()=>new Map(folds.map((f)=>[
11
+ f.startLine,
12
+ f
13
+ ])), [
14
+ folds
15
+ ]);
16
+ return /*#__PURE__*/ jsx("div", {
17
+ className: "flex flex-col select-none",
18
+ "data-slot": "code-snippet-fold",
19
+ children: visibleDisplayItems.map((item)=>{
20
+ if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
21
+ className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-4'),
22
+ children: /*#__PURE__*/ jsx(FoldToggle, {
23
+ fold: item.fold,
24
+ isCollapsed: true,
25
+ onToggle: ()=>toggleFold(item.fold.id)
26
+ })
27
+ }, `fold-${item.fold.id}`);
28
+ const fold = foldByStartLine.get(item.lineNumber);
29
+ const lineConfig = lines.get(item.lineNumber);
30
+ const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
31
+ return /*#__PURE__*/ jsx("span", {
32
+ className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-4', colorStyles?.bg),
33
+ children: fold && !collapsedFolds.has(fold.id) ? /*#__PURE__*/ jsx(FoldToggle, {
34
+ fold: fold,
35
+ isCollapsed: false,
36
+ onToggle: ()=>toggleFold(fold.id)
37
+ }) : null
38
+ }, item.lineNumber);
39
+ })
40
+ });
41
+ };
42
+ export { FoldColumn };
@@ -0,0 +1,8 @@
1
+ import type { FC } from 'react';
2
+ import type { FoldRegion } from '../lib/foldUtils';
3
+ export declare const FoldSummaryLine: FC<{
4
+ fold: FoldRegion;
5
+ lineCount: number;
6
+ lineHeightClass: string;
7
+ onToggle: () => void;
8
+ }>;
@@ -0,0 +1,20 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { getFoldSummaryLabel } from "../lib/foldUtils.js";
4
+ const FoldSummaryLine = ({ fold, lineCount, lineHeightClass, onToggle })=>{
5
+ const label = getFoldSummaryLabel(fold, lineCount);
6
+ const ariaLabel = `Collapsed region: ${label}, ${lineCount} lines`;
7
+ return /*#__PURE__*/ jsx("button", {
8
+ type: "button",
9
+ className: cn(lineHeightClass, 'flex w-full items-center gap-4 px-4 text-text-secondary hover:text-text-primary hover:bg-bg-secondary-hover transition-colors cursor-pointer select-none text-left'),
10
+ "aria-expanded": false,
11
+ "aria-label": ariaLabel,
12
+ onClick: onToggle,
13
+ children: /*#__PURE__*/ jsx("span", {
14
+ className: "inline-flex items-center rounded-2 border border-border-secondary px-6 text-xs leading-sm",
15
+ children: label
16
+ })
17
+ });
18
+ };
19
+ FoldSummaryLine.displayName = 'FoldSummaryLine';
20
+ export { FoldSummaryLine };
@@ -0,0 +1,7 @@
1
+ import type { FC } from 'react';
2
+ import type { FoldRegion } from '../lib/foldUtils';
3
+ export declare const FoldToggle: FC<{
4
+ fold: FoldRegion;
5
+ isCollapsed: boolean;
6
+ onToggle: () => void;
7
+ }>;
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ChevronDown } from "../../../icons/ChevronDown.js";
3
+ import { ChevronRight } from "../../../icons/ChevronRight.js";
4
+ import { getFoldSummaryLabel } from "../lib/foldUtils.js";
5
+ const FoldToggle = ({ fold, isCollapsed, onToggle })=>{
6
+ const lineCount = fold.endLine - fold.startLine + 1;
7
+ const label = getFoldSummaryLabel(fold, lineCount);
8
+ const ariaLabel = isCollapsed ? `Expand ${label}` : `Collapse ${label}`;
9
+ return /*#__PURE__*/ jsx("button", {
10
+ type: "button",
11
+ className: "flex items-center justify-center w-16 h-16 rounded-2 text-text-secondary hover:text-text-primary hover:bg-bg-secondary-hover transition-colors cursor-pointer",
12
+ "aria-expanded": !isCollapsed,
13
+ "aria-label": ariaLabel,
14
+ onClick: onToggle,
15
+ children: isCollapsed ? /*#__PURE__*/ jsx(ChevronRight, {}) : /*#__PURE__*/ jsx(ChevronDown, {})
16
+ });
17
+ };
18
+ FoldToggle.displayName = 'FoldToggle';
19
+ export { FoldToggle };
@@ -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
  /** Prefix column component - renders all line prefixes in a separate column */
4
5
  export declare const PrefixColumn: FC<{
5
- lineCount: number;
6
- startingLineNumber: number;
6
+ visibleDisplayItems: DisplayItem[];
7
7
  lines: Map<number, LineConfig>;
8
8
  lineHeightClass: string;
9
9
  }>;
@@ -1,19 +1,19 @@
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 PrefixColumn = ({ lineCount, startingLineNumber, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
4
+ const PrefixColumn = ({ visibleDisplayItems, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
5
5
  className: "flex flex-col select-none",
6
6
  "data-slot": "code-snippet-prefix",
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, 'flex h-lh items-center justify-center px-8')
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, 'flex h-lh items-center justify-center px-8 bg-bg-primary', colorStyles?.text, colorStyles?.bg),
14
+ className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-8', colorStyles?.text, colorStyles?.bg),
15
15
  children: lineConfig?.prefix
16
- }, lineNumber);
16
+ }, item.lineNumber);
17
17
  })
18
18
  });
19
19
  export { PrefixColumn };
@@ -5,8 +5,8 @@ import { Button } from "../../Button/index.js";
5
5
  import { MIN_HIDDEN_LINES_THRESHOLD } from "../CodeSnippetContext.js";
6
6
  import { useCodeSnippet } from "../hooks/index.js";
7
7
  const ShowMoreButton = ()=>{
8
- const { totalLines, maxLines, isExpanded, setIsExpanded } = useCodeSnippet();
9
- const hiddenLines = totalLines - maxLines;
8
+ const { displayItems, maxLines, isExpanded, setIsExpanded } = useCodeSnippet();
9
+ const hiddenLines = displayItems.length - maxLines;
10
10
  if (maxLines <= 0 || hiddenLines < MIN_HIDDEN_LINES_THRESHOLD) return null;
11
11
  return /*#__PURE__*/ jsx("div", {
12
12
  "data-slot": "code-snippet-show-more",
@@ -0,0 +1,28 @@
1
+ export type FoldRegion = {
2
+ /** Unique identifier for this fold region */
3
+ id: string;
4
+ /** Inclusive start line number (1-based) */
5
+ startLine: number;
6
+ /** Inclusive end line number */
7
+ endLine: number;
8
+ /** Optional label for the collapsed summary. When omitted, auto-generated from line count. */
9
+ label?: string;
10
+ /** Whether the fold starts collapsed. Default: true */
11
+ defaultCollapsed?: boolean;
12
+ };
13
+ export type DisplayItem = {
14
+ type: 'line';
15
+ index: number;
16
+ lineNumber: number;
17
+ } | {
18
+ type: 'fold-summary';
19
+ fold: FoldRegion;
20
+ lineCount: number;
21
+ };
22
+ export declare function getFoldSummaryLabel(fold: FoldRegion, lineCount: number): string;
23
+ /**
24
+ * Validates fold regions and returns a clean, sorted list.
25
+ * Dev: logs warnings for invalid folds. Prod: silently filters them out.
26
+ */
27
+ export declare function validateFolds(folds: FoldRegion[], totalLines: number, startingLineNumber?: number): FoldRegion[];
28
+ export declare function buildDisplayItems(totalLines: number, folds: FoldRegion[], collapsedFolds: Set<string>, startingLineNumber: number): DisplayItem[];