@hanzo/ui 8.27.9 → 8.27.10
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/backends/gui/3d-card.cjs +100 -0
- package/dist/backends/gui/3d-card.d.ts +59 -0
- package/dist/backends/gui/3d-card.d.ts.map +1 -0
- package/dist/backends/gui/3d-card.js +92 -0
- package/dist/backends/gui/3d-card.js.map +1 -0
- package/dist/backends/gui/3d-marquee.cjs +225 -0
- package/dist/backends/gui/3d-marquee.d.ts +114 -0
- package/dist/backends/gui/3d-marquee.d.ts.map +1 -0
- package/dist/backends/gui/3d-marquee.js +190 -0
- package/dist/backends/gui/3d-marquee.js.map +1 -0
- package/dist/backends/gui/3d-pin.cjs +51 -0
- package/dist/backends/gui/3d-pin.d.ts +14 -0
- package/dist/backends/gui/3d-pin.d.ts.map +1 -0
- package/dist/backends/gui/3d-pin.js +49 -0
- package/dist/backends/gui/3d-pin.js.map +1 -0
- package/dist/backends/gui/advanced-chart.cjs +61 -0
- package/dist/backends/gui/advanced-chart.d.ts +38 -0
- package/dist/backends/gui/advanced-chart.d.ts.map +1 -0
- package/dist/backends/gui/advanced-chart.js +59 -0
- package/dist/backends/gui/advanced-chart.js.map +1 -0
- package/dist/backends/gui/alert.cjs +106 -0
- package/dist/backends/gui/alert.d.ts +39 -0
- package/dist/backends/gui/alert.d.ts.map +1 -0
- package/dist/backends/gui/alert.js +102 -0
- package/dist/backends/gui/alert.js.map +1 -0
- package/dist/backends/gui/android.cjs +52 -0
- package/dist/backends/gui/android.d.ts +16 -0
- package/dist/backends/gui/android.d.ts.map +1 -0
- package/dist/backends/gui/android.js +50 -0
- package/dist/backends/gui/android.js.map +1 -0
- package/dist/backends/gui/animated-beam.cjs +97 -0
- package/dist/backends/gui/animated-beam.d.ts +57 -0
- package/dist/backends/gui/animated-beam.d.ts.map +1 -0
- package/dist/backends/gui/animated-beam.js +62 -0
- package/dist/backends/gui/animated-beam.js.map +1 -0
- package/dist/backends/gui/animated-cursor.cjs +183 -0
- package/dist/backends/gui/animated-cursor.d.ts +42 -0
- package/dist/backends/gui/animated-cursor.d.ts.map +1 -0
- package/dist/backends/gui/animated-cursor.js +181 -0
- package/dist/backends/gui/animated-cursor.js.map +1 -0
- package/dist/backends/gui/animated-testimonials.cjs +109 -0
- package/dist/backends/gui/animated-testimonials.d.ts +23 -0
- package/dist/backends/gui/animated-testimonials.d.ts.map +1 -0
- package/dist/backends/gui/animated-testimonials.js +74 -0
- package/dist/backends/gui/animated-testimonials.js.map +1 -0
- package/dist/backends/gui/animated-tooltip.cjs +100 -0
- package/dist/backends/gui/animated-tooltip.d.ts +15 -0
- package/dist/backends/gui/animated-tooltip.d.ts.map +1 -0
- package/dist/backends/gui/animated-tooltip.js +65 -0
- package/dist/backends/gui/animated-tooltip.js.map +1 -0
- package/dist/backends/gui/announcement.cjs +54 -0
- package/dist/backends/gui/announcement.d.ts +26 -0
- package/dist/backends/gui/announcement.d.ts.map +1 -0
- package/dist/backends/gui/announcement.js +52 -0
- package/dist/backends/gui/announcement.js.map +1 -0
- package/dist/backends/gui/apple-cards-carousel.cjs +198 -0
- package/dist/backends/gui/apple-cards-carousel.d.ts +45 -0
- package/dist/backends/gui/apple-cards-carousel.d.ts.map +1 -0
- package/dist/backends/gui/apple-cards-carousel.js +162 -0
- package/dist/backends/gui/apple-cards-carousel.js.map +1 -0
- package/dist/backends/gui/apple-hello-effect.cjs +38 -0
- package/dist/backends/gui/apple-hello-effect.d.ts +9 -0
- package/dist/backends/gui/apple-hello-effect.d.ts.map +1 -0
- package/dist/backends/gui/apple-hello-effect.js +36 -0
- package/dist/backends/gui/apple-hello-effect.js.map +1 -0
- package/dist/backends/gui/banner.cjs +108 -0
- package/dist/backends/gui/banner.d.ts +33 -0
- package/dist/backends/gui/banner.d.ts.map +1 -0
- package/dist/backends/gui/banner.js +106 -0
- package/dist/backends/gui/banner.js.map +1 -0
- package/dist/backends/gui/calendar.cjs +152 -0
- package/dist/backends/gui/calendar.d.ts +41 -0
- package/dist/backends/gui/calendar.d.ts.map +1 -0
- package/dist/backends/gui/calendar.js +151 -0
- package/dist/backends/gui/calendar.js.map +1 -0
- package/dist/backends/gui/choicebox.cjs +50 -0
- package/dist/backends/gui/choicebox.d.ts +33 -0
- package/dist/backends/gui/choicebox.d.ts.map +1 -0
- package/dist/backends/gui/choicebox.js +49 -0
- package/dist/backends/gui/choicebox.js.map +1 -0
- package/dist/backends/gui/code-block.cjs +120 -0
- package/dist/backends/gui/code-block.d.ts +22 -0
- package/dist/backends/gui/code-block.d.ts.map +1 -0
- package/dist/backends/gui/code-block.js +118 -0
- package/dist/backends/gui/code-block.js.map +1 -0
- package/dist/backends/gui/code-editor.cjs +195 -0
- package/dist/backends/gui/code-editor.d.ts +43 -0
- package/dist/backends/gui/code-editor.d.ts.map +1 -0
- package/dist/backends/gui/code-editor.js +160 -0
- package/dist/backends/gui/code-editor.js.map +1 -0
- package/dist/backends/gui/code-tabs.cjs +21 -0
- package/dist/backends/gui/code-tabs.d.ts +22 -0
- package/dist/backends/gui/code-tabs.d.ts.map +1 -0
- package/dist/backends/gui/code-tabs.js +19 -0
- package/dist/backends/gui/code-tabs.js.map +1 -0
- package/dist/backends/gui/color-picker.cjs +141 -0
- package/dist/backends/gui/color-picker.d.ts +25 -0
- package/dist/backends/gui/color-picker.d.ts.map +1 -0
- package/dist/backends/gui/color-picker.js +139 -0
- package/dist/backends/gui/color-picker.js.map +1 -0
- package/dist/backends/gui/combobox.cjs +42 -0
- package/dist/backends/gui/combobox.d.ts +39 -0
- package/dist/backends/gui/combobox.d.ts.map +1 -0
- package/dist/backends/gui/combobox.js +40 -0
- package/dist/backends/gui/combobox.js.map +1 -0
- package/dist/backends/gui/company-profile.cjs +43 -0
- package/dist/backends/gui/company-profile.d.ts +33 -0
- package/dist/backends/gui/company-profile.d.ts.map +1 -0
- package/dist/backends/gui/company-profile.js +41 -0
- package/dist/backends/gui/company-profile.js.map +1 -0
- package/dist/backends/gui/comparison.cjs +43 -0
- package/dist/backends/gui/comparison.d.ts +16 -0
- package/dist/backends/gui/comparison.d.ts.map +1 -0
- package/dist/backends/gui/comparison.js +41 -0
- package/dist/backends/gui/comparison.js.map +1 -0
- package/dist/backends/gui/credit-card.cjs +80 -0
- package/dist/backends/gui/credit-card.d.ts +21 -0
- package/dist/backends/gui/credit-card.d.ts.map +1 -0
- package/dist/backends/gui/credit-card.js +78 -0
- package/dist/backends/gui/credit-card.js.map +1 -0
- package/dist/backends/gui/crypto-market.cjs +128 -0
- package/dist/backends/gui/crypto-market.d.ts +52 -0
- package/dist/backends/gui/crypto-market.d.ts.map +1 -0
- package/dist/backends/gui/crypto-market.js +126 -0
- package/dist/backends/gui/crypto-market.js.map +1 -0
- package/dist/backends/gui/crypto-screener.cjs +55 -0
- package/dist/backends/gui/crypto-screener.d.ts +42 -0
- package/dist/backends/gui/crypto-screener.d.ts.map +1 -0
- package/dist/backends/gui/crypto-screener.js +53 -0
- package/dist/backends/gui/crypto-screener.js.map +1 -0
- package/dist/backends/gui/cursor.cjs +40 -0
- package/dist/backends/gui/cursor.d.ts +16 -0
- package/dist/backends/gui/cursor.d.ts.map +1 -0
- package/dist/backends/gui/cursor.js +38 -0
- package/dist/backends/gui/cursor.js.map +1 -0
- package/dist/backends/gui/data-table.cjs +211 -0
- package/dist/backends/gui/data-table.d.ts +71 -0
- package/dist/backends/gui/data-table.d.ts.map +1 -0
- package/dist/backends/gui/data-table.js +175 -0
- package/dist/backends/gui/data-table.js.map +1 -0
- package/dist/backends/gui/date-picker.cjs +66 -0
- package/dist/backends/gui/date-picker.d.ts +29 -0
- package/dist/backends/gui/date-picker.d.ts.map +1 -0
- package/dist/backends/gui/date-picker.js +64 -0
- package/dist/backends/gui/date-picker.js.map +1 -0
- package/dist/backends/gui/desktop-spotlight.cjs +46 -0
- package/dist/backends/gui/desktop-spotlight.d.ts +34 -0
- package/dist/backends/gui/desktop-spotlight.d.ts.map +1 -0
- package/dist/backends/gui/desktop-spotlight.js +44 -0
- package/dist/backends/gui/desktop-spotlight.js.map +1 -0
- package/dist/backends/gui/desktop-window.cjs +180 -0
- package/dist/backends/gui/desktop-window.d.ts +51 -0
- package/dist/backends/gui/desktop-window.d.ts.map +1 -0
- package/dist/backends/gui/desktop-window.js +177 -0
- package/dist/backends/gui/desktop-window.js.map +1 -0
- package/dist/backends/gui/desktop.cjs +238 -0
- package/dist/backends/gui/desktop.d.ts +109 -0
- package/dist/backends/gui/desktop.d.ts.map +1 -0
- package/dist/backends/gui/desktop.js +199 -0
- package/dist/backends/gui/desktop.js.map +1 -0
- package/dist/backends/gui/dialog-stack.cjs +26 -0
- package/dist/backends/gui/dialog-stack.d.ts +23 -0
- package/dist/backends/gui/dialog-stack.d.ts.map +1 -0
- package/dist/backends/gui/dialog-stack.js +24 -0
- package/dist/backends/gui/dialog-stack.js.map +1 -0
- package/dist/backends/gui/dock.cjs +99 -0
- package/dist/backends/gui/dock.d.ts +41 -0
- package/dist/backends/gui/dock.d.ts.map +1 -0
- package/dist/backends/gui/dock.js +96 -0
- package/dist/backends/gui/dock.js.map +1 -0
- package/dist/backends/gui/dropzone.cjs +142 -0
- package/dist/backends/gui/dropzone.d.ts +12 -0
- package/dist/backends/gui/dropzone.d.ts.map +1 -0
- package/dist/backends/gui/dropzone.js +140 -0
- package/dist/backends/gui/dropzone.js.map +1 -0
- package/dist/backends/gui/editor.cjs +80 -0
- package/dist/backends/gui/editor.d.ts +23 -0
- package/dist/backends/gui/editor.d.ts.map +1 -0
- package/dist/backends/gui/editor.js +45 -0
- package/dist/backends/gui/editor.js.map +1 -0
- package/dist/backends/gui/empty.cjs +71 -0
- package/dist/backends/gui/empty.d.ts +43 -0
- package/dist/backends/gui/empty.d.ts.map +1 -0
- package/dist/backends/gui/empty.js +63 -0
- package/dist/backends/gui/empty.js.map +1 -0
- package/dist/backends/gui/financials.cjs +45 -0
- package/dist/backends/gui/financials.d.ts +36 -0
- package/dist/backends/gui/financials.d.ts.map +1 -0
- package/dist/backends/gui/financials.js +43 -0
- package/dist/backends/gui/financials.js.map +1 -0
- package/dist/backends/gui/forex-market.cjs +21 -0
- package/dist/backends/gui/forex-market.d.ts +17 -0
- package/dist/backends/gui/forex-market.d.ts.map +1 -0
- package/dist/backends/gui/forex-market.js +19 -0
- package/dist/backends/gui/forex-market.js.map +1 -0
- package/dist/backends/gui/forex-screener.cjs +57 -0
- package/dist/backends/gui/forex-screener.d.ts +45 -0
- package/dist/backends/gui/forex-screener.d.ts.map +1 -0
- package/dist/backends/gui/forex-screener.js +55 -0
- package/dist/backends/gui/forex-screener.js.map +1 -0
- package/dist/backends/gui/gantt.cjs +50 -0
- package/dist/backends/gui/gantt.d.ts +19 -0
- package/dist/backends/gui/gantt.d.ts.map +1 -0
- package/dist/backends/gui/gantt.js +49 -0
- package/dist/backends/gui/gantt.js.map +1 -0
- package/dist/backends/gui/index.cjs +108 -1
- package/dist/backends/gui/index.d.ts +41 -0
- package/dist/backends/gui/index.d.ts.map +1 -1
- package/dist/backends/gui/index.js +41 -0
- package/dist/backends/gui/index.js.map +1 -1
- package/dist/gallery.html +1 -1
- package/dist/index.cjs +70 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/index.node.cjs +70 -1
- package/dist/index.node.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.CodeBlock = CodeBlock;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* CodeBlock — a bordered panel that shows source code line by line: a header
|
|
8
|
+
* naming the file and its language with a copy-to-clipboard button, a gutter
|
|
9
|
+
* of line numbers, and a colored band on any line a caller highlights or marks
|
|
10
|
+
* as added or removed in a diff.
|
|
11
|
+
*
|
|
12
|
+
* A line is plain monospace text, not colorized tokens: the workspace ships no
|
|
13
|
+
* highlighter. `theme` names a fixed syntax palette — a chosen surface,
|
|
14
|
+
* independent of the page's own light or dark theme — and `size` picks the
|
|
15
|
+
* type scale. A line is never wrapped, so the body scrolls on both axes once it
|
|
16
|
+
* outgrows `maxHeight` or the panel's width.
|
|
17
|
+
*/
|
|
18
|
+
const gui_1 = require("@hanzo/gui");
|
|
19
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
20
|
+
const react_1 = require("react");
|
|
21
|
+
const badge_1 = require("./badge.cjs");
|
|
22
|
+
const button_1 = require("./button.cjs");
|
|
23
|
+
const slot_1 = require("./slot.cjs");
|
|
24
|
+
/**
|
|
25
|
+
* One fixed color quartet per theme. `as const satisfies` keeps each hex a
|
|
26
|
+
* literal type rather than widening it to `string` — gui's style props are
|
|
27
|
+
* typed against a hex-literal pattern, not against `string` itself, so a
|
|
28
|
+
* literal reads straight into `bg`/`borderColor`/`color` with no cast.
|
|
29
|
+
*/
|
|
30
|
+
const PALETTE = {
|
|
31
|
+
dark: { bg: '#020617', border: '#1e293b', fg: '#e2e8f0', muted: '#94a3b8' },
|
|
32
|
+
light: { bg: '#f8fafc', border: '#e2e8f0', fg: '#0f172a', muted: '#64748b' },
|
|
33
|
+
github: { bg: '#ffffff', border: '#e2e8f0', fg: '#24292e', muted: '#6e7781' },
|
|
34
|
+
'github-dark': { bg: '#0d1117', border: '#30363d', fg: '#c9d1d9', muted: '#8b949e' },
|
|
35
|
+
'vs-dark': { bg: '#1e1e1e', border: '#333333', fg: '#d4d4d4', muted: '#808080' },
|
|
36
|
+
monokai: { bg: '#272822', border: '#49483e', fg: '#f8f8f2', muted: '#75715e' },
|
|
37
|
+
dracula: { bg: '#282a36', border: '#44475a', fg: '#f8f8f2', muted: '#6272a4' },
|
|
38
|
+
nord: { bg: '#2e3440', border: '#4c566a', fg: '#d8dee9', muted: '#81a1c1' },
|
|
39
|
+
};
|
|
40
|
+
const FONT_SIZE = { sm: 12, default: 14, lg: 16 };
|
|
41
|
+
/** The band color per marked line, and the sign a diff line carries in its gutter. */
|
|
42
|
+
const MARK = {
|
|
43
|
+
highlight: { color: '#3b82f6', sign: undefined },
|
|
44
|
+
added: { color: '#22c55e', sign: '+' },
|
|
45
|
+
removed: { color: '#ef4444', sign: '-' },
|
|
46
|
+
};
|
|
47
|
+
/** The color at 10% — its hex with an alpha byte, kept as a literal template type. */
|
|
48
|
+
const band = (hex) => `${hex}1a`;
|
|
49
|
+
const Frame = (0, gui_1.styled)(gui_1.YStack, {
|
|
50
|
+
name: 'CodeBlock',
|
|
51
|
+
position: 'relative',
|
|
52
|
+
width: '100%',
|
|
53
|
+
rounded: '$3',
|
|
54
|
+
borderWidth: 1,
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
});
|
|
57
|
+
const Header = (0, gui_1.styled)(gui_1.XStack, {
|
|
58
|
+
name: 'CodeBlockHeader',
|
|
59
|
+
items: 'center',
|
|
60
|
+
justify: 'space-between',
|
|
61
|
+
gap: '$2',
|
|
62
|
+
px: '$4',
|
|
63
|
+
py: '$2',
|
|
64
|
+
borderBottomWidth: 1,
|
|
65
|
+
});
|
|
66
|
+
const Line = (0, gui_1.styled)(gui_1.XStack, {
|
|
67
|
+
name: 'CodeBlockLine',
|
|
68
|
+
items: 'center',
|
|
69
|
+
minH: 24,
|
|
70
|
+
px: '$4',
|
|
71
|
+
py: '$0.5',
|
|
72
|
+
gap: '$2',
|
|
73
|
+
borderLeftWidth: 2,
|
|
74
|
+
borderLeftColor: 'transparent',
|
|
75
|
+
variants: {
|
|
76
|
+
marker: {
|
|
77
|
+
highlight: { bg: band(MARK.highlight.color), borderLeftColor: MARK.highlight.color },
|
|
78
|
+
added: { bg: band(MARK.added.color), borderLeftColor: MARK.added.color },
|
|
79
|
+
removed: { bg: band(MARK.removed.color), borderLeftColor: MARK.removed.color },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const Mono = (0, gui_1.styled)(gui_1.SizableText, {
|
|
84
|
+
name: 'CodeBlockText',
|
|
85
|
+
fontFamily: '$mono',
|
|
86
|
+
whiteSpace: 'pre',
|
|
87
|
+
});
|
|
88
|
+
/** `code` as numbered rows, each with the marker it carries: a diff side first, then a highlight. */
|
|
89
|
+
const rows = (code, highlightLines, diff) => code.split('\n').map((text, i) => {
|
|
90
|
+
const n = i + 1;
|
|
91
|
+
const marker = diff?.added?.includes(n)
|
|
92
|
+
? 'added'
|
|
93
|
+
: diff?.removed?.includes(n)
|
|
94
|
+
? 'removed'
|
|
95
|
+
: highlightLines.includes(n)
|
|
96
|
+
? 'highlight'
|
|
97
|
+
: undefined;
|
|
98
|
+
return { n, text, marker };
|
|
99
|
+
});
|
|
100
|
+
function CodeBlock({ code, language = 'text', filename, theme, size, showLineNumbers = true, highlightLines = [], showCopyButton = true, maxHeight = 400, diff, ...props }) {
|
|
101
|
+
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
102
|
+
const palette = PALETTE[theme ?? 'dark'];
|
|
103
|
+
const fontSize = FONT_SIZE[size ?? 'default'];
|
|
104
|
+
const lineHeight = fontSize * 1.5;
|
|
105
|
+
const label = copied ? 'Copied' : 'Copy code';
|
|
106
|
+
const copy = (0, react_1.useCallback)(async () => {
|
|
107
|
+
try {
|
|
108
|
+
await navigator.clipboard.writeText(code);
|
|
109
|
+
setCopied(true);
|
|
110
|
+
setTimeout(() => setCopied(false), 2000);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// No clipboard permission or no clipboard API — nothing else to do.
|
|
114
|
+
}
|
|
115
|
+
}, [code]);
|
|
116
|
+
return ((0, jsx_runtime_1.jsxs)(Frame, { ...(0, slot_1.slot)('code-block'), style: { backgroundColor: palette.bg, borderColor: palette.border }, ...props, children: [(filename || language || showCopyButton) && ((0, jsx_runtime_1.jsxs)(Header, { ...(0, slot_1.slot)('code-block-header'), style: { borderBottomColor: palette.border }, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", children: [filename && ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", children: [(0, jsx_runtime_1.jsx)(lucide_icons_2_1.FileText, { size: 14, color: palette.muted }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", fontWeight: "500", style: { color: palette.fg }, children: filename })] })), language && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { ...(0, slot_1.slot)('code-block-language'), variant: "secondary", children: language }))] }), showCopyButton && ((0, jsx_runtime_1.jsx)(button_1.Button, { variant: "ghost", size: "icon-sm", onClick: copy, "aria-label": label, title: label, children: copied ? ((0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 14, color: MARK.added.color })) : ((0, jsx_runtime_1.jsx)(lucide_icons_2_1.Copy, { size: 14, color: palette.muted })) }))] })), (0, jsx_runtime_1.jsx)(gui_1.YStack, { style: { maxHeight, overflow: 'auto' }, children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { py: "$2", minW: "100%", self: "flex-start", children: rows(code, highlightLines, diff).map(({ n, text, marker }) => {
|
|
117
|
+
const mark = marker && MARK[marker];
|
|
118
|
+
return ((0, jsx_runtime_1.jsxs)(Line, { marker: marker, "data-slot": "code-block-line", "data-line": n, children: [showLineNumbers && ((0, jsx_runtime_1.jsx)(Mono, { width: 32, text: "right", select: "none", fontSize: FONT_SIZE.sm, lineHeight: lineHeight, style: { color: palette.muted }, children: n })), mark?.sign && ((0, jsx_runtime_1.jsx)(Mono, { select: "none", fontSize: FONT_SIZE.sm, lineHeight: lineHeight, style: { color: mark.color }, children: mark.sign })), (0, jsx_runtime_1.jsx)(Mono, { fontSize: fontSize, lineHeight: lineHeight, style: { color: palette.fg }, children: text || ' ' })] }, n));
|
|
119
|
+
}) }) })] }));
|
|
120
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ComponentProps } from 'react';
|
|
2
|
+
export type CodeBlockTheme = 'dark' | 'light' | 'github' | 'github-dark' | 'vs-dark' | 'monokai' | 'dracula' | 'nord';
|
|
3
|
+
export type CodeBlockSize = 'sm' | 'default' | 'lg';
|
|
4
|
+
export interface CodeBlockDiff {
|
|
5
|
+
added?: number[];
|
|
6
|
+
removed?: number[];
|
|
7
|
+
}
|
|
8
|
+
export interface CodeBlockProps extends Omit<ComponentProps<'div'>, 'children'> {
|
|
9
|
+
code: string;
|
|
10
|
+
language?: string;
|
|
11
|
+
filename?: string;
|
|
12
|
+
theme?: CodeBlockTheme | null;
|
|
13
|
+
size?: CodeBlockSize | null;
|
|
14
|
+
showLineNumbers?: boolean;
|
|
15
|
+
highlightLines?: number[];
|
|
16
|
+
showCopyButton?: boolean;
|
|
17
|
+
/** The body's ceiling, in px or any CSS length; past it the body scrolls. */
|
|
18
|
+
maxHeight?: number | string;
|
|
19
|
+
diff?: CodeBlockDiff;
|
|
20
|
+
}
|
|
21
|
+
export declare function CodeBlock({ code, language, filename, theme, size, showLineNumbers, highlightLines, showCopyButton, maxHeight, diff, ...props }: CodeBlockProps): import("react").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=code-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/code-block.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,OAAO,CAAA;AAKlE,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,OAAO,GACP,QAAQ,GACR,aAAa,GACb,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,CAAA;AAEV,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAA;AAEnD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAC7B,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3B,IAAI,CAAC,EAAE,aAAa,CAAA;CACrB;AA2FD,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,QAAiB,EACjB,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,eAAsB,EACtB,cAAmB,EACnB,cAAqB,EACrB,SAAe,EACf,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,cAAc,+BA+FhB"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* CodeBlock — a bordered panel that shows source code line by line: a header
|
|
5
|
+
* naming the file and its language with a copy-to-clipboard button, a gutter
|
|
6
|
+
* of line numbers, and a colored band on any line a caller highlights or marks
|
|
7
|
+
* as added or removed in a diff.
|
|
8
|
+
*
|
|
9
|
+
* A line is plain monospace text, not colorized tokens: the workspace ships no
|
|
10
|
+
* highlighter. `theme` names a fixed syntax palette — a chosen surface,
|
|
11
|
+
* independent of the page's own light or dark theme — and `size` picks the
|
|
12
|
+
* type scale. A line is never wrapped, so the body scrolls on both axes once it
|
|
13
|
+
* outgrows `maxHeight` or the panel's width.
|
|
14
|
+
*/
|
|
15
|
+
import { SizableText, XStack, YStack, styled } from '@hanzo/gui';
|
|
16
|
+
import { Check, Copy, FileText } from '@hanzogui/lucide-icons-2';
|
|
17
|
+
import { useCallback, useState } from 'react';
|
|
18
|
+
import { Badge } from './badge.js';
|
|
19
|
+
import { Button } from './button.js';
|
|
20
|
+
import { slot } from './slot.js';
|
|
21
|
+
/**
|
|
22
|
+
* One fixed color quartet per theme. `as const satisfies` keeps each hex a
|
|
23
|
+
* literal type rather than widening it to `string` — gui's style props are
|
|
24
|
+
* typed against a hex-literal pattern, not against `string` itself, so a
|
|
25
|
+
* literal reads straight into `bg`/`borderColor`/`color` with no cast.
|
|
26
|
+
*/
|
|
27
|
+
const PALETTE = {
|
|
28
|
+
dark: { bg: '#020617', border: '#1e293b', fg: '#e2e8f0', muted: '#94a3b8' },
|
|
29
|
+
light: { bg: '#f8fafc', border: '#e2e8f0', fg: '#0f172a', muted: '#64748b' },
|
|
30
|
+
github: { bg: '#ffffff', border: '#e2e8f0', fg: '#24292e', muted: '#6e7781' },
|
|
31
|
+
'github-dark': { bg: '#0d1117', border: '#30363d', fg: '#c9d1d9', muted: '#8b949e' },
|
|
32
|
+
'vs-dark': { bg: '#1e1e1e', border: '#333333', fg: '#d4d4d4', muted: '#808080' },
|
|
33
|
+
monokai: { bg: '#272822', border: '#49483e', fg: '#f8f8f2', muted: '#75715e' },
|
|
34
|
+
dracula: { bg: '#282a36', border: '#44475a', fg: '#f8f8f2', muted: '#6272a4' },
|
|
35
|
+
nord: { bg: '#2e3440', border: '#4c566a', fg: '#d8dee9', muted: '#81a1c1' },
|
|
36
|
+
};
|
|
37
|
+
const FONT_SIZE = { sm: 12, default: 14, lg: 16 };
|
|
38
|
+
/** The band color per marked line, and the sign a diff line carries in its gutter. */
|
|
39
|
+
const MARK = {
|
|
40
|
+
highlight: { color: '#3b82f6', sign: undefined },
|
|
41
|
+
added: { color: '#22c55e', sign: '+' },
|
|
42
|
+
removed: { color: '#ef4444', sign: '-' },
|
|
43
|
+
};
|
|
44
|
+
/** The color at 10% — its hex with an alpha byte, kept as a literal template type. */
|
|
45
|
+
const band = (hex) => `${hex}1a`;
|
|
46
|
+
const Frame = styled(YStack, {
|
|
47
|
+
name: 'CodeBlock',
|
|
48
|
+
position: 'relative',
|
|
49
|
+
width: '100%',
|
|
50
|
+
rounded: '$3',
|
|
51
|
+
borderWidth: 1,
|
|
52
|
+
overflow: 'hidden',
|
|
53
|
+
});
|
|
54
|
+
const Header = styled(XStack, {
|
|
55
|
+
name: 'CodeBlockHeader',
|
|
56
|
+
items: 'center',
|
|
57
|
+
justify: 'space-between',
|
|
58
|
+
gap: '$2',
|
|
59
|
+
px: '$4',
|
|
60
|
+
py: '$2',
|
|
61
|
+
borderBottomWidth: 1,
|
|
62
|
+
});
|
|
63
|
+
const Line = styled(XStack, {
|
|
64
|
+
name: 'CodeBlockLine',
|
|
65
|
+
items: 'center',
|
|
66
|
+
minH: 24,
|
|
67
|
+
px: '$4',
|
|
68
|
+
py: '$0.5',
|
|
69
|
+
gap: '$2',
|
|
70
|
+
borderLeftWidth: 2,
|
|
71
|
+
borderLeftColor: 'transparent',
|
|
72
|
+
variants: {
|
|
73
|
+
marker: {
|
|
74
|
+
highlight: { bg: band(MARK.highlight.color), borderLeftColor: MARK.highlight.color },
|
|
75
|
+
added: { bg: band(MARK.added.color), borderLeftColor: MARK.added.color },
|
|
76
|
+
removed: { bg: band(MARK.removed.color), borderLeftColor: MARK.removed.color },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
const Mono = styled(SizableText, {
|
|
81
|
+
name: 'CodeBlockText',
|
|
82
|
+
fontFamily: '$mono',
|
|
83
|
+
whiteSpace: 'pre',
|
|
84
|
+
});
|
|
85
|
+
/** `code` as numbered rows, each with the marker it carries: a diff side first, then a highlight. */
|
|
86
|
+
const rows = (code, highlightLines, diff) => code.split('\n').map((text, i) => {
|
|
87
|
+
const n = i + 1;
|
|
88
|
+
const marker = diff?.added?.includes(n)
|
|
89
|
+
? 'added'
|
|
90
|
+
: diff?.removed?.includes(n)
|
|
91
|
+
? 'removed'
|
|
92
|
+
: highlightLines.includes(n)
|
|
93
|
+
? 'highlight'
|
|
94
|
+
: undefined;
|
|
95
|
+
return { n, text, marker };
|
|
96
|
+
});
|
|
97
|
+
export function CodeBlock({ code, language = 'text', filename, theme, size, showLineNumbers = true, highlightLines = [], showCopyButton = true, maxHeight = 400, diff, ...props }) {
|
|
98
|
+
const [copied, setCopied] = useState(false);
|
|
99
|
+
const palette = PALETTE[theme ?? 'dark'];
|
|
100
|
+
const fontSize = FONT_SIZE[size ?? 'default'];
|
|
101
|
+
const lineHeight = fontSize * 1.5;
|
|
102
|
+
const label = copied ? 'Copied' : 'Copy code';
|
|
103
|
+
const copy = useCallback(async () => {
|
|
104
|
+
try {
|
|
105
|
+
await navigator.clipboard.writeText(code);
|
|
106
|
+
setCopied(true);
|
|
107
|
+
setTimeout(() => setCopied(false), 2000);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// No clipboard permission or no clipboard API — nothing else to do.
|
|
111
|
+
}
|
|
112
|
+
}, [code]);
|
|
113
|
+
return (_jsxs(Frame, { ...slot('code-block'), style: { backgroundColor: palette.bg, borderColor: palette.border }, ...props, children: [(filename || language || showCopyButton) && (_jsxs(Header, { ...slot('code-block-header'), style: { borderBottomColor: palette.border }, children: [_jsxs(XStack, { items: "center", gap: "$2", children: [filename && (_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(FileText, { size: 14, color: palette.muted }), _jsx(SizableText, { size: "$2", fontWeight: "500", style: { color: palette.fg }, children: filename })] })), language && (_jsx(Badge, { ...slot('code-block-language'), variant: "secondary", children: language }))] }), showCopyButton && (_jsx(Button, { variant: "ghost", size: "icon-sm", onClick: copy, "aria-label": label, title: label, children: copied ? (_jsx(Check, { size: 14, color: MARK.added.color })) : (_jsx(Copy, { size: 14, color: palette.muted })) }))] })), _jsx(YStack, { style: { maxHeight, overflow: 'auto' }, children: _jsx(YStack, { py: "$2", minW: "100%", self: "flex-start", children: rows(code, highlightLines, diff).map(({ n, text, marker }) => {
|
|
114
|
+
const mark = marker && MARK[marker];
|
|
115
|
+
return (_jsxs(Line, { marker: marker, "data-slot": "code-block-line", "data-line": n, children: [showLineNumbers && (_jsx(Mono, { width: 32, text: "right", select: "none", fontSize: FONT_SIZE.sm, lineHeight: lineHeight, style: { color: palette.muted }, children: n })), mark?.sign && (_jsx(Mono, { select: "none", fontSize: FONT_SIZE.sm, lineHeight: lineHeight, style: { color: mark.color }, children: mark.sign })), _jsx(Mono, { fontSize: fontSize, lineHeight: lineHeight, style: { color: palette.fg }, children: text || ' ' })] }, n));
|
|
116
|
+
}) }) })] }));
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../../src/backends/gui/code-block.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.CodeEditor = CodeEditor;
|
|
38
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
|
+
/**
|
|
40
|
+
* CodeEditor — a bordered, monospace editing surface: a toolbar carrying a
|
|
41
|
+
* language menu and a copy-to-clipboard button, above a plain-text field with
|
|
42
|
+
* a gutter of line numbers that scrolls with it.
|
|
43
|
+
*
|
|
44
|
+
* The field is gui's `TextArea`, so what is edited is the text itself. Tab
|
|
45
|
+
* inserts two spaces and Escape releases focus; `wordWrap` is the field's own
|
|
46
|
+
* white-space; the gutter numbers lines of text, so a line that wraps keeps
|
|
47
|
+
* one number; there is no syntax colouring, and `minimap` is accepted and
|
|
48
|
+
* ignored because a plain field has no surface to draw one on.
|
|
49
|
+
*
|
|
50
|
+
* `theme` is the editor's own palette, independent of the surrounding gui
|
|
51
|
+
* theme the way a syntax theme is: `'light'` and `'dark'` paint a fixed set of
|
|
52
|
+
* literal colours, and `'auto'` (the default) takes the ambient `$background`,
|
|
53
|
+
* `$color` and `$borderColor` tokens, so it follows the host page.
|
|
54
|
+
*/
|
|
55
|
+
const gui_1 = require("@hanzo/gui");
|
|
56
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
57
|
+
const React = __importStar(require("react"));
|
|
58
|
+
const button_1 = require("./button.cjs");
|
|
59
|
+
const control_1 = require("./control.cjs");
|
|
60
|
+
const select_1 = require("./select.cjs");
|
|
61
|
+
const slot_1 = require("./slot.cjs");
|
|
62
|
+
const toaster_1 = require("./toaster.cjs");
|
|
63
|
+
/** The languages the menu offers when a caller does not narrow the list. */
|
|
64
|
+
const LANGUAGES = [
|
|
65
|
+
'javascript',
|
|
66
|
+
'typescript',
|
|
67
|
+
'python',
|
|
68
|
+
'java',
|
|
69
|
+
'csharp',
|
|
70
|
+
'cpp',
|
|
71
|
+
'c',
|
|
72
|
+
'go',
|
|
73
|
+
'rust',
|
|
74
|
+
'ruby',
|
|
75
|
+
'php',
|
|
76
|
+
'swift',
|
|
77
|
+
'kotlin',
|
|
78
|
+
'dart',
|
|
79
|
+
'html',
|
|
80
|
+
'css',
|
|
81
|
+
'scss',
|
|
82
|
+
'json',
|
|
83
|
+
'xml',
|
|
84
|
+
'yaml',
|
|
85
|
+
'sql',
|
|
86
|
+
'markdown',
|
|
87
|
+
'shell',
|
|
88
|
+
'plaintext',
|
|
89
|
+
];
|
|
90
|
+
const LABEL = {
|
|
91
|
+
javascript: 'JavaScript',
|
|
92
|
+
typescript: 'TypeScript',
|
|
93
|
+
python: 'Python',
|
|
94
|
+
java: 'Java',
|
|
95
|
+
csharp: 'C#',
|
|
96
|
+
cpp: 'C++',
|
|
97
|
+
c: 'C',
|
|
98
|
+
go: 'Go',
|
|
99
|
+
rust: 'Rust',
|
|
100
|
+
ruby: 'Ruby',
|
|
101
|
+
php: 'PHP',
|
|
102
|
+
swift: 'Swift',
|
|
103
|
+
kotlin: 'Kotlin',
|
|
104
|
+
dart: 'Dart',
|
|
105
|
+
html: 'HTML',
|
|
106
|
+
css: 'CSS',
|
|
107
|
+
scss: 'SCSS',
|
|
108
|
+
json: 'JSON',
|
|
109
|
+
xml: 'XML',
|
|
110
|
+
yaml: 'YAML',
|
|
111
|
+
sql: 'SQL',
|
|
112
|
+
markdown: 'Markdown',
|
|
113
|
+
shell: 'Shell',
|
|
114
|
+
plaintext: 'Plain Text',
|
|
115
|
+
};
|
|
116
|
+
/** The two named palettes; `'auto'` paints none of this. */
|
|
117
|
+
const PALETTE = {
|
|
118
|
+
light: { bg: '#ffffff', fg: '#1f2328', border: '#d0d7de', head: '#f6f8fa', gutter: '#8c959f' },
|
|
119
|
+
dark: { bg: '#1e1e1e', fg: '#d4d4d4', border: '#3c3c3c', head: '#252526', gutter: '#858585' },
|
|
120
|
+
};
|
|
121
|
+
/** What Tab inserts. */
|
|
122
|
+
const INDENT = ' ';
|
|
123
|
+
/** Line height as a multiple of the font size. */
|
|
124
|
+
const LEADING = 1.5;
|
|
125
|
+
function CodeEditor({ value, defaultValue = '', language = 'javascript', height = '400px', theme = 'auto', onChange, onMount, readOnly = false, lineNumbers = true, minimap: _minimap, wordWrap = 'on', fontSize = 14, showCopyButton = true, showLanguageSelector = true, availableLanguages = LANGUAGES, ...props }) {
|
|
126
|
+
const [typed, setTyped] = React.useState(defaultValue);
|
|
127
|
+
const text = value ?? typed;
|
|
128
|
+
const [selected, setSelected] = React.useState(language);
|
|
129
|
+
const [copied, setCopied] = React.useState(false);
|
|
130
|
+
const [scrollTop, setScrollTop] = React.useState(0);
|
|
131
|
+
const field = React.useRef(null);
|
|
132
|
+
const timer = React.useRef(undefined);
|
|
133
|
+
React.useEffect(() => {
|
|
134
|
+
if (field.current)
|
|
135
|
+
onMount?.(field.current);
|
|
136
|
+
return () => clearTimeout(timer.current);
|
|
137
|
+
}, []);
|
|
138
|
+
// Bound directly rather than through the gui field's `onScroll` prop: native
|
|
139
|
+
// `scroll` does not bubble, and a listener attached this way fires however
|
|
140
|
+
// the offset changed — a real drag or a test stating it outright.
|
|
141
|
+
React.useEffect(() => {
|
|
142
|
+
const el = field.current;
|
|
143
|
+
if (!el)
|
|
144
|
+
return;
|
|
145
|
+
const onScroll = () => setScrollTop(el.scrollTop);
|
|
146
|
+
el.addEventListener('scroll', onScroll);
|
|
147
|
+
return () => el.removeEventListener('scroll', onScroll);
|
|
148
|
+
}, []);
|
|
149
|
+
const change = React.useCallback((next) => {
|
|
150
|
+
setTyped(next);
|
|
151
|
+
onChange?.(next);
|
|
152
|
+
}, [onChange]);
|
|
153
|
+
const key = (e) => {
|
|
154
|
+
const el = e.currentTarget;
|
|
155
|
+
if (e.key === 'Escape')
|
|
156
|
+
el.blur();
|
|
157
|
+
if (e.key !== 'Tab' || e.shiftKey || readOnly)
|
|
158
|
+
return;
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
el.setRangeText(INDENT, el.selectionStart ?? 0, el.selectionEnd ?? 0, 'end');
|
|
161
|
+
change(el.value);
|
|
162
|
+
};
|
|
163
|
+
const copy = React.useCallback(async () => {
|
|
164
|
+
try {
|
|
165
|
+
await navigator.clipboard.writeText(text);
|
|
166
|
+
setCopied(true);
|
|
167
|
+
clearTimeout(timer.current);
|
|
168
|
+
timer.current = setTimeout(() => setCopied(false), 2000);
|
|
169
|
+
toaster_1.toast.success('Code copied to clipboard');
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
toaster_1.toast.error('Failed to copy code');
|
|
173
|
+
}
|
|
174
|
+
}, [text]);
|
|
175
|
+
const named = theme === 'auto' ? undefined : PALETTE[theme];
|
|
176
|
+
const lineHeight = `${fontSize * LEADING}px`;
|
|
177
|
+
const wrap = wordWrap !== 'off';
|
|
178
|
+
const gutter = Array.from({ length: text.split('\n').length }, (_, i) => i + 1).join('\n');
|
|
179
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('code-editor'), borderWidth: 1, borderColor: "$borderColor", rounded: "$3", overflow: "hidden", bg: "$background", style: named && { backgroundColor: named.bg, borderColor: named.border }, ...props, children: [(showLanguageSelector || showCopyButton) && ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('code-editor-toolbar'), items: "center", justify: showLanguageSelector ? 'space-between' : 'flex-end', borderBottomWidth: 1, borderColor: "$borderColor", bg: "$panel", px: "$3", py: "$2", style: named && { backgroundColor: named.head, borderColor: named.border }, children: [showLanguageSelector && ((0, jsx_runtime_1.jsxs)(select_1.Select, { value: selected, onValueChange: setSelected, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { ...(0, slot_1.slot)('code-editor-language-trigger'), width: "auto", minHeight: 32, borderWidth: 0, bg: "transparent", gap: "$1.5", px: "$2", children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", fontFamily: "$mono", style: named && { color: named.fg }, children: LABEL[selected] ?? selected }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { ...(0, slot_1.slot)('code-editor-language-content'), children: availableLanguages.map((lang) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: lang, children: LABEL[lang] ?? lang }, lang))) })] })), showCopyButton && ((0, jsx_runtime_1.jsxs)(button_1.Button, { ...(0, slot_1.slot)('code-editor-copy-button'), variant: "ghost", size: "sm", disabled: !text, onClick: copy, children: [copied ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 14 }) : (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Copy, { size: 14 }), copied ? 'Copied!' : 'Copy'] }))] })), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('code-editor-body'), overflow: "hidden", style: { height }, children: [lineNumbers && ((0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('code-editor-gutter'), width: 48, overflow: "hidden", borderRightWidth: 1, borderColor: "$borderColor", bg: "$panel", py: control_1.RISER, style: named && { backgroundColor: named.head, borderColor: named.border }, children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", fontFamily: "$mono", color: "$soft", px: "$2", whiteSpace: "pre", style: {
|
|
180
|
+
fontSize,
|
|
181
|
+
lineHeight,
|
|
182
|
+
textAlign: 'right',
|
|
183
|
+
transform: `translateY(${-scrollTop}px)`,
|
|
184
|
+
...(named && { color: named.gutter }),
|
|
185
|
+
}, children: gutter }) })), (0, jsx_runtime_1.jsx)(gui_1.TextArea, { ref: field, ...(0, slot_1.slot)('code-editor-textarea'), value: text, onChangeText: change, onKeyDown: key, readOnly: readOnly, unstyled: true, flex: 1, minH: 0, borderWidth: 0, rounded: 0, px: "$2", py: control_1.RISER, fontFamily: "$mono", color: "$color", bg: "transparent", style: {
|
|
186
|
+
fontSize,
|
|
187
|
+
lineHeight,
|
|
188
|
+
whiteSpace: wrap ? 'pre-wrap' : 'pre',
|
|
189
|
+
overflowWrap: wrap ? 'break-word' : 'normal',
|
|
190
|
+
overflow: 'auto',
|
|
191
|
+
resize: 'none',
|
|
192
|
+
height: '100%',
|
|
193
|
+
...(named && { color: named.fg }),
|
|
194
|
+
} })] })] }));
|
|
195
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeEditor — a bordered, monospace editing surface: a toolbar carrying a
|
|
3
|
+
* language menu and a copy-to-clipboard button, above a plain-text field with
|
|
4
|
+
* a gutter of line numbers that scrolls with it.
|
|
5
|
+
*
|
|
6
|
+
* The field is gui's `TextArea`, so what is edited is the text itself. Tab
|
|
7
|
+
* inserts two spaces and Escape releases focus; `wordWrap` is the field's own
|
|
8
|
+
* white-space; the gutter numbers lines of text, so a line that wraps keeps
|
|
9
|
+
* one number; there is no syntax colouring, and `minimap` is accepted and
|
|
10
|
+
* ignored because a plain field has no surface to draw one on.
|
|
11
|
+
*
|
|
12
|
+
* `theme` is the editor's own palette, independent of the surrounding gui
|
|
13
|
+
* theme the way a syntax theme is: `'light'` and `'dark'` paint a fixed set of
|
|
14
|
+
* literal colours, and `'auto'` (the default) takes the ambient `$background`,
|
|
15
|
+
* `$color` and `$borderColor` tokens, so it follows the host page.
|
|
16
|
+
*/
|
|
17
|
+
import { type YStackProps } from '@hanzo/gui';
|
|
18
|
+
import * as React from 'react';
|
|
19
|
+
export type CodeEditorTheme = 'light' | 'dark' | 'auto';
|
|
20
|
+
export type CodeEditorWordWrap = 'on' | 'off' | 'wordWrapColumn' | 'bounded';
|
|
21
|
+
export interface CodeEditorProps extends Omit<YStackProps, 'children' | 'height' | 'theme' | 'onChange'> {
|
|
22
|
+
/** A controlled value; omit it and set `defaultValue` to let the field own its text. */
|
|
23
|
+
value?: string;
|
|
24
|
+
defaultValue?: string;
|
|
25
|
+
language?: string;
|
|
26
|
+
/** The height of the field; the toolbar sits above it, unsized. */
|
|
27
|
+
height?: string | number;
|
|
28
|
+
theme?: CodeEditorTheme;
|
|
29
|
+
onChange?: (value: string) => void;
|
|
30
|
+
/** Called once, after mount, with the field's `<textarea>`. */
|
|
31
|
+
onMount?: (element: HTMLTextAreaElement) => void;
|
|
32
|
+
readOnly?: boolean;
|
|
33
|
+
lineNumbers?: boolean;
|
|
34
|
+
/** Accepted and ignored: a plain text field has no surface to draw a minimap on. */
|
|
35
|
+
minimap?: boolean;
|
|
36
|
+
wordWrap?: CodeEditorWordWrap;
|
|
37
|
+
fontSize?: number;
|
|
38
|
+
showCopyButton?: boolean;
|
|
39
|
+
showLanguageSelector?: boolean;
|
|
40
|
+
availableLanguages?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
export declare function CodeEditor({ value, defaultValue, language, height, theme, onChange, onMount, readOnly, lineNumbers, minimap: _minimap, wordWrap, fontSize, showCopyButton, showLanguageSelector, availableLanguages, ...props }: CodeEditorProps): React.JSX.Element;
|
|
43
|
+
//# sourceMappingURL=code-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-editor.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/code-editor.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AAEpF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AACvD,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,KAAK,GAAG,gBAAgB,GAAG,SAAS,CAAA;AAoE5E,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;IACtG,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACxB,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,YAAiB,EACjB,QAAuB,EACvB,MAAgB,EAChB,KAAc,EACd,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,WAAkB,EAClB,OAAO,EAAE,QAAQ,EACjB,QAAe,EACf,QAAa,EACb,cAAqB,EACrB,oBAA2B,EAC3B,kBAA8B,EAC9B,GAAG,KAAK,EACT,EAAE,eAAe,qBAqLjB"}
|