@petrarca/sonnet-ui 0.7.0 → 0.9.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/README.md CHANGED
@@ -15,6 +15,8 @@ pagination, row actions and pluggable cell renderers. `SearchInput` produces a
15
15
  `FilterExpr` in simple, multi-field or faceted mode. `EntitySelect` is an async
16
16
  typeahead over an injected fetcher, `EntityTree` and `TreeView` cover
17
17
  hierarchies, and `PropertyList` covers read-only display.
18
+ `CodedValueDisplay` and `CodeableConceptDisplay` show a code with its label,
19
+ and a `coding` cell does the same in a table.
18
20
 
19
21
  **Date and time inputs.** Locale-aware date, time and datetime controls with a
20
22
  calendar, built on the parsing and formatting layer in `sonnet-core`.
@@ -0,0 +1,239 @@
1
+ // src/CodeEditor/languages.ts
2
+ import { LanguageDescription } from "@codemirror/language";
3
+ import { languages } from "@codemirror/language-data";
4
+ function findLanguage(name) {
5
+ return LanguageDescription.matchFilename(languages, name) ?? // Exact names and aliases only: a fuzzy match would read a word inside a
6
+ // file name ("readme", "notes") as a language.
7
+ LanguageDescription.matchLanguageName(languages, name, false);
8
+ }
9
+ async function loadLanguage(name) {
10
+ const description = findLanguage(name);
11
+ return description ? description.load() : null;
12
+ }
13
+
14
+ // src/CodeEditor/theme.ts
15
+ import { EditorView } from "@codemirror/view";
16
+ import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
17
+ import { tags as t } from "@lezer/highlight";
18
+ var surface = EditorView.theme({
19
+ "&": {
20
+ fontSize: "13px",
21
+ border: "1px solid hsl(var(--border))",
22
+ borderRadius: "6px",
23
+ backgroundColor: "hsl(var(--background))"
24
+ },
25
+ "&.cm-focused": { outline: "none" },
26
+ ".cm-content": {
27
+ fontFamily: "'JetBrains Mono', ui-monospace, SFMono-Regular, monospace"
28
+ },
29
+ ".cm-gutters": {
30
+ backgroundColor: "hsl(var(--muted) / 0.3)",
31
+ border: "none"
32
+ },
33
+ ".cm-highlighted-line": {
34
+ backgroundColor: "hsl(var(--state-selected))"
35
+ }
36
+ });
37
+ var syntax = HighlightStyle.define([
38
+ {
39
+ tag: [t.keyword, t.modifier, t.controlKeyword],
40
+ color: "var(--code-keyword)"
41
+ },
42
+ {
43
+ tag: [t.string, t.special(t.string), t.regexp],
44
+ color: "var(--code-string)"
45
+ },
46
+ { tag: [t.number, t.bool, t.null, t.atom], color: "var(--code-number)" },
47
+ {
48
+ tag: [t.comment, t.lineComment, t.blockComment],
49
+ color: "var(--code-comment)",
50
+ fontStyle: "italic"
51
+ },
52
+ {
53
+ tag: [t.function(t.variableName), t.function(t.propertyName)],
54
+ color: "var(--code-function)"
55
+ },
56
+ { tag: [t.typeName, t.className, t.namespace], color: "var(--code-type)" },
57
+ { tag: [t.propertyName, t.attributeName], color: "var(--code-property)" },
58
+ {
59
+ tag: [t.operator, t.punctuation, t.separator, t.bracket],
60
+ color: "var(--code-operator)"
61
+ },
62
+ {
63
+ tag: [t.meta, t.annotation, t.processingInstruction],
64
+ color: "var(--code-meta)"
65
+ },
66
+ { tag: t.heading, color: "var(--code-heading)", fontWeight: "600" },
67
+ {
68
+ tag: [t.link, t.url],
69
+ color: "var(--code-link)",
70
+ textDecoration: "underline"
71
+ },
72
+ { tag: t.emphasis, fontStyle: "italic" },
73
+ { tag: t.strong, fontWeight: "600" },
74
+ { tag: t.invalid, color: "var(--code-invalid)" }
75
+ ]);
76
+ var codeTheme = [surface, syntaxHighlighting(syntax)];
77
+
78
+ // src/CodeEditor/CodeEditor.tsx
79
+ import { useEffect, useMemo, useState } from "react";
80
+ import CodeMirror from "@uiw/react-codemirror";
81
+ import { Decoration, EditorView as EditorView2, lineNumbers } from "@codemirror/view";
82
+ import { cn } from "@petrarca/sonnet-core";
83
+ import { jsx } from "react/jsx-runtime";
84
+ var NO_EXTENSIONS = [];
85
+ function highlightLines(from, to) {
86
+ const mark = Decoration.line({ class: "cm-highlighted-line" });
87
+ return EditorView2.decorations.of((view) => {
88
+ const { doc } = view.state;
89
+ const ranges = [];
90
+ for (let n = Math.max(1, from); n <= Math.min(to, doc.lines); n++) {
91
+ ranges.push(mark.range(doc.line(n).from));
92
+ }
93
+ return Decoration.set(ranges);
94
+ });
95
+ }
96
+ function useLanguage(name) {
97
+ const [lang, setLang] = useState(null);
98
+ useEffect(() => {
99
+ let cancelled = false;
100
+ setLang(null);
101
+ if (name) {
102
+ void loadLanguage(name).then((ext) => {
103
+ if (!cancelled) setLang(ext);
104
+ });
105
+ }
106
+ return () => {
107
+ cancelled = true;
108
+ };
109
+ }, [name]);
110
+ return lang;
111
+ }
112
+ function buildExtensions(o, lines, lang) {
113
+ const optional = [
114
+ [
115
+ o.full,
116
+ () => lineNumbers({ formatNumber: (n) => String(n + o.firstLine - 1) })
117
+ ],
118
+ [o.wrap, () => EditorView2.lineWrapping],
119
+ [!!lines, () => highlightLines(lines.from, lines.to)],
120
+ [
121
+ !!o.ariaLabel,
122
+ () => EditorView2.contentAttributes.of({ "aria-label": o.ariaLabel })
123
+ ],
124
+ [!!lang, () => lang]
125
+ ];
126
+ return [
127
+ o.theme,
128
+ ...optional.flatMap(([on, make]) => on ? [make()] : []),
129
+ ...o.extensions
130
+ ];
131
+ }
132
+ function useExtensions({
133
+ theme,
134
+ full,
135
+ firstLine,
136
+ wrap,
137
+ highlight,
138
+ ariaLabel,
139
+ language,
140
+ extensions
141
+ }) {
142
+ const lang = useLanguage(language);
143
+ const lines = documentLines(highlight, firstLine);
144
+ const from = lines?.from;
145
+ const to = lines?.to;
146
+ return useMemo(
147
+ () => buildExtensions(
148
+ { theme, full, firstLine, wrap, ariaLabel, extensions },
149
+ from === void 0 || to === void 0 ? void 0 : { from, to },
150
+ lang
151
+ ),
152
+ [theme, full, firstLine, wrap, from, to, ariaLabel, lang, extensions]
153
+ );
154
+ }
155
+ function useScrollToLines(view, highlight, firstLine, value) {
156
+ const from = documentLines(highlight, firstLine)?.from;
157
+ useEffect(() => {
158
+ if (!view || from === void 0) return;
159
+ const { doc } = view.state;
160
+ const line = doc.line(Math.min(Math.max(1, from), doc.lines));
161
+ view.dispatch({
162
+ effects: EditorView2.scrollIntoView(line.from, { y: "center" })
163
+ });
164
+ }, [view, from, value]);
165
+ }
166
+ function basicSetupFor(readOnly, full) {
167
+ return {
168
+ // Numbers come from buildExtensions (they honour firstLine).
169
+ lineNumbers: false,
170
+ foldGutter: full,
171
+ highlightActiveLine: !readOnly,
172
+ highlightActiveLineGutter: !readOnly,
173
+ autocompletion: false,
174
+ closeBrackets: !readOnly,
175
+ indentOnInput: !readOnly,
176
+ bracketMatching: true,
177
+ searchKeymap: true
178
+ };
179
+ }
180
+ function documentLines(range, firstLine) {
181
+ return range && { from: range.from - firstLine + 1, to: range.to - firstLine + 1 };
182
+ }
183
+ function sizeFor(full, fillContainer, height, maxHeight) {
184
+ if (!full) return { maxHeight };
185
+ return fillContainer ? { height: "100%", style: { height: "100%" } } : { height };
186
+ }
187
+ function CodeEditor({
188
+ value,
189
+ onChange,
190
+ readOnly = false,
191
+ language,
192
+ variant = "full",
193
+ firstLine = 1,
194
+ highlight,
195
+ height = "400px",
196
+ fillContainer = false,
197
+ maxHeight = "16rem",
198
+ wrap = false,
199
+ theme = codeTheme,
200
+ extensions = NO_EXTENSIONS,
201
+ className,
202
+ "aria-label": ariaLabel
203
+ }) {
204
+ const full = variant === "full";
205
+ const [view, setView] = useState(null);
206
+ const allExtensions = useExtensions({
207
+ theme,
208
+ full,
209
+ firstLine,
210
+ wrap,
211
+ highlight,
212
+ ariaLabel,
213
+ language,
214
+ extensions
215
+ });
216
+ useScrollToLines(view, highlight, firstLine, value);
217
+ return /* @__PURE__ */ jsx(
218
+ CodeMirror,
219
+ {
220
+ value,
221
+ onChange,
222
+ readOnly,
223
+ theme: "none",
224
+ ...sizeFor(full, fillContainer, height, maxHeight),
225
+ className: cn("sonnet-code-editor", className),
226
+ extensions: allExtensions,
227
+ onCreateEditor: setView,
228
+ basicSetup: basicSetupFor(readOnly, full)
229
+ }
230
+ );
231
+ }
232
+
233
+ export {
234
+ findLanguage,
235
+ loadLanguage,
236
+ codeTheme,
237
+ CodeEditor
238
+ };
239
+ //# sourceMappingURL=chunk-JDD3LLTR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/CodeEditor/languages.ts","../src/CodeEditor/theme.ts","../src/CodeEditor/CodeEditor.tsx"],"sourcesContent":["import { LanguageDescription } from \"@codemirror/language\";\nimport { languages } from \"@codemirror/language-data\";\nimport type { Extension } from \"@codemirror/state\";\n\n/**\n * The language a name stands for -- a file name (\"app.py\", \"Dockerfile\") or a\n * language name or alias (\"python\", \"cypher\", \"ts\") -- from CodeMirror's\n * language list, which covers the modern parsers and the legacy modes. Each\n * language is a separate chunk, loaded only when a document needs it.\n */\nexport function findLanguage(name: string): LanguageDescription | null {\n return (\n LanguageDescription.matchFilename(languages, name) ??\n // Exact names and aliases only: a fuzzy match would read a word inside a\n // file name (\"readme\", \"notes\") as a language.\n LanguageDescription.matchLanguageName(languages, name, false)\n );\n}\n\n/** Load the language for `name`; null when none is known (plain text). */\nexport async function loadLanguage(name: string): Promise<Extension | null> {\n const description = findLanguage(name);\n return description ? description.load() : null;\n}\n","import { EditorView } from \"@codemirror/view\";\nimport { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { tags as t } from \"@lezer/highlight\";\nimport type { Extension } from \"@codemirror/state\";\n\n/**\n * The look of every CodeEditor (and the editors built on it), from the design\n * tokens in @petrarca/sonnet-ui/styles.css, so it follows light and dark mode.\n *\n * Surface tokens (--border, --muted, --background, --state-selected) hold bare\n * HSL triples and are wrapped in hsl(); the --code-* syntax colours are plain\n * colours. Consumers not using sonnet-ui tokens pass their own theme.\n */\nconst surface = EditorView.theme({\n \"&\": {\n fontSize: \"13px\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: \"6px\",\n backgroundColor: \"hsl(var(--background))\",\n },\n \"&.cm-focused\": { outline: \"none\" },\n \".cm-content\": {\n fontFamily: \"'JetBrains Mono', ui-monospace, SFMono-Regular, monospace\",\n },\n \".cm-gutters\": {\n backgroundColor: \"hsl(var(--muted) / 0.3)\",\n border: \"none\",\n },\n \".cm-highlighted-line\": {\n backgroundColor: \"hsl(var(--state-selected))\",\n },\n});\n\nconst syntax = HighlightStyle.define([\n {\n tag: [t.keyword, t.modifier, t.controlKeyword],\n color: \"var(--code-keyword)\",\n },\n {\n tag: [t.string, t.special(t.string), t.regexp],\n color: \"var(--code-string)\",\n },\n { tag: [t.number, t.bool, t.null, t.atom], color: \"var(--code-number)\" },\n {\n tag: [t.comment, t.lineComment, t.blockComment],\n color: \"var(--code-comment)\",\n fontStyle: \"italic\",\n },\n {\n tag: [t.function(t.variableName), t.function(t.propertyName)],\n color: \"var(--code-function)\",\n },\n { tag: [t.typeName, t.className, t.namespace], color: \"var(--code-type)\" },\n { tag: [t.propertyName, t.attributeName], color: \"var(--code-property)\" },\n {\n tag: [t.operator, t.punctuation, t.separator, t.bracket],\n color: \"var(--code-operator)\",\n },\n {\n tag: [t.meta, t.annotation, t.processingInstruction],\n color: \"var(--code-meta)\",\n },\n { tag: t.heading, color: \"var(--code-heading)\", fontWeight: \"600\" },\n {\n tag: [t.link, t.url],\n color: \"var(--code-link)\",\n textDecoration: \"underline\",\n },\n { tag: t.emphasis, fontStyle: \"italic\" },\n { tag: t.strong, fontWeight: \"600\" },\n { tag: t.invalid, color: \"var(--code-invalid)\" },\n]);\n\n/** The default CodeEditor theme: the surface and the syntax colours. */\nexport const codeTheme: Extension = [surface, syntaxHighlighting(syntax)];\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport type { Extension } from \"@codemirror/state\";\nimport { Decoration, EditorView, lineNumbers } from \"@codemirror/view\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { loadLanguage } from \"./languages\";\nimport { codeTheme } from \"./theme\";\n\n/** A range of lines, 1-based and inclusive, numbered as shown. */\nexport interface LineRange {\n from: number;\n to: number;\n}\n\nexport interface CodeEditorProps {\n /** The text shown. */\n value: string;\n /** Called with the new text as the user edits. */\n onChange?: (value: string) => void;\n /** Show and select, but not change. Search (Ctrl/Cmd-F) still works. */\n readOnly?: boolean;\n /**\n * The language, by file name (\"app.py\", \"Dockerfile\") or by name or alias\n * (\"python\", \"cypher\"). Loaded on demand; plain text when unknown.\n */\n language?: string;\n /**\n * \"full\" (default): a gutter with line numbers and folding, at `height` or\n * filling its container. \"compact\": no gutter, as tall as the text up to\n * `maxHeight` -- for snippets in a card or a message.\n */\n variant?: \"full\" | \"compact\";\n /** The number of the first line, when the text is part of a file. Default 1. */\n firstLine?: number;\n /** Lines to mark and scroll to, numbered as shown. */\n highlight?: LineRange;\n /** Height in \"full\". Default \"400px\". */\n height?: string;\n /** Fill the container's height (it must have one) and scroll inside. */\n fillContainer?: boolean;\n /** Height limit in \"compact\". Default \"16rem\". */\n maxHeight?: string;\n /** Wrap long lines instead of scrolling sideways. Default false. */\n wrap?: boolean;\n /** Replaces the default look (design tokens). */\n theme?: Extension;\n /** Further CodeMirror extensions -- for editors built on this one. */\n extensions?: Extension[];\n className?: string;\n \"aria-label\"?: string;\n}\n\n// One empty list, so a default does not change identity on every render.\nconst NO_EXTENSIONS: Extension[] = [];\n\n// Mark lines [from, to] of the document (1-based) as highlighted.\nfunction highlightLines(from: number, to: number): Extension {\n const mark = Decoration.line({ class: \"cm-highlighted-line\" });\n return EditorView.decorations.of((view) => {\n const { doc } = view.state;\n const ranges = [];\n for (let n = Math.max(1, from); n <= Math.min(to, doc.lines); n++) {\n ranges.push(mark.range(doc.line(n).from));\n }\n return Decoration.set(ranges);\n });\n}\n\n// The language for `name`, once loaded; null until then and when unknown.\nfunction useLanguage(name: string | undefined): Extension | null {\n const [lang, setLang] = useState<Extension | null>(null);\n useEffect(() => {\n let cancelled = false;\n setLang(null);\n if (name) {\n void loadLanguage(name).then((ext) => {\n if (!cancelled) setLang(ext);\n });\n }\n return () => {\n cancelled = true;\n };\n }, [name]);\n return lang;\n}\n\ninterface ExtensionOptions {\n theme: Extension;\n full: boolean;\n firstLine: number;\n wrap: boolean;\n /** The highlighted lines, numbered as shown. */\n highlight?: LineRange;\n ariaLabel?: string;\n language?: string;\n extensions: Extension[];\n}\n\n// The editor's extensions, each present only when its option asks for it.\nfunction buildExtensions(\n o: ExtensionOptions,\n lines: LineRange | undefined,\n lang: Extension | null,\n): Extension[] {\n const optional: [boolean, () => Extension][] = [\n [\n o.full,\n () => lineNumbers({ formatNumber: (n) => String(n + o.firstLine - 1) }),\n ],\n [o.wrap, () => EditorView.lineWrapping],\n [!!lines, () => highlightLines(lines!.from, lines!.to)],\n [\n !!o.ariaLabel,\n () => EditorView.contentAttributes.of({ \"aria-label\": o.ariaLabel! }),\n ],\n [!!lang, () => lang!],\n ];\n return [\n o.theme,\n ...optional.flatMap(([on, make]) => (on ? [make()] : [])),\n ...o.extensions,\n ];\n}\n\n// The extensions for the options, rebuilt only when one of them changes.\nfunction useExtensions({\n theme,\n full,\n firstLine,\n wrap,\n highlight,\n ariaLabel,\n language,\n extensions,\n}: ExtensionOptions): Extension[] {\n const lang = useLanguage(language);\n const lines = documentLines(highlight, firstLine);\n const from = lines?.from;\n const to = lines?.to;\n return useMemo(\n () =>\n buildExtensions(\n { theme, full, firstLine, wrap, ariaLabel, extensions },\n from === undefined || to === undefined ? undefined : { from, to },\n lang,\n ),\n [theme, full, firstLine, wrap, from, to, ariaLabel, lang, extensions],\n );\n}\n\n// Bring the highlighted lines to the middle of the editor's own scroller, when\n// the editor exists (it is created after the first render) and when the lines\n// or the text change.\nfunction useScrollToLines(\n view: EditorView | null,\n highlight: LineRange | undefined,\n firstLine: number,\n value: string,\n): void {\n const from = documentLines(highlight, firstLine)?.from;\n useEffect(() => {\n if (!view || from === undefined) return;\n const { doc } = view.state;\n const line = doc.line(Math.min(Math.max(1, from), doc.lines));\n view.dispatch({\n effects: EditorView.scrollIntoView(line.from, { y: \"center\" }),\n });\n }, [view, from, value]);\n}\n\n// CodeMirror's basic setup: viewing keeps search and bracket matching; the\n// editing aids come with editing.\nfunction basicSetupFor(readOnly: boolean, full: boolean) {\n return {\n // Numbers come from buildExtensions (they honour firstLine).\n lineNumbers: false,\n foldGutter: full,\n highlightActiveLine: !readOnly,\n highlightActiveLineGutter: !readOnly,\n autocompletion: false,\n closeBrackets: !readOnly,\n indentOnInput: !readOnly,\n bracketMatching: true,\n searchKeymap: true,\n };\n}\n\n// A range numbered as shown, as lines of the document (it may start mid-file).\nfunction documentLines(\n range: LineRange | undefined,\n firstLine: number,\n): LineRange | undefined {\n return (\n range && { from: range.from - firstLine + 1, to: range.to - firstLine + 1 }\n );\n}\n\n// The height props: a fixed or filling height in \"full\", a limit in \"compact\".\nfunction sizeFor(\n full: boolean,\n fillContainer: boolean,\n height: string,\n maxHeight: string,\n) {\n if (!full) return { maxHeight };\n // The wrapper takes the height too, so \"100%\" resolves inside it.\n return fillContainer\n ? { height: \"100%\", style: { height: \"100%\" } }\n : { height };\n}\n\n/**\n * CodeEditor -- CodeMirror with the sonnet look: a read-only code viewer, an\n * editor for text and Markdown, and the base JsonEditor is built on.\n *\n * The language loads on demand (by file name or language name). In \"full\" the\n * view draws only the lines on screen, so large files stay fast; a highlighted\n * range is marked and scrolled to the middle of the editor.\n *\n * Usage:\n * <CodeEditor value={source} language=\"app.py\" readOnly fillContainer\n * highlight={{ from: 120, to: 130 }} />\n * <CodeEditor value={query} language=\"cypher\" readOnly variant=\"compact\" />\n */\nexport function CodeEditor({\n value,\n onChange,\n readOnly = false,\n language,\n variant = \"full\",\n firstLine = 1,\n highlight,\n height = \"400px\",\n fillContainer = false,\n maxHeight = \"16rem\",\n wrap = false,\n theme = codeTheme,\n extensions = NO_EXTENSIONS,\n className,\n \"aria-label\": ariaLabel,\n}: CodeEditorProps): React.ReactElement {\n const full = variant === \"full\";\n const [view, setView] = useState<EditorView | null>(null);\n const allExtensions = useExtensions({\n theme,\n full,\n firstLine,\n wrap,\n highlight,\n ariaLabel,\n language,\n extensions,\n });\n useScrollToLines(view, highlight, firstLine, value);\n\n return (\n <CodeMirror\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n theme=\"none\"\n {...sizeFor(full, fillContainer, height, maxHeight)}\n className={cn(\"sonnet-code-editor\", className)}\n extensions={allExtensions}\n onCreateEditor={setView}\n basicSetup={basicSetupFor(readOnly, full)}\n />\n );\n}\n"],"mappings":"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","names":["EditorView","EditorView"]}
@@ -0,0 +1,177 @@
1
+ // src/button.tsx
2
+ import * as React from "react";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { cva } from "class-variance-authority";
5
+ import { cn } from "@petrarca/sonnet-core";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
15
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
16
+ ghost: "hover:bg-accent hover:text-accent-foreground",
17
+ link: "text-primary underline-offset-4 hover:underline"
18
+ },
19
+ size: {
20
+ default: "h-9 px-4 py-2",
21
+ sm: "h-8 rounded-md px-3 text-xs",
22
+ lg: "h-10 rounded-md px-8",
23
+ icon: "h-9 w-9",
24
+ compact: "h-7 w-7 p-0"
25
+ }
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default"
30
+ }
31
+ }
32
+ );
33
+ var Button = React.forwardRef(
34
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
35
+ const Comp = asChild ? Slot : "button";
36
+ return /* @__PURE__ */ jsx(
37
+ Comp,
38
+ {
39
+ className: cn(buttonVariants({ variant, size, className })),
40
+ ref,
41
+ ...props
42
+ }
43
+ );
44
+ }
45
+ );
46
+ Button.displayName = "Button";
47
+
48
+ // src/tooltip.tsx
49
+ import * as React2 from "react";
50
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
51
+ import { cn as cn2 } from "@petrarca/sonnet-core";
52
+ import { jsx as jsx2 } from "react/jsx-runtime";
53
+ var TooltipProvider = TooltipPrimitive.Provider;
54
+ var Tooltip = TooltipPrimitive.Root;
55
+ var TooltipTrigger = TooltipPrimitive.Trigger;
56
+ var TooltipContent = React2.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx2(
57
+ TooltipPrimitive.Content,
58
+ {
59
+ ref,
60
+ sideOffset,
61
+ className: cn2(
62
+ "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
63
+ className
64
+ ),
65
+ ...props
66
+ }
67
+ ));
68
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
69
+
70
+ // src/simple-tooltip.tsx
71
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
72
+ function SimpleTooltip({
73
+ label,
74
+ children,
75
+ side,
76
+ align,
77
+ delayDuration = 700,
78
+ sideOffset
79
+ }) {
80
+ return /* @__PURE__ */ jsx3(TooltipProvider, { delayDuration, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
81
+ /* @__PURE__ */ jsx3(TooltipTrigger, { asChild: true, children }),
82
+ /* @__PURE__ */ jsx3(TooltipContent, { side, align, sideOffset, children: label })
83
+ ] }) });
84
+ }
85
+
86
+ // src/icon-button.tsx
87
+ import * as React3 from "react";
88
+ import { cn as cn3 } from "@petrarca/sonnet-core";
89
+ import { jsx as jsx4 } from "react/jsx-runtime";
90
+ function revealClass(disabled) {
91
+ return disabled ? "disabled:opacity-30 group-hover/row:disabled:opacity-30" : "opacity-0 group-hover/row:opacity-100 focus:opacity-100";
92
+ }
93
+ var IconButton = React3.forwardRef(
94
+ function IconButton2({
95
+ label,
96
+ children,
97
+ tone = "default",
98
+ variant = "ghost",
99
+ tooltipSide,
100
+ revealOnHover = false,
101
+ className,
102
+ disabled,
103
+ ...props
104
+ }, ref) {
105
+ const toneClass = variant === "ghost" ? tone === "destructive" ? "text-muted-foreground hover:text-destructive" : "text-muted-foreground hover:text-foreground" : tone === "destructive" ? "hover:text-destructive" : void 0;
106
+ return /* @__PURE__ */ jsx4(SimpleTooltip, { label, side: tooltipSide, children: /* @__PURE__ */ jsx4(
107
+ Button,
108
+ {
109
+ ref,
110
+ type: "button",
111
+ size: "compact",
112
+ variant,
113
+ "aria-label": label,
114
+ disabled,
115
+ className: cn3(
116
+ toneClass,
117
+ revealOnHover && revealClass(disabled),
118
+ className
119
+ ),
120
+ ...props,
121
+ children
122
+ }
123
+ ) });
124
+ }
125
+ );
126
+
127
+ // src/copy-button.tsx
128
+ import { Check, Copy } from "lucide-react";
129
+ import { useClipboard } from "@petrarca/sonnet-core/hooks";
130
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
131
+ function CopyButton({
132
+ value,
133
+ label = "Copy",
134
+ withText = false,
135
+ className
136
+ }) {
137
+ const { copy, copied } = useClipboard();
138
+ const Icon = copied ? Check : Copy;
139
+ if (withText) {
140
+ return /* @__PURE__ */ jsxs2(
141
+ Button,
142
+ {
143
+ type: "button",
144
+ variant: "ghost",
145
+ size: "sm",
146
+ className,
147
+ onClick: () => copy(value),
148
+ children: [
149
+ /* @__PURE__ */ jsx5(Icon, {}),
150
+ copied ? "Copied" : label
151
+ ]
152
+ }
153
+ );
154
+ }
155
+ return /* @__PURE__ */ jsx5(
156
+ IconButton,
157
+ {
158
+ label: copied ? "Copied" : label,
159
+ className,
160
+ onClick: () => copy(value),
161
+ children: /* @__PURE__ */ jsx5(Icon, {})
162
+ }
163
+ );
164
+ }
165
+
166
+ export {
167
+ buttonVariants,
168
+ Button,
169
+ TooltipProvider,
170
+ Tooltip,
171
+ TooltipTrigger,
172
+ TooltipContent,
173
+ SimpleTooltip,
174
+ IconButton,
175
+ CopyButton
176
+ };
177
+ //# sourceMappingURL=chunk-ZQAHSGOS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/button.tsx","../src/tooltip.tsx","../src/simple-tooltip.tsx","../src/icon-button.tsx","../src/copy-button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@petrarca/sonnet-core\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n compact: \"h-7 w-7 p-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"@petrarca/sonnet-core\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ComponentRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]\",\n className,\n )}\n {...props}\n />\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import * as React from \"react\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"./tooltip\";\n\ninterface SimpleTooltipProps {\n label: React.ReactNode;\n children: React.ReactElement;\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n align?: \"start\" | \"center\" | \"end\";\n delayDuration?: number;\n sideOffset?: number;\n}\n\n/**\n * Simplified Tooltip wrapper that matches Mantine's simple API.\n * Usage: <SimpleTooltip label=\"Click me\"><Button>Click</Button></SimpleTooltip>\n */\nexport function SimpleTooltip({\n label,\n children,\n side,\n align,\n delayDuration = 700,\n sideOffset,\n}: SimpleTooltipProps) {\n return (\n <TooltipProvider delayDuration={delayDuration}>\n <Tooltip>\n <TooltipTrigger asChild>{children}</TooltipTrigger>\n <TooltipContent side={side} align={align} sideOffset={sideOffset}>\n {label}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n );\n}\n","import * as React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { Button, type ButtonProps } from \"./button\";\nimport { SimpleTooltip } from \"./simple-tooltip\";\n\n/**\n * IconButton -- a compact, square icon-only button with an accessible label\n * and a tooltip. The house standard for inline affordances (rename, delete,\n * edit) and small toolbar actions across the Sonnet apps.\n *\n * - `variant` defaults to \"ghost\" (borderless, for inline/hover-reveal rows).\n * Use \"outline\" for standalone toolbar buttons.\n * - `tone=\"destructive\"` tints the hover state red (e.g. delete).\n * - The `label` is both the tooltip text and the aria-label, so the button is\n * always accessible without visible text.\n * - Do not size the icon yourself: the underlying Button forces child SVGs to\n * `size-4` (16px), so pass a bare `<Icon />` (no h-/w- class).\n * - `revealOnHover` applies the house row-action convention: the button is\n * hidden at rest and revealed when the row is hovered or the button is\n * keyboard-focused; a disabled action stays faintly visible (opacity-30)\n * rather than hidden. The row must set the `group/row` class for this.\n *\n * Usage:\n * <IconButton label=\"Delete\" tone=\"destructive\" onClick={...}>\n * <Trash2 />\n * </IconButton>\n *\n * // row-action (parent row has className=\"... group/row\"):\n * <IconButton label=\"Edit\" revealOnHover onClick={...}><Pencil /></IconButton>\n */\nexport interface IconButtonProps\n extends Omit<ButtonProps, \"size\" | \"children\" | \"aria-label\"> {\n /** Tooltip text and accessible label. */\n label: string;\n /** The icon to render. */\n children: React.ReactNode;\n /** Hover tone. \"destructive\" tints hover red. Default \"default\". */\n tone?: \"default\" | \"destructive\";\n /** Tooltip side. */\n tooltipSide?: \"top\" | \"right\" | \"bottom\" | \"left\";\n /**\n * Row-action reveal: hidden at rest, shown on row hover (`group/row`) or\n * keyboard focus. Disabled buttons stay at opacity-30 instead of hidden, so\n * the affordance reads as \"present but unavailable\". Default false.\n */\n revealOnHover?: boolean;\n}\n\n// The house row-action reveal convention (matches EntityTable's RowActionCell):\n// hidden until the row is hovered or the button focused; a disabled action\n// stays faintly visible rather than hidden. The `disabled:` variant is used so\n// it overrides Button's base `disabled:opacity-50` (tailwind-merge resolves the\n// conflicting disabled-opacity utilities to this one).\nfunction revealClass(disabled: boolean | undefined): string {\n return disabled\n ? \"disabled:opacity-30 group-hover/row:disabled:opacity-30\"\n : \"opacity-0 group-hover/row:opacity-100 focus:opacity-100\";\n}\n\nexport const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(\n function IconButton(\n {\n label,\n children,\n tone = \"default\",\n variant = \"ghost\",\n tooltipSide,\n revealOnHover = false,\n className,\n disabled,\n ...props\n },\n ref,\n ) {\n const toneClass =\n variant === \"ghost\"\n ? tone === \"destructive\"\n ? \"text-muted-foreground hover:text-destructive\"\n : \"text-muted-foreground hover:text-foreground\"\n : tone === \"destructive\"\n ? \"hover:text-destructive\"\n : undefined;\n return (\n <SimpleTooltip label={label} side={tooltipSide}>\n <Button\n ref={ref}\n type=\"button\"\n size=\"compact\"\n variant={variant}\n aria-label={label}\n disabled={disabled}\n className={cn(\n toneClass,\n revealOnHover && revealClass(disabled),\n className,\n )}\n {...props}\n >\n {children}\n </Button>\n </SimpleTooltip>\n );\n },\n);\n","import * as React from \"react\";\nimport { Check, Copy } from \"lucide-react\";\nimport { useClipboard } from \"@petrarca/sonnet-core/hooks\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./icon-button\";\n\n/**\n * CopyButton -- copies a value to the clipboard and confirms it for a moment\n * (a check mark, \"Copied\"). Icon-only by default, as an IconButton whose label\n * is the tooltip and the accessible name; `withText` shows the label beside\n * the icon instead, for a standalone toolbar button.\n *\n * Usage:\n * <CopyButton value={path} label=\"Copy path\" />\n * <CopyButton value={json} withText />\n */\nexport interface CopyButtonProps {\n /** The text to copy. */\n value: string;\n /** What the button does. Default \"Copy\". */\n label?: string;\n /** Show the label as text beside the icon. Default false. */\n withText?: boolean;\n className?: string;\n}\n\nexport function CopyButton({\n value,\n label = \"Copy\",\n withText = false,\n className,\n}: CopyButtonProps): React.ReactElement {\n const { copy, copied } = useClipboard();\n const Icon = copied ? Check : Copy;\n if (withText) {\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={className}\n onClick={() => copy(value)}\n >\n <Icon />\n {copied ? \"Copied\" : label}\n </Button>\n );\n }\n return (\n <IconButton\n label={copied ? \"Copied\" : label}\n className={className}\n onClick={() => copy(value)}\n >\n <Icon />\n </IconButton>\n );\n}\n"],"mappings":";AAAA,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,WAA8B;AAEvC,SAAS,UAAU;AA0Cb;AAxCN,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,aACE;AAAA,QACF,SACE;AAAA,QACF,WACE;AAAA,QACF,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAQA,IAAM,SAAe;AAAA,EACnB,CAAC,EAAE,WAAW,SAAS,MAAM,UAAU,OAAO,GAAG,MAAM,GAAG,QAAQ;AAChE,UAAM,OAAO,UAAU,OAAO;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC;AAAA,QAC1D;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,OAAO,cAAc;;;ACpDrB,YAAYA,YAAW;AACvB,YAAY,sBAAsB;AAElC,SAAS,MAAAC,WAAU;AAYjB,gBAAAC,YAAA;AAVF,IAAM,kBAAmC;AAEzC,IAAM,UAA2B;AAEjC,IAAM,iBAAkC;AAExC,IAAM,iBAAuB,kBAG3B,CAAC,EAAE,WAAW,aAAa,GAAG,GAAG,MAAM,GAAG,QAC1C,gBAAAA;AAAA,EAAkB;AAAA,EAAjB;AAAA,IACC;AAAA,IACA;AAAA,IACA,WAAWD;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,eAAe,cAA+B,yBAAQ;;;ACIhD,SACE,OAAAE,MADF;AAVC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACF,GAAuB;AACrB,SACE,gBAAAA,KAAC,mBAAgB,eACf,+BAAC,WACC;AAAA,oBAAAA,KAAC,kBAAe,SAAO,MAAE,UAAS;AAAA,IAClC,gBAAAA,KAAC,kBAAe,MAAY,OAAc,YACvC,iBACH;AAAA,KACF,GACF;AAEJ;;;ACvCA,YAAYC,YAAW;AACvB,SAAS,MAAAC,WAAU;AAmFX,gBAAAC,YAAA;AA/BR,SAAS,YAAY,UAAuC;AAC1D,SAAO,WACH,4DACA;AACN;AAEO,IAAM,aAAmB;AAAA,EAC9B,SAASC,YACP;AAAA,IACE;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,KACA;AACA,UAAM,YACJ,YAAY,UACR,SAAS,gBACP,iDACA,gDACF,SAAS,gBACP,2BACA;AACR,WACE,gBAAAD,KAAC,iBAAc,OAAc,MAAM,aACjC,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,MAAK;AAAA,QACL;AAAA,QACA,cAAY;AAAA,QACZ;AAAA,QACA,WAAWE;AAAA,UACT;AAAA,UACA,iBAAiB,YAAY,QAAQ;AAAA,UACrC;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,IACH,GACF;AAAA,EAEJ;AACF;;;ACtGA,SAAS,OAAO,YAAY;AAC5B,SAAS,oBAAoB;AAkCvB,SAOE,OAAAC,MAPF,QAAAC,aAAA;AAVC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX;AACF,GAAwC;AACtC,QAAM,EAAE,MAAM,OAAO,IAAI,aAAa;AACtC,QAAM,OAAO,SAAS,QAAQ;AAC9B,MAAI,UAAU;AACZ,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAK;AAAA,QACL;AAAA,QACA,SAAS,MAAM,KAAK,KAAK;AAAA,QAEzB;AAAA,0BAAAD,KAAC,QAAK;AAAA,UACL,SAAS,WAAW;AAAA;AAAA;AAAA,IACvB;AAAA,EAEJ;AACA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,SAAS,WAAW;AAAA,MAC3B;AAAA,MACA,SAAS,MAAM,KAAK,KAAK;AAAA,MAEzB,0BAAAA,KAAC,QAAK;AAAA;AAAA,EACR;AAEJ;","names":["React","cn","jsx","jsx","React","cn","jsx","IconButton","cn","jsx","jsxs"]}
@@ -0,0 +1,76 @@
1
+ import React__default from 'react';
2
+ import { Extension } from '@codemirror/state';
3
+ import { LanguageDescription } from '@codemirror/language';
4
+
5
+ /** A range of lines, 1-based and inclusive, numbered as shown. */
6
+ interface LineRange {
7
+ from: number;
8
+ to: number;
9
+ }
10
+ interface CodeEditorProps {
11
+ /** The text shown. */
12
+ value: string;
13
+ /** Called with the new text as the user edits. */
14
+ onChange?: (value: string) => void;
15
+ /** Show and select, but not change. Search (Ctrl/Cmd-F) still works. */
16
+ readOnly?: boolean;
17
+ /**
18
+ * The language, by file name ("app.py", "Dockerfile") or by name or alias
19
+ * ("python", "cypher"). Loaded on demand; plain text when unknown.
20
+ */
21
+ language?: string;
22
+ /**
23
+ * "full" (default): a gutter with line numbers and folding, at `height` or
24
+ * filling its container. "compact": no gutter, as tall as the text up to
25
+ * `maxHeight` -- for snippets in a card or a message.
26
+ */
27
+ variant?: "full" | "compact";
28
+ /** The number of the first line, when the text is part of a file. Default 1. */
29
+ firstLine?: number;
30
+ /** Lines to mark and scroll to, numbered as shown. */
31
+ highlight?: LineRange;
32
+ /** Height in "full". Default "400px". */
33
+ height?: string;
34
+ /** Fill the container's height (it must have one) and scroll inside. */
35
+ fillContainer?: boolean;
36
+ /** Height limit in "compact". Default "16rem". */
37
+ maxHeight?: string;
38
+ /** Wrap long lines instead of scrolling sideways. Default false. */
39
+ wrap?: boolean;
40
+ /** Replaces the default look (design tokens). */
41
+ theme?: Extension;
42
+ /** Further CodeMirror extensions -- for editors built on this one. */
43
+ extensions?: Extension[];
44
+ className?: string;
45
+ "aria-label"?: string;
46
+ }
47
+ /**
48
+ * CodeEditor -- CodeMirror with the sonnet look: a read-only code viewer, an
49
+ * editor for text and Markdown, and the base JsonEditor is built on.
50
+ *
51
+ * The language loads on demand (by file name or language name). In "full" the
52
+ * view draws only the lines on screen, so large files stay fast; a highlighted
53
+ * range is marked and scrolled to the middle of the editor.
54
+ *
55
+ * Usage:
56
+ * <CodeEditor value={source} language="app.py" readOnly fillContainer
57
+ * highlight={{ from: 120, to: 130 }} />
58
+ * <CodeEditor value={query} language="cypher" readOnly variant="compact" />
59
+ */
60
+ declare function CodeEditor({ value, onChange, readOnly, language, variant, firstLine, highlight, height, fillContainer, maxHeight, wrap, theme, extensions, className, "aria-label": ariaLabel, }: CodeEditorProps): React__default.ReactElement;
61
+
62
+ /** The default CodeEditor theme: the surface and the syntax colours. */
63
+ declare const codeTheme: Extension;
64
+
65
+ /**
66
+ * The language a name stands for -- a file name ("app.py", "Dockerfile") or a
67
+ * language name or alias ("python", "cypher", "ts") -- from CodeMirror's
68
+ * language list, which covers the modern parsers and the legacy modes. Each
69
+ * language is a separate chunk, loaded only when a document needs it.
70
+ */
71
+ declare function findLanguage(name: string): LanguageDescription | null;
72
+ /** Load the language for `name`; null when none is known (plain text). */
73
+ declare function loadLanguage(name: string): Promise<Extension | null>;
74
+
75
+ export { CodeEditor, codeTheme, findLanguage, loadLanguage };
76
+ export type { CodeEditorProps, LineRange };
@@ -0,0 +1,13 @@
1
+ import {
2
+ CodeEditor,
3
+ codeTheme,
4
+ findLanguage,
5
+ loadLanguage
6
+ } from "../chunk-JDD3LLTR.js";
7
+ export {
8
+ CodeEditor,
9
+ codeTheme,
10
+ findLanguage,
11
+ loadLanguage
12
+ };
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}