@labpane/ui 0.0.0-stage → 1.0.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../terminal.tsx"],
4
+ "sourcesContent": ["import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport { WTerm, type TerminalCore } from \"@wterm/dom\";\nimport { cn } from \"./classes.js\";\nimport {\n Maximize2Icon,\n Minimize2Icon,\n RotateCwIcon,\n XIcon,\n} from \"lucide-react\";\nimport { Button } from \"./primitives.js\";\n\nexport type TerminalTheme = {\n foreground: number;\n background: number;\n cursor: number;\n palette: number[];\n};\nexport type TerminalShell = {\n attach: (write: (data: string) => void) => unknown;\n handleInput: (data: string) => unknown;\n};\nexport type TerminalHandle = { reconnect: () => void; focus: () => void };\n\nexport const terminalThemes = {\n dark: {\n foreground: 0xe5e5e5,\n background: 0x262626,\n cursor: 0xe5e5e5,\n palette: [\n 0x1e1e1e, 0xf44747, 0x6a9955, 0xd7ba7d, 0x569cd6, 0xc586c0, 0x4ec9b0,\n 0xd4d4d4, 0x808080, 0xff6b6b, 0x8ec07c, 0xffd866, 0x82aaff, 0xd7a1d7,\n 0x7fdbca, 0xffffff,\n ],\n },\n light: {\n foreground: 0x262626,\n background: 0xffffff,\n cursor: 0x262626,\n palette: [\n 0x262626, 0xb42318, 0x237a36, 0x876000, 0x245ac0, 0x8b3fa0, 0x006e7a,\n 0xd4d4d4, 0x616161, 0xc43326, 0x287d3c, 0x8a6500, 0x2563c7, 0x9542aa,\n 0x007c8a, 0xffffff,\n ],\n },\n solarized: {\n foreground: 0x839496,\n background: 0x002b36,\n cursor: 0x93a1a1,\n palette: [\n 0x073642, 0xdc322f, 0x859900, 0xb58900, 0x268bd2, 0xd33682, 0x2aa198,\n 0xeee8d5, 0x586e75, 0xcb4b16, 0x586e75, 0x657b83, 0x839496, 0x6c71c4,\n 0x93a1a1, 0xfdf6e3,\n ],\n },\n monokai: {\n foreground: 0xf8f8f2,\n background: 0x272822,\n cursor: 0xf8f8f0,\n palette: [\n 0x272822, 0xf92672, 0xa6e22e, 0xf4bf75, 0x66d9ef, 0xae81ff, 0xa1efe4,\n 0xf8f8f2, 0x75715e, 0xf92672, 0xa6e22e, 0xf4bf75, 0x66d9ef, 0xae81ff,\n 0xa1efe4, 0xf9f8f5,\n ],\n },\n} satisfies Record<string, TerminalTheme>;\nexport type TerminalThemeName = \"auto\" | keyof typeof terminalThemes;\n\nexport type TerminalProps = (\n | {\n connect: () => string | URL | Promise<string | URL>;\n shell?: undefined;\n }\n | {\n shell: () => TerminalShell | Promise<TerminalShell>;\n connect?: undefined;\n }\n) & {\n core?: () => Promise<TerminalCore & { dispose?: () => void }>;\n theme?: TerminalThemeName | TerminalTheme;\n // expandable adds a button that fills the screen with the terminal\n expandable?: boolean;\n className?: string;\n};\n\nexport const Terminal = forwardRef<TerminalHandle, TerminalProps>(\n function Terminal(\n { connect, shell, core, theme = \"auto\", expandable, className },\n ref,\n ) {\n const reconnect = useRef(() => {});\n const terminal = useRef<WTerm | undefined>(undefined);\n const [status, setStatus] = useState(\"Connecting\u2026\");\n const [error, setError] = useState(\"\");\n const [full, setFull] = useState(false);\n const frame = useRef<HTMLDivElement>(null);\n const host = useRef<HTMLDivElement>(null);\n const chosen = useRef(theme);\n chosen.current = theme;\n const applyTheme = useRef(() => {});\n useImperativeHandle(ref, () => ({\n reconnect: () => reconnect.current(),\n focus: () => terminal.current?.focus(),\n }));\n\n useEffect(() => applyTheme.current(), [theme]);\n\n useLayoutEffect(() => {\n const element = frame.current;\n if (!full || !element || !(\"showPopover\" in element)) return;\n element.setAttribute(\"popover\", \"manual\");\n element.showPopover();\n return () => {\n if (element.matches(\":popover-open\")) element.hidePopover();\n element.removeAttribute(\"popover\");\n };\n }, [full]);\n\n useEffect(() => {\n let disposed = false,\n term: WTerm | undefined,\n loaded: (TerminalCore & { dispose?: () => void }) | undefined,\n ws: WebSocket | undefined,\n input: (text: string) => void = () => {},\n initialized: Promise<WTerm> | undefined,\n retryTimer: ReturnType<typeof setTimeout> | undefined,\n generation = 0,\n sessions = 0;\n const observers: { disconnect: () => void }[] = [];\n const disconnect = () => {\n clearTimeout(retryTimer);\n input = () => {};\n if (ws) {\n ws.onopen = ws.onmessage = ws.onclose = ws.onerror = null;\n ws.close();\n ws = undefined;\n }\n };\n const send = (bytes: Uint8Array) => {\n if (ws?.readyState !== WebSocket.OPEN) return;\n if (ws.bufferedAmount + bytes.length > 1024 * 1024) {\n setError(\"Terminal input buffer is full; input was not sent\");\n return;\n }\n for (let i = 0; i < bytes.length; i += 16384)\n ws.send(bytes.slice(i, i + 16384));\n };\n const resize = (cols: number, rows: number) => {\n if (ws?.readyState === WebSocket.OPEN)\n ws.send(JSON.stringify({ cols, rows }));\n };\n const updateTheme = () => {\n if (!host.current || !term) return;\n const name = chosen.current;\n term.setThemeColors(\n typeof name === \"object\"\n ? name\n : name !== \"auto\"\n ? terminalThemes[name]\n : getComputedStyle(host.current).colorScheme === \"dark\"\n ? terminalThemes.dark\n : terminalThemes.light,\n );\n };\n const open = async (canRetry = true) => {\n const attempt = ++generation;\n const stale = () => disposed || attempt !== generation;\n disconnect();\n setError(\"\");\n setStatus(canRetry ? \"Connecting\u2026\" : \"Reconnecting\u2026\");\n const failed = (message: string) => {\n if (stale()) return;\n if (canRetry) {\n setStatus(\"Reconnecting\u2026\");\n retryTimer = setTimeout(() => void open(false), 1000);\n } else {\n setStatus(\"Disconnected\");\n setError(message);\n }\n };\n try {\n if (shell) {\n const session = await shell();\n if (stale()) return;\n await (initialized ??= term!.init());\n if (stale()) return;\n if (sessions++) term!.write(\"\\x1bc\");\n input = (text) => void session.handleInput(text);\n await session.attach((data) => {\n if (!stale()) term!.write(data);\n });\n if (!stale()) setStatus(\"Connected\");\n return;\n }\n const target = await connect!();\n if (stale()) return;\n await (initialized ??= term!.init());\n if (stale()) return;\n const address = new URL(target, document.baseURI);\n address.protocol = address.protocol.replace(/^http/, \"ws\");\n ws = new WebSocket(address);\n ws.binaryType = \"arraybuffer\";\n input = (text) => send(new TextEncoder().encode(text));\n let connectionError = \"\";\n ws.onopen = () => {\n setStatus(\"Connected\");\n resize(term!.cols, term!.rows);\n };\n ws.onmessage = (event) => {\n if (typeof event.data === \"string\") connectionError = event.data;\n else {\n canRetry = true;\n term!.write(new Uint8Array(event.data));\n }\n };\n ws.onerror = () => {\n connectionError = \"Could not connect to the terminal\";\n };\n ws.onclose = (event) => {\n if (event.code === 1000 && !connectionError)\n setStatus(\"Disconnected\");\n else failed(connectionError || event.reason || \"Connection lost\");\n };\n } catch (err) {\n failed(err instanceof Error ? err.message : String(err));\n }\n };\n void (async () => {\n try {\n loaded = await core?.();\n } catch (err) {\n console.warn(\"Using the built-in terminal core:\", err);\n }\n if (disposed) return loaded?.dispose?.();\n term = new WTerm(host.current!, {\n core: loaded,\n onData: (text) => input(text),\n onBinary: send,\n onResize: resize,\n announceOutput: true,\n });\n terminal.current = term;\n const themeObserver = new MutationObserver(updateTheme);\n for (\n let parent = host.current!.parentElement;\n parent;\n parent = parent.parentElement\n )\n themeObserver.observe(parent, {\n attributes: true,\n attributeFilter: [\"class\", \"style\"],\n });\n const preferred = matchMedia(\"(prefers-color-scheme: dark)\");\n preferred.addEventListener(\"change\", updateTheme);\n const visible = new ResizeObserver(() =>\n term!.setRenderingPaused(!host.current?.clientHeight),\n );\n visible.observe(host.current!);\n observers.push(themeObserver, visible, {\n disconnect: () =>\n preferred.removeEventListener(\"change\", updateTheme),\n });\n applyTheme.current = updateTheme;\n updateTheme();\n reconnect.current = () => {\n void open();\n term!.focus();\n };\n void open();\n })();\n return () => {\n disposed = true;\n reconnect.current = applyTheme.current = () => {};\n terminal.current = undefined;\n disconnect();\n for (const observer of observers) observer.disconnect();\n term?.destroy();\n loaded?.dispose?.();\n };\n }, [connect, shell, core]);\n\n return (\n <div\n ref={frame}\n data-slot=\"terminal\"\n className={cn(\n \"labui:flex labui:h-full labui:min-h-0 labui:flex-col\",\n full &&\n \"labui:fixed labui:inset-0 labui:z-50 labui:m-0 labui:size-full labui:max-h-none labui:max-w-none labui:border-0 labui:bg-background labui:p-0 labui:text-foreground\",\n className,\n )}\n >\n <span role=\"status\" className=\"labui:sr-only\">\n {status}\n </span>\n {(status === \"Disconnected\" || error) && (\n <div\n key=\"banner\"\n role=\"alert\"\n className=\"labui:flex labui:shrink-0 labui:items-center labui:gap-2 labui:border-b labui:bg-muted labui:px-3 labui:py-1 labui:text-xs\"\n >\n <span className=\"labui:min-w-0 labui:flex-1\">\n {error || \"Terminal disconnected\"}\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={\n status === \"Connected\"\n ? \"Dismiss terminal message\"\n : \"Reconnect terminal\"\n }\n title={status === \"Connected\" ? \"Dismiss\" : \"Reconnect terminal\"}\n onClick={() =>\n status === \"Connected\" ? setError(\"\") : reconnect.current()\n }\n >\n {status === \"Connected\" ? (\n <XIcon aria-hidden=\"true\" />\n ) : (\n <RotateCwIcon aria-hidden=\"true\" />\n )}\n </Button>\n </div>\n )}\n <div\n key=\"terminal\"\n className=\"labui:group labui:relative labui:min-h-0 labui:flex-1\"\n >\n <div\n ref={host}\n aria-label=\"Terminal input\"\n className=\"labui:h-full\"\n style={{ borderRadius: 0, boxShadow: \"none\" }}\n />\n {expandable && (\n <Button\n variant=\"secondary\"\n size=\"icon-xs\"\n aria-label=\"Full screen\"\n aria-pressed={full}\n title={full ? \"Exit full screen\" : \"Full screen\"}\n className=\"labui:absolute labui:top-2 labui:right-2 labui:opacity-0 labui:transition-opacity labui:group-hover:opacity-100 labui:focus-visible:opacity-100\"\n onClick={() => {\n setFull(!full);\n requestAnimationFrame(() => terminal.current?.focus());\n }}\n >\n {full ? (\n <Minimize2Icon aria-hidden=\"true\" />\n ) : (\n <Maximize2Icon aria-hidden=\"true\" />\n )}\n </Button>\n )}\n </div>\n </div>\n );\n },\n);\n"],
5
+ "mappings": "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",
6
+ "names": ["Terminal"]
7
+ }
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from "react";
2
+ export declare function AppMenu({ name, logo, version, dark, onThemeChange, size, align, onSizeChange, onSizeReset, fullscreen, onFullscreenChange, }: {
3
+ name: string;
4
+ logo: ReactNode;
5
+ version?: ReactNode;
6
+ dark?: boolean;
7
+ onThemeChange?: (dark: boolean) => void;
8
+ size?: number;
9
+ onSizeChange?: (size: number) => void;
10
+ onSizeReset?: () => void;
11
+ fullscreen?: boolean;
12
+ onFullscreenChange?: (fullscreen: boolean) => void;
13
+ align?: "start" | "end";
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ declare const Dialog: React.FC<DialogPrimitive.DialogProps>;
4
+ declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
+ declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
6
+ declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
7
+ showCloseButton?: boolean;
8
+ } & React.RefAttributes<HTMLDivElement>>;
9
+ declare const DialogHeader: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
10
+ declare const DialogFooter: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
11
+ declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
12
+ declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
13
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, };
14
+ export declare function ConfirmDialog({ trigger, title, name, confirmLabel, typeToConfirm, onConfirm, }: {
15
+ trigger: React.ReactElement;
16
+ title: string;
17
+ name: string;
18
+ confirmLabel: string;
19
+ typeToConfirm?: boolean;
20
+ onConfirm: () => Promise<void>;
21
+ }): React.JSX.Element;
22
+ export declare function DiscardDialog({ label, onCancel, onConfirm, onCloseAutoFocus, }: {
23
+ label?: string;
24
+ onCancel: () => void;
25
+ onConfirm: () => void;
26
+ onCloseAutoFocus?: (event: Event) => void;
27
+ }): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ import { Input } from "./primitives.js";
3
+ type TextProps = {
4
+ label: string;
5
+ value: string;
6
+ onChange: (value: string) => void;
7
+ } & Omit<React.ComponentProps<typeof Input>, "value" | "onChange">;
8
+ export declare function TextField({ label, value, onChange, ...props }: TextProps): import("react").JSX.Element;
9
+ export declare function SelectField({ label, value, onChange, options, children, }: Pick<TextProps, "label" | "value" | "onChange"> & {
10
+ options: {
11
+ id: string;
12
+ name: string;
13
+ disabled?: boolean;
14
+ }[];
15
+ children?: ReactNode;
16
+ }): import("react").JSX.Element;
17
+ export declare function AddButton({ children, onClick, }: {
18
+ children: ReactNode;
19
+ onClick: () => void;
20
+ }): import("react").JSX.Element;
21
+ export {};
@@ -0,0 +1,6 @@
1
+ export * from "./primitives.js";
2
+ export * from "./dialog.js";
3
+ export * from "./menu.js";
4
+ export * from "./app-menu.js";
5
+ export * from "./form-fields.js";
6
+ export * from "./table.js";
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ export type MenuAction = {
3
+ label: string;
4
+ onSelect: () => void;
5
+ disabled?: boolean;
6
+ checked?: boolean;
7
+ } | {
8
+ label: string;
9
+ items: MenuAction[];
10
+ } | null;
11
+ export type MenuOption = {
12
+ id: string;
13
+ label: string;
14
+ group?: string;
15
+ icon?: React.ReactNode;
16
+ };
17
+ export declare function MenuContent({ children, className, }: {
18
+ children: React.ReactNode;
19
+ className?: string;
20
+ }): React.JSX.Element;
21
+ export declare function ActionMenu({ children, actions, onCloseAutoFocus, }: {
22
+ children: React.ReactElement;
23
+ actions: MenuAction[];
24
+ onCloseAutoFocus?: (event: Event) => void;
25
+ }): React.JSX.Element;
26
+ export declare function OptionMenu({ trigger, options, onSelect, open, onOpenChange, onCloseAutoFocus, }: {
27
+ trigger: React.ReactElement;
28
+ options: readonly MenuOption[];
29
+ onSelect: (id: string) => void;
30
+ open?: boolean;
31
+ onOpenChange?: (open: boolean) => void;
32
+ onCloseAutoFocus?: (event: Event) => void;
33
+ }): React.JSX.Element;
@@ -0,0 +1,85 @@
1
+ import * as React from "react";
2
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
3
+ import * as SelectPrimitive from "@radix-ui/react-select";
4
+ export { cn } from "./classes.js";
5
+ export declare function PageActions({ children }: {
6
+ children?: React.ReactNode;
7
+ }): React.JSX.Element;
8
+ export declare function PageHeader({ title, actions, }: {
9
+ title: string;
10
+ actions?: React.ReactNode;
11
+ }): React.JSX.Element;
12
+ export declare function Page({ title, actions, className, children, }: {
13
+ title?: string;
14
+ actions?: React.ReactNode;
15
+ className?: string;
16
+ children?: React.ReactNode;
17
+ }): React.JSX.Element;
18
+ declare const buttonVariants: {
19
+ default: string;
20
+ destructive: string;
21
+ secondary: string;
22
+ ghost: string;
23
+ link: string;
24
+ };
25
+ declare const buttonSizes: {
26
+ default: string;
27
+ sm: string;
28
+ "icon-xs": string;
29
+ "icon-sm": string;
30
+ };
31
+ declare const Button: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
32
+ variant?: keyof typeof buttonVariants | null;
33
+ size?: keyof typeof buttonSizes | null;
34
+ asChild?: boolean;
35
+ } & React.RefAttributes<HTMLButtonElement>>;
36
+ export { Button };
37
+ declare const badgeVariants: {
38
+ default: string;
39
+ destructive: string;
40
+ success: string;
41
+ warning: string;
42
+ };
43
+ declare const Badge: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
44
+ variant?: keyof typeof badgeVariants | null;
45
+ } & React.RefAttributes<HTMLSpanElement>>;
46
+ export { Badge };
47
+ declare const alertVariants: {
48
+ destructive: string;
49
+ warning: string;
50
+ };
51
+ declare const Alert: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
52
+ variant?: keyof typeof alertVariants | null;
53
+ } & React.RefAttributes<HTMLDivElement>>;
54
+ declare const AlertTitle: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
55
+ declare const AlertDescription: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
56
+ export { Alert, AlertTitle, AlertDescription };
57
+ declare const Input: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
58
+ export { Input };
59
+ declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
60
+ export { Checkbox };
61
+ declare const Field: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
62
+ orientation?: "horizontal";
63
+ } & React.RefAttributes<HTMLDivElement>>;
64
+ declare const FieldLabel: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "ref"> & React.RefAttributes<HTMLLabelElement>>;
65
+ declare const FieldError: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
66
+ export { Field, FieldLabel, FieldError };
67
+ declare function Select(props: React.ComponentProps<typeof SelectPrimitive.Root>): React.JSX.Element;
68
+ declare function SelectValue(props: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
69
+ declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & {
70
+ size?: "sm" | "default";
71
+ } & React.RefAttributes<HTMLButtonElement>>;
72
+ declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
73
+ declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
74
+ export { Select, SelectContent, SelectItem, SelectTrigger, SelectValue };
75
+ declare const Spinner: React.ForwardRefExoticComponent<Omit<React.SVGProps<SVGSVGElement>, "ref"> & React.RefAttributes<SVGSVGElement>>;
76
+ export { Spinner };
77
+ declare const Skeleton: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
78
+ export { Skeleton };
79
+ export declare function CopyButton({ value, label }: {
80
+ value: string;
81
+ label: string;
82
+ }): React.JSX.Element;
83
+ export declare function Back({ label, ...props }: React.ComponentProps<"a"> & {
84
+ label: string;
85
+ }): React.JSX.Element;
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { type ReactNode } from "react";
3
+ export type TableColumn<T> = {
4
+ id: string;
5
+ header: string;
6
+ headerDetail?: string;
7
+ rowHeader?: boolean;
8
+ numeric?: boolean;
9
+ cell: (row: T) => React.ReactNode;
10
+ sortValue: (row: T) => string | number | null | undefined;
11
+ };
12
+ type Sort = {
13
+ column: string;
14
+ direction: "asc" | "desc";
15
+ };
16
+ export declare function Table<T>({ label, columns, rows, rowKey, loading, empty, defaultSort, onRowActivate, rowLabel, }: {
17
+ label: string;
18
+ columns: TableColumn<T>[];
19
+ rows: readonly T[];
20
+ rowKey: (row: T, index: number) => string;
21
+ loading?: boolean;
22
+ empty?: React.ReactNode;
23
+ defaultSort?: Sort | null;
24
+ onRowActivate?: (row: T) => void;
25
+ rowLabel?: (row: T) => string;
26
+ }): React.JSX.Element;
27
+ export declare function column<T>(id: string, header: string, value: (row: T) => string | number | null | undefined, cell?: (row: T) => ReactNode): TableColumn<T>;
28
+ export declare const mono: <T>(id: string, header: string, value: (row: T) => unknown) => TableColumn<T>;
29
+ export declare const num: <T>(id: string, header: string, value: (row: T) => number, digits?: number, headerDetail?: string) => TableColumn<T>;
30
+ export {};
@@ -0,0 +1,57 @@
1
+ import { type TerminalCore } from "@wterm/dom";
2
+ export type TerminalTheme = {
3
+ foreground: number;
4
+ background: number;
5
+ cursor: number;
6
+ palette: number[];
7
+ };
8
+ export type TerminalShell = {
9
+ attach: (write: (data: string) => void) => unknown;
10
+ handleInput: (data: string) => unknown;
11
+ };
12
+ export type TerminalHandle = {
13
+ reconnect: () => void;
14
+ focus: () => void;
15
+ };
16
+ export declare const terminalThemes: {
17
+ dark: {
18
+ foreground: number;
19
+ background: number;
20
+ cursor: number;
21
+ palette: number[];
22
+ };
23
+ light: {
24
+ foreground: number;
25
+ background: number;
26
+ cursor: number;
27
+ palette: number[];
28
+ };
29
+ solarized: {
30
+ foreground: number;
31
+ background: number;
32
+ cursor: number;
33
+ palette: number[];
34
+ };
35
+ monokai: {
36
+ foreground: number;
37
+ background: number;
38
+ cursor: number;
39
+ palette: number[];
40
+ };
41
+ };
42
+ export type TerminalThemeName = "auto" | keyof typeof terminalThemes;
43
+ export type TerminalProps = ({
44
+ connect: () => string | URL | Promise<string | URL>;
45
+ shell?: undefined;
46
+ } | {
47
+ shell: () => TerminalShell | Promise<TerminalShell>;
48
+ connect?: undefined;
49
+ }) & {
50
+ core?: () => Promise<TerminalCore & {
51
+ dispose?: () => void;
52
+ }>;
53
+ theme?: TerminalThemeName | TerminalTheme;
54
+ expandable?: boolean;
55
+ className?: string;
56
+ };
57
+ export declare const Terminal: import("react").ForwardRefExoticComponent<TerminalProps & import("react").RefAttributes<TerminalHandle>>;
package/package.json CHANGED
@@ -1,6 +1,101 @@
1
1
  {
2
2
  "name": "@labpane/ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Page layouts, forms, dialogs, menus, tables, and an optional terminal for React",
5
+ "license": "MIT",
6
+ "homepage": "https://labpane.com",
7
+ "bugs": "https://github.com/labpane/labpane/issues",
8
+ "keywords": [
9
+ "react",
10
+ "radix",
11
+ "components",
12
+ "ui",
13
+ "tailwind",
14
+ "terminal"
15
+ ],
16
+ "type": "module",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/ui/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./styles.css": {
23
+ "types": "./dist/styles.d.ts",
24
+ "default": "./dist/styles.css"
25
+ },
26
+ "./tailwind.css": {
27
+ "types": "./dist/styles.d.ts",
28
+ "style": "./styles.css",
29
+ "default": "./styles.css"
30
+ },
31
+ "./terminal": {
32
+ "types": "./dist/ui/terminal.d.ts",
33
+ "default": "./dist/terminal.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "styles.css"
39
+ ],
40
+ "sideEffects": [
41
+ "**/*.css"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/labpane/labpane.git",
49
+ "directory": "packages/ui"
50
+ },
51
+ "scripts": {
52
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json && esbuild index.ts terminal.tsx --bundle --sourcemap --splitting --chunk-names=[name]-[hash] --format=esm --packages=external --outdir=dist && tailwindcss -i styles.css -o dist/styles.css --minify && node ../unlayer.mjs dist/styles.css && node ../package-assets.mjs",
53
+ "test": "bun test",
54
+ "prepack": "npm run build"
55
+ },
56
+ "dependencies": {
57
+ "clsx": "^2.1.1",
58
+ "lucide-react": "^1.55.0",
59
+ "tailwind-merge": "^3.6.0"
60
+ },
61
+ "peerDependencies": {
62
+ "@radix-ui/react-alert-dialog": "^1.1.15",
63
+ "@radix-ui/react-checkbox": "^1.3.3",
64
+ "@radix-ui/react-context-menu": "^2.2.16",
65
+ "@radix-ui/react-dialog": "^1.1.15",
66
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
67
+ "@radix-ui/react-popover": "^1.1.15",
68
+ "@radix-ui/react-select": "^2.2.6",
69
+ "@radix-ui/react-slot": "^1.2.3",
70
+ "@wterm/dom": "^0.5.4",
71
+ "react": "^18.2.0 || ^19.0.0",
72
+ "react-dom": "^18.2.0 || ^19.0.0"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "@wterm/dom": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "devDependencies": {
80
+ "@radix-ui/react-alert-dialog": "^1.1.15",
81
+ "@radix-ui/react-checkbox": "^1.3.3",
82
+ "@radix-ui/react-context-menu": "^2.2.16",
83
+ "@radix-ui/react-dialog": "^1.1.15",
84
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
85
+ "@radix-ui/react-popover": "^1.1.15",
86
+ "@radix-ui/react-select": "^2.2.6",
87
+ "@radix-ui/react-slot": "^1.2.3",
88
+ "@tailwindcss/cli": "^4.3.3",
89
+ "@types/bun": "^1.4.2",
90
+ "@types/react": "^19.3.0",
91
+ "@types/react-dom": "^19.3.0",
92
+ "@wterm/dom": "^0.5.4",
93
+ "esbuild": "^0.28.2",
94
+ "postcss": "^8.5.6",
95
+ "react": "^19.0.0",
96
+ "react-dom": "^19.0.0",
97
+ "tailwindcss": "^4.3.3",
98
+ "tailwindcss3": "npm:tailwindcss@^3.4.17",
99
+ "typescript": "^7.0.2"
100
+ }
101
+ }