@wallarm-org/design-system 1.30.0 → 1.31.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.
- package/dist/components/CodeEditor/CodeEditorContent.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContent.js +197 -0
- package/dist/components/CodeEditor/CodeEditorContext.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContext.js +8 -0
- package/dist/components/CodeEditor/CodeEditorRoot.d.ts +77 -0
- package/dist/components/CodeEditor/CodeEditorRoot.js +203 -0
- package/dist/components/CodeEditor/classes.d.ts +21 -0
- package/dist/components/CodeEditor/classes.js +41 -0
- package/dist/components/CodeEditor/engine/SearchPanel.d.ts +13 -0
- package/dist/components/CodeEditor/engine/SearchPanel.js +201 -0
- package/dist/components/CodeEditor/engine/adapterPainter.d.ts +23 -0
- package/dist/components/CodeEditor/engine/adapterPainter.js +110 -0
- package/dist/components/CodeEditor/engine/babelSyntax.d.ts +15 -0
- package/dist/components/CodeEditor/engine/babelSyntax.js +54 -0
- package/dist/components/CodeEditor/engine/completion.d.ts +17 -0
- package/dist/components/CodeEditor/engine/completion.js +129 -0
- package/dist/components/CodeEditor/engine/contentAttributes.d.ts +12 -0
- package/dist/components/CodeEditor/engine/contentAttributes.js +24 -0
- package/dist/components/CodeEditor/engine/diagnostics.d.ts +73 -0
- package/dist/components/CodeEditor/engine/diagnostics.js +327 -0
- package/dist/components/CodeEditor/engine/diff.d.ts +37 -0
- package/dist/components/CodeEditor/engine/diff.js +145 -0
- package/dist/components/CodeEditor/engine/folds.d.ts +25 -0
- package/dist/components/CodeEditor/engine/folds.js +458 -0
- package/dist/components/CodeEditor/engine/gutters.d.ts +22 -0
- package/dist/components/CodeEditor/engine/gutters.js +160 -0
- package/dist/components/CodeEditor/engine/index.d.ts +12 -0
- package/dist/components/CodeEditor/engine/index.js +387 -0
- package/dist/components/CodeEditor/engine/languages/http/index.d.ts +20 -0
- package/dist/components/CodeEditor/engine/languages/http/index.js +69 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.d.ts +2 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.js +31 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.d.ts +1 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.js +2 -0
- package/dist/components/CodeEditor/engine/languages/index.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/index.js +41 -0
- package/dist/components/CodeEditor/engine/languages/json.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/json.js +11 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.d.ts +47 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.js +125 -0
- package/dist/components/CodeEditor/engine/languages/yaml.d.ts +4 -0
- package/dist/components/CodeEditor/engine/languages/yaml.js +11 -0
- package/dist/components/CodeEditor/engine/lines.d.ts +22 -0
- package/dist/components/CodeEditor/engine/lines.js +131 -0
- package/dist/components/CodeEditor/engine/luaSyntax.d.ts +14 -0
- package/dist/components/CodeEditor/engine/luaSyntax.js +36 -0
- package/dist/components/CodeEditor/engine/positions.d.ts +19 -0
- package/dist/components/CodeEditor/engine/positions.js +22 -0
- package/dist/components/CodeEditor/engine/schema/complete.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/complete.js +211 -0
- package/dist/components/CodeEditor/engine/schema/hover.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/hover.js +73 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.d.ts +19 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.js +32 -0
- package/dist/components/CodeEditor/engine/schema/validate.d.ts +9 -0
- package/dist/components/CodeEditor/engine/schema/validate.js +67 -0
- package/dist/components/CodeEditor/engine/search.d.ts +13 -0
- package/dist/components/CodeEditor/engine/search.js +84 -0
- package/dist/components/CodeEditor/engine/searchMatches.d.ts +8 -0
- package/dist/components/CodeEditor/engine/searchMatches.js +15 -0
- package/dist/components/CodeEditor/engine/theme.d.ts +8 -0
- package/dist/components/CodeEditor/engine/theme.js +89 -0
- package/dist/components/CodeEditor/engine/types.d.ts +44 -0
- package/dist/components/CodeEditor/engine/types.js +0 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.d.ts +6 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.js +3 -0
- package/dist/components/CodeEditor/index.d.ts +6 -0
- package/dist/components/CodeEditor/index.js +5 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.d.ts +11 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.js +11 -0
- package/dist/components/CodeEditor/lib/httpCompletions.d.ts +7 -0
- package/dist/components/CodeEditor/lib/httpCompletions.js +102 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.d.ts +29 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.js +650 -0
- package/dist/components/CodeEditor/lib/keyboardHint.d.ts +5 -0
- package/dist/components/CodeEditor/lib/keyboardHint.js +2 -0
- package/dist/components/CodeEditor/lib/loadEngine.d.ts +7 -0
- package/dist/components/CodeEditor/lib/loadEngine.js +9 -0
- package/dist/components/CodeEditor/lib/portalRegistry.d.ts +20 -0
- package/dist/components/CodeEditor/lib/portalRegistry.js +47 -0
- package/dist/components/CodeEditor/lib/splitContentProps.d.ts +12 -0
- package/dist/components/CodeEditor/lib/splitContentProps.js +28 -0
- package/dist/components/CodeEditor/types.d.ts +72 -0
- package/dist/components/CodeEditor/types.js +0 -0
- package/dist/components/CodeSnippet/CodeSnippetCode.js +6 -23
- package/dist/components/CodeSnippet/CodeSnippetCopyButton.js +4 -3
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.d.ts +25 -0
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.js +3 -0
- package/dist/components/CodeSnippet/CodeSnippetFullscreenButton.js +2 -2
- package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +3 -5
- package/dist/components/CodeSnippet/CodeSnippetRoot.js +56 -50
- package/dist/components/CodeSnippet/CodeSnippetShowMoreButton.js +7 -5
- package/dist/components/CodeSnippet/CodeSnippetWrapButton.js +2 -2
- package/dist/components/CodeSnippet/adapters/highlightjs.js +1 -0
- package/dist/components/CodeSnippet/adapters/prism.js +3 -1
- package/dist/components/CodeSnippet/adapters/shiki.js +1 -0
- package/dist/components/CodeSnippet/adapters/types.d.ts +3 -3
- package/dist/components/CodeSnippet/classes.d.ts +3 -0
- package/dist/components/CodeSnippet/classes.js +14 -0
- package/dist/components/CodeSnippet/hooks/index.d.ts +1 -0
- package/dist/components/CodeSnippet/hooks/index.js +1 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.d.ts +2 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.js +8 -0
- package/dist/components/CodeSnippet/index.d.ts +2 -1
- package/dist/components/CodeSnippet/index.js +1 -1
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.d.ts +22 -0
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.js +81 -0
- package/dist/components/CodeSnippet/internal/CodeToken.d.ts +2 -1
- package/dist/components/CodeSnippet/internal/CodeToken.js +1 -1
- package/dist/components/CodeSnippet/internal/FoldSummary.d.ts +11 -0
- package/dist/components/CodeSnippet/internal/FoldSummary.js +27 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +2 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.js +2 -1
- package/dist/components/CodeSnippet/internal/index.d.ts +1 -0
- package/dist/components/CodeSnippet/internal/index.js +1 -0
- package/dist/components/CodeSnippet/lib/foldUtils.d.ts +9 -2
- package/dist/components/CodeSnippet/lib/foldUtils.js +2 -2
- package/dist/components/CodeSnippet/lib/showMore.d.ts +7 -0
- package/dist/components/CodeSnippet/lib/showMore.js +7 -0
- package/dist/components/Copyable/Copyable.d.ts +5 -2
- package/dist/components/Copyable/Copyable.js +1 -1
- package/dist/metadata/components.json +1235 -5
- package/package.json +24 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** A React node to render into a DOM host owned by the editor engine (gutter marker, widget, panel). */
|
|
3
|
+
export interface PortalEntry {
|
|
4
|
+
id: number;
|
|
5
|
+
host: HTMLElement;
|
|
6
|
+
node: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* External store connecting plain-DOM CodeMirror extensions to React.
|
|
10
|
+
* The engine registers `(host, node)` pairs; `PortalOutlet` renders each one with `createPortal`.
|
|
11
|
+
*/
|
|
12
|
+
export interface PortalRegistry {
|
|
13
|
+
register(host: HTMLElement, node: ReactNode): number;
|
|
14
|
+
update(id: number, node: ReactNode): void;
|
|
15
|
+
unregister(id: number): void;
|
|
16
|
+
subscribe(listener: () => void): () => void;
|
|
17
|
+
/** Stable reference until a change */
|
|
18
|
+
getSnapshot(): readonly PortalEntry[];
|
|
19
|
+
}
|
|
20
|
+
export declare const createPortalRegistry: () => PortalRegistry;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const createPortalRegistry = ()=>{
|
|
2
|
+
let nextId = 1;
|
|
3
|
+
let entries = [];
|
|
4
|
+
const listeners = new Set();
|
|
5
|
+
const emit = (next)=>{
|
|
6
|
+
entries = next;
|
|
7
|
+
for (const listener of [
|
|
8
|
+
...listeners
|
|
9
|
+
])listener();
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
register (host, node) {
|
|
13
|
+
const id = nextId++;
|
|
14
|
+
emit([
|
|
15
|
+
...entries,
|
|
16
|
+
{
|
|
17
|
+
id,
|
|
18
|
+
host,
|
|
19
|
+
node
|
|
20
|
+
}
|
|
21
|
+
]);
|
|
22
|
+
return id;
|
|
23
|
+
},
|
|
24
|
+
update (id, node) {
|
|
25
|
+
const current = entries.find((entry)=>entry.id === id);
|
|
26
|
+
if (!current || current.node === node) return;
|
|
27
|
+
emit(entries.map((entry)=>entry.id === id ? {
|
|
28
|
+
...entry,
|
|
29
|
+
node
|
|
30
|
+
} : entry));
|
|
31
|
+
},
|
|
32
|
+
unregister (id) {
|
|
33
|
+
if (!entries.some((entry)=>entry.id === id)) return;
|
|
34
|
+
emit(entries.filter((entry)=>entry.id !== id));
|
|
35
|
+
},
|
|
36
|
+
subscribe (listener) {
|
|
37
|
+
listeners.add(listener);
|
|
38
|
+
return ()=>{
|
|
39
|
+
listeners.delete(listener);
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
getSnapshot () {
|
|
43
|
+
return entries;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export { createPortalRegistry };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SplitContentProps<TWrapper> {
|
|
2
|
+
/** Attributes for the typing surface (`.cm-content`), as DOM attribute strings */
|
|
3
|
+
contentAttributes: Record<string, string>;
|
|
4
|
+
/** Everything else: className, style, event handlers, other wrapper attributes */
|
|
5
|
+
wrapperProps: TWrapper;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Attribute routing for `CodeEditorContent` (spec D10): `data-*`, `aria-*`,
|
|
9
|
+
* `id`, `title` and `tabIndex` go to the editor's typing surface; the rest stays
|
|
10
|
+
* on the wrapper div. `undefined` / `null` values are dropped, as React would.
|
|
11
|
+
*/
|
|
12
|
+
export declare const splitContentProps: <TProps extends object>(props: TProps) => SplitContentProps<TProps>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const isContentAttribute = (key)=>key.startsWith('data-') || key.startsWith('aria-') || 'id' === key || 'title' === key;
|
|
2
|
+
const toAttributeValue = (value)=>{
|
|
3
|
+
if ('string' == typeof value) return value;
|
|
4
|
+
if ('number' == typeof value || 'boolean' == typeof value) return String(value);
|
|
5
|
+
return null;
|
|
6
|
+
};
|
|
7
|
+
const splitContentProps = (props)=>{
|
|
8
|
+
const contentAttributes = {};
|
|
9
|
+
const wrapperProps = {};
|
|
10
|
+
for (const [key, value] of Object.entries(props)){
|
|
11
|
+
if ('tabIndex' === key) {
|
|
12
|
+
const attribute = toAttributeValue(value);
|
|
13
|
+
if (null !== attribute) contentAttributes.tabindex = attribute;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (isContentAttribute(key)) {
|
|
17
|
+
const attribute = toAttributeValue(value);
|
|
18
|
+
if (null !== attribute) contentAttributes[key] = attribute;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
wrapperProps[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
contentAttributes,
|
|
25
|
+
wrapperProps: wrapperProps
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export { splitContentProps };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { FoldRegion } from '../CodeSnippet/lib/foldUtils';
|
|
2
|
+
/** Language of the document. Passed verbatim to the syntax adapter and selects the structural parser. */
|
|
3
|
+
export type CodeEditorLanguage = 'http' | 'json' | 'yaml' | 'bash' | 'text' | 'javascript' | 'typescript' | 'python' | 'lua';
|
|
4
|
+
/** Imperative handle exposed through `apiRef` and `useCodeEditor()`. */
|
|
5
|
+
export interface CodeEditorApi {
|
|
6
|
+
focus(): void;
|
|
7
|
+
getValue(): string;
|
|
8
|
+
/** Replaces the selection, or inserts at the cursor. Undoable. */
|
|
9
|
+
insertText(text: string): void;
|
|
10
|
+
openSearch(): void;
|
|
11
|
+
foldAll(): void;
|
|
12
|
+
unfoldAll(): void;
|
|
13
|
+
}
|
|
14
|
+
export interface CodeEditorPosition {
|
|
15
|
+
/** Absolute line number (startingLineNumber-based) */
|
|
16
|
+
line: number;
|
|
17
|
+
/** 1-based column */
|
|
18
|
+
column: number;
|
|
19
|
+
}
|
|
20
|
+
export interface CodeEditorDiagnostic {
|
|
21
|
+
from: CodeEditorPosition;
|
|
22
|
+
/**
|
|
23
|
+
* Defaults to one character after `from` (a point when `from` is at the end of its line).
|
|
24
|
+
* A line past the document end is clamped to the document end; never before `from`.
|
|
25
|
+
*/
|
|
26
|
+
to?: CodeEditorPosition;
|
|
27
|
+
severity: 'error' | 'warning' | 'info';
|
|
28
|
+
message: string;
|
|
29
|
+
/** 'syntax' | 'schema' for built-ins; free text for consumer diagnostics */
|
|
30
|
+
source?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface CodeEditorCompletion {
|
|
33
|
+
label: string;
|
|
34
|
+
/** Text to insert; defaults to label */
|
|
35
|
+
apply?: string;
|
|
36
|
+
detail?: string;
|
|
37
|
+
info?: string;
|
|
38
|
+
kind?: 'keyword' | 'property' | 'value' | 'method' | 'header' | 'snippet' | 'text';
|
|
39
|
+
}
|
|
40
|
+
/** HTTP-specific location of the cursor, present when `language === 'http'`. */
|
|
41
|
+
export interface CodeEditorHttpContext {
|
|
42
|
+
section: 'start-line' | 'header-name' | 'header-value' | 'body';
|
|
43
|
+
headerName?: string;
|
|
44
|
+
messageKind: 'request' | 'response';
|
|
45
|
+
}
|
|
46
|
+
export interface CodeEditorCompletionContext {
|
|
47
|
+
value: string;
|
|
48
|
+
position: CodeEditorPosition;
|
|
49
|
+
lineText: string;
|
|
50
|
+
/** Word being typed before the cursor and where it starts */
|
|
51
|
+
word: {
|
|
52
|
+
text: string;
|
|
53
|
+
from: CodeEditorPosition;
|
|
54
|
+
};
|
|
55
|
+
/** true when opened with Ctrl-Space */
|
|
56
|
+
explicit: boolean;
|
|
57
|
+
/** Present when language === 'http' */
|
|
58
|
+
http?: CodeEditorHttpContext;
|
|
59
|
+
/** Present in a JSON document or JSON body: JSON pointer of the cursor location */
|
|
60
|
+
jsonPointer?: string;
|
|
61
|
+
}
|
|
62
|
+
export type CodeEditorCompletionSource = (ctx: CodeEditorCompletionContext) => CodeEditorCompletion[] | null | Promise<CodeEditorCompletion[] | null>;
|
|
63
|
+
/** Static fold regions, or a function re-run (debounced) after edits. */
|
|
64
|
+
export type CodeEditorFolds = FoldRegion[] | ((value: string, ctx: {
|
|
65
|
+
startingLineNumber: number;
|
|
66
|
+
}) => FoldRegion[]);
|
|
67
|
+
/**
|
|
68
|
+
* JSON Schema document (any draft supported by json-schema-library).
|
|
69
|
+
* Structural on purpose: the engine casts it to json-schema-library's own type,
|
|
70
|
+
* so the public API never depends on that module statically.
|
|
71
|
+
*/
|
|
72
|
+
export type JsonSchema = Record<string, unknown> | boolean;
|
|
File without changes
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from "../../utils/cn.js";
|
|
3
2
|
import { useTestId } from "../../utils/testId.js";
|
|
4
3
|
import { useCodeSnippet } from "./hooks/index.js";
|
|
5
|
-
import { CodeContent, CodeLine, TokenizedCodeLine } from "./internal/index.js";
|
|
6
|
-
import { getFoldSummaryLabel } from "./lib/foldUtils.js";
|
|
4
|
+
import { CodeContent, CodeLine, FoldSummary, TokenizedCodeLine } from "./internal/index.js";
|
|
7
5
|
import { SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
|
|
8
6
|
import { splitTextByRanges } from "./lib/lineUtils.js";
|
|
9
7
|
const CodeSnippetCode = ({ className, ...props })=>{
|
|
@@ -21,14 +19,7 @@ const CodeSnippetCode = ({ className, ...props })=>{
|
|
|
21
19
|
hasFolds: true
|
|
22
20
|
};
|
|
23
21
|
};
|
|
24
|
-
const renderFoldSummary = (item)
|
|
25
|
-
const label = getFoldSummaryLabel(item.fold, item.lineCount);
|
|
26
|
-
const { className: summaryClassName, onClick: consumerOnClick, ...summaryProps } = item.fold.summaryProps ?? {};
|
|
27
|
-
const handleSummaryClick = (event)=>{
|
|
28
|
-
toggleFold(item.fold.id);
|
|
29
|
-
consumerOnClick?.(event);
|
|
30
|
-
};
|
|
31
|
-
return /*#__PURE__*/ jsx(CodeLine, {
|
|
22
|
+
const renderFoldSummary = (item)=>/*#__PURE__*/ jsx(CodeLine, {
|
|
32
23
|
lineConfig: void 0,
|
|
33
24
|
lineHeightClass: lineHeightClass,
|
|
34
25
|
showInlineGutter: inlineGutter,
|
|
@@ -37,20 +28,12 @@ const CodeSnippetCode = ({ className, ...props })=>{
|
|
|
37
28
|
isFoldCollapsed: inlineGutter || void 0,
|
|
38
29
|
onFoldToggle: inlineGutter ? ()=>toggleFold(item.fold.id) : void 0,
|
|
39
30
|
hasFolds: inlineGutter && hasFolds,
|
|
40
|
-
children: /*#__PURE__*/ jsx(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"aria-label": `Collapsed region: ${label}, ${item.lineCount} lines`,
|
|
45
|
-
...summaryProps,
|
|
46
|
-
onClick: handleSummaryClick,
|
|
47
|
-
children: /*#__PURE__*/ jsx("span", {
|
|
48
|
-
className: "inline-flex items-center italic text-text-secondary hover:text-text-primary transition-colors",
|
|
49
|
-
children: label
|
|
50
|
-
})
|
|
31
|
+
children: /*#__PURE__*/ jsx(FoldSummary, {
|
|
32
|
+
fold: item.fold,
|
|
33
|
+
lineCount: item.lineCount,
|
|
34
|
+
onToggle: ()=>toggleFold(item.fold.id)
|
|
51
35
|
})
|
|
52
36
|
}, `fold-${item.fold.id}`);
|
|
53
|
-
};
|
|
54
37
|
if (isLoading || !tokens) {
|
|
55
38
|
const codeLines = code.split('\n');
|
|
56
39
|
return /*#__PURE__*/ jsx(CodeContent, {
|
|
@@ -2,12 +2,13 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useTestId } from "../../utils/testId.js";
|
|
3
3
|
import { Button } from "../Button/index.js";
|
|
4
4
|
import { Copyable, CopyableIcon } from "../Copyable/index.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useCodeSnippetFrame } from "./hooks/index.js";
|
|
6
6
|
const CodeSnippetCopyButton = ({ ref, ...props })=>{
|
|
7
7
|
const testId = useTestId('copy-button');
|
|
8
|
-
const {
|
|
8
|
+
const { getCode, notifyCopied } = useCodeSnippetFrame();
|
|
9
9
|
return /*#__PURE__*/ jsx(Copyable, {
|
|
10
|
-
text:
|
|
10
|
+
text: getCode,
|
|
11
|
+
onCopied: notifyCopied,
|
|
11
12
|
tooltip: true,
|
|
12
13
|
children: /*#__PURE__*/ jsx(Button, {
|
|
13
14
|
ref: ref,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CodeSnippetSize } from './CodeSnippetContext';
|
|
2
|
+
/**
|
|
3
|
+
* Frame-level state shared by the toolbar/show-more buttons.
|
|
4
|
+
*
|
|
5
|
+
* Provided by `CodeSnippetRoot` (and by `CodeEditorRoot`), so the frame
|
|
6
|
+
* buttons work under either root without depending on the snippet's
|
|
7
|
+
* render pipeline (`CodeSnippetContext`).
|
|
8
|
+
*/
|
|
9
|
+
export type CodeSnippetFrameContextValue = {
|
|
10
|
+
size: CodeSnippetSize;
|
|
11
|
+
/** Lazily reads the current text (snippet: `code` prop; editor: live document) */
|
|
12
|
+
getCode: () => string;
|
|
13
|
+
/** Called by the copy button after a copy; roots forward it to their `onCopy` */
|
|
14
|
+
notifyCopied: () => void;
|
|
15
|
+
wrapLines: boolean;
|
|
16
|
+
setWrapLines: (wrap: boolean) => void;
|
|
17
|
+
isFullscreen: boolean;
|
|
18
|
+
setIsFullscreen: (fullscreen: boolean) => void;
|
|
19
|
+
maxLines: number;
|
|
20
|
+
isExpanded: boolean;
|
|
21
|
+
setIsExpanded: (expanded: boolean) => void;
|
|
22
|
+
/** max(0, totalRows - maxLines) — independent of isExpanded; 0 when maxLines <= 0 */
|
|
23
|
+
hiddenLineCount: number;
|
|
24
|
+
};
|
|
25
|
+
export declare const CodeSnippetFrameContext: import("react").Context<CodeSnippetFrameContextValue | null>;
|
|
@@ -5,10 +5,10 @@ import { useTestId } from "../../utils/testId.js";
|
|
|
5
5
|
import { Button } from "../Button/index.js";
|
|
6
6
|
import { Kbd } from "../Kbd/index.js";
|
|
7
7
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
|
|
8
|
-
import {
|
|
8
|
+
import { useCodeSnippetFrame } from "./hooks/index.js";
|
|
9
9
|
const CodeSnippetFullscreenButton = ({ onClick, ref, ...props })=>{
|
|
10
10
|
const testId = useTestId('fullscreen-button');
|
|
11
|
-
const { isFullscreen, setIsFullscreen } =
|
|
11
|
+
const { isFullscreen, setIsFullscreen } = useCodeSnippetFrame();
|
|
12
12
|
const handleClick = (event)=>{
|
|
13
13
|
setIsFullscreen(!isFullscreen);
|
|
14
14
|
onClick?.(event);
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { HTMLAttributes, ReactNode, Ref } from 'react';
|
|
2
|
-
import {
|
|
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 { codeSnippetRootVariants } from './classes';
|
|
5
6
|
import { type FoldRegion } from './lib/foldUtils';
|
|
6
|
-
declare const codeSnippetRootVariants: (props?: ({
|
|
7
|
-
size?: "lg" | "md" | "sm" | null | undefined;
|
|
8
|
-
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
9
7
|
type CodeSnippetRootVariantProps = VariantProps<typeof codeSnippetRootVariants>;
|
|
10
8
|
type CodeSnippetRootNativeProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
11
9
|
export type CodeSnippetRootProps<TLanguage extends string = string> = CodeSnippetRootNativeProps & CodeSnippetRootVariantProps & TestableProps & {
|
|
@@ -33,7 +31,7 @@ export type CodeSnippetRootProps<TLanguage extends string = string> = CodeSnippe
|
|
|
33
31
|
/** Child components */
|
|
34
32
|
children?: ReactNode;
|
|
35
33
|
};
|
|
36
|
-
export declare function CodeSnippetRoot<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.Element;
|
|
34
|
+
export declare function CodeSnippetRoot<TLanguage extends string = string>({ code, language, size, lines, startingLineNumber, wrapLines: initialWrapLines, maxLines, folds: foldsProp, onCopy, className, children, ref, 'data-testid': testId, ...props }: CodeSnippetRootProps<TLanguage>): import("react").JSX.Element;
|
|
37
35
|
export declare namespace CodeSnippetRoot {
|
|
38
36
|
var displayName: string;
|
|
39
37
|
}
|
|
@@ -1,30 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { cva } from "class-variance-authority";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
3
|
import { cn } from "../../utils/cn.js";
|
|
6
4
|
import { copyText } from "../../utils/copyText.js";
|
|
7
5
|
import { TestIdProvider } from "../../utils/testId.js";
|
|
8
6
|
import { plainAdapter } from "./adapters/plain.js";
|
|
9
|
-
import { CodeSnippetContext
|
|
7
|
+
import { CodeSnippetContext } from "./CodeSnippetContext.js";
|
|
8
|
+
import { CodeSnippetFrameContext } from "./CodeSnippetFrameContext.js";
|
|
10
9
|
import { CodeSnippetShowMoreButton } from "./CodeSnippetShowMoreButton.js";
|
|
10
|
+
import { codeSnippetRootVariants } from "./classes.js";
|
|
11
11
|
import { useAdapter } from "./hooks/index.js";
|
|
12
|
+
import { CodeSnippetFrame } from "./internal/CodeSnippetFrame.js";
|
|
12
13
|
import { buildDisplayItems, validateFolds } from "./lib/foldUtils.js";
|
|
13
|
-
|
|
14
|
-
variants: {
|
|
15
|
-
size: {
|
|
16
|
-
sm: 'text-xs leading-sm',
|
|
17
|
-
md: 'text-sm',
|
|
18
|
-
lg: 'text-base leading-sm'
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
defaultVariants: {
|
|
22
|
-
size: 'sm'
|
|
23
|
-
}
|
|
24
|
-
});
|
|
14
|
+
import { getHiddenLineCount, hasExplicitShowMoreButton, isClamped } from "./lib/showMore.js";
|
|
25
15
|
const EMPTY_LINES = {};
|
|
26
|
-
const
|
|
27
|
-
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 })=>{
|
|
16
|
+
const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_LINES, startingLineNumber = 1, wrapLines: initialWrapLines = false, maxLines = 0, folds: foldsProp, onCopy, className, children, ref, 'data-testid': testId, ...props })=>{
|
|
28
17
|
const adapterContext = useAdapter();
|
|
29
18
|
const [wrapLines, setWrapLines] = useState(initialWrapLines);
|
|
30
19
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
@@ -65,16 +54,6 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
65
54
|
language,
|
|
66
55
|
adapter
|
|
67
56
|
]);
|
|
68
|
-
useEffect(()=>{
|
|
69
|
-
if (!isFullscreen) return;
|
|
70
|
-
const handleKeyDown = (e)=>{
|
|
71
|
-
if ('Escape' === e.key) setIsFullscreen(false);
|
|
72
|
-
};
|
|
73
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
74
|
-
return ()=>document.removeEventListener('keydown', handleKeyDown);
|
|
75
|
-
}, [
|
|
76
|
-
isFullscreen
|
|
77
|
-
]);
|
|
78
57
|
const copyToClipboard = useCallback(async ()=>{
|
|
79
58
|
await copyText(code);
|
|
80
59
|
onCopy?.(code);
|
|
@@ -82,6 +61,15 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
82
61
|
code,
|
|
83
62
|
onCopy
|
|
84
63
|
]);
|
|
64
|
+
const getCode = useCallback(()=>code, [
|
|
65
|
+
code
|
|
66
|
+
]);
|
|
67
|
+
const notifyCopied = useCallback(()=>{
|
|
68
|
+
onCopy?.(code);
|
|
69
|
+
}, [
|
|
70
|
+
code,
|
|
71
|
+
onCopy
|
|
72
|
+
]);
|
|
85
73
|
const totalLines = tokens?.length ?? code.split('\n').length;
|
|
86
74
|
const validatedFolds = useMemo(()=>foldsProp ? validateFolds(foldsProp, totalLines, startingLineNumber) : [], [
|
|
87
75
|
foldsProp,
|
|
@@ -119,13 +107,11 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
119
107
|
collapsedFolds,
|
|
120
108
|
startingLineNumber
|
|
121
109
|
]);
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
const shouldClip = maxLines > 0 && !isExpanded && hiddenRows >= MIN_HIDDEN_LINES_THRESHOLD;
|
|
126
|
-
return shouldClip ? displayItems.slice(0, maxLines) : displayItems;
|
|
127
|
-
}, [
|
|
110
|
+
const hasExplicitShowMore = hasExplicitShowMoreButton(children);
|
|
111
|
+
const hiddenLineCount = getHiddenLineCount(displayItems.length, maxLines);
|
|
112
|
+
const visibleDisplayItems = useMemo(()=>isClamped(hiddenLineCount, isExpanded) ? displayItems.slice(0, maxLines) : displayItems, [
|
|
128
113
|
displayItems,
|
|
114
|
+
hiddenLineCount,
|
|
129
115
|
maxLines,
|
|
130
116
|
isExpanded
|
|
131
117
|
]);
|
|
@@ -180,31 +166,51 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
180
166
|
copyToClipboard,
|
|
181
167
|
adapter
|
|
182
168
|
]);
|
|
183
|
-
const
|
|
169
|
+
const frameValue = useMemo(()=>({
|
|
170
|
+
size: size ?? 'sm',
|
|
171
|
+
getCode,
|
|
172
|
+
notifyCopied,
|
|
173
|
+
wrapLines,
|
|
174
|
+
setWrapLines,
|
|
175
|
+
isFullscreen,
|
|
176
|
+
setIsFullscreen,
|
|
177
|
+
maxLines,
|
|
178
|
+
isExpanded,
|
|
179
|
+
setIsExpanded,
|
|
180
|
+
hiddenLineCount
|
|
181
|
+
}), [
|
|
182
|
+
size,
|
|
183
|
+
getCode,
|
|
184
|
+
notifyCopied,
|
|
185
|
+
wrapLines,
|
|
186
|
+
isFullscreen,
|
|
187
|
+
maxLines,
|
|
188
|
+
isExpanded,
|
|
189
|
+
hiddenLineCount
|
|
190
|
+
]);
|
|
191
|
+
const snippet = /*#__PURE__*/ jsxs(CodeSnippetFrame, {
|
|
184
192
|
"data-slot": "code-snippet",
|
|
185
193
|
"data-testid": testId,
|
|
194
|
+
...props,
|
|
195
|
+
ref: ref,
|
|
186
196
|
className: cn(codeSnippetRootVariants({
|
|
187
197
|
size
|
|
188
|
-
}),
|
|
189
|
-
|
|
198
|
+
}), className),
|
|
199
|
+
isFullscreen: isFullscreen,
|
|
200
|
+
setIsFullscreen: setIsFullscreen,
|
|
190
201
|
children: [
|
|
191
202
|
children,
|
|
192
|
-
maxLines > 0 && !
|
|
203
|
+
maxLines > 0 && !hasExplicitShowMore && /*#__PURE__*/ jsx(CodeSnippetShowMoreButton, {})
|
|
193
204
|
]
|
|
194
205
|
});
|
|
195
206
|
return /*#__PURE__*/ jsx(TestIdProvider, {
|
|
196
207
|
value: testId,
|
|
197
|
-
children: /*#__PURE__*/ jsx(
|
|
198
|
-
value:
|
|
199
|
-
children:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
onClick: ()=>setIsFullscreen(false)
|
|
204
|
-
}),
|
|
205
|
-
snippet
|
|
206
|
-
]
|
|
207
|
-
}), document.body) : snippet
|
|
208
|
+
children: /*#__PURE__*/ jsx(CodeSnippetFrameContext.Provider, {
|
|
209
|
+
value: frameValue,
|
|
210
|
+
children: /*#__PURE__*/ jsx(CodeSnippetContext.Provider, {
|
|
211
|
+
value: contextValue,
|
|
212
|
+
children: snippet
|
|
213
|
+
})
|
|
208
214
|
})
|
|
209
215
|
});
|
|
210
216
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDown } from "../../icons/ChevronDown.js";
|
|
3
3
|
import { ChevronUp } from "../../icons/ChevronUp.js";
|
|
4
|
+
import { useTestId } from "../../utils/testId.js";
|
|
4
5
|
import { Button } from "../Button/index.js";
|
|
5
6
|
import { MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
|
|
6
|
-
import {
|
|
7
|
+
import { useCodeSnippetFrame } from "./hooks/index.js";
|
|
7
8
|
const CodeSnippetShowMoreButton = ({ onClick, ref, ...props })=>{
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
if (maxLines <= 0 ||
|
|
9
|
+
const testId = useTestId('show-more-button');
|
|
10
|
+
const { maxLines, isExpanded, setIsExpanded, hiddenLineCount } = useCodeSnippetFrame();
|
|
11
|
+
if (maxLines <= 0 || hiddenLineCount < MIN_HIDDEN_LINES_THRESHOLD) return null;
|
|
11
12
|
const handleClick = (event)=>{
|
|
12
13
|
setIsExpanded(!isExpanded);
|
|
13
14
|
onClick?.(event);
|
|
@@ -21,6 +22,7 @@ const CodeSnippetShowMoreButton = ({ onClick, ref, ...props })=>{
|
|
|
21
22
|
color: "neutral",
|
|
22
23
|
size: "small",
|
|
23
24
|
fullWidth: true,
|
|
25
|
+
"data-testid": testId,
|
|
24
26
|
...props,
|
|
25
27
|
onClick: handleClick,
|
|
26
28
|
children: isExpanded ? /*#__PURE__*/ jsxs(Fragment, {
|
|
@@ -31,7 +33,7 @@ const CodeSnippetShowMoreButton = ({ onClick, ref, ...props })=>{
|
|
|
31
33
|
}) : /*#__PURE__*/ jsxs(Fragment, {
|
|
32
34
|
children: [
|
|
33
35
|
"Show more (",
|
|
34
|
-
|
|
36
|
+
hiddenLineCount,
|
|
35
37
|
" lines)",
|
|
36
38
|
/*#__PURE__*/ jsx(ChevronDown, {})
|
|
37
39
|
]
|
|
@@ -3,10 +3,10 @@ import { WrapText } from "../../icons/WrapText.js";
|
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
4
|
import { ToggleButton } from "../ToggleButton/index.js";
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useCodeSnippetFrame } from "./hooks/index.js";
|
|
7
7
|
const CodeSnippetWrapButton = ({ onToggle, ref, ...props })=>{
|
|
8
8
|
const testId = useTestId('wrap-button');
|
|
9
|
-
const { wrapLines, setWrapLines } =
|
|
9
|
+
const { wrapLines, setWrapLines } = useCodeSnippetFrame();
|
|
10
10
|
const handleToggle = (active, event)=>{
|
|
11
11
|
setWrapLines(active);
|
|
12
12
|
onToggle?.(active, event);
|
|
@@ -4,7 +4,8 @@ let languagesPromise = null;
|
|
|
4
4
|
async function loadAdditionalLanguages() {
|
|
5
5
|
if (!languagesPromise) languagesPromise = Promise.all([
|
|
6
6
|
import("prismjs/components/prism-bash"),
|
|
7
|
-
import("prismjs/components/prism-http")
|
|
7
|
+
import("prismjs/components/prism-http"),
|
|
8
|
+
import("prismjs/components/prism-lua")
|
|
8
9
|
]);
|
|
9
10
|
return languagesPromise;
|
|
10
11
|
}
|
|
@@ -24,6 +25,7 @@ const SUPPORTED_LANGUAGES = [
|
|
|
24
25
|
'sql',
|
|
25
26
|
'yaml',
|
|
26
27
|
'markdown',
|
|
28
|
+
'lua',
|
|
27
29
|
'http'
|
|
28
30
|
];
|
|
29
31
|
const PRISM_TOKEN_MAP = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Supported language union types per adapter */
|
|
2
|
-
export type PrismLanguage = 'javascript' | 'typescript' | 'jsx' | 'tsx' | 'bash' | 'shell' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'yaml' | 'markdown' | 'http';
|
|
3
|
-
export type ShikiLanguage = 'javascript' | 'typescript' | 'jsx' | 'tsx' | 'bash' | 'shell' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'yaml' | 'markdown' | 'rust' | 'java' | 'c' | 'cpp' | 'http';
|
|
4
|
-
export type HighlightJsLanguage = 'javascript' | 'typescript' | 'bash' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'http';
|
|
2
|
+
export type PrismLanguage = 'javascript' | 'typescript' | 'jsx' | 'tsx' | 'bash' | 'shell' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'yaml' | 'markdown' | 'lua' | 'http';
|
|
3
|
+
export type ShikiLanguage = 'javascript' | 'typescript' | 'jsx' | 'tsx' | 'bash' | 'shell' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'yaml' | 'markdown' | 'lua' | 'rust' | 'java' | 'c' | 'cpp' | 'http';
|
|
4
|
+
export type HighlightJsLanguage = 'javascript' | 'typescript' | 'bash' | 'json' | 'html' | 'xml' | 'css' | 'python' | 'go' | 'sql' | 'lua' | 'http';
|
|
5
5
|
export type PlainLanguage = 'text' | 'plain';
|
|
6
6
|
/** Strict token types for syntax highlighting */
|
|
7
7
|
export type TokenType = 'plain' | 'keyword' | 'string' | 'comment' | 'function' | 'number' | 'operator' | 'punctuation' | 'class-name' | 'property' | 'tag' | 'attr-name' | 'attr-value' | 'boolean' | 'builtin' | 'constant' | 'regex' | 'variable';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
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", {
|
|
3
|
+
variants: {
|
|
4
|
+
size: {
|
|
5
|
+
sm: 'text-xs leading-sm',
|
|
6
|
+
md: 'text-sm',
|
|
7
|
+
lg: 'text-base leading-sm'
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
defaultVariants: {
|
|
11
|
+
size: 'sm'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
export { codeSnippetRootVariants };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { CodeSnippetFrameContext } from "../CodeSnippetFrameContext.js";
|
|
3
|
+
const useCodeSnippetFrame = ()=>{
|
|
4
|
+
const context = useContext(CodeSnippetFrameContext);
|
|
5
|
+
if (!context) throw new Error('useCodeSnippetFrame must be used within CodeSnippetRoot or CodeEditorRoot');
|
|
6
|
+
return context;
|
|
7
|
+
};
|
|
8
|
+
export { useCodeSnippetFrame };
|